Merge "Make avb_bindgen host supported"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 80d0807..f40da7e 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -21,7 +21,7 @@
   ],
   "postsubmit": [
     {
-      "name": "odsign_e2e_tests"
+      "name": "odsign_e2e_tests_full"
     }
   ],
   "imports": [
diff --git a/apkdmverity/TEST_MAPPING b/apkdmverity/TEST_MAPPING
index 1bbec76..997b3f9 100644
--- a/apkdmverity/TEST_MAPPING
+++ b/apkdmverity/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit" : [
+  "postsubmit" : [
     {
       "name" : "apkdmverity.test"
     }
diff --git a/authfs/src/file/remote_file.rs b/authfs/src/file/remote_file.rs
index 039285f..4c112bd 100644
--- a/authfs/src/file/remote_file.rs
+++ b/authfs/src/file/remote_file.rs
@@ -40,7 +40,6 @@
 }
 
 pub struct RemoteFileReader {
-    // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
     service: VirtFdService,
     file_fd: i32,
 }
@@ -81,7 +80,6 @@
 }
 
 pub struct RemoteMerkleTreeReader {
-    // This needs to be a Sync to be used in fuse::worker::start_message_loop.
     service: VirtFdService,
     file_fd: i32,
 }
@@ -108,7 +106,6 @@
 }
 
 pub struct RemoteFileEditor {
-    // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
     service: VirtFdService,
     file_fd: i32,
 }
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 511db68..beb6b30 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -184,10 +184,9 @@
 
 type DirHandleTable = BTreeMap<Handle, Arc<DirEntriesSnapshot>>;
 
-// AuthFS needs to be `Sync` to be accepted by fuse::worker::start_message_loop as a `FileSystem`.
+// AuthFS needs to be `Sync` to be used with the `fuse` crate.
 pub struct AuthFs {
-    /// Table for `Inode` to `InodeState` lookup. This needs to be `Sync` to be used in
-    /// `fuse::worker::start_message_loop`.
+    /// Table for `Inode` to `InodeState` lookup.
     inode_table: RwLock<BTreeMap<Inode, InodeState>>,
 
     /// The next available inode number.
diff --git a/authfs/src/fusefs/mount.rs b/authfs/src/fusefs/mount.rs
index 294c6b1..7f8bac1 100644
--- a/authfs/src/fusefs/mount.rs
+++ b/authfs/src/fusefs/mount.rs
@@ -16,16 +16,18 @@
 
 use fuse::mount::MountOption;
 use std::fs::OpenOptions;
+use std::num::NonZeroU8;
 use std::os::unix::io::AsRawFd;
 use std::path::Path;
 
 use super::AuthFs;
 
-/// Maximum bytes in the write transaction to the FUSE device. This limits the maximum buffer
-/// size in a read request (including FUSE protocol overhead) that the filesystem writes to.
+/// Maximum bytes (excluding the FUSE header) `AuthFs` will receive from the kernel for write
+/// operations by another process.
 pub const MAX_WRITE_BYTES: u32 = 65536;
 
-/// Maximum bytes in a read operation.
+/// Maximum bytes (excluding the FUSE header) `AuthFs` will receive from the kernel for read
+/// operations by another process.
 /// TODO(victorhsieh): This option is deprecated by FUSE. Figure out if we can remove this.
 const MAX_READ_BYTES: u32 = 65536;
 
@@ -34,6 +36,7 @@
     authfs: AuthFs,
     mountpoint: &Path,
     extra_options: &Option<String>,
+    threads: Option<NonZeroU8>,
 ) -> Result<(), fuse::Error> {
     let dev_fuse = OpenOptions::new()
         .read(true)
@@ -61,5 +64,10 @@
     )
     .expect("Failed to mount fuse");
 
-    fuse::worker::start_message_loop(dev_fuse, MAX_WRITE_BYTES, MAX_READ_BYTES, authfs)
+    let mut config = fuse::FuseConfig::new();
+    config.dev_fuse(dev_fuse).max_write(MAX_WRITE_BYTES).max_read(MAX_READ_BYTES);
+    if let Some(num) = threads {
+        config.num_threads(u8::from(num).into());
+    }
+    config.enter_message_loop(authfs)
 }
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index bdca5b4..60318e8 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -37,6 +37,7 @@
 use protobuf::Message;
 use std::convert::TryInto;
 use std::fs::File;
