Add example using VirtIO console device.

Bug: 237250409
Test: atest vmbase_example.integration_test
Change-Id: I564c759ac3b43ea2e85110f053c2bcca002a559e
diff --git a/vmbase/example/src/pci.rs b/vmbase/example/src/pci.rs
index c0a2d2b..117cbc8 100644
--- a/vmbase/example/src/pci.rs
+++ b/vmbase/example/src/pci.rs
@@ -20,7 +20,7 @@
 use fdtpci::PciInfo;
 use log::{debug, info};
 use virtio_drivers::{
-    device::blk::VirtIOBlk,
+    device::{blk::VirtIOBlk, console::VirtIOConsole},
     transport::{
         pci::{bus::PciRoot, virtio_device_type, PciTransport},
         DeviceType, Transport,
@@ -53,29 +53,41 @@
         }
     }
 
-    assert_eq!(checked_virtio_device_count, 1);
+    assert_eq!(checked_virtio_device_count, 4);
 }
 
 /// Checks the given VirtIO device, if we know how to.
 ///
 /// Returns true if the device was checked, or false if it was ignored.
 fn check_virtio_device(transport: impl Transport, device_type: DeviceType) -> bool {
-    if device_type == DeviceType::Block {
-        let mut blk = VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
-        info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
-        assert_eq!(blk.capacity(), EXPECTED_SECTOR_COUNT as u64);
-        let mut data = [0; SECTOR_SIZE_BYTES * EXPECTED_SECTOR_COUNT];
-        for i in 0..EXPECTED_SECTOR_COUNT {
-            blk.read_block(i, &mut data[i * SECTOR_SIZE_BYTES..(i + 1) * SECTOR_SIZE_BYTES])
-                .expect("Failed to read block device.");
+    match device_type {
+        DeviceType::Block => {
+            let mut blk =
+                VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
+            info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
+            assert_eq!(blk.capacity(), EXPECTED_SECTOR_COUNT as u64);
+            let mut data = [0; SECTOR_SIZE_BYTES * EXPECTED_SECTOR_COUNT];
+            for i in 0..EXPECTED_SECTOR_COUNT {
+                blk.read_block(i, &mut data[i * SECTOR_SIZE_BYTES..(i + 1) * SECTOR_SIZE_BYTES])
+                    .expect("Failed to read block device.");
+            }
+            for (i, chunk) in data.chunks(size_of::<u32>()).enumerate() {
+                assert_eq!(chunk, &(i as u32).to_le_bytes());
+            }
+            info!("Read expected data from block device.");
+            true
         }
-        for (i, chunk) in data.chunks(size_of::<u32>()).enumerate() {
-            assert_eq!(chunk, &(i as u32).to_le_bytes());
+        DeviceType::Console => {
+            let mut console = VirtIOConsole::<HalImpl, _>::new(transport)
+                .expect("Failed to create VirtIO console driver");
+            info!("Found console device: {:?}", console.info());
+            for &c in b"Hello VirtIO console\n" {
+                console.send(c).expect("Failed to send character to VirtIO console device");
+            }
+            info!("Wrote to VirtIO console.");
+            true
         }
-        info!("Read expected data from block device.");
-        true
-    } else {
-        false
+        _ => false,
     }
 }
 
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
index 930e137..8f0eaa5 100644
--- a/vmbase/example/tests/test.rs
+++ b/vmbase/example/tests/test.rs
@@ -25,7 +25,7 @@
 use log::info;
 use std::{
     fs::File,
-    io::{self, BufRead, BufReader, Write},
+    io::{self, BufRead, BufReader, Read, Write},
     os::unix::io::FromRawFd,
     panic, thread,
 };
@@ -90,8 +90,8 @@
         gdbPort: 0, // no gdb
     });
     let console = android_log_fd()?;
-    let log = android_log_fd()?;
-    let vm = VmInstance::create(service.as_ref(), &config, Some(console), Some(log), None)
+    let (mut log_reader, log_writer) = pipe()?;
+    let vm = VmInstance::create(service.as_ref(), &config, Some(console), Some(log_writer), None)
         .context("Failed to create VM")?;
     vm.start().context("Failed to start VM")?;
     info!("Started example VM.");
@@ -100,15 +100,17 @@
     let death_reason = vm.wait_for_death();
     assert_eq!(death_reason, DeathReason::Shutdown);
 
+    // Check that the expected string was written to the log VirtIO console device.
+    let expected = "Hello VirtIO console\n";
+    let mut log_output = String::new();
+    assert_eq!(log_reader.read_to_string(&mut log_output)?, expected.len());
+    assert_eq!(log_output, expected);
+
     Ok(())
 }
 
 fn android_log_fd() -> io::Result<File> {
-    let (reader_fd, writer_fd) = nix::unistd::pipe()?;
-
-    // SAFETY: These are new FDs with no previous owner.
-    let reader = unsafe { File::from_raw_fd(reader_fd) };
-    let writer = unsafe { File::from_raw_fd(writer_fd) };
+    let (reader, writer) = pipe()?;
 
     thread::spawn(|| {
         for line in BufReader::new(reader).lines() {
@@ -117,3 +119,13 @@
     });
     Ok(writer)
 }
+
+fn pipe() -> io::Result<(File, File)> {
+    let (reader_fd, writer_fd) = nix::unistd::pipe()?;
+
+    // SAFETY: These are new FDs with no previous owner.
+    let reader = unsafe { File::from_raw_fd(reader_fd) };
+    let writer = unsafe { File::from_raw_fd(writer_fd) };
+
+    Ok((reader, writer))
+}