Send logd logs from VM to host

When the debug level of a VM is not "none", logd logs from the VM is
sent to the host. This is done by running the VM with another virtual
console and running logcat as a daemon process whose output is set to
the new virtual console device. The launch of the daemon process is
controlled by microdroid. It starts the process only when the debug
level is set to above "none".

For now, the virtual console device is backed by the same file
descriptor as the kernel console logs. A follow-up change will introduce
a new dedicated file descriptor.

Bug: 200914564
Test: start microdroid using the `vm` tool. logcat logs are shown in
stdout.

Change-Id: I1748d30c5c997cda73f7b9f082ca84b0b3d25f1e
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 8a5a7dd..dfb1cbb 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -243,28 +243,31 @@
         command.arg("--mem").arg(memory_mib.to_string());
     }
 
+    // Keep track of what file descriptors should be mapped to the crosvm process.
+    let mut preserved_fds = config.indirect_files.iter().map(|file| file.as_raw_fd()).collect();
+
     // Setup the serial devices.
     // 1. uart device: used as the output device by bootloaders and as early console by linux
     // 2. virtio-console device: used as the console device
+    // 3. virtio-console device: used as the logcat output
     //
     // When log_fd is not specified, the devices are attached to sink, which means what's written
     // there is discarded.
-    //
+    let path = config.log_fd.as_ref().map(|fd| add_preserved_fd(&mut preserved_fds, fd));
+    let backend = path.as_ref().map_or("sink", |_| "file");
+    let path_arg = path.as_ref().map_or(String::new(), |path| format!(",path={}", path));
+
     // Warning: Adding more serial devices requires you to shift the PCI device ID of the boot
     // disks in bootconfig.x86_64. This is because x86 crosvm puts serial devices and the block
     // devices in the same PCI bus and serial devices comes before the block devices. Arm crosvm
     // doesn't have the issue.
-    let backend = if let Some(log_fd) = config.log_fd {
-        command.stdout(log_fd);
-        "stdout"
-    } else {
-        "sink"
-    };
-    command.arg(format!("--serial=type={},hardware=serial", backend));
-    command.arg(format!("--serial=type={},hardware=virtio-console", backend));
-
-    // Keep track of what file descriptors should be mapped to the crosvm process.
-    let mut preserved_fds = config.indirect_files.iter().map(|file| file.as_raw_fd()).collect();
+    // /dev/ttyS0
+    command.arg(format!("--serial=type={}{},hardware=serial", backend, &path_arg));
+    // /dev/hvc0
+    command.arg(format!("--serial=type={}{},hardware=virtio-console,num=1", backend, &path_arg));
+    // /dev/hvc1
+    // TODO(b/200914564) use a different fd for logcat log
+    command.arg(format!("--serial=type={}{},hardware=virtio-console,num=2", backend, &path_arg));
 
     if let Some(bootloader) = &config.bootloader {
         command.arg("--bios").arg(add_preserved_fd(&mut preserved_fds, bootloader));