+use std::num::NonZeroU8;
 use std::path::{Path, PathBuf};
 use structopt::StructOpt;
 
@@ -67,6 +68,10 @@
     #[structopt(short = "o")]
     extra_options: Option<String>,
 
+    /// Number of threads to serve FUSE requests.
+    #[structopt(short = "j")]
+    thread_number: Option<NonZeroU8>,
+
     /// A read-only remote file with integrity check. Can be multiple.
     ///
     /// For example, `--remote-ro-file 5:sha256-1234abcd` tells the filesystem to associate the
@@ -312,7 +317,12 @@
     let mut authfs = AuthFs::new(RemoteFsStatsReader::new(service.clone()));
     prepare_root_dir_entries(service, &mut authfs, &args)?;
 
-    fusefs::mount_and_enter_message_loop(authfs, &args.mount_point, &args.extra_options)?;
+    fusefs::mount_and_enter_message_loop(
+        authfs,
+        &args.mount_point,
+        &args.extra_options,
+        args.thread_number,
+    )?;
     bail!("Unexpected exit after the handler loop")
 }
 
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index b265d5c..60f8fd4 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -77,6 +77,7 @@
         "linkerconfig",
         "servicemanager.microdroid",
         "tombstoned",
+        "tombstone_transmit.microdroid",
         "cgroups.json",
         "public.libraries.android.txt",
 
@@ -88,11 +89,9 @@
         "microdroid_property_contexts",
         "microdroid_service_contexts",
 
-        // TODO(b/195425111) these four should be added automatically
-        "android.hardware.security.secureclock-V1-ndk",
-        "android.hardware.security.sharedsecret-V1-ndk",
-        "libcrypto",
-        "liblzma",
+        // TODO(b/195425111) these should be added automatically
+        "libcrypto", // used by many (init_second_stage, microdroid_manager, toybox, etc)
+        "liblzma", // used by init_second_stage
     ] + microdroid_shell_and_utilities,
     multilib: {
         common: {
@@ -111,9 +110,6 @@
                 "authfs_service",
                 "microdroid_manager",
                 "zipfuse",
-
-                // TODO(b/184872979): Needed by authfs. Remove once the Rust API is created.
-                "libbinder_rpc_unstable",
             ],
         },
     },
@@ -532,7 +528,7 @@
 genrule {
     name: "microdroid_uboot_env_gen",
     tools: [
-        "mkenvimage_host",
+        "mkenvimage_slim",
         "avbtool",
     ],
     srcs: [
@@ -540,7 +536,7 @@
         ":microdroid_sign_key",
     ],
     out: ["output.img"],
-    cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(location uboot-env.txt) && " +
+    cmd: "$(location mkenvimage_slim) -output_path $(out) -input_path $(location uboot-env.txt) && " +
         "$(location avbtool) add_hash_footer " +
         "--algorithm SHA256_RSA4096 " +
         "--partition_name uboot_env " +
@@ -612,4 +608,4 @@
     src: "microdroid_event-log-tags",
     filename: "event-log-tags",
     installable: false,
-}
\ No newline at end of file
+}
diff --git a/microdroid/bootconfig.common b/microdroid/bootconfig.common
index eda95a2..c202ba0 100644
--- a/microdroid/bootconfig.common
+++ b/microdroid/bootconfig.common
@@ -1,2 +1,6 @@
 androidboot.first_stage_console = 1
 androidboot.hardware = microdroid
+
+# tombstone_transmit is enabled. Tombstones will be transmitted to the host
+# TODO(b/227443903) : Make this configurable by VM owners
+androidboot.tombstone_transmit.enabled=1
diff --git a/microdroid/init.rc b/microdroid/init.rc
index f6d5092..dbe5ac7 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -173,6 +173,13 @@
     mkdir /data/local 0751 root root
     mkdir /data/local/tmp 0771 shell shell
 
+on init && property:ro.boot.tombstone_transmit.enabled=1
+     start tombstone_transmit
+
+service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000
+    user root
+    group system
+
 service apexd-vm /system/bin/apexd --vm
     user root
     group system
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
new file mode 100644
index 0000000..66b4c9e
--- /dev/null
+++ b/pvmfw/Android.bp
@@ -0,0 +1,22 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_ffi_static {
+    name: "libpvmfw",
+    crate_name: "pvmfw",
+    srcs: ["src/main.rs"],
+    edition: "2021",
+    no_stdlibs: true,
+    stdlibs: [
+        "libcompiler_builtins.rust_sysroot",
+        "libcore.rust_sysroot",
+    ],
+    enabled: false,
+    target: {
+        android_arm64: {
+            enabled: true,
+        },
+    },
+    apex_available: ["com.android.virt"],
+}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
new file mode 100644
index 0000000..0a359f6
--- /dev/null
+++ b/pvmfw/src/main.rs
@@ -0,0 +1,37 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! pVM firmware.
+
+#![no_main]
+#![no_std]
+
+mod psci;
+
+use core::panic::PanicInfo;
+use psci::{system_off, system_reset};
+
+/// Entry point for pVM firmware.
+#[no_mangle]
+pub extern "C" fn main() -> ! {
+    system_off();
+    #[allow(clippy::empty_loop)]
+    loop {}
+}
+
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+    system_reset();
+    loop {}
+}
diff --git a/pvmfw/src/psci.rs b/pvmfw/src/psci.rs
new file mode 100644
index 0000000..8dcbcaa
--- /dev/null
+++ b/pvmfw/src/psci.rs
@@ -0,0 +1,69 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! PSCI calls.
+
+const PSCI_SYSTEM_OFF: u32 = 0x84000008;
+const PSCI_SYSTEM_RESET: u32 = 0x84000009;
+const PSCI_SYSTEM_RESET2: u32 = 0x84000012;
+
+pub fn system_off() -> u32 {
+    hvc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0, 0)[0]
+}
+
+pub fn system_reset() -> u32 {
+    hvc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0, 0)[0]
+}
+
+#[allow(unused)]
+pub fn system_reset2(reset_type: u32, cookie: u32) -> u32 {
+    hvc32(PSCI_SYSTEM_RESET2, reset_type, cookie, 0, 0, 0, 0, 0)[0]
+}
+
+/// Make an HVC32 call to the hypervisor, following the SMC Calling Convention version 1.3.
+#[inline(always)]
+#[allow(clippy::too_many_arguments)]
+fn hvc32(
+    function: u32,
+    arg1: u32,
+    arg2: u32,
+    arg3: u32,
+    arg4: u32,
+    arg5: u32,
+    arg6: u32,
+    arg7: u32,
+) -> [u32; 8] {
+    let mut ret = [0; 8];
+
+    #[cfg(target_arch = "aarch64")]
+    unsafe {
+        core::arch::asm!(
+            "hvc #0",
+            inout("w0") function => ret[0],
+            inout("w1") arg1 => ret[1],
+            inout("w2") arg2 => ret[2],
+            inout("w3") arg3 => ret[3],
+            inout("w4") arg4 => ret[4],
+            inout("w5") arg5 => ret[5],
+            inout("w6") arg6 => ret[6],
+            inout("w7") arg7 => ret[7],
+            options(nomem, nostack)
+        )
+    }
+
+    #[cfg(not(target_arch = "aarch64"))]
+    unimplemented!();
+
+    ret
+}
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index d5c5d62..440ae18 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -105,7 +105,7 @@
     }
 
     // Run an arbitrary command in the host side and returns the result
-    private static String runOnHost(String... cmd) {
+    public static String runOnHost(String... cmd) {
         return runOnHostWithTimeout(10000, cmd);
     }
 
@@ -133,6 +133,19 @@
         return result.getStdout().trim();
     }
 
+    // Same as runOnMicrodroid, but keeps retrying on error till timeout
+    private static String runOnMicrodroidRetryingOnFailure(String... cmd) {
+        final long timeoutMs = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
+        int attempts = (int) MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000 / 500;
+        CommandResult result = RunUtil.getDefault()
+                .runTimedCmdRetry(timeoutMs, 500, attempts,
+                        "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
+        if (result.getStatus() != CommandStatus.SUCCESS) {
+            fail(join(cmd) + " has failed: " + result);
+        }
+        return result.getStdout().trim();
+    }
+
     // Same as runOnMicrodroid, but returns null on error.
     public static String tryRunOnMicrodroid(String... cmd) {
         CommandResult result = runOnMicrodroidForResult(cmd);
@@ -332,13 +345,16 @@
 
     public static void rootMicrodroid() {
         runOnHost("adb", "-s", MICRODROID_SERIAL, "root");
-
         runOnHostWithTimeout(
                 MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000,
                 "adb",
                 "-s",
                 MICRODROID_SERIAL,
                 "wait-for-device");
+        // There have been tests when adb wait-for-device succeeded but the following command
+        // fails with error: closed. Hence, we run adb shell true in microdroid with retries
+        // before returning.
+        runOnMicrodroidRetryingOnFailure("true");
     }
 
     // Establish an adb connection to microdroid by letting Android forward the connection to
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 5b71eba..7944245 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -22,6 +22,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -100,6 +101,13 @@
                 false);
     }
 
+    // Wait until logd-init starts. The service is one of the last services that are started in
+    // the microdroid boot procedure. Therefore, waiting for the service means that we wait for
+    // the boot to complete. TODO: we need a better marker eventually.
+    private void waitForLogdInit() {
+        tryRunOnMicrodroid("watch -e \"getprop init.svc.logd-reinit | grep '^$'\"");
+    }
+
     @Test
     public void testCreateVmRequiresPermission() throws Exception {
         // Revoke the MANAGE_VIRTUAL_MACHINE permission for the test app
@@ -350,6 +358,41 @@
     }
 
     @Test
+    public void testTombstonesAreBeingForwarded() throws Exception {
+        // Note this test relies on logcat values being printed by tombstone_transmit on
+        // and the reeceiver on host (virtualization_service)
+        final String configPath = "assets/vm_config.json"; // path inside the APK
+        final String cid =
+                startMicrodroid(
+                        getDevice(),
+                        getBuild(),
+                        APK_NAME,
+                        PACKAGE_NAME,
+                        configPath,
+                        /* debug */ true,
+                        minMemorySize(),
+                        Optional.of(NUM_VCPUS),
+                        Optional.of(CPU_AFFINITY));
+        adbConnectToMicrodroid(getDevice(), cid);
+        waitForLogdInit();
+        runOnMicrodroid("logcat -c");
+        // We need root permission to write to /data/tombstones/
+        rootMicrodroid();
+        // Write a test tombstone file in /data/tombstones
+        runOnMicrodroid("echo -n \'Test tombstone in VM with 34 bytes\'"
+                    + "> /data/tombstones/transmit.txt");
+        // check if the tombstone have been tranferred from VM
+        assertNotEquals(runOnMicrodroid("timeout 15s logcat | grep -m 1 "
+                            + "'tombstone_transmit.microdroid:.*data/tombstones/transmit.txt'"),
+                "");
+        // Confirm that tombstone is received (from host logcat)
+        assertNotEquals(runOnHost("adb", "-s", getDevice().getSerialNumber(),
+                            "logcat", "-d", "-e",
+                            "Received 34 bytes from guest & wrote to tombstone file.*"),
+                "");
+    }
+
+    @Test
     public void testMicrodroidBoots() throws Exception {
         final String configPath = "assets/vm_config.json"; // path inside the APK
         final String cid =
@@ -364,12 +407,7 @@
                         Optional.of(NUM_VCPUS),
                         Optional.of(CPU_AFFINITY));
         adbConnectToMicrodroid(getDevice(), cid);
-
-        // Wait until logd-init starts. The service is one of the last services that are started in
-        // the microdroid boot procedure. Therefore, waiting for the service means that we wait for
-        // the boot to complete. TODO: we need a better marker eventually.
-        tryRunOnMicrodroid("watch -e \"getprop init.svc.logd-reinit | grep '^$'\"");
-
+        waitForLogdInit();
         // Test writing to /data partition
         runOnMicrodroid("echo MicrodroidTest > /data/local/tmp/test.txt");
         assertThat(runOnMicrodroid("cat /data/local/tmp/test.txt"), is("MicrodroidTest"));
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 9b2b740..7a8da96 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -45,6 +45,7 @@
         "libserde_xml_rs",
         "libshared_child",
         "libstatslog_virtualization_rust",
+        "libtombstoned_client_rust",
         "libvmconfig",
         "libzip",
         "libvsock",
diff --git a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
index 1a16f2a..dff5d46 100644
--- a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
@@ -30,6 +30,12 @@
     const int VM_BINDER_SERVICE_PORT = 5000;
 
     /**
+     * Port number that VirtualMachineService listens on connections from the guest VMs for the
+     * tombtones
+     */
+    const int VM_TOMBSTONES_SERVICE_PORT = 2000;
+
+    /**
      * Notifies that the payload has started.
      */
     void notifyPayloadStarted();
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index d9825dc..73cbcfa 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -42,7 +42,7 @@
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::{
     IVirtualMachineService::{
         BnVirtualMachineService, IVirtualMachineService, VM_BINDER_SERVICE_PORT,
-        VM_STREAM_SERVICE_PORT,
+        VM_STREAM_SERVICE_PORT, VM_TOMBSTONES_SERVICE_PORT,
     },
 };
 use anyhow::{anyhow, bail, Context, Result};
@@ -57,13 +57,14 @@
 use std::convert::TryInto;
 use std::ffi::CStr;
 use std::fs::{create_dir, File, OpenOptions};
-use std::io::{Error, ErrorKind, Write};
+use std::io::{Error, ErrorKind, Write, Read};
 use std::num::NonZeroU32;
 use std::os::raw;
 use std::os::unix::io::{FromRawFd, IntoRawFd};
 use std::path::{Path, PathBuf};
 use std::ptr::null_mut;
 use std::sync::{Arc, Mutex, Weak};
+use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
 use vmconfig::VmConfig;
 use vsock::{SockAddr, VsockListener, VsockStream};
 use zip::ZipArchive;
@@ -86,6 +87,8 @@
 /// Version of the instance image format
 const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
 
+const CHUNK_RECV_MAX_LEN: usize = 1024;
+
 /// Implementation of `IVirtualizationService`, the entry point of the AIDL service.
 #[derive(Debug, Default)]
 pub struct VirtualizationService {
@@ -371,6 +374,55 @@
     }
 }
 
+fn handle_stream_connection_tombstoned() -> Result<()> {
+    let listener =
+        VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as u32)?;
+    info!("Listening to tombstones from guests ...");
+    for incoming_stream in listener.incoming() {
+        let mut incoming_stream = match incoming_stream {
+            Err(e) => {
+                warn!("invalid incoming connection: {}", e);
+                continue;
+            }
+            Ok(s) => s,
+        };
+        std::thread::spawn(move || {
+            if let Err(e) = handle_tombstone(&mut incoming_stream) {
+                error!("Failed to write tombstone- {:?}", e);
+            }
+        });
+    }
+    Ok(())
+}
+
+fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
+    if let Ok(SockAddr::Vsock(addr)) = stream.peer_addr() {
+        info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
+    }
+    let tb_connection =
+        TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
+            .context("Failed to connect to tombstoned")?;
+    let mut text_output = tb_connection
+        .text_output
+        .as_ref()
+        .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
+    let mut num_bytes_read = 0;
+    loop {
+        let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
+        let n = stream
+            .read(&mut chunk_recv)
+            .context("Failed to read tombstone data from Vsock stream")?;
+        if n == 0 {
+            break;
+        }
+        num_bytes_read += n;
+        text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
+    }
+    info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
+    tb_connection.notify_completion()?;
+    Ok(())
+}
+
 impl VirtualizationService {
     pub fn init() -> VirtualizationService {
         let service = VirtualizationService::default();
@@ -381,8 +433,15 @@
             handle_stream_connection_from_vm(state).unwrap();
         });
 
+        std::thread::spawn(|| {
+            if let Err(e) = handle_stream_connection_tombstoned() {
+                warn!("Error receiving tombstone from guest or writing them. Error: {}", e);
+            }
+        });
+
         // binder server for vm
-        let mut state = service.state.clone(); // reference to state (not the state itself) is copied
+        // reference to state (not the state itself) is copied
+        let mut state = service.state.clone();
         std::thread::spawn(move || {
             let state_ptr = &mut state as *mut _ as *mut raw::c_void;
 
diff --git a/zipfuse/src/main.rs b/zipfuse/src/main.rs
index a91642c..c3fae69 100644
--- a/zipfuse/src/main.rs
+++ b/zipfuse/src/main.rs
@@ -82,7 +82,9 @@
         libc::MS_NOSUID | libc::MS_NODEV | libc::MS_RDONLY,
         &mount_options,
     )?;
-    Ok(fuse::worker::start_message_loop(dev_fuse, MAX_READ, MAX_WRITE, ZipFuse::new(zip_file)?)?)
+    let mut config = fuse::FuseConfig::new();
+    config.dev_fuse(dev_fuse).max_write(MAX_WRITE).max_read(MAX_READ);
+    Ok(config.enter_message_loop(ZipFuse::new(zip_file)?)?)
 }
 
 struct ZipFuse {