summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--aarch64/src/fdt.rs41
-rw-r--r--aarch64/src/lib.rs45
-rw-r--r--acpi_tables/src/aml.rs1966
-rw-r--r--acpi_tables/src/lib.rs1
-rw-r--r--arch/src/lib.rs104
-rw-r--r--arch/src/serial.rs481
-rw-r--r--async_core/src/eventfd.rs105
-rw-r--r--async_core/src/lib.rs3
-rw-r--r--async_core/src/timerfd.rs243
-rwxr-xr-xbin/clippy34
-rwxr-xr-xbin/smoke_test5
-rw-r--r--cros_async/src/complete.rs1
-rw-r--r--cros_async/src/executor.rs48
-rw-r--r--cros_async/src/fd_executor.rs133
-rw-r--r--cros_async/src/lib.rs172
-rw-r--r--cros_async/src/select.rs4
-rw-r--r--cros_async/src/waker.rs4
-rw-r--r--devices/src/lib.rs11
-rw-r--r--devices/src/pci/ac97.rs44
-rw-r--r--devices/src/pci/ac97_bus_master.rs1
-rw-r--r--devices/src/pci/mod.rs2
-rw-r--r--devices/src/pci/pci_device.rs11
-rw-r--r--devices/src/pci/pci_root.rs164
-rw-r--r--devices/src/pci/vfio_pci.rs32
-rw-r--r--devices/src/register_space/register.rs1
-rw-r--r--devices/src/serial.rs234
-rw-r--r--devices/src/serial_device.rs19
-rw-r--r--devices/src/usb/host_backend/host_device.rs29
-rw-r--r--devices/src/usb/xhci/event_ring.rs1
-rw-r--r--devices/src/usb/xhci/xhci_abi.rs2
-rw-r--r--devices/src/usb/xhci/xhci_controller.rs25
-rw-r--r--devices/src/virtio/balloon.rs1
-rw-r--r--devices/src/virtio/console.rs23
-rw-r--r--devices/src/virtio/descriptor_utils.rs10
-rw-r--r--devices/src/virtio/fs/filesystem.rs4
-rw-r--r--devices/src/virtio/fs/fuse.rs1
-rw-r--r--devices/src/virtio/fs/passthrough.rs110
-rw-r--r--devices/src/virtio/fs/server.rs13
-rw-r--r--devices/src/virtio/gpu/mod.rs11
-rw-r--r--devices/src/virtio/gpu/protocol.rs4
-rw-r--r--devices/src/virtio/input/constants.rs14
-rw-r--r--devices/src/virtio/net.rs1
-rw-r--r--devices/src/virtio/p9.rs1
-rw-r--r--devices/src/virtio/pmem.rs11
-rw-r--r--devices/src/virtio/rng.rs1
-rw-r--r--devices/src/virtio/tpm.rs1
-rw-r--r--devices/src/virtio/vhost/mod.rs1
-rw-r--r--devices/src/virtio/vhost/net.rs1
-rw-r--r--devices/src/virtio/virtio_device.rs4
-rw-r--r--devices/src/virtio/virtio_pci_device.rs41
-rw-r--r--devices/src/virtio/wl.rs11
-rw-r--r--disk/src/qcow/refcount.rs1
-rw-r--r--disk/src/qcow/vec_cache.rs2
-rw-r--r--docker/Dockerfile18
-rw-r--r--docker/Dockerfile.crosvm4
-rw-r--r--docker/checkout_commits.env4
-rw-r--r--docker/pkgconfig/libvda.pc4
-rw-r--r--gpu_buffer/src/raw.rs2
-rw-r--r--gpu_display/src/gpu_display_stub.rs2
-rw-r--r--gpu_display/src/gpu_display_x.rs2
-rw-r--r--gpu_display/src/keycode_converter/mod.rs1
-rw-r--r--hypervisor/Cargo.toml10
-rw-r--r--hypervisor/src/caps.rs6
-rw-r--r--hypervisor/src/kvm/mod.rs62
-rw-r--r--hypervisor/src/lib.rs25
-rw-r--r--hypervisor/src/types/mod.rs9
-rw-r--r--hypervisor/src/types/x86.rs10
-rw-r--r--io_jail/src/lib.rs13
-rw-r--r--kvm/src/cap.rs1
-rw-r--r--kvm/src/lib.rs44
-rw-r--r--kvm_sys/src/aarch64/bindings.rs5
-rw-r--r--kvm_sys/src/x86/bindings.rs1
-rw-r--r--msg_socket/Cargo.toml1
-rw-r--r--msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs168
-rw-r--r--msg_socket/src/lib.rs22
-rw-r--r--msg_socket/src/msg_on_socket.rs51
-rw-r--r--p9/src/protocol/wire_format.rs1
-rw-r--r--p9/src/server.rs2
-rw-r--r--resources/src/gpu_allocator.rs3
-rw-r--r--resources/src/lib.rs4
-rw-r--r--resources/src/system_allocator.rs5
-rw-r--r--seccomp/aarch64/gpu_device.policy1
-rw-r--r--seccomp/arm/gpu_device.policy1
-rw-r--r--seccomp/x86_64/gpu_device.policy1
-rw-r--r--src/argument.rs10
-rw-r--r--src/crosvm.rs6
-rw-r--r--src/linux.rs64
-rw-r--r--src/main.rs145
-rw-r--r--src/plugin/mod.rs6
-rw-r--r--src/plugin/process.rs19
-rw-r--r--src/plugin/vcpu.rs1
-rw-r--r--sys_util/src/descriptor.rs88
-rw-r--r--sys_util/src/ioctl.rs2
-rw-r--r--sys_util/src/lib.rs5
-rw-r--r--sys_util/src/mmap.rs200
-rw-r--r--sys_util/src/shm.rs8
-rw-r--r--sys_util/src/struct_util.rs1
-rw-r--r--sys_util/src/timerfd.rs10
-rw-r--r--tests/boot.rs11
-rw-r--r--tests/plugins.rs2
-rw-r--r--usb_util/src/error.rs1
-rw-r--r--vhost/src/net.rs2
-rw-r--r--vhost/src/vsock.rs1
-rw-r--r--vm_control/src/lib.rs110
-rw-r--r--x86_64/src/acpi.rs4
-rw-r--r--x86_64/src/cpuid.rs3
-rw-r--r--x86_64/src/interrupts.rs2
-rw-r--r--x86_64/src/lib.rs43
-rw-r--r--x86_64/src/mptable.rs39
-rw-r--r--x86_64/src/regs.rs1
112 files changed, 4518 insertions, 984 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e9da7f9..41843ee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -447,6 +447,7 @@ dependencies = [
  "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg_on_socket_derive 0.1.0",
+ "sync 0.1.0",
  "sys_util 0.1.0",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index 66a616a..3fdad6b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,6 +27,7 @@ exclude = [
     "sys_util",
     "syscall_defines",
     "tempfile",
+    "hypervisor"
 ]
 
 [features]
diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs
index 9dcafa5..19662c6 100644
--- a/aarch64/src/fdt.rs
+++ b/aarch64/src/fdt.rs
@@ -10,7 +10,8 @@ use arch::fdt::{
     begin_node, end_node, finish_fdt, generate_prop32, generate_prop64, property, property_cstring,
     property_null, property_string, property_u32, property_u64, start_fdt, Error, Result,
 };
-use devices::{PciInterruptPin, SERIAL_ADDR};
+use arch::SERIAL_ADDR;
+use devices::{PciAddress, PciInterruptPin};
 use sys_util::{GuestAddress, GuestMemory};
 
 // This is the start of DRAM in the physical address space.
@@ -36,12 +37,13 @@ use crate::AARCH64_SERIAL_SIZE;
 use crate::AARCH64_SERIAL_SPEED;
 
 // These are related to guest virtio devices.
-use crate::AARCH64_IRQ_BASE;
 use crate::AARCH64_MMIO_BASE;
 use crate::AARCH64_MMIO_SIZE;
 use crate::AARCH64_PCI_CFG_BASE;
 use crate::AARCH64_PCI_CFG_SIZE;
 
+use crate::AARCH64_PMU_IRQ;
+
 // This is an arbitrary number to specify the node for the GIC.
 // If we had a more complex interrupt architecture, then we'd need an enum for
 // these.
@@ -137,6 +139,23 @@ fn create_timer_node(fdt: &mut Vec<u8>, num_cpus: u32) -> Result<()> {
     Ok(())
 }
 
+fn create_pmu_node(fdt: &mut Vec<u8>, num_cpus: u32) -> Result<()> {
+    let compatible = "arm,armv8-pmuv3";
+    let cpu_mask: u32 =
+        (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
+    let irq = generate_prop32(&[
+        GIC_FDT_IRQ_TYPE_PPI,
+        AARCH64_PMU_IRQ,
+        cpu_mask | IRQ_TYPE_LEVEL_HIGH,
+    ]);
+
+    begin_node(fdt, "pmu")?;
+    property_string(fdt, "compatible", compatible)?;
+    property(fdt, "interrupts", &irq)?;
+    end_node(fdt)?;
+    Ok(())
+}
+
 fn create_serial_node(fdt: &mut Vec<u8>, addr: u64, irq: u32) -> Result<()> {
     let serial_reg_prop = generate_prop64(&[addr, AARCH64_SERIAL_SIZE]);
     let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING]);
@@ -216,7 +235,7 @@ fn create_chosen_node(
 
 fn create_pci_nodes(
     fdt: &mut Vec<u8>,
-    pci_irqs: Vec<(u32, PciInterruptPin)>,
+    pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
     pci_device_base: u64,
     pci_device_size: u64,
 ) -> Result<()> {
@@ -247,14 +266,14 @@ fn create_pci_nodes(
     let mut interrupts: Vec<u32> = Vec::new();
     let mut masks: Vec<u32> = Vec::new();
 
-    for (i, pci_irq) in pci_irqs.iter().enumerate() {
+    for (address, irq_num, irq_pin) in pci_irqs.iter() {
         // PCI_DEVICE(3)
-        interrupts.push((pci_irq.0 + 1) << 11);
+        interrupts.push(address.to_config_address(0));
         interrupts.push(0);
         interrupts.push(0);
 
         // INT#(1)
-        interrupts.push(pci_irq.1.to_mask() + 1);
+        interrupts.push(irq_pin.to_mask() + 1);
 
         // CONTROLLER(PHANDLE)
         interrupts.push(PHANDLE_GIC);
@@ -263,7 +282,7 @@ fn create_pci_nodes(
 
         // CONTROLLER_DATA(3)
         interrupts.push(GIC_FDT_IRQ_TYPE_SPI);
-        interrupts.push(AARCH64_IRQ_BASE + i as u32);
+        interrupts.push(*irq_num);
         interrupts.push(IRQ_TYPE_LEVEL_HIGH);
 
         // PCI_DEVICE(3)
@@ -330,7 +349,7 @@ fn create_rtc_node(fdt: &mut Vec<u8>) -> Result<()> {
 ///
 /// * `fdt_max_size` - The amount of space reserved for the device tree
 /// * `guest_mem` - The guest memory object
-/// * `pci_irqs` - List of PCI device number to PCI interrupt pin mappings
+/// * `pci_irqs` - List of PCI device address to PCI interrupt number and pin mappings
 /// * `num_cpus` - Number of virtual CPUs the guest will have
 /// * `fdt_load_offset` - The offset into physical memory for the device tree
 /// * `pci_device_base` - The offset into physical memory for PCI device memory
@@ -342,7 +361,7 @@ fn create_rtc_node(fdt: &mut Vec<u8>) -> Result<()> {
 pub fn create_fdt(
     fdt_max_size: usize,
     guest_mem: &GuestMemory,
-    pci_irqs: Vec<(u32, PciInterruptPin)>,
+    pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
     num_cpus: u32,
     fdt_load_offset: u64,
     pci_device_base: u64,
@@ -351,6 +370,7 @@ pub fn create_fdt(
     initrd: Option<(GuestAddress, usize)>,
     android_fstab: Option<File>,
     is_gicv3: bool,
+    use_pmu: bool,
 ) -> Result<()> {
     let mut fdt = vec![0; fdt_max_size];
     start_fdt(&mut fdt, fdt_max_size)?;
@@ -369,6 +389,9 @@ pub fn create_fdt(
     create_cpu_nodes(&mut fdt, num_cpus)?;
     create_gic_node(&mut fdt, is_gicv3, num_cpus as u64)?;
     create_timer_node(&mut fdt, num_cpus)?;
+    if use_pmu {
+        create_pmu_node(&mut fdt, num_cpus)?;
+    }
     create_serial_nodes(&mut fdt)?;
     create_psci_node(&mut fdt)?;
     create_pci_nodes(&mut fdt, pci_irqs, pci_device_base, pci_device_size)?;
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
index f8a36b9..0a90fd4 100644
--- a/aarch64/src/lib.rs
+++ b/aarch64/src/lib.rs
@@ -11,11 +11,11 @@ use std::io;
 use std::os::unix::io::FromRawFd;
 use std::sync::Arc;
 
-use arch::{RunnableLinuxVm, VmComponents, VmImage};
-use devices::{
-    get_serial_tty_string, Bus, BusError, PciConfigMmio, PciDevice, PciInterruptPin,
-    SerialParameters,
+use arch::{
+    get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware, SerialParameters,
+    VmComponents, VmImage,
 };
+use devices::{Bus, BusError, PciAddress, PciConfigMmio, PciDevice, PciInterruptPin};
 use io_jail::Minijail;
 use remain::sorted;
 use resources::SystemAllocator;
@@ -109,6 +109,9 @@ const AARCH64_MMIO_SIZE: u64 = 0x100000;
 // Virtio devices start at SPI interrupt number 3
 const AARCH64_IRQ_BASE: u32 = 3;
 
+// PMU PPI interrupt, same as qemu
+const AARCH64_PMU_IRQ: u32 = 7;
+
 #[sorted]
 #[derive(Debug)]
 pub enum Error {
@@ -124,6 +127,7 @@ pub enum Error {
     CreateSocket(io::Error),
     CreateVcpu(sys_util::Error),
     CreateVm(sys_util::Error),
+    GetSerialCmdline(GetSerialCmdlineError),
     InitrdLoadFailure(arch::LoadImageError),
     KernelLoadFailure(arch::LoadImageError),
     KernelMissing,
@@ -156,6 +160,7 @@ impl Display for Error {
             CreateSocket(e) => write!(f, "failed to create socket: {}", e),
             CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
             CreateVm(e) => write!(f, "failed to create vm: {}", e),
+            GetSerialCmdline(e) => write!(f, "failed to get serial cmdline: {}", e),
             InitrdLoadFailure(e) => write!(f, "initrd cound not be loaded: {}", e),
             KernelLoadFailure(e) => write!(f, "kernel cound not be loaded: {}", e),
             KernelMissing => write!(f, "aarch64 requires a kernel"),
@@ -197,7 +202,7 @@ impl arch::LinuxArch for AArch64 {
         mut components: VmComponents,
         _split_irqchip: bool,
         _ioapic_device_socket: VmIrqRequestSocket,
-        serial_parameters: &BTreeMap<u8, SerialParameters>,
+        serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
         create_devices: F,
     ) -> Result<RunnableLinuxVm>
@@ -235,6 +240,11 @@ impl arch::LinuxArch for AArch64 {
 
         let (irq_chip, is_gicv3) = Self::create_irq_chip(&vm, vcpu_count as u64)?;
 
+        let mut use_pmu = true;
+        for vcpu in &vcpus {
+            use_pmu &= vcpu.arm_pmu_init(AARCH64_PMU_IRQ as u64 + 16).is_ok();
+        }
+
         let mut mmio_bus = devices::Bus::new();
 
         let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
@@ -262,11 +272,11 @@ impl arch::LinuxArch for AArch64 {
 
         let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
         let com_evt_2_4 = EventFd::new().map_err(Error::CreateEventFd)?;
-        let stdio_serial_num = arch::add_serial_devices(
+        arch::add_serial_devices(
             &mut mmio_bus,
             &com_evt_1_3,
             &com_evt_2_4,
-            &serial_parameters,
+            serial_parameters,
             serial_jail,
         )
         .map_err(Error::CreateSerialDevices)?;
@@ -285,7 +295,9 @@ impl arch::LinuxArch for AArch64 {
             )
             .map_err(Error::RegisterPci)?;
 
-        let mut cmdline = Self::get_base_linux_cmdline(stdio_serial_num);
+        let mut cmdline = Self::get_base_linux_cmdline();
+        get_serial_cmdline(&mut cmdline, serial_parameters, "mmio")
+            .map_err(Error::GetSerialCmdline)?;
         for param in components.extra_kernel_params {
             cmdline.insert_str(&param).map_err(Error::Cmdline)?;
         }
@@ -311,6 +323,7 @@ impl arch::LinuxArch for AArch64 {
             components.android_fstab,
             kernel_end,
             is_gicv3,
+            use_pmu,
         )?;
 
         Ok(RunnableLinuxVm {
@@ -338,10 +351,11 @@ impl AArch64 {
         vcpu_count: u32,
         cmdline: &CStr,
         initrd_file: Option<File>,
-        pci_irqs: Vec<(u32, PciInterruptPin)>,
+        pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
         android_fstab: Option<File>,
         kernel_end: u64,
         is_gicv3: bool,
+        use_pmu: bool,
     ) -> Result<()> {
         let initrd = match initrd_file {
             Some(initrd_file) => {
@@ -370,6 +384,7 @@ impl AArch64 {
             initrd,
             android_fstab,
             is_gicv3,
+            use_pmu,
         )
         .map_err(Error::CreateFdt)?;
         Ok(())
@@ -388,12 +403,8 @@ impl AArch64 {
     }
 
     /// This returns a base part of the kernel command for this architecture
-    fn get_base_linux_cmdline(stdio_serial_num: Option<u8>) -> kernel_cmdline::Cmdline {
+    fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
         let mut cmdline = kernel_cmdline::Cmdline::new(sys_util::pagesize());
-        if let Some(stdio_serial_num) = stdio_serial_num {
-            let tty_string = get_serial_tty_string(stdio_serial_num);
-            cmdline.insert("console", &tty_string).unwrap();
-        }
         cmdline.insert_str("panic=-1").unwrap();
         cmdline
     }
@@ -540,7 +551,7 @@ impl AArch64 {
 
     fn configure_vcpu(
         guest_mem: &GuestMemory,
-        _kvm: &Kvm,
+        kvm: &Kvm,
         vm: &Vm,
         vcpu: &Vcpu,
         cpu_id: u64,
@@ -555,8 +566,10 @@ impl AArch64 {
         vm.arm_preferred_target(&mut kvi)
             .map_err(Error::ReadPreferredTarget)?;
 
-        // TODO(sonnyrao): need to verify this feature is supported by host kernel
         kvi.features[0] |= 1 << kvm_sys::KVM_ARM_VCPU_PSCI_0_2;
+        if kvm.check_extension(Cap::ArmPmuV3) {
+            kvi.features[0] |= 1 << kvm_sys::KVM_ARM_VCPU_PMU_V3;
+        }
 
         // Non-boot cpus are powered off initially
         if cpu_id > 0 {
diff --git a/acpi_tables/src/aml.rs b/acpi_tables/src/aml.rs
new file mode 100644
index 0000000..7d16153
--- /dev/null
+++ b/acpi_tables/src/aml.rs
@@ -0,0 +1,1966 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// The trait Aml can be implemented by the ACPI objects to translate itself
+/// into the AML raw data. So that these AML raw data can be added into the
+/// ACPI DSDT for guest.
+pub trait Aml {
+    /// Translate an ACPI object into AML code and append to the vector
+    /// buffer.
+    /// * `bytes` - The vector used to append the AML code.
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>);
+}
+
+// AML byte stream defines
+const ZEROOP: u8 = 0x00;
+const ONEOP: u8 = 0x01;
+const NAMEOP: u8 = 0x08;
+const BYTEPREFIX: u8 = 0x0a;
+const WORDPREFIX: u8 = 0x0b;
+const DWORDPREFIX: u8 = 0x0c;
+const STRINGOP: u8 = 0x0d;
+const QWORDPREFIX: u8 = 0x0e;
+const SCOPEOP: u8 = 0x10;
+const BUFFEROP: u8 = 0x11;
+const PACKAGEOP: u8 = 0x12;
+const METHODOP: u8 = 0x14;
+const DUALNAMEPREFIX: u8 = 0x2e;
+const MULTINAMEPREFIX: u8 = 0x2f;
+const NAMECHARBASE: u8 = 0x40;
+
+const EXTOPPREFIX: u8 = 0x5b;
+const MUTEXOP: u8 = 0x01;
+const ACQUIREOP: u8 = 0x23;
+const RELEASEOP: u8 = 0x27;
+const OPREGIONOP: u8 = 0x80;
+const FIELDOP: u8 = 0x81;
+const DEVICEOP: u8 = 0x82;
+
+const LOCAL0OP: u8 = 0x60;
+const ARG0OP: u8 = 0x68;
+const STOREOP: u8 = 0x70;
+const ADDOP: u8 = 0x72;
+const CONCATOP: u8 = 0x73;
+const SUBTRACTOP: u8 = 0x74;
+const MULTIPLYOP: u8 = 0x77;
+const SHIFTLEFTOP: u8 = 0x79;
+const SHIFTRIGHTOP: u8 = 0x7a;
+const ANDOP: u8 = 0x7b;
+const NANDOP: u8 = 0x7c;
+const OROP: u8 = 0x7d;
+const NOROP: u8 = 0x7e;
+const XOROP: u8 = 0x7f;
+const CONCATRESOP: u8 = 0x84;
+const MODOP: u8 = 0x85;
+const NOTIFYOP: u8 = 0x86;
+const INDEXOP: u8 = 0x88;
+const LEQUALOP: u8 = 0x93;
+const LLESSOP: u8 = 0x95;
+const TOSTRINGOP: u8 = 0x9c;
+const IFOP: u8 = 0xa0;
+const WHILEOP: u8 = 0xa2;
+const RETURNOP: u8 = 0xa4;
+const ONESOP: u8 = 0xff;
+
+// AML resouce data fields
+const IOPORTDESC: u8 = 0x47;
+const ENDTAG: u8 = 0x79;
+const MEMORY32FIXEDDESC: u8 = 0x86;
+const DWORDADDRSPACEDESC: u8 = 0x87;
+const WORDADDRSPACEDESC: u8 = 0x88;
+const EXTIRQDESC: u8 = 0x89;
+const QWORDADDRSPACEDESC: u8 = 0x8A;
+
+/// Zero object in ASL.
+pub const ZERO: Zero = Zero {};
+pub struct Zero {}
+
+impl Aml for Zero {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.append(&mut vec![ZEROOP]);
+    }
+}
+
+/// One object in ASL.
+pub const ONE: One = One {};
+pub struct One {}
+
+impl Aml for One {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.append(&mut vec![ONEOP]);
+    }
+}
+
+/// Ones object represents all bits 1.
+pub const ONES: Ones = Ones {};
+pub struct Ones {}
+
+impl Aml for Ones {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.append(&mut vec![ONESOP]);
+    }
+}
+
+/// Represents Namestring to construct ACPI objects like
+/// Name/Device/Method/Scope and so on...
+pub struct Path {
+    root: bool,
+    name_parts: Vec<[u8; 4]>,
+}
+
+impl Aml for Path {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        if self.root {
+            bytes.push(b'\\');
+        }
+
+        match self.name_parts.len() {
+            0 => panic!("Name cannot be empty"),
+            1 => {}
+            2 => {
+                bytes.push(DUALNAMEPREFIX);
+            }
+            n => {
+                bytes.push(MULTINAMEPREFIX);
+                bytes.push(n as u8);
+            }
+        };
+
+        for part in self.name_parts.clone().iter_mut() {
+            bytes.append(&mut part.to_vec());
+        }
+    }
+}
+
+impl Path {
+    /// Per ACPI Spec, the Namestring split by "." has 4 bytes long. So any name
+    /// not has 4 bytes will not be accepted.
+    pub fn new(name: &str) -> Self {
+        let root = name.starts_with('\\');
+        let offset = root as usize;
+        let mut name_parts = Vec::new();
+        for part in name[offset..].split('.') {
+            assert_eq!(part.len(), 4);
+            let mut name_part = [0u8; 4];
+            name_part.copy_from_slice(part.as_bytes());
+            name_parts.push(name_part);
+        }
+
+        Path { root, name_parts }
+    }
+}
+
+impl From<&str> for Path {
+    fn from(s: &str) -> Self {
+        Path::new(s)
+    }
+}
+
+pub type Byte = u8;
+
+impl Aml for Byte {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(BYTEPREFIX);
+        bytes.push(*self);
+    }
+}
+
+pub type Word = u16;
+
+impl Aml for Word {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(WORDPREFIX);
+        bytes.append(&mut self.to_le_bytes().to_vec());
+    }
+}
+
+pub type DWord = u32;
+
+impl Aml for DWord {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(DWORDPREFIX);
+        bytes.append(&mut self.to_le_bytes().to_vec());
+    }
+}
+
+pub type QWord = u64;
+
+impl Aml for QWord {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(QWORDPREFIX);
+        bytes.append(&mut self.to_le_bytes().to_vec());
+    }
+}
+
+/// Name object. bytes represents the raw AML data for it.
+pub struct Name {
+    bytes: Vec<u8>,
+}
+
+impl Aml for Name {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.append(&mut self.bytes.clone());
+    }
+}
+
+impl Name {
+    /// Create Name object:
+    ///
+    /// * `path` - The namestring.
+    /// * `inner` - AML objects contained in this namespace.
+    pub fn new(path: Path, inner: &dyn Aml) -> Self {
+        let mut bytes = Vec::new();
+        bytes.push(NAMEOP);
+        path.to_aml_bytes(&mut bytes);
+        inner.to_aml_bytes(&mut bytes);
+        Name { bytes }
+    }
+}
+
+/// Package object. 'children' represents the ACPI objects contained in this package.
+pub struct Package<'a> {
+    children: Vec<&'a dyn Aml>,
+}
+
+impl<'a> Aml for Package<'a> {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        bytes.push(self.children.len() as u8);
+        for child in &self.children {
+            child.to_aml_bytes(&mut bytes);
+        }
+
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, PACKAGEOP);
+
+        aml.append(&mut bytes);
+    }
+}
+
+impl<'a> Package<'a> {
+    /// Create Package object:
+    pub fn new(children: Vec<&'a dyn Aml>) -> Self {
+        Package { children }
+    }
+}
+
+/*
+
+From the ACPI spec for PkgLength:
+
+"The high 2 bits of the first byte reveal how many follow bytes are in the PkgLength. If the
+PkgLength has only one byte, bit 0 through 5 are used to encode the package length (in other
+words, values 0-63). If the package length value is more than 63, more than one byte must be
+used for the encoding in which case bit 4 and 5 of the PkgLeadByte are reserved and must be zero.
+If the multiple bytes encoding is used, bits 0-3 of the PkgLeadByte become the least significant 4
+bits of the resulting package length value. The next ByteData will become the next least
+significant 8 bits of the resulting value and so on, up to 3 ByteData bytes. Thus, the maximum
+package length is 2**28."
+
+*/
+
+/* Also used for NamedField but in that case the length is not included in itself */
+fn create_pkg_length(data: &[u8], include_self: bool) -> Vec<u8> {
+    let mut result = Vec::new();
+
+    /* PkgLength is inclusive and includes the length bytes */
+    let length_length = if data.len() < (2usize.pow(6) - 1) {
+        1
+    } else if data.len() < (2usize.pow(12) - 2) {
+        2
+    } else if data.len() < (2usize.pow(20) - 3) {
+        3
+    } else {
+        4
+    };
+
+    let length = data.len() + if include_self { length_length } else { 0 };
+
+    match length_length {
+        1 => result.push(length as u8),
+        2 => {
+            result.push((1u8 << 6) | (length & 0xf) as u8);
+            result.push((length >> 4) as u8)
+        }
+        3 => {
+            result.push((2u8 << 6) | (length & 0xf) as u8);
+            result.push((length >> 4) as u8);
+            result.push((length >> 12) as u8);
+        }
+        _ => {
+            result.push((3u8 << 6) | (length & 0xf) as u8);
+            result.push((length >> 4) as u8);
+            result.push((length >> 12) as u8);
+            result.push((length >> 20) as u8);
+        }
+    }
+
+    result
+}
+
+/// EISAName object. 'value' means the encoded u32 EisaIdString.
+pub struct EISAName {
+    value: DWord,
+}
+
+impl EISAName {
+    /// Per ACPI Spec, the EisaIdString must be a String
+    /// object of the form UUUNNNN, where U is an uppercase letter
+    /// and N is a hexadecimal digit. No asterisks or other characters
+    /// are allowed in the string.
+    pub fn new(name: &str) -> Self {
+        assert_eq!(name.len(), 7);
+
+        let data = name.as_bytes();
+
+        let value: u32 = (u32::from(data[0].checked_sub(NAMECHARBASE).unwrap()) << 26
+            | u32::from(data[1].checked_sub(NAMECHARBASE).unwrap()) << 21
+            | u32::from(data[2].checked_sub(NAMECHARBASE).unwrap()) << 16
+            | name.chars().nth(3).unwrap().to_digit(16).unwrap() << 12
+            | name.chars().nth(4).unwrap().to_digit(16).unwrap() << 8
+            | name.chars().nth(5).unwrap().to_digit(16).unwrap() << 4
+            | name.chars().nth(6).unwrap().to_digit(16).unwrap())
+        .swap_bytes();
+
+        EISAName { value }
+    }
+}
+
+impl Aml for EISAName {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        self.value.to_aml_bytes(bytes);
+    }
+}
+
+fn create_integer(v: usize, bytes: &mut Vec<u8>) {
+    if v <= u8::max_value().into() {
+        (v as u8).to_aml_bytes(bytes);
+    } else if v <= u16::max_value().into() {
+        (v as u16).to_aml_bytes(bytes);
+    } else if v <= u32::max_value() as usize {
+        (v as u32).to_aml_bytes(bytes);
+    } else {
+        (v as u64).to_aml_bytes(bytes);
+    }
+}
+
+pub type Usize = usize;
+
+impl Aml for Usize {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        create_integer(*self, bytes);
+    }
+}
+
+fn create_aml_string(v: &str) -> Vec<u8> {
+    let mut data = Vec::new();
+    data.push(STRINGOP);
+    data.extend_from_slice(v.as_bytes());
+    data.push(0x0); /* NullChar */
+    data
+}
+
+/// implement Aml trait for 'str' so that 'str' can be directly append to the aml vector
+pub type AmlStr = &'static str;
+
+impl Aml for AmlStr {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.append(&mut create_aml_string(self));
+    }
+}
+
+/// implement Aml trait for 'String'. So purpose with str.
+pub type AmlString = String;
+
+impl Aml for AmlString {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.append(&mut create_aml_string(self));
+    }
+}
+
+/// ResouceTemplate object. 'children' represents the ACPI objects in it.
+pub struct ResourceTemplate<'a> {
+    children: Vec<&'a dyn Aml>,
+}
+
+impl<'a> Aml for ResourceTemplate<'a> {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+
+        // Add buffer data
+        for child in &self.children {
+            child.to_aml_bytes(&mut bytes);
+        }
+
+        // Mark with end and mark checksum as as always valid
+        bytes.push(ENDTAG);
+        bytes.push(0); /* zero checksum byte */
+
+        // Buffer length is an encoded integer including buffer data
+        // and EndTag and checksum byte
+        let mut buffer_length = Vec::new();
+        bytes.len().to_aml_bytes(&mut buffer_length);
+        buffer_length.reverse();
+        for byte in buffer_length {
+            bytes.insert(0, byte);
+        }
+
+        // PkgLength is everything else
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, BUFFEROP);
+
+        aml.append(&mut bytes);
+    }
+}
+
+impl<'a> ResourceTemplate<'a> {
+    /// Create ResouceTemplate object
+    pub fn new(children: Vec<&'a dyn Aml>) -> Self {
+        ResourceTemplate { children }
+    }
+}
+
+/// Memory32Fixed object with read_write accessing type, and the base address/length.
+pub struct Memory32Fixed {
+    read_write: bool, /* true for read & write, false for read only */
+    base: u32,
+    length: u32,
+}
+
+impl Memory32Fixed {
+    /// Create Memory32Fixed object.
+    pub fn new(read_write: bool, base: u32, length: u32) -> Self {
+        Memory32Fixed {
+            read_write,
+            base,
+            length,
+        }
+    }
+}
+
+impl Aml for Memory32Fixed {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(MEMORY32FIXEDDESC); /* 32bit Fixed Memory Range Descriptor */
+        bytes.append(&mut 9u16.to_le_bytes().to_vec());
+
+        // 9 bytes of payload
+        bytes.push(self.read_write as u8);
+        bytes.append(&mut self.base.to_le_bytes().to_vec());
+        bytes.append(&mut self.length.to_le_bytes().to_vec());
+    }
+}
+
+#[derive(Copy, Clone)]
+enum AddressSpaceType {
+    Memory,
+    IO,
+    BusNumber,
+}
+
+/// AddressSpaceCachable represent cache types for AddressSpace object
+#[derive(Copy, Clone)]
+pub enum AddressSpaceCachable {
+    NotCacheable,
+    Cacheable,
+    WriteCombining,
+    PreFetchable,
+}
+
+/// AddressSpace structure with type, resouce range and flags to
+/// construct Memory/IO/BusNumber objects
+pub struct AddressSpace<T> {
+    type_: AddressSpaceType,
+    min: T,
+    max: T,
+    type_flags: u8,
+}
+
+impl<T> AddressSpace<T> {
+    /// Create DWordMemory/QWordMemory object
+    pub fn new_memory(cacheable: AddressSpaceCachable, read_write: bool, min: T, max: T) -> Self {
+        AddressSpace {
+            type_: AddressSpaceType::Memory,
+            min,
+            max,
+            type_flags: (cacheable as u8) << 1 | read_write as u8,
+        }
+    }
+
+    /// Create WordIO/DWordIO/QWordIO object
+    pub fn new_io(min: T, max: T) -> Self {
+        AddressSpace {
+            type_: AddressSpaceType::IO,
+            min,
+            max,
+            type_flags: 3, /* EntireRange */
+        }
+    }
+
+    /// Create WordBusNumber object
+    pub fn new_bus_number(min: T, max: T) -> Self {
+        AddressSpace {
+            type_: AddressSpaceType::BusNumber,
+            min,
+            max,
+            type_flags: 0,
+        }
+    }
+
+    fn push_header(&self, bytes: &mut Vec<u8>, descriptor: u8, length: usize) {
+        bytes.push(descriptor); /* Word Address Space Descriptor */
+        bytes.append(&mut (length as u16).to_le_bytes().to_vec());
+        bytes.push(self.type_ as u8); /* type */
+        let generic_flags = 1 << 2 /* Min Fixed */ | 1 << 3; /* Max Fixed */
+        bytes.push(generic_flags);
+        bytes.push(self.type_flags);
+    }
+}
+
+impl Aml for AddressSpace<u16> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        self.push_header(
+            bytes,
+            WORDADDRSPACEDESC,                  /* Word Address Space Descriptor */
+            3 + 5 * std::mem::size_of::<u16>(), /* 3 bytes of header + 5 u16 fields */
+        );
+
+        bytes.append(&mut 0u16.to_le_bytes().to_vec()); /* Granularity */
+        bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
+        bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
+        bytes.append(&mut 0u16.to_le_bytes().to_vec()); /* Translation */
+        let len = self.max - self.min + 1;
+        bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
+    }
+}
+
+impl Aml for AddressSpace<u32> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        self.push_header(
+            bytes,
+            DWORDADDRSPACEDESC, /* DWord Address Space Descriptor */
+            3 + 5 * std::mem::size_of::<u32>(), /* 3 bytes of header + 5 u32 fields */
+        );
+
+        bytes.append(&mut 0u32.to_le_bytes().to_vec()); /* Granularity */
+        bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
+        bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
+        bytes.append(&mut 0u32.to_le_bytes().to_vec()); /* Translation */
+        let len = self.max - self.min + 1;
+        bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
+    }
+}
+
+impl Aml for AddressSpace<u64> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        self.push_header(
+            bytes,
+            QWORDADDRSPACEDESC, /* QWord Address Space Descriptor */
+            3 + 5 * std::mem::size_of::<u64>(), /* 3 bytes of header + 5 u64 fields */
+        );
+
+        bytes.append(&mut 0u64.to_le_bytes().to_vec()); /* Granularity */
+        bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
+        bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
+        bytes.append(&mut 0u64.to_le_bytes().to_vec()); /* Translation */
+        let len = self.max - self.min + 1;
+        bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
+    }
+}
+
+/// IO resouce object with the IO range, alignment and length
+pub struct IO {
+    min: u16,
+    max: u16,
+    alignment: u8,
+    length: u8,
+}
+
+impl IO {
+    /// Create IO object
+    pub fn new(min: u16, max: u16, alignment: u8, length: u8) -> Self {
+        IO {
+            min,
+            max,
+            alignment,
+            length,
+        }
+    }
+}
+
+impl Aml for IO {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(IOPORTDESC); /* IO Port Descriptor */
+        bytes.push(1); /* IODecode16 */
+        bytes.append(&mut self.min.to_le_bytes().to_vec());
+        bytes.append(&mut self.max.to_le_bytes().to_vec());
+        bytes.push(self.alignment);
+        bytes.push(self.length);
+    }
+}
+
+/// Interrupt resouce object with the interrupt characters.
+pub struct Interrupt {
+    consumer: bool,
+    edge_triggered: bool,
+    active_low: bool,
+    shared: bool,
+    number: u32,
+}
+
+impl Interrupt {
+    /// Create Interrupt object
+    pub fn new(
+        consumer: bool,
+        edge_triggered: bool,
+        active_low: bool,
+        shared: bool,
+        number: u32,
+    ) -> Self {
+        Interrupt {
+            consumer,
+            edge_triggered,
+            active_low,
+            shared,
+            number,
+        }
+    }
+}
+
+impl Aml for Interrupt {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(EXTIRQDESC); /* Extended IRQ Descriptor */
+        bytes.append(&mut 6u16.to_le_bytes().to_vec());
+        let flags = (self.shared as u8) << 3
+            | (self.active_low as u8) << 2
+            | (self.edge_triggered as u8) << 1
+            | self.consumer as u8;
+        bytes.push(flags);
+        bytes.push(1u8); /* count */
+        bytes.append(&mut self.number.to_le_bytes().to_vec());
+    }
+}
+
+/// Device object with its device name and children objects in it.
+pub struct Device<'a> {
+    path: Path,
+    children: Vec<&'a dyn Aml>,
+}
+
+impl<'a> Aml for Device<'a> {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        self.path.to_aml_bytes(&mut bytes);
+        for child in &self.children {
+            child.to_aml_bytes(&mut bytes);
+        }
+
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, DEVICEOP); /* DeviceOp */
+        bytes.insert(0, EXTOPPREFIX); /* ExtOpPrefix */
+        aml.append(&mut bytes)
+    }
+}
+
+impl<'a> Device<'a> {
+    /// Create Device object
+    pub fn new(path: Path, children: Vec<&'a dyn Aml>) -> Self {
+        Device { path, children }
+    }
+}
+
+/// Scope object with its name and children objects in it.
+pub struct Scope<'a> {
+    path: Path,
+    children: Vec<&'a dyn Aml>,
+}
+
+impl<'a> Aml for Scope<'a> {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        self.path.to_aml_bytes(&mut bytes);
+        for child in &self.children {
+            child.to_aml_bytes(&mut bytes);
+        }
+
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, SCOPEOP);
+        aml.append(&mut bytes)
+    }
+}
+
+impl<'a> Scope<'a> {
+    /// Create Scope object
+    pub fn new(path: Path, children: Vec<&'a dyn Aml>) -> Self {
+        Scope { path, children }
+    }
+}
+
+/// Method object with its name, children objects, arguments and serialized character.
+pub struct Method<'a> {
+    path: Path,
+    children: Vec<&'a dyn Aml>,
+    args: u8,
+    serialized: bool,
+}
+
+impl<'a> Method<'a> {
+    /// Create Method object.
+    pub fn new(path: Path, args: u8, serialized: bool, children: Vec<&'a dyn Aml>) -> Self {
+        Method {
+            path,
+            children,
+            args,
+            serialized,
+        }
+    }
+}
+
+impl<'a> Aml for Method<'a> {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        self.path.to_aml_bytes(&mut bytes);
+        let flags: u8 = (self.args & 0x7) | (self.serialized as u8) << 3;
+        bytes.push(flags);
+        for child in &self.children {
+            child.to_aml_bytes(&mut bytes);
+        }
+
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, METHODOP);
+        aml.append(&mut bytes)
+    }
+}
+
+/// Return object with its return value.
+pub struct Return<'a> {
+    value: &'a dyn Aml,
+}
+
+impl<'a> Return<'a> {
+    /// Create Return object
+    pub fn new(value: &'a dyn Aml) -> Self {
+        Return { value }
+    }
+}
+
+impl<'a> Aml for Return<'a> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(RETURNOP);
+        self.value.to_aml_bytes(bytes);
+    }
+}
+
+/// FiledAccessType defines the filed accessing types.
+#[derive(Clone, Copy)]
+pub enum FieldAccessType {
+    Any,
+    Byte,
+    Word,
+    DWord,
+    QWord,
+    Buffer,
+}
+
+/// FiledUpdateRule defines the rules to update the filed.
+#[derive(Clone, Copy)]
+pub enum FieldUpdateRule {
+    Preserve = 0,
+    WriteAsOnes = 1,
+    WriteAsZeroes = 2,
+}
+
+/// FiledEntry defines the filed entry.
+pub enum FieldEntry {
+    Named([u8; 4], usize),
+    Reserved(usize),
+}
+
+/// Field object with the region name, filed entries, access type and update rules.
+pub struct Field {
+    path: Path,
+
+    fields: Vec<FieldEntry>,
+    access_type: FieldAccessType,
+    update_rule: FieldUpdateRule,
+}
+
+impl Field {
+    /// Create Field object
+    pub fn new(
+        path: Path,
+        access_type: FieldAccessType,
+        update_rule: FieldUpdateRule,
+        fields: Vec<FieldEntry>,
+    ) -> Self {
+        Field {
+            path,
+            access_type,
+            update_rule,
+            fields,
+        }
+    }
+}
+
+impl Aml for Field {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        self.path.to_aml_bytes(&mut bytes);
+
+        let flags: u8 = self.access_type as u8 | (self.update_rule as u8) << 5;
+        bytes.push(flags);
+
+        for field in self.fields.iter() {
+            match field {
+                FieldEntry::Named(name, length) => {
+                    bytes.extend_from_slice(name);
+                    bytes.append(&mut create_pkg_length(&vec![0; *length], false));
+                }
+                FieldEntry::Reserved(length) => {
+                    bytes.push(0x0);
+                    bytes.append(&mut create_pkg_length(&vec![0; *length], false));
+                }
+            }
+        }
+
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, FIELDOP);
+        bytes.insert(0, EXTOPPREFIX);
+        aml.append(&mut bytes)
+    }
+}
+
+/// The space type for OperationRegion object
+#[derive(Clone, Copy)]
+pub enum OpRegionSpace {
+    SystemMemory,
+    SystemIO,
+    PCIConfig,
+    EmbeddedControl,
+    SMBus,
+    SystemCMOS,
+    PciBarTarget,
+    IPMI,
+    GeneralPurposeIO,
+    GenericSerialBus,
+}
+
+/// OperationRegion object with region name, region space type, its offset and length.
+pub struct OpRegion {
+    path: Path,
+    space: OpRegionSpace,
+    offset: usize,
+    length: usize,
+}
+
+impl OpRegion {
+    /// Create OperationRegion object.
+    pub fn new(path: Path, space: OpRegionSpace, offset: usize, length: usize) -> Self {
+        OpRegion {
+            path,
+            space,
+            offset,
+            length,
+        }
+    }
+}
+
+impl Aml for OpRegion {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        self.path.to_aml_bytes(&mut bytes);
+        bytes.push(self.space as u8);
+        self.offset.to_aml_bytes(&mut bytes); /* RegionOffset */
+        self.length.to_aml_bytes(&mut bytes); /* RegionLen */
+        bytes.insert(0, OPREGIONOP);
+        bytes.insert(0, EXTOPPREFIX);
+        aml.append(&mut bytes)
+    }
+}
+
+/// If object with the if condition(predicate) and the body presented by the if_children objects.
+pub struct If<'a> {
+    predicate: &'a dyn Aml,
+    if_children: Vec<&'a dyn Aml>,
+}
+
+impl<'a> If<'a> {
+    /// Create If object.
+    pub fn new(predicate: &'a dyn Aml, if_children: Vec<&'a dyn Aml>) -> Self {
+        If {
+            predicate,
+            if_children,
+        }
+    }
+}
+
+impl<'a> Aml for If<'a> {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        self.predicate.to_aml_bytes(&mut bytes);
+        for child in self.if_children.iter() {
+            child.to_aml_bytes(&mut bytes);
+        }
+
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, IFOP);
+        aml.append(&mut bytes)
+    }
+}
+
+/// Equal object with its right part and left part, which are both ACPI objects.
+pub struct Equal<'a> {
+    right: &'a dyn Aml,
+    left: &'a dyn Aml,
+}
+
+impl<'a> Equal<'a> {
+    /// Create Equal object.
+    pub fn new(left: &'a dyn Aml, right: &'a dyn Aml) -> Self {
+        Equal { left, right }
+    }
+}
+
+impl<'a> Aml for Equal<'a> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(LEQUALOP);
+        self.left.to_aml_bytes(bytes);
+        self.right.to_aml_bytes(bytes);
+    }
+}
+
+/// LessThan object with its right part and left part, which are both ACPI objects.
+pub struct LessThan<'a> {
+    right: &'a dyn Aml,
+    left: &'a dyn Aml,
+}
+
+impl<'a> LessThan<'a> {
+    /// Create LessThan object.
+    pub fn new(left: &'a dyn Aml, right: &'a dyn Aml) -> Self {
+        LessThan { left, right }
+    }
+}
+
+impl<'a> Aml for LessThan<'a> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(LLESSOP);
+        self.left.to_aml_bytes(bytes);
+        self.right.to_aml_bytes(bytes);
+    }
+}
+
+/// Argx object.
+pub struct Arg(pub u8);
+
+impl Aml for Arg {
+    /// Per ACPI spec, there is maximum 7 Argx objects from
+    /// Arg0 ~ Arg6. Any other Arg object will not be accepted.
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        assert!(self.0 <= 6);
+        bytes.push(ARG0OP + self.0);
+    }
+}
+
+/// Localx object.
+pub struct Local(pub u8);
+
+impl Aml for Local {
+    /// Per ACPI spec, there is maximum 8 Localx objects from
+    /// Local0 ~ Local7. Any other Local object will not be accepted.
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        assert!(self.0 <= 7);
+        bytes.push(LOCAL0OP + self.0);
+    }
+}
+
+/// Store object with the ACPI object name which can be stored to and
+/// the ACPI object value which is to store.
+pub struct Store<'a> {
+    name: &'a dyn Aml,
+    value: &'a dyn Aml,
+}
+
+impl<'a> Store<'a> {
+    /// Create Store object.
+    pub fn new(name: &'a dyn Aml, value: &'a dyn Aml) -> Self {
+        Store { name, value }
+    }
+}
+
+impl<'a> Aml for Store<'a> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(STOREOP);
+        self.value.to_aml_bytes(bytes);
+        self.name.to_aml_bytes(bytes);
+    }
+}
+
+/// Mutex object with a mutex name and a synchronization level.
+pub struct Mutex {
+    path: Path,
+    sync_level: u8,
+}
+
+impl Mutex {
+    /// Create Mutex object.
+    pub fn new(path: Path, sync_level: u8) -> Self {
+        Self { path, sync_level }
+    }
+}
+
+impl Aml for Mutex {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(EXTOPPREFIX);
+        bytes.push(MUTEXOP);
+        self.path.to_aml_bytes(bytes);
+        bytes.push(self.sync_level);
+    }
+}
+
+/// Acquire object with a Mutex object and timeout value.
+pub struct Acquire {
+    mutex: Path,
+    timeout: u16,
+}
+
+impl Acquire {
+    /// Create Acquire object.
+    pub fn new(mutex: Path, timeout: u16) -> Self {
+        Acquire { mutex, timeout }
+    }
+}
+
+impl Aml for Acquire {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(EXTOPPREFIX);
+        bytes.push(ACQUIREOP);
+        self.mutex.to_aml_bytes(bytes);
+        bytes.extend_from_slice(&self.timeout.to_le_bytes());
+    }
+}
+
+/// Release object with a Mutex object to release.
+pub struct Release {
+    mutex: Path,
+}
+
+impl Release {
+    /// Create Release object.
+    pub fn new(mutex: Path) -> Self {
+        Release { mutex }
+    }
+}
+
+impl Aml for Release {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(EXTOPPREFIX);
+        bytes.push(RELEASEOP);
+        self.mutex.to_aml_bytes(bytes);
+    }
+}
+
+/// Notify object with an object which is to be notified with the value.
+pub struct Notify<'a> {
+    object: &'a dyn Aml,
+    value: &'a dyn Aml,
+}
+
+impl<'a> Notify<'a> {
+    /// Create Notify object.
+    pub fn new(object: &'a dyn Aml, value: &'a dyn Aml) -> Self {
+        Notify { object, value }
+    }
+}
+
+impl<'a> Aml for Notify<'a> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        bytes.push(NOTIFYOP);
+        self.object.to_aml_bytes(bytes);
+        self.value.to_aml_bytes(bytes);
+    }
+}
+
+/// While object with the while condition objects(predicate) and
+/// the while body objects(while_children).
+pub struct While<'a> {
+    predicate: &'a dyn Aml,
+    while_children: Vec<&'a dyn Aml>,
+}
+
+impl<'a> While<'a> {
+    /// Create While object.
+    pub fn new(predicate: &'a dyn Aml, while_children: Vec<&'a dyn Aml>) -> Self {
+        While {
+            predicate,
+            while_children,
+        }
+    }
+}
+
+impl<'a> Aml for While<'a> {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        self.predicate.to_aml_bytes(&mut bytes);
+        for child in self.while_children.iter() {
+            child.to_aml_bytes(&mut bytes);
+        }
+
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, WHILEOP);
+        aml.append(&mut bytes)
+    }
+}
+
+macro_rules! binary_op {
+    ($name:ident, $opcode:expr) => {
+        /// General operation object with the operator a/b and a target.
+        pub struct $name<'a> {
+            a: &'a dyn Aml,
+            b: &'a dyn Aml,
+            target: &'a dyn Aml,
+        }
+
+        impl<'a> $name<'a> {
+            /// Create the object.
+            pub fn new(target: &'a dyn Aml, a: &'a dyn Aml, b: &'a dyn Aml) -> Self {
+                $name { target, a, b }
+            }
+        }
+
+        impl<'a> Aml for $name<'a> {
+            fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+                bytes.push($opcode); /* Op for the binary operator */
+                self.a.to_aml_bytes(bytes);
+                self.b.to_aml_bytes(bytes);
+                self.target.to_aml_bytes(bytes);
+            }
+        }
+    };
+}
+
+binary_op!(Add, ADDOP);
+binary_op!(Concat, CONCATOP);
+binary_op!(Subtract, SUBTRACTOP);
+binary_op!(Multiply, MULTIPLYOP);
+binary_op!(ShiftLeft, SHIFTLEFTOP);
+binary_op!(ShiftRight, SHIFTRIGHTOP);
+binary_op!(And, ANDOP);
+binary_op!(Nand, NANDOP);
+binary_op!(Or, OROP);
+binary_op!(Nor, NOROP);
+binary_op!(Xor, XOROP);
+binary_op!(ConcatRes, CONCATRESOP);
+binary_op!(Mod, MODOP);
+binary_op!(Index, INDEXOP);
+binary_op!(ToString, TOSTRINGOP);
+
+/// MethodCall object with the method name and parameter objects.
+pub struct MethodCall<'a> {
+    name: Path,
+    args: Vec<&'a dyn Aml>,
+}
+
+impl<'a> MethodCall<'a> {
+    /// Create MethodCall object.
+    pub fn new(name: Path, args: Vec<&'a dyn Aml>) -> Self {
+        MethodCall { name, args }
+    }
+}
+
+impl<'a> Aml for MethodCall<'a> {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        self.name.to_aml_bytes(bytes);
+        for arg in self.args.iter() {
+            arg.to_aml_bytes(bytes);
+        }
+    }
+}
+
+/// Buffer object with the data in it.
+pub struct Buffer {
+    data: Vec<u8>,
+}
+
+impl Buffer {
+    /// Create Buffer object.
+    pub fn new(data: Vec<u8>) -> Self {
+        Buffer { data }
+    }
+}
+
+impl Aml for Buffer {
+    fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
+        let mut bytes = Vec::new();
+        self.data.len().to_aml_bytes(&mut bytes);
+        bytes.extend_from_slice(&self.data);
+
+        let mut pkg_length = create_pkg_length(&bytes, true);
+        pkg_length.reverse();
+        for byte in pkg_length {
+            bytes.insert(0, byte);
+        }
+
+        bytes.insert(0, BUFFEROP);
+
+        aml.append(&mut bytes)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_device() {
+        /*
+        Device (_SB.COM1)
+        {
+            Name (_HID, EisaId ("PNP0501") /* 16550A-compatible COM Serial Port */) // _HID: Hardware ID
+            Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
+            {
+                Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
+                {
+                    0x00000004,
+                }
+                IO (Decode16,
+                    0x03F8,             // Range Minimum
+                    0x03F8,             // Range Maximum
+                    0x00,               // Alignment
+                    0x08,               // Length
+                    )
+            }
+        }
+            */
+        let com1_device = [
+            0x5B, 0x82, 0x30, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x43, 0x4F, 0x4D, 0x31, 0x08, 0x5F,
+            0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11,
+            0x16, 0x0A, 0x13, 0x89, 0x06, 0x00, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x47, 0x01,
+            0xF8, 0x03, 0xF8, 0x03, 0x00, 0x08, 0x79, 0x00,
+        ];
+        let mut aml = Vec::new();
+
+        Device::new(
+            "_SB_.COM1".into(),
+            vec![
+                &Name::new("_HID".into(), &EISAName::new("PNP0501")),
+                &Name::new(
+                    "_CRS".into(),
+                    &ResourceTemplate::new(vec![
+                        &Interrupt::new(true, true, false, false, 4),
+                        &IO::new(0x3f8, 0x3f8, 0, 0x8),
+                    ]),
+                ),
+            ],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &com1_device[..]);
+    }
+
+    #[test]
+    fn test_scope() {
+        /*
+        Scope (_SB.MBRD)
+        {
+            Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
+            {
+                Memory32Fixed (ReadWrite,
+                    0xE8000000,         // Address Base
+                    0x10000000,         // Address Length
+                    )
+            })
+        }
+        */
+
+        let mbrd_scope = [
+            0x10, 0x21, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x4D, 0x42, 0x52, 0x44, 0x08, 0x5F, 0x43,
+            0x52, 0x53, 0x11, 0x11, 0x0A, 0x0E, 0x86, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE8,
+            0x00, 0x00, 0x00, 0x10, 0x79, 0x00,
+        ];
+        let mut aml = Vec::new();
+
+        Scope::new(
+            "_SB_.MBRD".into(),
+            vec![&Name::new(
+                "_CRS".into(),
+                &ResourceTemplate::new(vec![&Memory32Fixed::new(true, 0xE800_0000, 0x1000_0000)]),
+            )],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &mbrd_scope[..]);
+    }
+
+    #[test]
+    fn test_resource_template() {
+        /*
+        Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
+        {
+            Memory32Fixed (ReadWrite,
+                0xE8000000,         // Address Base
+                0x10000000,         // Address Length
+                )
+        })
+        */
+        let crs_memory_32_fixed = [
+            0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x11, 0x0A, 0x0E, 0x86, 0x09, 0x00, 0x01, 0x00,
+            0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x10, 0x79, 0x00,
+        ];
+        let mut aml = Vec::new();
+
+        Name::new(
+            "_CRS".into(),
+            &ResourceTemplate::new(vec![&Memory32Fixed::new(true, 0xE800_0000, 0x1000_0000)]),
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, crs_memory_32_fixed);
+
+        /*
+            Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
+            {
+                WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,
+                    0x0000,             // Granularity
+                    0x0000,             // Range Minimum
+                    0x00FF,             // Range Maximum
+                    0x0000,             // Translation Offset
+                    0x0100,             // Length
+                    ,, )
+                WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
+                    0x0000,             // Granularity
+                    0x0000,             // Range Minimum
+                    0x0CF7,             // Range Maximum
+                    0x0000,             // Translation Offset
+                    0x0CF8,             // Length
+                    ,, , TypeStatic, DenseTranslation)
+                WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
+                    0x0000,             // Granularity
+                    0x0D00,             // Range Minimum
+                    0xFFFF,             // Range Maximum
+                    0x0000,             // Translation Offset
+                    0xF300,             // Length
+                    ,, , TypeStatic, DenseTranslation)
+                DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
+                    0x00000000,         // Granularity
+                    0x000A0000,         // Range Minimum
+                    0x000BFFFF,         // Range Maximum
+                    0x00000000,         // Translation Offset
+                    0x00020000,         // Length
+                    ,, , AddressRangeMemory, TypeStatic)
+                DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite,
+                    0x00000000,         // Granularity
+                    0xC0000000,         // Range Minimum
+                    0xFEBFFFFF,         // Range Maximum
+                    0x00000000,         // Translation Offset
+                    0x3EC00000,         // Length
+                    ,, , AddressRangeMemory, TypeStatic)
+                QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
+                    0x0000000000000000, // Granularity
+                    0x0000000800000000, // Range Minimum
+                    0x0000000FFFFFFFFF, // Range Maximum
+                    0x0000000000000000, // Translation Offset
+                    0x0000000800000000, // Length
+                    ,, , AddressRangeMemory, TypeStatic)
+            })
+        */
+
+        // WordBusNumber from above
+        let crs_word_bus_number = [
+            0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x15, 0x0A, 0x12, 0x88, 0x0D, 0x00, 0x02, 0x0C,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x79, 0x00,
+        ];
+        aml.clear();
+
+        Name::new(
+            "_CRS".into(),
+            &ResourceTemplate::new(vec![&AddressSpace::new_bus_number(0x0u16, 0xffu16)]),
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &crs_word_bus_number);
+
+        // WordIO blocks (x 2) from above
+        let crs_word_io = [
+            0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x25, 0x0A, 0x22, 0x88, 0x0D, 0x00, 0x01, 0x0C,
+            0x03, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0xF8, 0x0C, 0x88, 0x0D, 0x00,
+            0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF3, 0x79,
+            0x00,
+        ];
+        aml.clear();
+
+        Name::new(
+            "_CRS".into(),
+            &ResourceTemplate::new(vec![
+                &AddressSpace::new_io(0x0u16, 0xcf7u16),
+                &AddressSpace::new_io(0xd00u16, 0xffffu16),
+            ]),
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &crs_word_io[..]);
+
+        // DWordMemory blocks (x 2) from above
+        let crs_dword_memory = [
+            0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x39, 0x0A, 0x36, 0x87, 0x17, 0x00, 0x00, 0x0C,
+            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x0B, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x01, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xBF, 0xFE, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0xC0, 0x3E, 0x79, 0x00,
+        ];
+        aml.clear();
+
+        Name::new(
+            "_CRS".into(),
+            &ResourceTemplate::new(vec![
+                &AddressSpace::new_memory(
+                    AddressSpaceCachable::Cacheable,
+                    true,
+                    0xa_0000u32,
+                    0xb_ffffu32,
+                ),
+                &AddressSpace::new_memory(
+                    AddressSpaceCachable::NotCacheable,
+                    true,
+                    0xc000_0000u32,
+                    0xfebf_ffffu32,
+                ),
+            ]),
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &crs_dword_memory[..]);
+
+        // QWordMemory from above
+        let crs_qword_memory = [
+            0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x33, 0x0A, 0x30, 0x8A, 0x2B, 0x00, 0x00, 0x0C,
+            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+            0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x79,
+            0x00,
+        ];
+        aml.clear();
+        Name::new(
+            "_CRS".into(),
+            &ResourceTemplate::new(vec![&AddressSpace::new_memory(
+                AddressSpaceCachable::Cacheable,
+                true,
+                0x8_0000_0000u64,
+                0xf_ffff_ffffu64,
+            )]),
+        )
+        .to_aml_bytes(&mut aml);
+
+        assert_eq!(aml, &crs_qword_memory[..]);
+
+        /*
+            Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
+            {
+                Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
+                {
+                    0x00000004,
+                }
+                IO (Decode16,
+                    0x03F8,             // Range Minimum
+                    0x03F8,             // Range Maximum
+                    0x00,               // Alignment
+                    0x08,               // Length
+                    )
+            })
+
+        */
+        let interrupt_io_data = [
+            0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x16, 0x0A, 0x13, 0x89, 0x06, 0x00, 0x03, 0x01,
+            0x04, 0x00, 0x00, 0x00, 0x47, 0x01, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x08, 0x79, 0x00,
+        ];
+        aml.clear();
+        Name::new(
+            "_CRS".into(),
+            &ResourceTemplate::new(vec![
+                &Interrupt::new(true, true, false, false, 4),
+                &IO::new(0x3f8, 0x3f8, 0, 0x8),
+            ]),
+        )
+        .to_aml_bytes(&mut aml);
+
+        assert_eq!(aml, &interrupt_io_data[..]);
+    }
+
+    #[test]
+    fn test_pkg_length() {
+        assert_eq!(create_pkg_length(&[0u8; 62].to_vec(), true), vec![63]);
+        assert_eq!(
+            create_pkg_length(&[0u8; 64].to_vec(), true),
+            vec![1 << 6 | (66 & 0xf), 66 >> 4]
+        );
+        assert_eq!(
+            create_pkg_length(&[0u8; 4096].to_vec(), true),
+            vec![
+                2 << 6 | (4099 & 0xf) as u8,
+                (4099 >> 4) as u8,
+                (4099 >> 12) as u8
+            ]
+        );
+    }
+
+    #[test]
+    fn test_package() {
+        /*
+        Name (_S5, Package (0x01)  // _S5_: S5 System State
+        {
+            0x05
+        })
+        */
+        let s5_sleep_data = [0x08, 0x5F, 0x53, 0x35, 0x5F, 0x12, 0x04, 0x01, 0x0A, 0x05];
+        let mut aml = Vec::new();
+
+        Name::new("_S5_".into(), &Package::new(vec![&5u8])).to_aml_bytes(&mut aml);
+
+        assert_eq!(s5_sleep_data.to_vec(), aml);
+    }
+
+    #[test]
+    fn test_eisa_name() {
+        let mut aml = Vec::new();
+        Name::new("_HID".into(), &EISAName::new("PNP0501")).to_aml_bytes(&mut aml);
+        assert_eq!(
+            aml,
+            [0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01],
+        )
+    }
+    #[test]
+    fn test_name_path() {
+        let mut aml = Vec::new();
+        (&"_SB_".into() as &Path).to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x5Fu8, 0x53, 0x42, 0x5F]);
+        aml.clear();
+        (&"\\_SB_".into() as &Path).to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x5C, 0x5F, 0x53, 0x42, 0x5F]);
+        aml.clear();
+        (&"_SB_.COM1".into() as &Path).to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x43, 0x4F, 0x4D, 0x31]);
+        aml.clear();
+        (&"_SB_.PCI0._HID".into() as &Path).to_aml_bytes(&mut aml);
+        assert_eq!(
+            aml,
+            [0x2F, 0x03, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x30, 0x5F, 0x48, 0x49, 0x44]
+        );
+    }
+
+    #[test]
+    fn test_numbers() {
+        let mut aml = Vec::new();
+        128u8.to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x0a, 0x80]);
+        aml.clear();
+        1024u16.to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x0b, 0x0, 0x04]);
+        aml.clear();
+        (16u32 << 20).to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x0c, 0x00, 0x00, 0x0, 0x01]);
+        aml.clear();
+        0xdeca_fbad_deca_fbadu64.to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x0e, 0xad, 0xfb, 0xca, 0xde, 0xad, 0xfb, 0xca, 0xde]);
+    }
+
+    #[test]
+    fn test_name() {
+        let mut aml = Vec::new();
+        Name::new("_SB_.PCI0._UID".into(), &0x1234u16).to_aml_bytes(&mut aml);
+        assert_eq!(
+            aml,
+            [
+                0x08, /* NameOp */
+                0x2F, /* MultiNamePrefix */
+                0x03, /* 3 name parts */
+                0x5F, 0x53, 0x42, 0x5F, /* _SB_ */
+                0x50, 0x43, 0x49, 0x30, /* PCI0 */
+                0x5F, 0x55, 0x49, 0x44, /* _UID  */
+                0x0b, /* WordPrefix */
+                0x34, 0x12
+            ]
+        );
+    }
+
+    #[test]
+    fn test_string() {
+        let mut aml = Vec::new();
+        (&"ACPI" as &dyn Aml).to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x0d, b'A', b'C', b'P', b'I', 0]);
+        aml.clear();
+        "ACPI".to_owned().to_aml_bytes(&mut aml);
+        assert_eq!(aml, [0x0d, b'A', b'C', b'P', b'I', 0]);
+    }
+
+    #[test]
+    fn test_method() {
+        let mut aml = Vec::new();
+        Method::new("_STA".into(), 0, false, vec![&Return::new(&0xfu8)]).to_aml_bytes(&mut aml);
+        assert_eq!(
+            aml,
+            [0x14, 0x09, 0x5F, 0x53, 0x54, 0x41, 0x00, 0xA4, 0x0A, 0x0F]
+        );
+    }
+
+    #[test]
+    fn test_field() {
+        /*
+            Field (PRST, ByteAcc, NoLock, WriteAsZeros)
+            {
+                Offset (0x04),
+                CPEN,   1,
+                CINS,   1,
+                CRMV,   1,
+                CEJ0,   1,
+                Offset (0x05),
+                CCMD,   8
+            }
+
+        */
+
+        let field_data = [
+            0x5Bu8, 0x81, 0x23, 0x50, 0x52, 0x53, 0x54, 0x41, 0x00, 0x20, 0x43, 0x50, 0x45, 0x4E,
+            0x01, 0x43, 0x49, 0x4E, 0x53, 0x01, 0x43, 0x52, 0x4D, 0x56, 0x01, 0x43, 0x45, 0x4A,
+            0x30, 0x01, 0x00, 0x04, 0x43, 0x43, 0x4D, 0x44, 0x08,
+        ];
+        let mut aml = Vec::new();
+
+        Field::new(
+            "PRST".into(),
+            FieldAccessType::Byte,
+            FieldUpdateRule::WriteAsZeroes,
+            vec![
+                FieldEntry::Reserved(32),
+                FieldEntry::Named(*b"CPEN", 1),
+                FieldEntry::Named(*b"CINS", 1),
+                FieldEntry::Named(*b"CRMV", 1),
+                FieldEntry::Named(*b"CEJ0", 1),
+                FieldEntry::Reserved(4),
+                FieldEntry::Named(*b"CCMD", 8),
+            ],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &field_data[..]);
+
+        /*
+            Field (PRST, DWordAcc, NoLock, Preserve)
+            {
+                CSEL,   32,
+                Offset (0x08),
+                CDAT,   32
+            }
+        */
+
+        let field_data = [
+            0x5Bu8, 0x81, 0x12, 0x50, 0x52, 0x53, 0x54, 0x03, 0x43, 0x53, 0x45, 0x4C, 0x20, 0x00,
+            0x20, 0x43, 0x44, 0x41, 0x54, 0x20,
+        ];
+        aml.clear();
+
+        Field::new(
+            "PRST".into(),
+            FieldAccessType::DWord,
+            FieldUpdateRule::Preserve,
+            vec![
+                FieldEntry::Named(*b"CSEL", 32),
+                FieldEntry::Reserved(32),
+                FieldEntry::Named(*b"CDAT", 32),
+            ],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &field_data[..]);
+    }
+
+    #[test]
+    fn test_op_region() {
+        /*
+            OperationRegion (PRST, SystemIO, 0x0CD8, 0x0C)
+        */
+        let op_region_data = [
+            0x5Bu8, 0x80, 0x50, 0x52, 0x53, 0x54, 0x01, 0x0B, 0xD8, 0x0C, 0x0A, 0x0C,
+        ];
+        let mut aml = Vec::new();
+
+        OpRegion::new("PRST".into(), OpRegionSpace::SystemIO, 0xcd8, 0xc).to_aml_bytes(&mut aml);
+        assert_eq!(aml, &op_region_data[..]);
+    }
+
+    #[test]
+    fn test_arg_if() {
+        /*
+            Method(TEST, 1, NotSerialized) {
+                If (Arg0 == Zero) {
+                        Return(One)
+                }
+                Return(Zero)
+            }
+        */
+        let arg_if_data = [
+            0x14, 0x0F, 0x54, 0x45, 0x53, 0x54, 0x01, 0xA0, 0x06, 0x93, 0x68, 0x00, 0xA4, 0x01,
+            0xA4, 0x00,
+        ];
+        let mut aml = Vec::new();
+
+        Method::new(
+            "TEST".into(),
+            1,
+            false,
+            vec![
+                &If::new(&Equal::new(&Arg(0), &ZERO), vec![&Return::new(&ONE)]),
+                &Return::new(&ZERO),
+            ],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &arg_if_data);
+    }
+
+    #[test]
+    fn test_local_if() {
+        /*
+            Method(TEST, 0, NotSerialized) {
+                Local0 = One
+                If (Local0 == Zero) {
+                        Return(One)
+                }
+                Return(Zero)
+            }
+        */
+        let local_if_data = [
+            0x14, 0x12, 0x54, 0x45, 0x53, 0x54, 0x00, 0x70, 0x01, 0x60, 0xA0, 0x06, 0x93, 0x60,
+            0x00, 0xA4, 0x01, 0xA4, 0x00,
+        ];
+        let mut aml = Vec::new();
+
+        Method::new(
+            "TEST".into(),
+            0,
+            false,
+            vec![
+                &Store::new(&Local(0), &ONE),
+                &If::new(&Equal::new(&Local(0), &ZERO), vec![&Return::new(&ONE)]),
+                &Return::new(&ZERO),
+            ],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &local_if_data);
+    }
+
+    #[test]
+    fn test_mutex() {
+        /*
+        Device (_SB_.MHPC)
+        {
+                Name (_HID, EisaId("PNP0A06") /* Generic Container Device */)  // _HID: Hardware ID
+                Mutex (MLCK, 0x00)
+                Method (TEST, 0, NotSerialized)
+                {
+                    Acquire (MLCK, 0xFFFF)
+                    Local0 = One
+                    Release (MLCK)
+                }
+        }
+        */
+
+        let mutex_data = [
+            0x5B, 0x82, 0x33, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x4D, 0x48, 0x50, 0x43, 0x08, 0x5F,
+            0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x06, 0x5B, 0x01, 0x4D, 0x4C, 0x43, 0x4B,
+            0x00, 0x14, 0x17, 0x54, 0x45, 0x53, 0x54, 0x00, 0x5B, 0x23, 0x4D, 0x4C, 0x43, 0x4B,
+            0xFF, 0xFF, 0x70, 0x01, 0x60, 0x5B, 0x27, 0x4D, 0x4C, 0x43, 0x4B,
+        ];
+        let mut aml = Vec::new();
+
+        let mutex = Mutex::new("MLCK".into(), 0);
+        Device::new(
+            "_SB_.MHPC".into(),
+            vec![
+                &Name::new("_HID".into(), &EISAName::new("PNP0A06")),
+                &mutex,
+                &Method::new(
+                    "TEST".into(),
+                    0,
+                    false,
+                    vec![
+                        &Acquire::new("MLCK".into(), 0xffff),
+                        &Store::new(&Local(0), &ONE),
+                        &Release::new("MLCK".into()),
+                    ],
+                ),
+            ],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &mutex_data[..]);
+    }
+
+    #[test]
+    fn test_notify() {
+        /*
+        Device (_SB.MHPC)
+        {
+            Name (_HID, EisaId ("PNP0A06") /* Generic Container Device */)  // _HID: Hardware ID
+            Method (TEST, 0, NotSerialized)
+            {
+                Notify (MHPC, One) // Device Check
+            }
+        }
+        */
+        let notify_data = [
+            0x5B, 0x82, 0x21, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x4D, 0x48, 0x50, 0x43, 0x08, 0x5F,
+            0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x06, 0x14, 0x0C, 0x54, 0x45, 0x53, 0x54,
+            0x00, 0x86, 0x4D, 0x48, 0x50, 0x43, 0x01,
+        ];
+        let mut aml = Vec::new();
+
+        Device::new(
+            "_SB_.MHPC".into(),
+            vec![
+                &Name::new("_HID".into(), &EISAName::new("PNP0A06")),
+                &Method::new(
+                    "TEST".into(),
+                    0,
+                    false,
+                    vec![&Notify::new(&Path::new("MHPC"), &ONE)],
+                ),
+            ],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &notify_data[..]);
+    }
+
+    #[test]
+    fn test_while() {
+        /*
+        Device (_SB.MHPC)
+        {
+            Name (_HID, EisaId ("PNP0A06") /* Generic Container Device */)  // _HID: Hardware ID
+            Method (TEST, 0, NotSerialized)
+            {
+                Local0 = Zero
+                While ((Local0 < 0x04))
+                {
+                    Local0 += One
+                }
+            }
+        }
+        */
+
+        let while_data = [
+            0x5B, 0x82, 0x28, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x4D, 0x48, 0x50, 0x43, 0x08, 0x5F,
+            0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x06, 0x14, 0x13, 0x54, 0x45, 0x53, 0x54,
+            0x00, 0x70, 0x00, 0x60, 0xA2, 0x09, 0x95, 0x60, 0x0A, 0x04, 0x72, 0x60, 0x01, 0x60,
+        ];
+        let mut aml = Vec::new();
+
+        Device::new(
+            "_SB_.MHPC".into(),
+            vec![
+                &Name::new("_HID".into(), &EISAName::new("PNP0A06")),
+                &Method::new(
+                    "TEST".into(),
+                    0,
+                    false,
+                    vec![
+                        &Store::new(&Local(0), &ZERO),
+                        &While::new(
+                            &LessThan::new(&Local(0), &4usize),
+                            vec![&Add::new(&Local(0), &Local(0), &ONE)],
+                        ),
+                    ],
+                ),
+            ],
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &while_data[..])
+    }
+
+    #[test]
+    fn test_method_call() {
+        /*
+            Method (TST1, 1, NotSerialized)
+            {
+                TST2 (One, One)
+            }
+
+            Method (TST2, 2, NotSerialized)
+            {
+                TST1 (One)
+            }
+        */
+        let test_data = [
+            0x14, 0x0C, 0x54, 0x53, 0x54, 0x31, 0x01, 0x54, 0x53, 0x54, 0x32, 0x01, 0x01, 0x14,
+            0x0B, 0x54, 0x53, 0x54, 0x32, 0x02, 0x54, 0x53, 0x54, 0x31, 0x01,
+        ];
+
+        let mut methods = Vec::new();
+        Method::new(
+            "TST1".into(),
+            1,
+            false,
+            vec![&MethodCall::new("TST2".into(), vec![&ONE, &ONE])],
+        )
+        .to_aml_bytes(&mut methods);
+        Method::new(
+            "TST2".into(),
+            2,
+            false,
+            vec![&MethodCall::new("TST1".into(), vec![&ONE])],
+        )
+        .to_aml_bytes(&mut methods);
+        assert_eq!(&methods[..], &test_data[..])
+    }
+
+    #[test]
+    fn test_buffer() {
+        /*
+        Name (_MAT, Buffer (0x08)  // _MAT: Multiple APIC Table Entry
+        {
+            0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00   /* ........ */
+        })
+        */
+        let buffer_data = [
+            0x08, 0x5F, 0x4D, 0x41, 0x54, 0x11, 0x0B, 0x0A, 0x08, 0x00, 0x08, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00,
+        ];
+        let mut aml = Vec::new();
+
+        Name::new(
+            "_MAT".into(),
+            &Buffer::new(vec![0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]),
+        )
+        .to_aml_bytes(&mut aml);
+        assert_eq!(aml, &buffer_data[..])
+    }
+}
diff --git a/acpi_tables/src/lib.rs b/acpi_tables/src/lib.rs
index 49cf760..e45aa40 100644
--- a/acpi_tables/src/lib.rs
+++ b/acpi_tables/src/lib.rs
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+pub mod aml;
 pub mod rsdp;
 pub mod sdt;
 
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index 62e5b25..3445f18 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -5,6 +5,7 @@
 pub mod android;
 pub mod fdt;
 pub mod pstore;
+pub mod serial;
 
 use std::collections::BTreeMap;
 use std::error::Error as StdError;
@@ -18,8 +19,8 @@ use std::sync::Arc;
 use devices::split_irqchip_common::GsiRelay;
 use devices::virtio::VirtioDevice;
 use devices::{
-    Bus, BusDevice, BusError, PciDevice, PciDeviceError, PciInterruptPin, PciRoot, ProxyDevice,
-    SerialParameters, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR,
+    Bus, BusDevice, BusError, PciAddress, PciDevice, PciDeviceError, PciInterruptPin, PciRoot,
+    ProxyDevice,
 };
 use io_jail::Minijail;
 use kvm::{IoeventAddress, Kvm, Vcpu, Vm};
@@ -28,6 +29,11 @@ use sync::Mutex;
 use sys_util::{syslog, EventFd, GuestAddress, GuestMemory, GuestMemoryError};
 use vm_control::VmIrqRequestSocket;
 
+pub use serial::{
+    add_serial_devices, get_serial_cmdline, set_default_serial_parameters, GetSerialCmdlineError,
+    SerialHardware, SerialParameters, SerialType, SERIAL_ADDR,
+};
+
 pub enum VmImage {
     Kernel(File),
     Bios(File),
@@ -93,7 +99,7 @@ pub trait LinuxArch {
         components: VmComponents,
         split_irqchip: bool,
         ioapic_device_socket: VmIrqRequestSocket,
-        serial_parameters: &BTreeMap<u8, SerialParameters>,
+        serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
         create_devices: F,
     ) -> Result<RunnableLinuxVm, Self::Error>
@@ -119,11 +125,13 @@ pub enum DeviceRegistrationError {
     /// Unable to create a pipe.
     CreatePipe(sys_util::Error),
     /// Unable to create serial device from serial parameters.
-    CreateSerialDevice(devices::SerialError),
+    CreateSerialDevice(serial::Error),
     /// Could not clone an event fd.
     EventFdClone(sys_util::Error),
     /// Could not create an event fd.
     EventFdCreate(sys_util::Error),
+    /// Missing a required serial device.
+    MissingRequiredSerialDevice(u8),
     /// Could not add a device to the mmio bus.
     MmioInsert(BusError),
     /// Failed to register ioevent with VM.
@@ -155,6 +163,7 @@ impl Display for DeviceRegistrationError {
             Cmdline(e) => write!(f, "unable to add device to kernel command line: {}", e),
             EventFdClone(e) => write!(f, "failed to clone eventfd: {}", e),
             EventFdCreate(e) => write!(f, "failed to create eventfd: {}", e),
+            MissingRequiredSerialDevice(n) => write!(f, "missing required serial device {}", n),
             MmioInsert(e) => write!(f, "failed to add to mmio bus: {}", e),
             RegisterIoevent(e) => write!(f, "failed to register ioevent to VM: {}", e),
             RegisterIrqfd(e) => write!(f, "failed to register irq eventfd to VM: {}", e),
@@ -175,14 +184,25 @@ pub fn generate_pci_root(
     mmio_bus: &mut Bus,
     resources: &mut SystemAllocator,
     vm: &mut Vm,
-) -> Result<(PciRoot, Vec<(u32, PciInterruptPin)>, BTreeMap<u32, String>), DeviceRegistrationError>
-{
+) -> Result<
+    (
+        PciRoot,
+        Vec<(PciAddress, u32, PciInterruptPin)>,
+        BTreeMap<u32, String>,
+    ),
+    DeviceRegistrationError,
+> {
     let mut root = PciRoot::new();
     let mut pci_irqs = Vec::new();
     let mut pid_labels = BTreeMap::new();
     for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() {
-        // Only support one bus.
-        device.assign_bus_dev(0, dev_idx as u8);
+        // Auto assign PCI device numbers starting from 1
+        let address = PciAddress {
+            bus: 0,
+            dev: 1 + dev_idx as u8,
+            func: 0,
+        };
+        device.assign_address(address);
 
         let mut keep_fds = device.keep_fds();
         syslog::push_fds(&mut keep_fds);
@@ -216,7 +236,7 @@ pub fn generate_pci_root(
         keep_fds.push(irqfd.as_raw_fd());
         keep_fds.push(irq_resample_fd.as_raw_fd());
         device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin);
-        pci_irqs.push((dev_idx as u32, pci_irq_pin));
+        pci_irqs.push((address, irq_num, pci_irq_pin));
 
         let ranges = device
             .allocate_io_bars(resources)
@@ -242,7 +262,7 @@ pub fn generate_pci_root(
             device.on_sandboxed();
             Arc::new(Mutex::new(device))
         };
-        root.add_device(arced_dev.clone());
+        root.add_device(address, arced_dev.clone());
         for range in &ranges {
             mmio_bus
                 .insert(arced_dev.clone(), range.0, range.1, true)
@@ -258,70 +278,6 @@ pub fn generate_pci_root(
     Ok((root, pci_irqs, pid_labels))
 }
 
-/// Adds serial devices to the provided bus based on the serial parameters given. Returns the serial
-///  port number and serial device to be used for stdout if defined.
-///
-/// # Arguments
-///
-/// * `io_bus` - Bus to add the devices to
-/// * `com_evt_1_3` - eventfd for com1 and com3
-/// * `com_evt_1_4` - eventfd for com2 and com4
-/// * `io_bus` - Bus to add the devices to
-/// * `serial_parameters` - definitions of serial parameter configurations. If a setting is not
-///   provided for a port, then it will use the default configuration.
-pub fn add_serial_devices(
-    io_bus: &mut Bus,
-    com_evt_1_3: &EventFd,
-    com_evt_2_4: &EventFd,
-    serial_parameters: &BTreeMap<u8, SerialParameters>,
-    serial_jail: Option<Minijail>,
-) -> Result<Option<u8>, DeviceRegistrationError> {
-    let mut stdio_serial_num = None;
-
-    for x in 0..=3 {
-        let com_evt = match x {
-            0 => com_evt_1_3,
-            1 => com_evt_2_4,
-            2 => com_evt_1_3,
-            3 => com_evt_2_4,
-            _ => com_evt_1_3,
-        };
-
-        let param = serial_parameters
-            .get(&(x + 1))
-            .unwrap_or(&DEFAULT_SERIAL_PARAMS[x as usize]);
-
-        if param.console {
-            stdio_serial_num = Some(x + 1);
-        }
-
-        let mut preserved_fds = Vec::new();
-        let com = param
-            .create_serial_device(&com_evt, &mut preserved_fds)
-            .map_err(DeviceRegistrationError::CreateSerialDevice)?;
-
-        match serial_jail.as_ref() {
-            Some(jail) => {
-                let com = Arc::new(Mutex::new(
-                    ProxyDevice::new(com, &jail, preserved_fds)
-                        .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
-                ));
-                io_bus
-                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
-                    .unwrap();
-            }
-            None => {
-                let com = Arc::new(Mutex::new(com));
-                io_bus
-                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
-                    .unwrap();
-            }
-        }
-    }
-
-    Ok(stdio_serial_num)
-}
-
 /// Errors for image loading.
 #[derive(Debug)]
 pub enum LoadImageError {
diff --git a/arch/src/serial.rs b/arch/src/serial.rs
new file mode 100644
index 0000000..f24f4bc
--- /dev/null
+++ b/arch/src/serial.rs
@@ -0,0 +1,481 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::collections::BTreeMap;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::{self, stdin, stdout};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::PathBuf;
+use std::str::FromStr;
+use std::sync::Arc;
+
+use devices::{Bus, ProxyDevice, Serial, SerialDevice};
+use io_jail::Minijail;
+use sync::Mutex;
+use sys_util::{read_raw_stdin, syslog, EventFd};
+
+use crate::DeviceRegistrationError;
+
+#[derive(Debug)]
+pub enum Error {
+    CloneEventFd(sys_util::Error),
+    FileError(std::io::Error),
+    InvalidSerialHardware(String),
+    InvalidSerialType(String),
+    PathRequired,
+    Unimplemented(SerialType),
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        match self {
+            CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
+            FileError(e) => write!(f, "unable to open/create file: {}", e),
+            InvalidSerialHardware(e) => write!(f, "invalid serial hardware: {}", e),
+            InvalidSerialType(e) => write!(f, "invalid serial type: {}", e),
+            PathRequired => write!(f, "serial device type file requires a path"),
+            Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()),
+        }
+    }
+}
+
+/// Enum for possible type of serial devices
+#[derive(Clone, Debug)]
+pub enum SerialType {
+    File,
+    Stdout,
+    Sink,
+    Syslog,
+    UnixSocket, // NOT IMPLEMENTED
+}
+
+impl Display for SerialType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let s = match &self {
+            SerialType::File => "File".to_string(),
+            SerialType::Stdout => "Stdout".to_string(),
+            SerialType::Sink => "Sink".to_string(),
+            SerialType::Syslog => "Syslog".to_string(),
+            SerialType::UnixSocket => "UnixSocket".to_string(),
+        };
+
+        write!(f, "{}", s)
+    }
+}
+
+impl FromStr for SerialType {
+    type Err = Error;
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        match s {
+            "file" | "File" => Ok(SerialType::File),
+            "stdout" | "Stdout" => Ok(SerialType::Stdout),
+            "sink" | "Sink" => Ok(SerialType::Sink),
+            "syslog" | "Syslog" => Ok(SerialType::Syslog),
+            "unix" | "UnixSocket" => Ok(SerialType::UnixSocket),
+            _ => Err(Error::InvalidSerialType(s.to_string())),
+        }
+    }
+}
+
+/// Serial device hardware types
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum SerialHardware {
+    Serial,        // Standard PC-style (8250/16550 compatible) UART
+    VirtioConsole, // virtio-console device
+}
+
+impl Display for SerialHardware {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let s = match &self {
+            SerialHardware::Serial => "serial".to_string(),
+            SerialHardware::VirtioConsole => "virtio-console".to_string(),
+        };
+
+        write!(f, "{}", s)
+    }
+}
+
+impl FromStr for SerialHardware {
+    type Err = Error;
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        match s {
+            "serial" => Ok(SerialHardware::Serial),
+            "virtio-console" => Ok(SerialHardware::VirtioConsole),
+            _ => Err(Error::InvalidSerialHardware(s.to_string())),
+        }
+    }
+}
+
+/// Holds the parameters for a serial device
+#[derive(Clone, Debug)]
+pub struct SerialParameters {
+    pub type_: SerialType,
+    pub hardware: SerialHardware,
+    pub path: Option<PathBuf>,
+    pub input: Option<PathBuf>,
+    pub num: u8,
+    pub console: bool,
+    pub earlycon: bool,
+    pub stdin: bool,
+}
+
+impl SerialParameters {
+    /// Helper function to create a serial device from the defined parameters.
+    ///
+    /// # Arguments
+    /// * `evt_fd` - eventfd used for interrupt events
+    /// * `keep_fds` - Vector of FDs required by this device if it were sandboxed in a child
+    ///                process. `evt_fd` will always be added to this vector by this function.
+    pub fn create_serial_device<T: SerialDevice>(
+        &self,
+        evt_fd: &EventFd,
+        keep_fds: &mut Vec<RawFd>,
+    ) -> std::result::Result<T, Error> {
+        let evt_fd = evt_fd.try_clone().map_err(Error::CloneEventFd)?;
+        keep_fds.push(evt_fd.as_raw_fd());
+        let input: Option<Box<dyn io::Read + Send>> = if let Some(input_path) = &self.input {
+            let input_file = File::open(input_path.as_path()).map_err(Error::FileError)?;
+            keep_fds.push(input_file.as_raw_fd());
+            Some(Box::new(input_file))
+        } else if self.stdin {
+            keep_fds.push(stdin().as_raw_fd());
+            // This wrapper is used in place of the libstd native version because we don't want
+            // buffering for stdin.
+            struct StdinWrapper;
+            impl io::Read for StdinWrapper {
+                fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
+                    read_raw_stdin(out).map_err(|e| e.into())
+                }
+            }
+            Some(Box::new(StdinWrapper))
+        } else {
+            None
+        };
+        let output: Option<Box<dyn io::Write + Send>> = match self.type_ {
+            SerialType::Stdout => {
+                keep_fds.push(stdout().as_raw_fd());
+                Some(Box::new(stdout()))
+            }
+            SerialType::Sink => None,
+            SerialType::Syslog => {
+                syslog::push_fds(keep_fds);
+                Some(Box::new(syslog::Syslogger::new(
+                    syslog::Priority::Info,
+                    syslog::Facility::Daemon,
+                )))
+            }
+            SerialType::File => match &self.path {
+                Some(path) => {
+                    let file = File::create(path.as_path()).map_err(Error::FileError)?;
+                    keep_fds.push(file.as_raw_fd());
+                    Some(Box::new(file))
+                }
+                None => return Err(Error::PathRequired),
+            },
+            SerialType::UnixSocket => return Err(Error::Unimplemented(SerialType::UnixSocket)),
+        };
+        Ok(T::new(evt_fd, input, output, keep_fds.to_vec()))
+    }
+}
+
+/// Add the default serial parameters for serial ports that have not already been specified.
+///
+/// This ensures that `serial_parameters` will contain parameters for each of the four PC-style
+/// serial ports (COM1-COM4).
+///
+/// It also sets the first `SerialHardware::Serial` to be the default console device if no other
+/// serial parameters exist with console=true and the first serial device has not already been
+/// configured explicitly.
+pub fn set_default_serial_parameters(
+    serial_parameters: &mut BTreeMap<(SerialHardware, u8), SerialParameters>,
+) {
+    // If no console device exists and the first serial port has not been specified,
+    // set the first serial port as a stdout+stdin console.
+    let default_console = (SerialHardware::Serial, 1);
+    if !serial_parameters.iter().any(|(_, p)| p.console) {
+        serial_parameters
+            .entry(default_console)
+            .or_insert(SerialParameters {
+                type_: SerialType::Stdout,
+                hardware: SerialHardware::Serial,
+                path: None,
+                input: None,
+                num: 1,
+                console: true,
+                earlycon: false,
+                stdin: true,
+            });
+    }
+
+    // Ensure all four of the COM ports exist.
+    // If one of these four SerialHardware::Serial port was not configured by the user,
+    // set it up as a sink.
+    for num in 1..=4 {
+        let key = (SerialHardware::Serial, num);
+        serial_parameters.entry(key).or_insert(SerialParameters {
+            type_: SerialType::Sink,
+            hardware: SerialHardware::Serial,
+            path: None,
+            input: None,
+            num,
+            console: false,
+            earlycon: false,
+            stdin: false,
+        });
+    }
+}
+
+/// Address for Serial ports in x86
+pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
+
+/// Adds serial devices to the provided bus based on the serial parameters given.
+///
+/// Only devices with hardware type `SerialHardware::Serial` are added by this function.
+///
+/// # Arguments
+///
+/// * `io_bus` - Bus to add the devices to
+/// * `com_evt_1_3` - eventfd for com1 and com3
+/// * `com_evt_1_4` - eventfd for com2 and com4
+/// * `io_bus` - Bus to add the devices to
+/// * `serial_parameters` - definitions of serial parameter configurations.
+///   All four of the traditional PC-style serial ports (COM1-COM4) must be specified.
+pub fn add_serial_devices(
+    io_bus: &mut Bus,
+    com_evt_1_3: &EventFd,
+    com_evt_2_4: &EventFd,
+    serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
+    serial_jail: Option<Minijail>,
+) -> Result<(), DeviceRegistrationError> {
+    for x in 0..=3 {
+        let com_evt = match x {
+            0 => com_evt_1_3,
+            1 => com_evt_2_4,
+            2 => com_evt_1_3,
+            3 => com_evt_2_4,
+            _ => com_evt_1_3,
+        };
+
+        let param = serial_parameters
+            .get(&(SerialHardware::Serial, x + 1))
+            .ok_or(DeviceRegistrationError::MissingRequiredSerialDevice(x + 1))?;
+
+        let mut preserved_fds = Vec::new();
+        let com = param
+            .create_serial_device::<Serial>(&com_evt, &mut preserved_fds)
+            .map_err(DeviceRegistrationError::CreateSerialDevice)?;
+
+        match serial_jail.as_ref() {
+            Some(jail) => {
+                let com = Arc::new(Mutex::new(
+                    ProxyDevice::new(com, &jail, preserved_fds)
+                        .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
+                ));
+                io_bus
+                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
+                    .unwrap();
+            }
+            None => {
+                let com = Arc::new(Mutex::new(com));
+                io_bus
+                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
+                    .unwrap();
+            }
+        }
+    }
+
+    Ok(())
+}
+
+#[derive(Debug)]
+pub enum GetSerialCmdlineError {
+    KernelCmdline(kernel_cmdline::Error),
+    UnsupportedEarlyconHardware(SerialHardware),
+}
+
+impl Display for GetSerialCmdlineError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::GetSerialCmdlineError::*;
+
+        match self {
+            KernelCmdline(e) => write!(f, "error appending to cmdline: {}", e),
+            UnsupportedEarlyconHardware(hw) => {
+                write!(f, "hardware {} not supported as earlycon", hw)
+            }
+        }
+    }
+}
+
+pub type GetSerialCmdlineResult<T> = std::result::Result<T, GetSerialCmdlineError>;
+
+/// Add serial options to the provided `cmdline` based on `serial_parameters`.
+/// `serial_io_type` should be "io" if the platform uses x86-style I/O ports for serial devices
+/// or "mmio" if the serial ports are memory mapped.
+pub fn get_serial_cmdline(
+    cmdline: &mut kernel_cmdline::Cmdline,
+    serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
+    serial_io_type: &str,
+) -> GetSerialCmdlineResult<()> {
+    match serial_parameters
+        .iter()
+        .filter(|(_, p)| p.console)
+        .map(|(k, _)| k)
+        .next()
+    {
+        Some((SerialHardware::Serial, num)) => {
+            cmdline
+                .insert("console", &format!("ttyS{}", num - 1))
+                .map_err(GetSerialCmdlineError::KernelCmdline)?;
+        }
+        Some((SerialHardware::VirtioConsole, num)) => {
+            cmdline
+                .insert("console", &format!("hvc{}", num - 1))
+                .map_err(GetSerialCmdlineError::KernelCmdline)?;
+        }
+        None => {}
+    }
+
+    match serial_parameters
+        .iter()
+        .filter(|(_, p)| p.earlycon)
+        .map(|(k, _)| k)
+        .next()
+    {
+        Some((SerialHardware::Serial, num)) => {
+            if let Some(addr) = SERIAL_ADDR.get(*num as usize - 1) {
+                cmdline
+                    .insert(
+                        "earlycon",
+                        &format!("uart8250,{},0x{:x}", serial_io_type, addr),
+                    )
+                    .map_err(GetSerialCmdlineError::KernelCmdline)?;
+            }
+        }
+        Some((hw, _num)) => {
+            return Err(GetSerialCmdlineError::UnsupportedEarlyconHardware(*hw));
+        }
+        None => {}
+    }
+
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use kernel_cmdline::Cmdline;
+
+    #[test]
+    fn get_serial_cmdline_default() {
+        let mut cmdline = Cmdline::new(4096);
+        let mut serial_parameters = BTreeMap::new();
+
+        set_default_serial_parameters(&mut serial_parameters);
+        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
+            .expect("get_serial_cmdline failed");
+
+        let cmdline_str = cmdline.as_str();
+        assert!(cmdline_str.contains("console=ttyS0"));
+    }
+
+    #[test]
+    fn get_serial_cmdline_virtio_console() {
+        let mut cmdline = Cmdline::new(4096);
+        let mut serial_parameters = BTreeMap::new();
+
+        // Add a virtio-console device with console=true.
+        serial_parameters.insert(
+            (SerialHardware::VirtioConsole, 1),
+            SerialParameters {
+                type_: SerialType::Stdout,
+                hardware: SerialHardware::VirtioConsole,
+                path: None,
+                input: None,
+                num: 1,
+                console: true,
+                earlycon: false,
+                stdin: true,
+            },
+        );
+
+        set_default_serial_parameters(&mut serial_parameters);
+        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
+            .expect("get_serial_cmdline failed");
+
+        let cmdline_str = cmdline.as_str();
+        assert!(cmdline_str.contains("console=hvc0"));
+    }
+
+    #[test]
+    fn get_serial_cmdline_virtio_console_serial_earlycon() {
+        let mut cmdline = Cmdline::new(4096);
+        let mut serial_parameters = BTreeMap::new();
+
+        // Add a virtio-console device with console=true.
+        serial_parameters.insert(
+            (SerialHardware::VirtioConsole, 1),
+            SerialParameters {
+                type_: SerialType::Stdout,
+                hardware: SerialHardware::VirtioConsole,
+                path: None,
+                input: None,
+                num: 1,
+                console: true,
+                earlycon: false,
+                stdin: true,
+            },
+        );
+
+        // Override the default COM1 with an earlycon device.
+        serial_parameters.insert(
+            (SerialHardware::Serial, 1),
+            SerialParameters {
+                type_: SerialType::Stdout,
+                hardware: SerialHardware::Serial,
+                path: None,
+                input: None,
+                num: 1,
+                console: false,
+                earlycon: true,
+                stdin: false,
+            },
+        );
+
+        set_default_serial_parameters(&mut serial_parameters);
+        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
+            .expect("get_serial_cmdline failed");
+
+        let cmdline_str = cmdline.as_str();
+        assert!(cmdline_str.contains("console=hvc0"));
+        assert!(cmdline_str.contains("earlycon=uart8250,io,0x3f8"));
+    }
+
+    #[test]
+    fn get_serial_cmdline_virtio_console_invalid_earlycon() {
+        let mut cmdline = Cmdline::new(4096);
+        let mut serial_parameters = BTreeMap::new();
+
+        // Try to add a virtio-console device with earlycon=true (unsupported).
+        serial_parameters.insert(
+            (SerialHardware::VirtioConsole, 1),
+            SerialParameters {
+                type_: SerialType::Stdout,
+                hardware: SerialHardware::VirtioConsole,
+                path: None,
+                input: None,
+                num: 1,
+                console: false,
+                earlycon: true,
+                stdin: true,
+            },
+        );
+
+        set_default_serial_parameters(&mut serial_parameters);
+        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
+            .expect_err("get_serial_cmdline succeeded");
+    }
+}
diff --git a/async_core/src/eventfd.rs b/async_core/src/eventfd.rs
index b930f07..e0822a7 100644
--- a/async_core/src/eventfd.rs
+++ b/async_core/src/eventfd.rs
@@ -2,24 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use futures::Stream;
 use std::convert::TryFrom;
 use std::fmt::{self, Display};
+use std::future::Future;
 use std::os::unix::io::AsRawFd;
 use std::pin::Pin;
 use std::task::{Context, Poll};
 
 use libc::{EWOULDBLOCK, O_NONBLOCK};
 
+use cros_async::{self, add_read_waker, cancel_waker, WakerToken};
 use sys_util::{self, add_fd_flags};
 
-use cros_async::fd_executor::{self, add_read_waker};
-
 /// Errors generated while polling for events.
 #[derive(Debug)]
 pub enum Error {
     /// An error occurred attempting to register a waker with the executor.
-    AddingWaker(fd_executor::Error),
+    AddingWaker(cros_async::Error),
     /// Failure creating the event FD.
     EventFdCreate(sys_util::Error),
     /// An error occurred when reading the event FD.
@@ -50,21 +49,20 @@ impl Display for Error {
     }
 }
 
-/// Asynchronous version of `sys_util::EventFd`. Provides an implementation of `futures::Stream` so
-/// that events can be consumed in an async context.
+/// Asynchronous version of `sys_util::EventFd`. Provides asynchronous values that complete when the
+/// next event can be read from the eventfd.
 ///
 /// # Example
 ///
 /// ```
 /// use std::convert::TryInto;
 ///
-/// use async_core::{EventFd };
-/// use futures::StreamExt;
+/// use async_core::{EventFd};
 /// use sys_util::{self};
 ///
 /// async fn process_events() -> std::result::Result<(), Box<dyn std::error::Error>> {
 ///     let mut async_events: EventFd = sys_util::EventFd::new()?.try_into()?;
-///     while let Some(e) = async_events.next().await {
+///     while let Ok(e) = async_events.read_next().await {
 ///         // Handle event here.
 ///     }
 ///     Ok(())
@@ -72,13 +70,32 @@ impl Display for Error {
 /// ```
 pub struct EventFd {
     inner: sys_util::EventFd,
-    done: bool,
 }
 
 impl EventFd {
     pub fn new() -> Result<EventFd> {
         Self::try_from(sys_util::EventFd::new().map_err(Error::EventFdCreate)?)
     }
+
+    /// Asynchronously read the next value from the eventfd.
+    /// Returns a Future that can be `awaited` for the next value.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use async_core::EventFd;
+    /// async fn print_events(mut event_fd: EventFd) {
+    ///     loop {
+    ///         match event_fd.read_next().await {
+    ///             Ok(e) => println!("Got event: {}", e),
+    ///             Err(e) => break,
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    pub fn read_next(&mut self) -> NextValFuture {
+        NextValFuture::new(self)
+    }
 }
 
 impl TryFrom<sys_util::EventFd> for EventFd {
@@ -87,58 +104,72 @@ impl TryFrom<sys_util::EventFd> for EventFd {
     fn try_from(eventfd: sys_util::EventFd) -> Result<EventFd> {
         let fd = eventfd.as_raw_fd();
         add_fd_flags(fd, O_NONBLOCK).map_err(Error::SettingNonBlocking)?;
-        Ok(EventFd {
-            inner: eventfd,
-            done: false,
-        })
+        Ok(EventFd { inner: eventfd })
+    }
+}
+
+/// A Future that yields the next value from the eventfd when it is ready.
+pub struct NextValFuture<'a> {
+    eventfd: &'a mut EventFd,
+    waker_token: Option<WakerToken>,
+}
+
+impl<'a> NextValFuture<'a> {
+    fn new(eventfd: &'a mut EventFd) -> NextValFuture<'a> {
+        NextValFuture {
+            eventfd,
+            waker_token: None,
+        }
     }
 }
 
-impl Stream for EventFd {
-    type Item = Result<u64>;
+impl<'a> Future for NextValFuture<'a> {
+    type Output = Result<u64>;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
-        if self.done {
-            return Poll::Ready(None);
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+        if let Some(token) = self.waker_token.take() {
+            let _ = cancel_waker(token);
         }
 
-        let res = self
-            .inner
-            .read()
-            .map(|v| Poll::Ready(Some(Ok(v))))
-            .or_else(|e| {
+        match self.eventfd.inner.read() {
+            Ok(v) => Poll::Ready(Ok(v)),
+            Err(e) => {
                 if e.errno() == EWOULDBLOCK {
-                    add_read_waker(self.inner.as_raw_fd(), cx.waker().clone())
-                        .map(|()| Poll::Pending)
-                        .map_err(Error::AddingWaker)
+                    match add_read_waker(self.eventfd.inner.as_raw_fd(), cx.waker().clone()) {
+                        Ok(token) => {
+                            self.waker_token = Some(token);
+                            Poll::Pending
+                        }
+                        Err(e) => Poll::Ready(Err(Error::AddingWaker(e))),
+                    }
                 } else {
-                    Err(Error::EventFdRead(e))
+                    Poll::Ready(Err(Error::EventFdRead(e)))
                 }
-            });
-
-        match res {
-            Ok(v) => v,
-            Err(e) => {
-                self.done = true;
-                Poll::Ready(Some(Err(e)))
             }
         }
     }
 }
 
+impl<'a> Drop for NextValFuture<'a> {
+    fn drop(&mut self) {
+        if let Some(token) = self.waker_token.take() {
+            let _ = cancel_waker(token);
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
     use cros_async::{select2, SelectResult};
     use futures::future::pending;
     use futures::pin_mut;
-    use futures::stream::StreamExt;
 
     #[test]
     fn eventfd_write_read() {
         let evt = EventFd::new().unwrap();
         async fn read_one(mut evt: EventFd) -> u64 {
-            if let Some(Ok(e)) = evt.next().await {
+            if let Ok(e) = evt.read_next().await {
                 e
             } else {
                 66
diff --git a/async_core/src/lib.rs b/async_core/src/lib.rs
index 96cdcf1..e577bb5 100644
--- a/async_core/src/lib.rs
+++ b/async_core/src/lib.rs
@@ -7,5 +7,8 @@
 //! crate.
 
 mod eventfd;
+mod timerfd;
 
 pub use eventfd::EventFd;
+pub use timerfd::Error as TimerFdError;
+pub use timerfd::TimerFd;
diff --git a/async_core/src/timerfd.rs b/async_core/src/timerfd.rs
new file mode 100644
index 0000000..e8ae172
--- /dev/null
+++ b/async_core/src/timerfd.rs
@@ -0,0 +1,243 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::convert::TryFrom;
+use std::fmt::{self, Display};
+use std::future::Future;
+use std::os::unix::io::AsRawFd;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+use std::time::Duration;
+
+use libc::{EWOULDBLOCK, O_NONBLOCK};
+
+use cros_async::Error as ExecutorError;
+use cros_async::{add_read_waker, cancel_waker, WakerToken};
+use sys_util::{self, add_fd_flags};
+
+/// Errors generated while polling for events.
+#[derive(Debug)]
+pub enum Error {
+    /// An error occurred attempting to register a waker with the executor.
+    AddingWaker(ExecutorError),
+    /// An error occurred when reading the timer FD.
+    TimerFdRead(sys_util::Error),
+    /// An error occurred when resetting the timer FD.
+    TimerFdReset(sys_util::Error),
+    /// An error occurred when setting the timer FD non-blocking.
+    SettingNonBlocking(sys_util::Error),
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        match self {
+            AddingWaker(e) => write!(
+                f,
+                "An error occurred attempting to register a waker with the executor: {}.",
+                e
+            ),
+            TimerFdRead(e) => write!(f, "An error occurred when reading the timer FD: {}.", e),
+            TimerFdReset(e) => write!(f, "An error occurred when resetting the timer FD: {}.", e),
+            SettingNonBlocking(e) => {
+                write!(f, "An error occurred setting the FD non-blocking: {}.", e)
+            }
+        }
+    }
+}
+
+/// Asynchronous version of `sys_util::TimerFd`. Provides a method for asynchronously waiting for a
+/// timer to fire.
+///
+/// # Example
+///
+/// ```
+/// use std::convert::TryInto;
+///
+/// use async_core::TimerFd;
+/// use sys_util::{self, Result};
+/// async fn process_events() -> Result<()> {
+///     let mut async_timer: TimerFd = sys_util::TimerFd::new()?.try_into().unwrap();
+///     while let Ok(e) = async_timer.next_expiration().await {
+///         // Handle timer here.
+///     }
+///     Ok(())
+/// }
+/// ```
+pub struct TimerFd(sys_util::TimerFd);
+
+impl TimerFd {
+    /// Reset the inner timer to a new duration.
+    pub fn reset(&self, dur: Duration, interval: Option<Duration>) -> Result<()> {
+        self.0.reset(dur, interval).map_err(Error::TimerFdReset)
+    }
+
+    /// Asynchronously wait for the timer to fire.
+    /// Returns a Future that can be `awaited` for the next interval.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use async_core::TimerFd;
+    /// async fn print_events(mut timer_fd: TimerFd) {
+    ///     loop {
+    ///         match timer_fd.next_expiration().await {
+    ///             Ok(_) => println!("timer fired"),
+    ///             Err(e) => break,
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    pub fn next_expiration(&self) -> TimerFuture {
+        TimerFuture::new(&self.0)
+    }
+}
+
+impl TryFrom<sys_util::TimerFd> for TimerFd {
+    type Error = Error;
+
+    fn try_from(timerfd: sys_util::TimerFd) -> Result<TimerFd> {
+        let fd = timerfd.as_raw_fd();
+        add_fd_flags(fd, O_NONBLOCK).map_err(Error::SettingNonBlocking)?;
+        Ok(TimerFd(timerfd))
+    }
+}
+
+/// A Future that yields completes when the timer has fired the next time.
+pub struct TimerFuture<'a> {
+    inner: &'a sys_util::TimerFd,
+    waker_token: Option<WakerToken>,
+}
+
+impl<'a> TimerFuture<'a> {
+    fn new(inner: &'a sys_util::TimerFd) -> TimerFuture<'a> {
+        TimerFuture {
+            inner,
+            waker_token: None,
+        }
+    }
+}
+
+impl<'a> Future for TimerFuture<'a> {
+    type Output = Result<u64>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+        if let Some(token) = self.waker_token.take() {
+            let _ = cancel_waker(token);
+        }
+
+        match self.inner.wait() {
+            Ok(count) => Poll::Ready(Ok(count)),
+            Err(e) => {
+                if e.errno() == EWOULDBLOCK {
+                    match add_read_waker(self.inner.as_raw_fd(), cx.waker().clone()) {
+                        Ok(token) => {
+                            self.waker_token = Some(token);
+                            Poll::Pending
+                        }
+                        Err(e) => Poll::Ready(Err(Error::AddingWaker(e))),
+                    }
+                } else {
+                    // Indicate something went wrong and no more events will be provided.
+                    Poll::Ready(Err(Error::TimerFdRead(e)))
+                }
+            }
+        }
+    }
+}
+
+impl<'a> Drop for TimerFuture<'a> {
+    fn drop(&mut self) {
+        if let Some(token) = self.waker_token.take() {
+            let _ = cancel_waker(token);
+        }
+    }
+}
+
+impl AsRef<sys_util::TimerFd> for TimerFd {
+    fn as_ref(&self) -> &sys_util::TimerFd {
+        &self.0
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::time::Duration;
+
+    use cros_async::{run_one, select2, SelectResult};
+    use futures::future::pending;
+    use futures::future::Either;
+    use futures::pin_mut;
+
+    use super::*;
+
+    #[test]
+    fn timer_timeout() {
+        async fn timeout(duration: Duration) -> u64 {
+            let t = sys_util::TimerFd::new().unwrap();
+            let async_timer = TimerFd::try_from(t).unwrap();
+            async_timer.reset(duration, None).unwrap();
+            async_timer.next_expiration().await.unwrap()
+        }
+
+        let to = timeout(Duration::from_millis(10));
+        pin_mut!(to);
+        if let Ok((SelectResult::Finished(timer_count), SelectResult::Pending(_pend_fut))) =
+            select2(to, pending::<()>())
+        {
+            assert_eq!(timer_count, 1);
+        } else {
+            panic!("wrong futures returned from select2");
+        }
+    }
+
+    #[test]
+    fn select_with_eventfd() {
+        async fn async_test() {
+            let mut e = crate::eventfd::EventFd::new().unwrap();
+            let t = sys_util::TimerFd::new().unwrap();
+            let async_timer = TimerFd::try_from(t).unwrap();
+            async_timer.reset(Duration::from_millis(10), None).unwrap();
+            let timer = async_timer.next_expiration();
+            let event = e.read_next();
+            pin_mut!(timer);
+            pin_mut!(event);
+            match futures::future::select(timer, event).await {
+                Either::Left((_timer, _event)) => (),
+                _ => panic!("unexpected select result"),
+            }
+        }
+
+        let fut = async_test();
+
+        run_one(Box::pin(fut)).unwrap();
+    }
+
+    #[test]
+    fn pend_unarmed_cancel() {
+        async fn async_test() {
+            let t = sys_util::TimerFd::new().unwrap();
+            let async_timer = TimerFd::try_from(t).unwrap();
+            let done = async {
+                std::thread::sleep(Duration::from_millis(10));
+                5usize
+            };
+            let timer = async_timer.next_expiration();
+            pin_mut!(done);
+            pin_mut!(timer);
+            match futures::future::select(timer, done).await {
+                Either::Right((_, _timer)) => (),
+                _ => panic!("unexpected select result"),
+            }
+        }
+
+        let fut = async_test();
+
+        run_one(Box::pin(fut)).unwrap();
+    }
+}
diff --git a/bin/clippy b/bin/clippy
index b668875..3adce5f 100755
--- a/bin/clippy
+++ b/bin/clippy
@@ -16,10 +16,33 @@ cd "$(dirname "${BASH_SOURCE[0]}")"
 cd ..
 
 SUPPRESS=(
-    # To be resolved.
+    # TODO(crbug/908640): To be resolved.
+    borrowed_box
+    char_lit_as_u8
+    clone_on_copy
+    collapsible_if
+    comparison_chain
+    extra_unused_lifetimes
+    for_kv_map
+    inefficient_to_string
+    into_iter_on_ref
     let_unit_value
+    missing_safety_doc
+    needless_doctest_main
+    needless_range_loop
+    needless_return
+    option_map_unit_fn
     question_mark
     range_plus_one
+    redundant_clone
+    redundant_closure
+    single_match
+    slow_vector_initialization
+    unnecessary_filter_map
+    unnecessary_mut_passed
+    unneeded_field_pattern
+    useless_format
+    wrong_self_convention
 
     # We don't care about these lints. Okay to remain suppressed globally.
     blacklisted_name
@@ -49,4 +72,11 @@ SUPPRESS=(
 # Needed or else clippy won't re-run on code that has already compiled.
 cargo clean
 
-cargo clippy --all-features -- ${SUPPRESS[@]/#/-Aclippy::} "$@"
+# Need to set pass --sysroot for cargo-clippy manually.
+# cf. https://github.com/rust-lang/rust-clippy/issues/3523
+RUST_SYSROOT=$(rustc --print sysroot)
+RUSTFLAGS="${RUSTFLAGS:-}"
+export RUSTFLAGS="$RUSTFLAGS --sysroot=$RUST_SYSROOT"
+
+cargo clippy --all-features --all-targets -- ${SUPPRESS[@]/#/-Aclippy::} "$@" \
+      -D warnings
diff --git a/bin/smoke_test b/bin/smoke_test
index 50cb79c..74e1cd0 100755
--- a/bin/smoke_test
+++ b/bin/smoke_test
@@ -10,10 +10,13 @@ cd ../
 
 rustup default "$(cat rust-toolchain)"
 rustup component add rustfmt-preview
-cargo --version && rustc --version && rustfmt --version
+cargo --version && rustc --version && rustfmt --version \
+    && cargo clippy --version
 echo "Running cargo test"
 cargo test --no-fail-fast --features plugin,default-no-sandbox,wl-dmabuf,gpu,tpm \
     --all --exclude aarch64 $TEST_FLAGS -- \
     --test-threads=1 $TEST_RUNNER_FLAGS
 echo "Running cargo fmt"
 bin/fmt --check
+echo "Running cargo clippy"
+bin/clippy
diff --git a/cros_async/src/complete.rs b/cros_async/src/complete.rs
index 8455066..f29f4e8 100644
--- a/cros_async/src/complete.rs
+++ b/cros_async/src/complete.rs
@@ -21,7 +21,6 @@ macro_rules! generate {
         $(#[$doc:meta])*
         ($Complete:ident, <$($Fut:ident),*>),
     )*) => ($(
-        $(#[$doc])*
         #[must_use = "Combinations of futures don't do anything unless run in an executor."]
         paste::item! {
             pub(crate) struct $Complete<$($Fut: Future + Unpin),*> {
diff --git a/cros_async/src/executor.rs b/cros_async/src/executor.rs
index ac788f1..85d3f70 100644
--- a/cros_async/src/executor.rs
+++ b/cros_async/src/executor.rs
@@ -10,6 +10,8 @@ use std::rc::Rc;
 use std::task::Waker;
 use std::task::{Context, Poll};
 
+use futures::future::FutureExt;
+
 use crate::waker::create_waker;
 
 /// Represents a future executor that can be run. Implementers of the trait will take a list of
@@ -24,6 +26,9 @@ pub trait Executor {
     fn run(&mut self) -> Self::Output;
 }
 
+/// A token returned from `add_waker` that can be used to cancel the waker before it completes.
+pub struct WakerToken(pub(crate) u64);
+
 // Tracks if a future needs to be polled and the waker to use.
 pub(crate) struct FutureState {
     pub needs_poll: Rc<Cell<bool>>,
@@ -149,6 +154,49 @@ impl FutureList for UnitFutures {
     }
 }
 
+// Execute one future until it completes.
+pub(crate) struct RunOne<F: Future + Unpin> {
+    fut: F,
+    fut_state: FutureState,
+    added_futures: UnitFutures,
+}
+
+impl<F: Future + Unpin> RunOne<F> {
+    pub fn new(f: F) -> RunOne<F> {
+        RunOne {
+            fut: f,
+            fut_state: FutureState::new(),
+            added_futures: UnitFutures::new(),
+        }
+    }
+}
+
+impl<F: Future + Unpin> FutureList for RunOne<F> {
+    type Output = F::Output;
+
+    fn futures_mut(&mut self) -> &mut UnitFutures {
+        &mut self.added_futures
+    }
+
+    fn poll_results(&mut self) -> Option<Self::Output> {
+        let _ = self.added_futures.poll_results();
+
+        if self.fut_state.needs_poll.replace(false) {
+            let mut ctx = Context::from_waker(&self.fut_state.waker);
+            // The future impls `Unpin`, use `poll_unpin` to avoid wrapping it in
+            // `Pin` to call `poll`.
+            if let Poll::Ready(o) = self.fut.poll_unpin(&mut ctx) {
+                return Some(o);
+            }
+        };
+        None
+    }
+
+    fn any_ready(&self) -> bool {
+        self.added_futures.any_ready() || self.fut_state.needs_poll.get()
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/cros_async/src/fd_executor.rs b/cros_async/src/fd_executor.rs
index 58d6013..1ab9ca8 100644
--- a/cros_async/src/fd_executor.rs
+++ b/cros_async/src/fd_executor.rs
@@ -8,23 +8,6 @@
 //!
 //! `FdExecutor` is meant to be used with the `futures-rs` crate that provides combinators and
 //! utility functions to combine futures.
-//!
-//! # Example of starting the framework and running a future:
-//!
-//! ```
-//! # use std::rc::Rc;
-//! # use std::cell::RefCell;
-//! use cros_async::Executor;
-//! async fn my_async(mut x: Rc<RefCell<u64>>) {
-//!     x.replace(4);
-//! }
-//!
-//! let mut ex = cros_async::empty_executor().expect("Failed creating executor");
-//! let x = Rc::new(RefCell::new(0));
-//! cros_async::fd_executor::add_future(Box::pin(my_async(x.clone())));
-//! ex.run();
-//! assert_eq!(*x.borrow(), 4);
-//! ```
 
 use std::cell::RefCell;
 use std::collections::{BTreeMap, VecDeque};
@@ -38,7 +21,7 @@ use std::task::Waker;
 
 use sys_util::{PollContext, WatchingEvents};
 
-use crate::executor::{ExecutableFuture, Executor, FutureList};
+use crate::executor::{ExecutableFuture, Executor, FutureList, WakerToken};
 
 #[derive(Debug, PartialEq)]
 pub enum Error {
@@ -80,7 +63,7 @@ impl Display for Error {
 // Tracks active wakers and the futures they are associated with.
 thread_local!(static STATE: RefCell<Option<FdWakerState>> = RefCell::new(None));
 
-fn add_waker(fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<()> {
+fn add_waker(fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<WakerToken> {
     STATE.with(|state| {
         let mut state = state.borrow_mut();
         if let Some(state) = state.as_mut() {
@@ -95,7 +78,8 @@ fn add_waker(fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<()> {
 /// The 'fd' must be fully owned by the future adding the waker, and must not be closed until the
 /// next time the future is polled. If the fd is closed, there is a race where another FD can be
 /// opened on top of it causing the next poll to access the new target file.
-pub fn add_read_waker(fd: RawFd, waker: Waker) -> Result<()> {
+/// Returns a `WakerToken` that can be used to cancel the waker before it completes.
+pub fn add_read_waker(fd: RawFd, waker: Waker) -> Result<WakerToken> {
     add_waker(fd, waker, WatchingEvents::empty().set_read())
 }
 
@@ -103,10 +87,23 @@ pub fn add_read_waker(fd: RawFd, waker: Waker) -> Result<()> {
 /// The 'fd' must be fully owned by the future adding the waker, and must not be closed until the
 /// next time the future is polled. If the fd is closed, there is a race where another FD can be
 /// opened on top of it causing the next poll to access the new target file.
-pub fn add_write_waker(fd: RawFd, waker: Waker) -> Result<()> {
+/// Returns a `WakerToken` that can be used to cancel the waker before it completes.
+pub fn add_write_waker(fd: RawFd, waker: Waker) -> Result<WakerToken> {
     add_waker(fd, waker, WatchingEvents::empty().set_write())
 }
 
+/// Cancels the waker that returned the given token if the waker hasn't yet fired.
+pub fn cancel_waker(token: WakerToken) -> Result<()> {
+    STATE.with(|state| {
+        let mut state = state.borrow_mut();
+        if let Some(state) = state.as_mut() {
+            state.cancel_waker(token)
+        } else {
+            Err(Error::InvalidContext)
+        }
+    })
+}
+
 /// Adds a new top level future to the Executor.
 /// These futures must return `()`, indicating they are intended to create side-effects only.
 pub fn add_future(future: Pin<Box<dyn Future<Output = ()>>>) -> Result<()> {
@@ -140,7 +137,7 @@ impl FdWakerState {
     }
 
     // Adds an fd that, when signaled, will trigger the given waker.
-    fn add_waker(&mut self, fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<()> {
+    fn add_waker(&mut self, fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<WakerToken> {
         let duped_fd = unsafe {
             // Safe because duplicating an FD doesn't affect memory safety, and the dup'd FD
             // will only be added to the poll loop.
@@ -152,7 +149,7 @@ impl FdWakerState {
         let next_token = self.next_token;
         self.token_map.insert(next_token, (duped_fd, waker));
         self.next_token += 1;
-        Ok(())
+        Ok(WakerToken(next_token))
     }
 
     // Waits until one of the FDs is readable and wakes the associated waker.
@@ -166,6 +163,14 @@ impl FdWakerState {
         }
         Ok(())
     }
+
+    // Remove the waker for the given token if it hasn't fired yet.
+    fn cancel_waker(&mut self, token: WakerToken) -> Result<()> {
+        if let Some((fd, _waker)) = self.token_map.remove(&token.0) {
+            self.poll_ctx.delete(&fd).map_err(Error::PollContextError)?;
+        }
+        Ok(())
+    }
 }
 
 /// Runs futures to completion on a single thread. Futures are allowed to block on file descriptors
@@ -248,3 +253,85 @@ unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
         Ok(ret)
     }
 }
+
+#[cfg(test)]
+mod test {
+    use std::cell::RefCell;
+    use std::future::Future;
+    use std::os::unix::io::AsRawFd;
+    use std::rc::Rc;
+    use std::task::{Context, Poll};
+
+    use futures::future::Either;
+    use futures::pin_mut;
+
+    use super::*;
+
+    struct TestFut {
+        f: File,
+        token: Option<WakerToken>,
+    }
+
+    impl TestFut {
+        fn new(f: File) -> TestFut {
+            TestFut { f, token: None }
+        }
+    }
+
+    impl Future for TestFut {
+        type Output = u64;
+        fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+            if self.token.is_none() {
+                self.token = Some(add_read_waker(self.f.as_raw_fd(), cx.waker().clone()).unwrap());
+            }
+            Poll::Pending
+        }
+    }
+
+    impl Drop for TestFut {
+        fn drop(&mut self) {
+            if let Some(token) = self.token.take() {
+                cancel_waker(token).unwrap();
+            }
+        }
+    }
+
+    #[test]
+    fn cancel() {
+        async fn do_test() {
+            let (r, _w) = sys_util::pipe(true).unwrap();
+            let done = async { 5usize };
+            let pending = TestFut::new(r);
+            pin_mut!(done);
+            pin_mut!(pending);
+            match futures::future::select(pending, done).await {
+                Either::Right((5, _pending)) => (),
+                _ => panic!("unexpected select result"),
+            }
+        }
+
+        let fut = do_test();
+
+        let mut ex = FdExecutor::new(crate::UnitFutures::new()).expect("Failed creating executor");
+        add_future(Box::pin(fut)).unwrap();
+        ex.run().unwrap();
+        STATE.with(|state| {
+            let state = state.borrow_mut();
+            assert!(state.as_ref().unwrap().token_map.is_empty());
+        });
+    }
+
+    #[test]
+    fn run() {
+        // Example of starting the framework and running a future:
+        async fn my_async(x: Rc<RefCell<u64>>) {
+            x.replace(4);
+        }
+
+        let mut ex = FdExecutor::new(crate::UnitFutures::new()).expect("Failed creating executor");
+        let x = Rc::new(RefCell::new(0));
+        crate::fd_executor::add_future(Box::pin(my_async(x.clone()))).unwrap();
+        ex.run().unwrap();
+        assert_eq!(*x.borrow(), 4);
+    }
+}
diff --git a/cros_async/src/lib.rs b/cros_async/src/lib.rs
index f671a0c..53f480d 100644
--- a/cros_async/src/lib.rs
+++ b/cros_async/src/lib.rs
@@ -41,8 +41,8 @@
 //!
 //! # Implementing new FD-based futures.
 //!
-//! When building futures to be run in an `FdExecutor` framework, use the following helper
-//! functions to perform common tasks:
+//! When building futures to be run in an `Executor` framework, use the following helper functions
+//! to perform common tasks:
 //!
 //! [`add_read_waker`](fn.add_read_waker.html) - Used to associate a provided FD becoming readable
 //! with the future being woken. Used before returning Poll::Pending from a future that waits until
@@ -57,16 +57,38 @@
 
 mod complete;
 mod executor;
-pub mod fd_executor;
+mod fd_executor;
 mod select;
 mod waker;
 
-pub use executor::Executor;
+pub use executor::{Executor, WakerToken};
 pub use select::SelectResult;
 
-use executor::UnitFutures;
-use fd_executor::{FdExecutor, Result};
+use executor::{RunOne, UnitFutures};
+use fd_executor::FdExecutor;
+
+use std::fmt::{self, Display};
 use std::future::Future;
+use std::os::unix::io::RawFd;
+use std::pin::Pin;
+use std::task::Waker;
+
+#[derive(Debug, PartialEq)]
+pub enum Error {
+    /// Error from the FD executor.
+    FdExecutor(fd_executor::Error),
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        match self {
+            FdExecutor(e) => write!(f, "Failure in the FD executor: {}", e),
+        }
+    }
+}
 
 /// Creates an empty FdExecutor that can have futures returning `()` added via
 /// [`add_future`](fn.add_future.html).
@@ -85,7 +107,23 @@ use std::future::Future;
 ///    ex.run();
 ///    ```
 pub fn empty_executor() -> Result<impl Executor> {
-    FdExecutor::new(UnitFutures::new())
+    FdExecutor::new(UnitFutures::new()).map_err(Error::FdExecutor)
+}
+
+/// Creates a FdExecutor that runs one future to completion.
+///
+///  # Example
+///
+///    ```
+///    use cros_async::run_one;
+///
+///    let fut = async { 55 };
+///    assert_eq!(Ok(55),run_one(Box::pin(fut)));
+///    ```
+pub fn run_one<F: Future + Unpin>(fut: F) -> Result<F::Output> {
+    FdExecutor::new(RunOne::new(fut))
+        .and_then(|mut ex| ex.run())
+        .map_err(Error::FdExecutor)
 }
 
 // Select helpers to run until any future completes.
@@ -114,7 +152,9 @@ pub fn select2<F1: Future + Unpin, F2: Future + Unpin>(
     f1: F1,
     f2: F2,
 ) -> Result<(SelectResult<F1>, SelectResult<F2>)> {
-    FdExecutor::new(select::Select2::new(f1, f2)).and_then(|mut f| f.run())
+    FdExecutor::new(select::Select2::new(f1, f2))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
 }
 
 /// Creates an executor that runs the three given futures until one or more completes, returning a
@@ -146,7 +186,9 @@ pub fn select3<F1: Future + Unpin, F2: Future + Unpin, F3: Future + Unpin>(
     f2: F2,
     f3: F3,
 ) -> Result<(SelectResult<F1>, SelectResult<F2>, SelectResult<F3>)> {
-    FdExecutor::new(select::Select3::new(f1, f2, f3)).and_then(|mut f| f.run())
+    FdExecutor::new(select::Select3::new(f1, f2, f3))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
 }
 
 /// Creates an executor that runs the four given futures until one or more completes, returning a
@@ -185,7 +227,9 @@ pub fn select4<F1: Future + Unpin, F2: Future + Unpin, F3: Future + Unpin, F4: F
     SelectResult<F3>,
     SelectResult<F4>,
 )> {
-    FdExecutor::new(select::Select4::new(f1, f2, f3, f4)).and_then(|mut f| f.run())
+    FdExecutor::new(select::Select4::new(f1, f2, f3, f4))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
 }
 
 /// Creates an executor that runs the five given futures until one or more completes, returning a
@@ -235,7 +279,66 @@ pub fn select5<
     SelectResult<F4>,
     SelectResult<F5>,
 )> {
-    FdExecutor::new(select::Select5::new(f1, f2, f3, f4, f5)).and_then(|mut f| f.run())
+    FdExecutor::new(select::Select5::new(f1, f2, f3, f4, f5))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
+}
+
+/// Creates an executor that runs the six given futures until one or more completes, returning a
+/// tuple containing the result of the finished future(s) and the still pending future(s).
+///
+///  # Example
+///
+///    ```
+///    use cros_async::{empty_executor, Executor, select6, SelectResult};
+///    use cros_async::fd_executor::add_future;
+///    use futures::future::pending;
+///    use futures::pin_mut;
+///
+///    let first = async {1};
+///    let second = async {let () = pending().await;};
+///    let third = async {3};
+///    let fourth = async {let () = pending().await;};
+///    let fifth = async {5};
+///    let sixth = async {6};
+///    pin_mut!(first);
+///    pin_mut!(second);
+///    pin_mut!(third);
+///    pin_mut!(fourth);
+///    pin_mut!(fifth);
+///    pin_mut!(sixth);
+///    match select6(first, second, third, fourth, fifth, sixth) {
+///        Ok((SelectResult::Finished(1), SelectResult::Pending(_second),
+///            SelectResult::Finished(3), SelectResult::Pending(_fourth),
+///            SelectResult::Finished(5), SelectResult::Finished(6))) => (),
+///        _ => panic!("Select didn't return the futures"),
+///    };
+///    ```
+pub fn select6<
+    F1: Future + Unpin,
+    F2: Future + Unpin,
+    F3: Future + Unpin,
+    F4: Future + Unpin,
+    F5: Future + Unpin,
+    F6: Future + Unpin,
+>(
+    f1: F1,
+    f2: F2,
+    f3: F3,
+    f4: F4,
+    f5: F5,
+    f6: F6,
+) -> Result<(
+    SelectResult<F1>,
+    SelectResult<F2>,
+    SelectResult<F3>,
+    SelectResult<F4>,
+    SelectResult<F5>,
+    SelectResult<F6>,
+)> {
+    FdExecutor::new(select::Select6::new(f1, f2, f3, f4, f5, f6))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
 }
 
 // Combination helpers to run until all futures are complete.
@@ -259,7 +362,9 @@ pub fn complete2<F1: Future + Unpin, F2: Future + Unpin>(
     f1: F1,
     f2: F2,
 ) -> Result<(F1::Output, F2::Output)> {
-    FdExecutor::new(complete::Complete2::new(f1, f2)).and_then(|mut f| f.run())
+    FdExecutor::new(complete::Complete2::new(f1, f2))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
 }
 
 /// Creates an executor that runs the three given futures to completion, returning a tuple of the
@@ -284,7 +389,9 @@ pub fn complete3<F1: Future + Unpin, F2: Future + Unpin, F3: Future + Unpin>(
     f2: F2,
     f3: F3,
 ) -> Result<(F1::Output, F2::Output, F3::Output)> {
-    FdExecutor::new(complete::Complete3::new(f1, f2, f3)).and_then(|mut f| f.run())
+    FdExecutor::new(complete::Complete3::new(f1, f2, f3))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
 }
 
 /// Creates an executor that runs the four given futures to completion, returning a tuple of the
@@ -312,7 +419,9 @@ pub fn complete4<F1: Future + Unpin, F2: Future + Unpin, F3: Future + Unpin, F4:
     f3: F3,
     f4: F4,
 ) -> Result<(F1::Output, F2::Output, F3::Output, F4::Output)> {
-    FdExecutor::new(complete::Complete4::new(f1, f2, f3, f4)).and_then(|mut f| f.run())
+    FdExecutor::new(complete::Complete4::new(f1, f2, f3, f4))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
 }
 
 /// Creates an executor that runs the five given futures to completion, returning a tuple of the
@@ -350,5 +459,38 @@ pub fn complete5<
     f4: F4,
     f5: F5,
 ) -> Result<(F1::Output, F2::Output, F3::Output, F4::Output, F5::Output)> {
-    FdExecutor::new(complete::Complete5::new(f1, f2, f3, f4, f5)).and_then(|mut f| f.run())
+    FdExecutor::new(complete::Complete5::new(f1, f2, f3, f4, f5))
+        .and_then(|mut f| f.run())
+        .map_err(Error::FdExecutor)
+}
+
+// Functions to be used by `Future` implementations
+
+/// Tells the waking system to wake `waker` when `fd` becomes readable.
+/// The 'fd' must be fully owned by the future adding the waker, and must not be closed until the
+/// next time the future is polled. If the fd is closed, there is a race where another FD can be
+/// opened on top of it causing the next poll to access the new target file.
+/// Returns a `WakerToken` that can be used to cancel the waker before it completes.
+pub fn add_read_waker(fd: RawFd, waker: Waker) -> Result<WakerToken> {
+    fd_executor::add_read_waker(fd, waker).map_err(Error::FdExecutor)
+}
+
+/// Tells the waking system to wake `waker` when `fd` becomes writable.
+/// The 'fd' must be fully owned by the future adding the waker, and must not be closed until the
+/// next time the future is polled. If the fd is closed, there is a race where another FD can be
+/// opened on top of it causing the next poll to access the new target file.
+/// Returns a `WakerToken` that can be used to cancel the waker before it completes.
+pub fn add_write_waker(fd: RawFd, waker: Waker) -> Result<WakerToken> {
+    fd_executor::add_write_waker(fd, waker).map_err(Error::FdExecutor)
+}
+
+/// Cancels the waker that returned the given token if the waker hasn't yet fired.
+pub fn cancel_waker(token: WakerToken) -> Result<()> {
+    fd_executor::cancel_waker(token).map_err(Error::FdExecutor)
+}
+
+/// Adds a new top level future to the Executor.
+/// These futures must return `()`, indicating they are intended to create side-effects only.
+pub fn add_future(future: Pin<Box<dyn Future<Output = ()>>>) -> Result<()> {
+    fd_executor::add_future(future).map_err(Error::FdExecutor)
 }
diff --git a/cros_async/src/select.rs b/cros_async/src/select.rs
index 8eef317..b10bc2c 100644
--- a/cros_async/src/select.rs
+++ b/cros_async/src/select.rs
@@ -26,7 +26,6 @@ macro_rules! generate {
         $(#[$doc:meta])*
         ($Select:ident, <$($Fut:ident),*>),
     )*) => ($(
-        $(#[$doc])*
         #[must_use = "Combinations of futures don't do anything unless run in an executor."]
         paste::item! {
             pub(crate) struct $Select<$($Fut: Future + Unpin),*> {
@@ -107,4 +106,7 @@ generate! {
 
     /// _Future for the [`select5`] function.
     (Select5, <_Fut1, _Fut2, _Fut3, _Fut4, _Fut5>),
+
+    /// _Future for the [`select6`] function.
+    (Select6, <_Fut1, _Fut2, _Fut3, _Fut4, _Fut5, _Fut6>),
 }
diff --git a/cros_async/src/waker.rs b/cros_async/src/waker.rs
index f0dac0f..6d277ee 100644
--- a/cros_async/src/waker.rs
+++ b/cros_async/src/waker.rs
@@ -16,7 +16,9 @@ unsafe fn waker_drop(data_ptr: *const ()) {
     let _rc_bool = Rc::<AtomicBool>::from_raw(data_ptr as *const _);
 }
 
-unsafe fn waker_wake(_: *const ()) {}
+unsafe fn waker_wake(data_ptr: *const ()) {
+    waker_wake_by_ref(data_ptr)
+}
 
 // Called when the bool should be set to true to wake the waker.
 unsafe fn waker_wake_by_ref(data_ptr: *const ()) {
diff --git a/devices/src/lib.rs b/devices/src/lib.rs
index 9ed8417..01c2b46 100644
--- a/devices/src/lib.rs
+++ b/devices/src/lib.rs
@@ -17,6 +17,7 @@ mod proxy;
 mod register_space;
 pub mod acpi;
 mod serial;
+mod serial_device;
 pub mod split_irqchip_common;
 pub mod usb;
 mod utils;
@@ -30,18 +31,16 @@ pub use self::cmos::Cmos;
 pub use self::i8042::I8042Device;
 pub use self::ioapic::{Ioapic, IOAPIC_BASE_ADDRESS, IOAPIC_MEM_LENGTH_BYTES};
 pub use self::pci::{
-    Ac97Backend, Ac97Dev, Ac97Parameters, PciConfigIo, PciConfigMmio, PciDevice, PciDeviceError,
-    PciInterruptPin, PciRoot, VfioPciDevice,
+    Ac97Backend, Ac97Dev, Ac97Parameters, PciAddress, PciConfigIo, PciConfigMmio, PciDevice,
+    PciDeviceError, PciInterruptPin, PciRoot, VfioPciDevice,
 };
 pub use self::pic::Pic;
 pub use self::pit::{Pit, PitError};
 pub use self::pl030::Pl030;
 pub use self::proxy::Error as ProxyError;
 pub use self::proxy::ProxyDevice;
-pub use self::serial::Error as SerialError;
-pub use self::serial::{
-    get_serial_tty_string, Serial, SerialParameters, SerialType, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR,
-};
+pub use self::serial::Serial;
+pub use self::serial_device::SerialDevice;
 pub use self::usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider;
 pub use self::usb::xhci::xhci_controller::XhciController;
 pub use self::vfio::{VfioContainer, VfioDevice};
diff --git a/devices/src/pci/ac97.rs b/devices/src/pci/ac97.rs
index d061746..8a6748d 100644
--- a/devices/src/pci/ac97.rs
+++ b/devices/src/pci/ac97.rs
@@ -12,7 +12,7 @@ use audio_streams::{
     shm_streams::{NullShmStreamSource, ShmStreamSource},
     StreamEffect,
 };
-use libcras::{CrasClient, CrasClientType};
+use libcras::{CrasClient, CrasClientType, CrasSocketType};
 use resources::{Alloc, MmioType, SystemAllocator};
 use sys_util::{error, EventFd, GuestMemory};
 
@@ -23,7 +23,7 @@ use crate::pci::pci_configuration::{
     PciBarConfiguration, PciClassCode, PciConfiguration, PciHeaderType, PciMultimediaSubclass,
 };
 use crate::pci::pci_device::{self, PciDevice, Result};
-use crate::pci::PciInterruptPin;
+use crate::pci::{PciAddress, PciInterruptPin};
 
 // Use 82801AA because it's what qemu does.
 const PCI_DEVICE_ID_INTEL_82801AA_5: u16 = 0x2415;
@@ -82,7 +82,7 @@ pub struct Ac97Parameters {
 
 pub struct Ac97Dev {
     config_regs: PciConfiguration,
-    pci_bus_dev: Option<(u8, u8)>,
+    pci_address: Option<PciAddress>,
     // The irq events are temporarily saved here. They need to be passed to the device after the
     // jail forks. This happens when the bus is first written.
     irq_evt: Option<EventFd>,
@@ -108,7 +108,7 @@ impl Ac97Dev {
 
         Ac97Dev {
             config_regs,
-            pci_bus_dev: None,
+            pci_address: None,
             irq_evt: None,
             irq_resample_evt: None,
             bus_master: Ac97BusMaster::new(mem, audio_server),
@@ -117,8 +117,10 @@ impl Ac97Dev {
     }
 
     fn create_cras_audio_device(params: Ac97Parameters, mem: GuestMemory) -> Result<Ac97Dev> {
-        let mut server =
-            Box::new(CrasClient::new().map_err(|e| pci_device::Error::CreateCrasClientFailed(e))?);
+        let mut server = Box::new(
+            CrasClient::with_type(CrasSocketType::Unified)
+                .map_err(|e| pci_device::Error::CreateCrasClientFailed(e))?,
+        );
         server.set_client_type(CrasClientType::CRAS_CLIENT_TYPE_CROSVM);
         if params.capture {
             server.enable_cras_capture();
@@ -214,8 +216,8 @@ impl PciDevice for Ac97Dev {
         "AC97".to_owned()
     }
 
-    fn assign_bus_dev(&mut self, bus: u8, device: u8) {
-        self.pci_bus_dev = Some((bus, device));
+    fn assign_address(&mut self, address: PciAddress) {
+        self.pci_address = Some(address);
     }
 
     fn assign_irq(
@@ -231,15 +233,20 @@ impl PciDevice for Ac97Dev {
     }
 
     fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
-        let (bus, dev) = self
-            .pci_bus_dev
-            .expect("assign_bus_dev must be called prior to allocate_io_bars");
+        let address = self
+            .pci_address
+            .expect("assign_address must be called prior to allocate_io_bars");
         let mut ranges = Vec::new();
         let mixer_regs_addr = resources
             .mmio_allocator(MmioType::Low)
             .allocate_with_align(
                 MIXER_REGS_SIZE,
-                Alloc::PciBar { bus, dev, bar: 0 },
+                Alloc::PciBar {
+                    bus: address.bus,
+                    dev: address.dev,
+                    func: address.func,
+                    bar: 0,
+                },
                 "ac97-mixer_regs".to_string(),
                 MIXER_REGS_SIZE,
             )
@@ -257,7 +264,12 @@ impl PciDevice for Ac97Dev {
             .mmio_allocator(MmioType::Low)
             .allocate_with_align(
                 MASTER_REGS_SIZE,
-                Alloc::PciBar { bus, dev, bar: 1 },
+                Alloc::PciBar {
+                    bus: address.bus,
+                    dev: address.dev,
+                    func: address.func,
+                    bar: 1,
+                },
                 "ac97-master_regs".to_string(),
                 MASTER_REGS_SIZE,
             )
@@ -336,7 +348,11 @@ mod tests {
             .add_high_mmio_addresses(0x3000_0000, 0x1000_0000)
             .create_allocator(5, false)
             .unwrap();
-        ac97_dev.assign_bus_dev(0, 0);
+        ac97_dev.assign_address(PciAddress {
+            bus: 0,
+            dev: 0,
+            func: 0,
+        });
         assert!(ac97_dev.allocate_io_bars(&mut allocator).is_ok());
     }
 }
diff --git a/devices/src/pci/ac97_bus_master.rs b/devices/src/pci/ac97_bus_master.rs
index 809f31f..22f3c92 100644
--- a/devices/src/pci/ac97_bus_master.rs
+++ b/devices/src/pci/ac97_bus_master.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::collections::VecDeque;
 use std::convert::AsRef;
 use std::convert::TryInto;
diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs
index b9ebc19..0edc3b2 100644
--- a/devices/src/pci/mod.rs
+++ b/devices/src/pci/mod.rs
@@ -23,7 +23,7 @@ pub use self::pci_configuration::{
 };
 pub use self::pci_device::Error as PciDeviceError;
 pub use self::pci_device::PciDevice;
-pub use self::pci_root::{PciConfigIo, PciConfigMmio, PciRoot};
+pub use self::pci_root::{PciAddress, PciConfigIo, PciConfigMmio, PciRoot};
 pub use self::vfio_pci::VfioPciDevice;
 
 /// PCI has four interrupt pins A->D.
diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs
index a1a3cca..244edbd 100644
--- a/devices/src/pci/pci_device.rs
+++ b/devices/src/pci/pci_device.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::fmt::{self, Display};
 use std::os::unix::io::RawFd;
 
@@ -11,7 +10,7 @@ use resources::{Error as SystemAllocatorFaliure, SystemAllocator};
 use sys_util::EventFd;
 
 use crate::pci::pci_configuration;
-use crate::pci::PciInterruptPin;
+use crate::pci::{PciAddress, PciInterruptPin};
 use crate::BusDevice;
 
 #[derive(Debug)]
@@ -49,8 +48,8 @@ impl Display for Error {
 pub trait PciDevice: Send {
     /// Returns a label suitable for debug output.
     fn debug_label(&self) -> String;
-    /// Assign a unique bus and device number to this device.
-    fn assign_bus_dev(&mut self, _bus: u8, _device: u8 /*u5*/) {}
+    /// Assign a unique bus, device and function number to this device.
+    fn assign_address(&mut self, _address: PciAddress) {}
     /// A vector of device-specific file descriptors that must be kept open
     /// after jailing. Must be called before the process is jailed.
     fn keep_fds(&self) -> Vec<RawFd>;
@@ -149,8 +148,8 @@ impl<T: PciDevice + ?Sized> PciDevice for Box<T> {
     fn debug_label(&self) -> String {
         (**self).debug_label()
     }
-    fn assign_bus_dev(&mut self, bus: u8, device: u8 /*u5*/) {
-        (**self).assign_bus_dev(bus, device)
+    fn assign_address(&mut self, address: PciAddress) {
+        (**self).assign_address(address)
     }
     fn keep_fds(&self) -> Vec<RawFd> {
         (**self).keep_fds()
diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs
index eef642e..91d6094 100644
--- a/devices/src/pci/pci_root.rs
+++ b/devices/src/pci/pci_root.rs
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use std::collections::BTreeMap;
 use std::convert::TryInto;
+use std::fmt::{self, Display};
 use std::os::unix::io::RawFd;
 use std::sync::Arc;
 
@@ -39,12 +41,67 @@ impl PciDevice for PciRootConfiguration {
     fn write_bar(&mut self, _addr: u64, _data: &[u8]) {}
 }
 
+/// PCI Device Address, AKA Bus:Device.Function
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct PciAddress {
+    pub bus: u8,
+    pub dev: u8,  /* u5 */
+    pub func: u8, /* u3 */
+}
+
+impl Display for PciAddress {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:04x}:{:02x}.{:0x}", self.bus, self.dev, self.func)
+    }
+}
+
+impl PciAddress {
+    const BUS_OFFSET: usize = 16;
+    const BUS_MASK: u32 = 0x00ff;
+    const DEVICE_OFFSET: usize = 11;
+    const DEVICE_MASK: u32 = 0x1f;
+    const FUNCTION_OFFSET: usize = 8;
+    const FUNCTION_MASK: u32 = 0x07;
+    const REGISTER_OFFSET: usize = 2;
+    const REGISTER_MASK: u32 = 0x3f;
+
+    /// Construct PciAddress and register tuple from CONFIG_ADDRESS value.
+    pub fn from_config_address(config_address: u32) -> (Self, usize) {
+        let bus = ((config_address >> Self::BUS_OFFSET) & Self::BUS_MASK) as u8;
+        let dev = ((config_address >> Self::DEVICE_OFFSET) & Self::DEVICE_MASK) as u8;
+        let func = ((config_address >> Self::FUNCTION_OFFSET) & Self::FUNCTION_MASK) as u8;
+        let register = ((config_address >> Self::REGISTER_OFFSET) & Self::REGISTER_MASK) as usize;
+
+        (PciAddress { bus, dev, func }, register)
+    }
+
+    /// Encode PciAddress into CONFIG_ADDRESS value.
+    pub fn to_config_address(&self, register: usize) -> u32 {
+        ((Self::BUS_MASK & self.bus as u32) << Self::BUS_OFFSET)
+            | ((Self::DEVICE_MASK & self.dev as u32) << Self::DEVICE_OFFSET)
+            | ((Self::FUNCTION_MASK & self.func as u32) << Self::FUNCTION_OFFSET)
+            | ((Self::REGISTER_MASK & register as u32) << Self::REGISTER_OFFSET)
+    }
+
+    /// Returns true if the address points to PCI root host-bridge.
+    fn is_root(&self) -> bool {
+        matches!(
+            &self,
+            PciAddress {
+                bus: 0,
+                dev: 0,
+                func: 0
+            }
+        )
+    }
+}
+
 /// Emulates the PCI Root bridge.
 pub struct PciRoot {
     /// Bus configuration for the root device.
     root_configuration: PciRootConfiguration,
     /// Devices attached to this bridge.
-    devices: Vec<Arc<Mutex<dyn BusDevice>>>,
+    devices: BTreeMap<PciAddress, Arc<Mutex<dyn BusDevice>>>,
 }
 
 const PCI_VENDOR_ID_INTEL: u16 = 0x8086;
@@ -66,44 +123,31 @@ impl PciRoot {
                     0,
                 ),
             },
-            devices: Vec::new(),
+            devices: BTreeMap::new(),
         }
     }
 
     /// Add a `device` to this root PCI bus.
-    pub fn add_device(&mut self, device: Arc<Mutex<dyn BusDevice>>) {
-        self.devices.push(device);
-    }
-
-    pub fn config_space_read(
-        &self,
-        bus: usize,
-        device: usize,
-        _function: usize,
-        register: usize,
-    ) -> u32 {
-        // Only support one bus.
-        if bus != 0 {
-            return 0xffff_ffff;
+    pub fn add_device(&mut self, address: PciAddress, device: Arc<Mutex<dyn BusDevice>>) {
+        // Ignore attempt to replace PCI Root host bridge.
+        if !address.is_root() {
+            self.devices.insert(address, device);
         }
+    }
 
-        match device {
-            0 => {
-                // If bus and device are both zero, then read from the root config.
-                self.root_configuration.config_register_read(register)
-            }
-            dev_num => self
-                .devices
-                .get(dev_num - 1)
-                .map_or(0xffff_ffff, |d| d.lock().config_register_read(register)),
+    pub fn config_space_read(&self, address: PciAddress, register: usize) -> u32 {
+        if address.is_root() {
+            self.root_configuration.config_register_read(register)
+        } else {
+            self.devices
+                .get(&address)
+                .map_or(0xffff_ffff, |d| d.lock().config_register_read(register))
         }
     }
 
     pub fn config_space_write(
         &mut self,
-        bus: usize,
-        device: usize,
-        _function: usize,
+        address: PciAddress,
         register: usize,
         offset: u64,
         data: &[u8],
@@ -111,24 +155,11 @@ impl PciRoot {
         if offset as usize + data.len() > 4 {
             return;
         }
-
-        // Only support one bus.
-        if bus != 0 {
-            return;
-        }
-
-        match device {
-            0 => {
-                // If bus and device are both zero, then read from the root config.
-                self.root_configuration
-                    .config_register_write(register, offset, data);
-            }
-            dev_num => {
-                // dev_num is 1-indexed here.
-                if let Some(d) = self.devices.get(dev_num - 1) {
-                    d.lock().config_register_write(register, offset, data);
-                }
-            }
+        if address.is_root() {
+            self.root_configuration
+                .config_register_write(register, offset, data);
+        } else if let Some(d) = self.devices.get(&address) {
+            d.lock().config_register_write(register, offset, data);
         }
     }
 }
@@ -155,10 +186,8 @@ impl PciConfigIo {
             return 0xffff_ffff;
         }
 
-        let (bus, device, function, register) =
-            parse_config_address(self.config_address & !0x8000_0000);
-        self.pci_root
-            .config_space_read(bus, device, function, register)
+        let (address, register) = PciAddress::from_config_address(self.config_address);
+        self.pci_root.config_space_read(address, register)
     }
 
     fn config_space_write(&mut self, offset: u64, data: &[u8]) {
@@ -167,10 +196,9 @@ impl PciConfigIo {
             return;
         }
 
-        let (bus, device, function, register) =
-            parse_config_address(self.config_address & !0x8000_0000);
+        let (address, register) = PciAddress::from_config_address(self.config_address);
         self.pci_root
-            .config_space_write(bus, device, function, register, offset, data)
+            .config_space_write(address, register, offset, data)
     }
 
     fn set_config_address(&mut self, offset: u64, data: &[u8]) {
@@ -242,15 +270,14 @@ impl PciConfigMmio {
     }
 
     fn config_space_read(&self, config_address: u32) -> u32 {
-        let (bus, device, function, register) = parse_config_address(config_address);
-        self.pci_root
-            .config_space_read(bus, device, function, register)
+        let (address, register) = PciAddress::from_config_address(config_address);
+        self.pci_root.config_space_read(address, register)
     }
 
     fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) {
-        let (bus, device, function, register) = parse_config_address(config_address);
+        let (address, register) = PciAddress::from_config_address(config_address);
         self.pci_root
-            .config_space_write(bus, device, function, register, offset, data)
+            .config_space_write(address, register, offset, data)
     }
 }
 
@@ -283,24 +310,3 @@ impl BusDevice for PciConfigMmio {
         self.config_space_write(offset as u32, offset % 4, data)
     }
 }
-
-// Parse the CONFIG_ADDRESS register to a (bus, device, function, register) tuple.
-fn parse_config_address(config_address: u32) -> (usize, usize, usize, usize) {
-    const BUS_NUMBER_OFFSET: usize = 16;
-    const BUS_NUMBER_MASK: u32 = 0x00ff;
-    const DEVICE_NUMBER_OFFSET: usize = 11;
-    const DEVICE_NUMBER_MASK: u32 = 0x1f;
-    const FUNCTION_NUMBER_OFFSET: usize = 8;
-    const FUNCTION_NUMBER_MASK: u32 = 0x07;
-    const REGISTER_NUMBER_OFFSET: usize = 2;
-    const REGISTER_NUMBER_MASK: u32 = 0x3f;
-
-    let bus_number = ((config_address >> BUS_NUMBER_OFFSET) & BUS_NUMBER_MASK) as usize;
-    let device_number = ((config_address >> DEVICE_NUMBER_OFFSET) & DEVICE_NUMBER_MASK) as usize;
-    let function_number =
-        ((config_address >> FUNCTION_NUMBER_OFFSET) & FUNCTION_NUMBER_MASK) as usize;
-    let register_number =
-        ((config_address >> REGISTER_NUMBER_OFFSET) & REGISTER_NUMBER_MASK) as usize;
-
-    (bus_number, device_number, function_number, register_number)
-}
diff --git a/devices/src/pci/vfio_pci.rs b/devices/src/pci/vfio_pci.rs
index 5c13d28..671e8cd 100644
--- a/devices/src/pci/vfio_pci.rs
+++ b/devices/src/pci/vfio_pci.rs
@@ -22,7 +22,7 @@ use crate::pci::msix::{
 };
 
 use crate::pci::pci_device::{Error as PciDeviceError, PciDevice};
-use crate::pci::{PciClassCode, PciInterruptPin};
+use crate::pci::{PciAddress, PciClassCode, PciInterruptPin};
 
 use crate::vfio::{VfioDevice, VfioIrqType};
 
@@ -457,7 +457,7 @@ enum DeviceData {
 pub struct VfioPciDevice {
     device: Arc<VfioDevice>,
     config: VfioPciConfig,
-    pci_bus_dev: Option<(u8, u8)>,
+    pci_address: Option<PciAddress>,
     interrupt_evt: Option<EventFd>,
     interrupt_resample_evt: Option<EventFd>,
     mmio_regions: Vec<MmioInfo>,
@@ -522,7 +522,7 @@ impl VfioPciDevice {
         VfioPciDevice {
             device: dev,
             config,
-            pci_bus_dev: None,
+            pci_address: None,
             interrupt_evt: None,
             interrupt_resample_evt: None,
             mmio_regions: Vec::new(),
@@ -776,8 +776,8 @@ impl PciDevice for VfioPciDevice {
         "vfio pci device".to_string()
     }
 
-    fn assign_bus_dev(&mut self, bus: u8, device: u8) {
-        self.pci_bus_dev = Some((bus, device));
+    fn assign_address(&mut self, address: PciAddress) {
+        self.pci_address = Some(address);
     }
 
     fn keep_fds(&self) -> Vec<RawFd> {
@@ -816,9 +816,9 @@ impl PciDevice for VfioPciDevice {
     ) -> Result<Vec<(u64, u64)>, PciDeviceError> {
         let mut ranges = Vec::new();
         let mut i = VFIO_PCI_BAR0_REGION_INDEX;
-        let (bus, dev) = self
-            .pci_bus_dev
-            .expect("assign_bus_dev must be called prior to allocate_io_bars");
+        let address = self
+            .pci_address
+            .expect("assign_address must be called prior to allocate_io_bars");
 
         while i <= VFIO_PCI_ROM_REGION_INDEX {
             let mut low: u32 = 0xffffffff;
@@ -854,8 +854,9 @@ impl PciDevice for VfioPciDevice {
                     .allocate_with_align(
                         size,
                         Alloc::PciBar {
-                            bus,
-                            dev,
+                            bus: address.bus,
+                            dev: address.dev,
+                            func: address.func,
                             bar: i as u8,
                         },
                         "vfio_bar".to_string(),
@@ -914,16 +915,17 @@ impl PciDevice for VfioPciDevice {
             VFIO_REGION_TYPE_PCI_VENDOR_TYPE | (INTEL_VENDOR_ID as u32),
             VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION,
         ) {
-            let (bus, dev) = self
-                .pci_bus_dev
-                .expect("assign_bus_dev must be called prior to allocate_device_bars");
+            let address = self
+                .pci_address
+                .expect("assign_address must be called prior to allocate_device_bars");
             let bar_addr = resources
                 .mmio_allocator(MmioType::Low)
                 .allocate(
                     size,
                     Alloc::PciBar {
-                        bus,
-                        dev,
+                        bus: address.bus,
+                        dev: address.dev,
+                        func: address.func,
                         bar: (index * 4) as u8,
                     },
                     "vfio_bar".to_string(),
diff --git a/devices/src/register_space/register.rs b/devices/src/register_space/register.rs
index c2f5184..2a4f551 100644
--- a/devices/src/register_space/register.rs
+++ b/devices/src/register_space/register.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::boxed::Box;
 use std::cmp::{max, min, Ord, Ordering, PartialOrd};
 use std::mem::size_of;
diff --git a/devices/src/serial.rs b/devices/src/serial.rs
index 6629d7e..2af988e 100644
--- a/devices/src/serial.rs
+++ b/devices/src/serial.rs
@@ -3,20 +3,16 @@
 // found in the LICENSE file.
 
 use std::collections::VecDeque;
-use std::fmt::{self, Display};
-use std::fs::File;
-use std::io::{self, stdin, stdout, Read, Write};
-use std::os::unix::io::{AsRawFd, RawFd};
-use std::path::PathBuf;
-use std::str::FromStr;
+use std::io::{self, Write};
+use std::os::unix::io::RawFd;
 use std::sync::atomic::{AtomicU8, Ordering};
 use std::sync::mpsc::{channel, Receiver, TryRecvError};
 use std::sync::Arc;
 use std::thread::{self};
 
-use sys_util::{error, read_raw_stdin, syslog, EventFd, Result};
+use sys_util::{error, EventFd, Result};
 
-use crate::BusDevice;
+use crate::{BusDevice, SerialDevice};
 
 const LOOP_SIZE: usize = 0x40;
 
@@ -62,187 +58,6 @@ const DEFAULT_MODEM_CONTROL: u8 = MCR_OUT2_BIT;
 const DEFAULT_MODEM_STATUS: u8 = MSR_DSR_BIT | MSR_CTS_BIT | MSR_DCD_BIT;
 const DEFAULT_BAUD_DIVISOR: u16 = 12; // 9600 bps
 
-#[derive(Debug)]
-pub enum Error {
-    CloneEventFd(sys_util::Error),
-    InvalidSerialType(String),
-    PathRequired,
-    FileError(std::io::Error),
-    Unimplemented(SerialType),
-}
-
-impl Display for Error {
-    #[remain::check]
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use self::Error::*;
-
-        #[sorted]
-        match self {
-            CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
-            FileError(e) => write!(f, "Unable to open/create file: {}", e),
-            InvalidSerialType(e) => write!(f, "invalid serial type: {}", e),
-            PathRequired => write!(f, "serial device type file requires a path"),
-            Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()),
-        }
-    }
-}
-
-/// Enum for possible type of serial devices
-#[derive(Debug)]
-pub enum SerialType {
-    File,
-    Stdout,
-    Sink,
-    Syslog,
-    UnixSocket, // NOT IMPLEMENTED
-}
-
-impl Display for SerialType {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let s = match &self {
-            SerialType::File => "File".to_string(),
-            SerialType::Stdout => "Stdout".to_string(),
-            SerialType::Sink => "Sink".to_string(),
-            SerialType::Syslog => "Syslog".to_string(),
-            SerialType::UnixSocket => "UnixSocket".to_string(),
-        };
-
-        write!(f, "{}", s)
-    }
-}
-
-impl FromStr for SerialType {
-    type Err = Error;
-    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
-        match s {
-            "file" | "File" => Ok(SerialType::File),
-            "stdout" | "Stdout" => Ok(SerialType::Stdout),
-            "sink" | "Sink" => Ok(SerialType::Sink),
-            "syslog" | "Syslog" => Ok(SerialType::Syslog),
-            "unix" | "UnixSocket" => Ok(SerialType::UnixSocket),
-            _ => Err(Error::InvalidSerialType(s.to_string())),
-        }
-    }
-}
-
-/// Holds the parameters for a serial device
-#[derive(Debug)]
-pub struct SerialParameters {
-    pub type_: SerialType,
-    pub path: Option<PathBuf>,
-    pub num: u8,
-    pub console: bool,
-    pub stdin: bool,
-}
-
-impl SerialParameters {
-    /// Helper function to create a serial device from the defined parameters.
-    ///
-    /// # Arguments
-    /// * `evt_fd` - eventfd used for interrupt events
-    /// * `keep_fds` - Vector of FDs required by this device if it were sandboxed in a child
-    ///                process. `evt_fd` will always be added to this vector by this function.
-    pub fn create_serial_device(
-        &self,
-        evt_fd: &EventFd,
-        keep_fds: &mut Vec<RawFd>,
-    ) -> std::result::Result<Serial, Error> {
-        let evt_fd = evt_fd.try_clone().map_err(Error::CloneEventFd)?;
-        keep_fds.push(evt_fd.as_raw_fd());
-        let input: Option<Box<dyn io::Read + Send>> = if self.stdin {
-            keep_fds.push(stdin().as_raw_fd());
-            // This wrapper is used in place of the libstd native version because we don't want
-            // buffering for stdin.
-            struct StdinWrapper;
-            impl io::Read for StdinWrapper {
-                fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
-                    read_raw_stdin(out).map_err(|e| e.into())
-                }
-            }
-            Some(Box::new(StdinWrapper))
-        } else {
-            None
-        };
-        match self.type_ {
-            SerialType::Stdout => {
-                keep_fds.push(stdout().as_raw_fd());
-                Ok(Serial::new(evt_fd, input, Some(Box::new(stdout()))))
-            }
-            SerialType::Sink => Ok(Serial::new(evt_fd, input, None)),
-            SerialType::Syslog => {
-                syslog::push_fds(keep_fds);
-                Ok(Serial::new(
-                    evt_fd,
-                    input,
-                    Some(Box::new(syslog::Syslogger::new(
-                        syslog::Priority::Info,
-                        syslog::Facility::Daemon,
-                    ))),
-                ))
-            }
-            SerialType::File => match &self.path {
-                None => Err(Error::PathRequired),
-                Some(path) => {
-                    let file = File::create(path.as_path()).map_err(Error::FileError)?;
-                    keep_fds.push(file.as_raw_fd());
-                    Ok(Serial::new(evt_fd, input, Some(Box::new(file))))
-                }
-            },
-            SerialType::UnixSocket => Err(Error::Unimplemented(SerialType::UnixSocket)),
-        }
-    }
-}
-
-// Structure for holding the default configuration of the serial devices.
-pub const DEFAULT_SERIAL_PARAMS: [SerialParameters; 4] = [
-    SerialParameters {
-        type_: SerialType::Stdout,
-        path: None,
-        num: 1,
-        console: true,
-        stdin: true,
-    },
-    SerialParameters {
-        type_: SerialType::Sink,
-        path: None,
-        num: 2,
-        console: false,
-        stdin: false,
-    },
-    SerialParameters {
-        type_: SerialType::Sink,
-        path: None,
-        num: 3,
-        console: false,
-        stdin: false,
-    },
-    SerialParameters {
-        type_: SerialType::Sink,
-        path: None,
-        num: 4,
-        console: false,
-        stdin: false,
-    },
-];
-
-/// Address for Serial ports in x86
-pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
-
-/// String representations of serial devices
-pub const SERIAL_TTY_STRINGS: [&str; 4] = ["ttyS0", "ttyS1", "ttyS2", "ttyS3"];
-
-/// Helper function to get the tty string of a serial device based on the port number. Will default
-///  to ttyS0 if an invalid number is given.
-pub fn get_serial_tty_string(stdio_serial_num: u8) -> String {
-    match stdio_serial_num {
-        1 => SERIAL_TTY_STRINGS[0].to_string(),
-        2 => SERIAL_TTY_STRINGS[1].to_string(),
-        3 => SERIAL_TTY_STRINGS[2].to_string(),
-        4 => SERIAL_TTY_STRINGS[3].to_string(),
-        _ => SERIAL_TTY_STRINGS[0].to_string(),
-    }
-}
-
 /// Emulates serial COM ports commonly seen on x86 I/O ports 0x3f8/0x2f8/0x3e8/0x2e8.
 ///
 /// This can optionally write the guest's output to a Write trait object. To send input to the
@@ -267,11 +82,12 @@ pub struct Serial {
     out: Option<Box<dyn io::Write + Send>>,
 }
 
-impl Serial {
+impl SerialDevice for Serial {
     fn new(
         interrupt_evt: EventFd,
         input: Option<Box<dyn io::Read + Send>>,
         out: Option<Box<dyn io::Write + Send>>,
+        _keep_fds: Vec<RawFd>,
     ) -> Serial {
         Serial {
             interrupt_enable: Default::default(),
@@ -289,28 +105,9 @@ impl Serial {
             out,
         }
     }
+}
 
-    /// Constructs a Serial port ready for input and output.
-    ///
-    /// The stream `input` should not block, instead returning 0 bytes if are no bytes available.
-    pub fn new_in_out(
-        interrupt_evt: EventFd,
-        input: Box<dyn io::Read + Send>,
-        out: Box<dyn io::Write + Send>,
-    ) -> Serial {
-        Self::new(interrupt_evt, Some(input), Some(out))
-    }
-
-    /// Constructs a Serial port ready for output but not input.
-    pub fn new_out(interrupt_evt: EventFd, out: Box<dyn io::Write + Send>) -> Serial {
-        Self::new(interrupt_evt, None, Some(out))
-    }
-
-    /// Constructs a Serial port with no connected input or output.
-    pub fn new_sink(interrupt_evt: EventFd) -> Serial {
-        Self::new(interrupt_evt, None, None)
-    }
-
+impl Serial {
     /// Queues raw bytes for the guest to read and signals the interrupt if the line status would
     /// change. These bytes will be read by the guest before any bytes from the input stream that
     /// have not already been queued.
@@ -611,7 +408,12 @@ mod tests {
         let intr_evt = EventFd::new().unwrap();
         let serial_out = SharedBuffer::new();
 
-        let mut serial = Serial::new_out(intr_evt, Box::new(serial_out.clone()));
+        let mut serial = Serial::new(
+            intr_evt,
+            None,
+            Some(Box::new(serial_out.clone())),
+            Vec::new(),
+        );
 
         serial.write(DATA as u64, &['a' as u8]);
         serial.write(DATA as u64, &['b' as u8]);
@@ -627,8 +429,12 @@ mod tests {
         let intr_evt = EventFd::new().unwrap();
         let serial_out = SharedBuffer::new();
 
-        let mut serial =
-            Serial::new_out(intr_evt.try_clone().unwrap(), Box::new(serial_out.clone()));
+        let mut serial = Serial::new(
+            intr_evt.try_clone().unwrap(),
+            None,
+            Some(Box::new(serial_out.clone())),
+            Vec::new(),
+        );
 
         serial.write(IER as u64, &[IER_RECV_BIT]);
         serial
diff --git a/devices/src/serial_device.rs b/devices/src/serial_device.rs
new file mode 100644
index 0000000..9377556
--- /dev/null
+++ b/devices/src/serial_device.rs
@@ -0,0 +1,19 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::io;
+use std::os::unix::io::RawFd;
+
+use sys_util::EventFd;
+
+/// Abstraction over serial-like devices that can be created given an event and optional input and
+/// output streams.
+pub trait SerialDevice {
+    fn new(
+        interrupt_evt: EventFd,
+        input: Option<Box<dyn io::Read + Send>>,
+        output: Option<Box<dyn io::Write + Send>>,
+        keep_fds: Vec<RawFd>,
+    ) -> Self;
+}
diff --git a/devices/src/usb/host_backend/host_device.rs b/devices/src/usb/host_backend/host_device.rs
index 196fa50..8f8723d 100644
--- a/devices/src/usb/host_backend/host_device.rs
+++ b/devices/src/usb/host_backend/host_device.rs
@@ -140,30 +140,21 @@ impl HostDevice {
             return Ok(());
         }
 
-        // Default buffer size for control data transfer.
-        const CONTROL_DATA_BUFFER_SIZE: usize = 1024;
-
-        // Buffer type for control transfer. The first 8 bytes is a UsbRequestSetup struct.
-        #[derive(Copy, Clone)]
-        #[repr(C, packed)]
-        struct ControlTransferBuffer {
-            pub setup: UsbRequestSetup,
-            pub data: [u8; CONTROL_DATA_BUFFER_SIZE],
-        }
-
-        // Safe because it only has data and has no implicit padding.
-        unsafe impl DataInit for ControlTransferBuffer {}
+        // Allocate a buffer for the control transfer.
+        // This buffer will hold a UsbRequestSetup struct followed by the data.
+        let control_buffer_len =
+            mem::size_of::<UsbRequestSetup>() + self.control_request_setup.length as usize;
+        let mut control_buffer = vec![0u8; control_buffer_len];
 
-        let mut control_request = ControlTransferBuffer {
-            setup: self.control_request_setup,
-            data: [0; CONTROL_DATA_BUFFER_SIZE],
-        };
+        // Copy the control request header.
+        control_buffer[..mem::size_of::<UsbRequestSetup>()]
+            .copy_from_slice(self.control_request_setup.as_slice());
 
         let direction = self.control_request_setup.get_direction();
         let buffer = if direction == ControlRequestDataPhaseTransferDirection::HostToDevice {
             if let Some(buffer) = buffer {
                 buffer
-                    .read(&mut control_request.data)
+                    .read(&mut control_buffer[mem::size_of::<UsbRequestSetup>()..])
                     .map_err(Error::ReadBuffer)?;
             }
             // buffer is consumed here for HostToDevice transfers.
@@ -173,8 +164,6 @@ impl HostDevice {
             buffer
         };
 
-        let control_buffer = control_request.as_slice().to_vec();
-
         let mut control_transfer =
             Transfer::new_control(control_buffer).map_err(Error::CreateTransfer)?;
 
diff --git a/devices/src/usb/xhci/event_ring.rs b/devices/src/usb/xhci/event_ring.rs
index fcaff23..1742c77 100644
--- a/devices/src/usb/xhci/event_ring.rs
+++ b/devices/src/usb/xhci/event_ring.rs
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 use data_model::DataInit;
-use std;
 use std::fmt::{self, Display};
 use std::mem::size_of;
 use std::sync::atomic::{fence, Ordering};
diff --git a/devices/src/usb/xhci/xhci_abi.rs b/devices/src/usb/xhci/xhci_abi.rs
index 5a8748e..e9be3c3 100644
--- a/devices/src/usb/xhci/xhci_abi.rs
+++ b/devices/src/usb/xhci/xhci_abi.rs
@@ -8,8 +8,6 @@ use data_model::DataInit;
 use std::fmt::{self, Display};
 use sys_util::GuestAddress;
 
-use std;
-
 #[derive(Debug)]
 pub enum Error {
     UnknownTrbType(BitFieldError),
diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs
index b77a73a..e357ae3 100644
--- a/devices/src/usb/xhci/xhci_controller.rs
+++ b/devices/src/usb/xhci/xhci_controller.rs
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 use crate::pci::{
-    PciBarConfiguration, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType,
-    PciInterruptPin, PciProgrammingInterface, PciSerialBusSubClass,
+    PciAddress, PciBarConfiguration, PciClassCode, PciConfiguration, PciDevice, PciDeviceError,
+    PciHeaderType, PciInterruptPin, PciProgrammingInterface, PciSerialBusSubClass,
 };
 use crate::register_space::{Register, RegisterSpace};
 use crate::usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider;
@@ -94,7 +94,7 @@ enum XhciControllerState {
 /// xHCI PCI interface implementation.
 pub struct XhciController {
     config_regs: PciConfiguration,
-    pci_bus_dev: Option<(u8, u8)>,
+    pci_address: Option<PciAddress>,
     mem: GuestMemory,
     state: XhciControllerState,
 }
@@ -114,7 +114,7 @@ impl XhciController {
         );
         XhciController {
             config_regs,
-            pci_bus_dev: None,
+            pci_address: None,
             mem,
             state: XhciControllerState::Created {
                 device_provider: usb_provider,
@@ -166,8 +166,8 @@ impl PciDevice for XhciController {
         "xhci controller".to_owned()
     }
 
-    fn assign_bus_dev(&mut self, bus: u8, device: u8) {
-        self.pci_bus_dev = Some((bus, device));
+    fn assign_address(&mut self, address: PciAddress) {
+        self.pci_address = Some(address);
     }
 
     fn keep_fds(&self) -> Vec<RawFd> {
@@ -206,15 +206,20 @@ impl PciDevice for XhciController {
         &mut self,
         resources: &mut SystemAllocator,
     ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> {
-        let (bus, dev) = self
-            .pci_bus_dev
-            .expect("assign_bus_dev must be called prior to allocate_io_bars");
+        let address = self
+            .pci_address
+            .expect("assign_address must be called prior to allocate_io_bars");
         // xHCI spec 5.2.1.
         let bar0_addr = resources
             .mmio_allocator(MmioType::Low)
             .allocate_with_align(
                 XHCI_BAR0_SIZE,
-                Alloc::PciBar { bus, dev, bar: 0 },
+                Alloc::PciBar {
+                    bus: address.bus,
+                    dev: address.dev,
+                    func: address.func,
+                    bar: 0,
+                },
                 "xhci_bar0".to_string(),
                 XHCI_BAR0_SIZE,
             )
diff --git a/devices/src/virtio/balloon.rs b/devices/src/virtio/balloon.rs
index 3e6d602..b549258 100644
--- a/devices/src/virtio/balloon.rs
+++ b/devices/src/virtio/balloon.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::fmt::{self, Display};
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::sync::atomic::{AtomicUsize, Ordering};
diff --git a/devices/src/virtio/console.rs b/devices/src/virtio/console.rs
index 38f5bf1..50a5a76 100644
--- a/devices/src/virtio/console.rs
+++ b/devices/src/virtio/console.rs
@@ -13,6 +13,7 @@ use sys_util::{error, EventFd, GuestMemory, PollContext, PollToken};
 use super::{
     copy_config, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_CONSOLE, VIRTIO_F_VERSION_1,
 };
+use crate::SerialDevice;
 
 const QUEUE_SIZE: u16 = 256;
 
@@ -303,8 +304,9 @@ pub struct Console {
     keep_fds: Vec<RawFd>,
 }
 
-impl Console {
+impl SerialDevice for Console {
     fn new(
+        _evt_fd: EventFd,
         input: Option<Box<dyn io::Read + Send>>,
         output: Option<Box<dyn io::Write + Send>>,
         keep_fds: Vec<RawFd>,
@@ -317,25 +319,6 @@ impl Console {
             keep_fds,
         }
     }
-
-    /// Constructs a console with input and output streams.
-    pub fn new_in_out(
-        input: Box<dyn io::Read + Send>,
-        out: Box<dyn io::Write + Send>,
-        keep_fds: Vec<RawFd>,
-    ) -> Console {
-        Self::new(Some(input), Some(out), keep_fds)
-    }
-
-    /// Constructs a console with an output stream but no input.
-    pub fn new_out(out: Box<dyn io::Write + Send>, keep_fds: Vec<RawFd>) -> Console {
-        Self::new(None, Some(out), keep_fds)
-    }
-
-    /// Constructs a console with no connected input or output.
-    pub fn new_sink() -> Console {
-        Self::new(None, None, Vec::new())
-    }
 }
 
 impl Drop for Console {
diff --git a/devices/src/virtio/descriptor_utils.rs b/devices/src/virtio/descriptor_utils.rs
index 990f147..f90264a 100644
--- a/devices/src/virtio/descriptor_utils.rs
+++ b/devices/src/virtio/descriptor_utils.rs
@@ -467,9 +467,17 @@ impl<'a> Writer<'a> {
         self.write_all(val.as_slice())
     }
 
+    /// Writes all objects produced by `iter` into the descriptor chain buffer. Unlike `consume`,
+    /// this doesn't require the values to be stored in an intermediate collection first. It also
+    /// allows callers to choose which elements in a collection to write, for example by using the
+    /// `filter` or `take` methods of the `Iterator` trait.
+    pub fn write_iter<T: DataInit, I: Iterator<Item = T>>(&mut self, iter: I) -> io::Result<()> {
+        iter.map(|v| self.write_obj(v)).collect()
+    }
+
     /// Writes a collection of objects into the descriptor chain buffer.
     pub fn consume<T: DataInit, C: IntoIterator<Item = T>>(&mut self, vals: C) -> io::Result<()> {
-        vals.into_iter().map(|v| self.write_obj(v)).collect()
+        self.write_iter(vals.into_iter())
     }
 
     /// Returns number of bytes available for writing.  May return an error if the combined
diff --git a/devices/src/virtio/fs/filesystem.rs b/devices/src/virtio/fs/filesystem.rs
index eb9726c..474a278 100644
--- a/devices/src/virtio/fs/filesystem.rs
+++ b/devices/src/virtio/fs/filesystem.rs
@@ -9,8 +9,6 @@ use std::io;
 use std::mem;
 use std::time::Duration;
 
-use libc;
-
 use crate::virtio::fs::fuse;
 
 pub use fuse::{FsOptions, IoctlFlags, IoctlIovec, OpenOptions, SetattrValid, ROOT_ID};
@@ -77,7 +75,7 @@ pub struct DirEntry<'a> {
 
     /// The name of this directory entry. There are no requirements for the contents of this field
     /// and any sequence of bytes is considered valid.
-    pub name: &'a [u8],
+    pub name: &'a CStr,
 }
 
 /// A reply to a `getxattr` method call.
diff --git a/devices/src/virtio/fs/fuse.rs b/devices/src/virtio/fs/fuse.rs
index 5c531cf..981596b 100644
--- a/devices/src/virtio/fs/fuse.rs
+++ b/devices/src/virtio/fs/fuse.rs
@@ -7,7 +7,6 @@ use std::mem;
 use bitflags::bitflags;
 use data_model::DataInit;
 use enumn::N;
-use libc;
 
 /// Version number of this interface.
 pub const KERNEL_VERSION: u32 = 7;
diff --git a/devices/src/virtio/fs/passthrough.rs b/devices/src/virtio/fs/passthrough.rs
index d2034ba..bcc7c5d 100644
--- a/devices/src/virtio/fs/passthrough.rs
+++ b/devices/src/virtio/fs/passthrough.rs
@@ -15,7 +15,6 @@ use std::sync::Arc;
 use std::time::Duration;
 
 use data_model::DataInit;
-use libc;
 use sync::Mutex;
 use sys_util::{error, ioctl_ior_nr, ioctl_iow_nr, ioctl_with_mut_ptr, ioctl_with_ptr};
 
@@ -26,8 +25,6 @@ use crate::virtio::fs::filesystem::{
 use crate::virtio::fs::fuse;
 use crate::virtio::fs::multikey::MultikeyBTreeMap;
 
-const CURRENT_DIR_CSTR: &[u8] = b".\0";
-const PARENT_DIR_CSTR: &[u8] = b"..\0";
 const EMPTY_CSTR: &[u8] = b"\0";
 const ROOT_CSTR: &[u8] = b"/\0";
 const PROC_CSTR: &[u8] = b"/proc\0";
@@ -199,6 +196,20 @@ fn stat(f: &File) -> io::Result<libc::stat64> {
     }
 }
 
+// Like `CStr::from_bytes_with_nul` but strips any bytes after the first '\0'-byte. Panics if `b`
+// doesn't contain any '\0' bytes.
+fn strip_padding(b: &[u8]) -> &CStr {
+    // It would be nice if we could use memchr here but that's locked behind an unstable gate.
+    let pos = b
+        .iter()
+        .position(|&c| c == 0)
+        .expect("`b` doesn't contain any nul bytes");
+
+    // Safe because we are creating this string with the first nul-byte we found so we can
+    // guarantee that it is nul-terminated and doesn't contain any interior nuls.
+    unsafe { CStr::from_bytes_with_nul_unchecked(&b[..pos + 1]) }
+}
+
 /// The caching policy that the file system should report to the FUSE client. By default the FUSE
 /// protocol uses close-to-open consistency. This means that any cached contents of the file are
 /// invalidated the next time that file is opened.
@@ -576,19 +587,15 @@ impl PassthroughFs {
             let namelen = dirent64.d_reclen as usize - size_of::<LinuxDirent64>();
             debug_assert!(namelen <= back.len(), "back is smaller than `namelen`");
 
-            let name = &back[..namelen];
-            let res = if name.starts_with(CURRENT_DIR_CSTR) || name.starts_with(PARENT_DIR_CSTR) {
-                // We don't want to report the "." and ".." entries. However, returning `Ok(0)` will
-                // break the loop so return `Ok` with a non-zero value instead.
-                Ok(1)
-            } else {
-                add_entry(DirEntry {
-                    ino: dirent64.d_ino,
-                    offset: dirent64.d_off as u64,
-                    type_: dirent64.d_ty as u32,
-                    name,
-                })
-            };
+            // The kernel will pad the name with additional nul bytes until it is 8-byte aligned so
+            // we need to strip those off here.
+            let name = strip_padding(&back[..namelen]);
+            let res = add_entry(DirEntry {
+                ino: dirent64.d_ino,
+                offset: dirent64.d_off as u64,
+                type_: dirent64.d_ty as u32,
+                name,
+            });
 
             debug_assert!(
                 rem.len() >= dirent64.d_reclen as usize,
@@ -806,7 +813,8 @@ impl FileSystem for PassthroughFs {
             }),
         );
 
-        let mut opts = FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO;
+        let mut opts =
+            FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO | FsOptions::EXPORT_SUPPORT;
         if self.cfg.writeback && capable.contains(FsOptions::WRITEBACK_CACHE) {
             opts |= FsOptions::WRITEBACK_CACHE;
             self.writeback.store(true, Ordering::Relaxed);
@@ -933,16 +941,38 @@ impl FileSystem for PassthroughFs {
         F: FnMut(DirEntry, Entry) -> io::Result<usize>,
     {
         self.do_readdir(inode, handle, size, offset, |dir_entry| {
-            // Safe because the kernel guarantees that the buffer is nul-terminated. Additionally,
-            // the kernel will pad the name with '\0' bytes up to 8-byte alignment and there's no
-            // way for us to know exactly how many padding bytes there are. This would cause
-            // `CStr::from_bytes_with_nul` to return an error because it would think there are
-            // interior '\0' bytes. We trust the kernel to provide us with properly formatted data
-            // so we'll just skip the checks here.
-            let name = unsafe { CStr::from_bytes_with_nul_unchecked(dir_entry.name) };
-            let entry = self.do_lookup(inode, name)?;
-
-            add_entry(dir_entry, entry)
+            let name = dir_entry.name.to_bytes();
+            let entry = if name == b"." || name == b".." {
+                // Don't do lookups on the current directory or the parent directory. Safe because
+                // this only contains integer fields and any value is valid.
+                let mut attr = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
+                attr.st_ino = dir_entry.ino;
+                attr.st_mode = dir_entry.type_;
+
+                // We use 0 for the inode value to indicate a negative entry.
+                Entry {
+                    inode: 0,
+                    generation: 0,
+                    attr,
+                    attr_timeout: Duration::from_secs(0),
+                    entry_timeout: Duration::from_secs(0),
+                }
+            } else {
+                self.do_lookup(inode, dir_entry.name)?
+            };
+
+            let entry_inode = entry.inode;
+            add_entry(dir_entry, entry).map_err(|e| {
+                if entry_inode != 0 {
+                    // Undo the `do_lookup` for this inode since we aren't going to report it to
+                    // the kernel. If `entry_inode` was 0 then that means this was the "." or
+                    // ".." entry and there wasn't a lookup in the first place.
+                    let mut inodes = self.inodes.lock();
+                    forget_one(&mut inodes, entry_inode, 1);
+                }
+
+                e
+            })
         })
     }
 
@@ -1747,3 +1777,29 @@ impl FileSystem for PassthroughFs {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn padded_cstrings() {
+        assert_eq!(strip_padding(b".\0\0\0\0\0\0\0").to_bytes(), b".");
+        assert_eq!(strip_padding(b"..\0\0\0\0\0\0").to_bytes(), b"..");
+        assert_eq!(
+            strip_padding(b"normal cstring\0").to_bytes(),
+            b"normal cstring"
+        );
+        assert_eq!(strip_padding(b"\0\0\0\0").to_bytes(), b"");
+        assert_eq!(
+            strip_padding(b"interior\0nul bytes\0\0\0").to_bytes(),
+            b"interior"
+        );
+    }
+
+    #[test]
+    #[should_panic(expected = "`b` doesn't contain any nul bytes")]
+    fn no_nul_byte() {
+        strip_padding(b"no nul bytes in string");
+    }
+}
diff --git a/devices/src/virtio/fs/server.rs b/devices/src/virtio/fs/server.rs
index c9025f2..33b7c98 100644
--- a/devices/src/virtio/fs/server.rs
+++ b/devices/src/virtio/fs/server.rs
@@ -8,7 +8,6 @@ use std::io::{self, Read, Write};
 use std::mem::size_of;
 
 use data_model::DataInit;
-use libc;
 use sys_util::error;
 
 use crate::virtio::fs::filesystem::{
@@ -19,7 +18,7 @@ use crate::virtio::fs::fuse::*;
 use crate::virtio::fs::{Error, Result};
 use crate::virtio::{Reader, Writer};
 
-const MAX_BUFFER_SIZE: u32 = (1 << 20);
+const MAX_BUFFER_SIZE: u32 = 1 << 20;
 const DIRENT_PADDING: [u8; 8] = [0; 8];
 
 struct ZCReader<'a>(Reader<'a>);
@@ -1369,12 +1368,14 @@ fn add_dirent(
     d: DirEntry,
     entry: Option<Entry>,
 ) -> io::Result<usize> {
-    if d.name.len() > ::std::u32::MAX as usize {
+    // Strip the trailing '\0'.
+    let name = d.name.to_bytes();
+    if name.len() > ::std::u32::MAX as usize {
         return Err(io::Error::from_raw_os_error(libc::EOVERFLOW));
     }
 
     let dirent_len = size_of::<Dirent>()
-        .checked_add(d.name.len())
+        .checked_add(name.len())
         .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
 
     // Directory entries must be padded to 8-byte alignment.  If adding 7 causes
@@ -1402,12 +1403,12 @@ fn add_dirent(
         let dirent = Dirent {
             ino: d.ino,
             off: d.offset,
-            namelen: d.name.len() as u32,
+            namelen: name.len() as u32,
             type_: d.type_,
         };
 
         cursor.write_all(dirent.as_slice())?;
-        cursor.write_all(d.name)?;
+        cursor.write_all(name)?;
 
         // We know that `dirent_len` <= `padded_dirent_len` due to the check above
         // so there's no need for checked arithmetic.
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index fec2904..b305089 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -42,7 +42,9 @@ use self::virtio_2d_backend::Virtio2DBackend;
 use self::virtio_3d_backend::Virtio3DBackend;
 #[cfg(feature = "gfxstream")]
 use self::virtio_gfxstream_backend::VirtioGfxStreamBackend;
-use crate::pci::{PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability};
+use crate::pci::{
+    PciAddress, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability,
+};
 
 use vm_control::VmMemoryControlRequestSocket;
 
@@ -1203,10 +1205,11 @@ impl VirtioDevice for Gpu {
     }
 
     // Require 1 BAR for mapping 3D buffers
-    fn get_device_bars(&mut self, bus: u8, dev: u8) -> Vec<PciBarConfiguration> {
+    fn get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration> {
         self.pci_bar = Some(Alloc::PciBar {
-            bus,
-            dev,
+            bus: address.bus,
+            dev: address.dev,
+            func: address.func,
             bar: GPU_BAR_NUM,
         });
         vec![PciBarConfiguration::new(
diff --git a/devices/src/virtio/gpu/protocol.rs b/devices/src/virtio/gpu/protocol.rs
index 3df6975..5605dd3 100644
--- a/devices/src/virtio/gpu/protocol.rs
+++ b/devices/src/virtio/gpu/protocol.rs
@@ -137,7 +137,7 @@ pub fn virtio_gpu_cmd_str(cmd: u32) -> &'static str {
     }
 }
 
-pub const VIRTIO_GPU_FLAG_FENCE: u32 = (1 << 0);
+pub const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0;
 
 #[derive(Copy, Clone, Debug, Default)]
 #[repr(C)]
@@ -336,7 +336,7 @@ pub struct virtio_gpu_transfer_host_3d {
 unsafe impl DataInit for virtio_gpu_transfer_host_3d {}
 
 /* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
-pub const VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP: u32 = (1 << 0);
+pub const VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP: u32 = 1 << 0;
 #[derive(Copy, Clone, Debug, Default)]
 #[repr(C)]
 pub struct virtio_gpu_resource_create_3d {
diff --git a/devices/src/virtio/input/constants.rs b/devices/src/virtio/input/constants.rs
index 84799b2..7973312 100644
--- a/devices/src/virtio/input/constants.rs
+++ b/devices/src/virtio/input/constants.rs
@@ -12,7 +12,7 @@ pub const INPUT_PROP_POINTING_STICK: u16 = 0x05;
 pub const INPUT_PROP_ACCELEROMETER: u16 = 0x06;
 
 pub const INPUT_PROP_MAX: u16 = 0x1f;
-pub const INPUT_PROP_CNT: u16 = (INPUT_PROP_MAX + 1);
+pub const INPUT_PROP_CNT: u16 = INPUT_PROP_MAX + 1;
 
 pub const EV_SYN: u16 = 0x00;
 pub const EV_KEY: u16 = 0x01;
@@ -638,7 +638,7 @@ pub const BTN_TRIGGER_HAPPY40: u16 = 0x2e7;
 
 pub const KEY_MIN_INTERESTING: u16 = KEY_MUTE;
 pub const KEY_MAX: u16 = 0x2ff;
-pub const KEY_CNT: u16 = (KEY_MAX + 1);
+pub const KEY_CNT: u16 = KEY_MAX + 1;
 
 pub const REL_X: u16 = 0x00;
 pub const REL_Y: u16 = 0x01;
@@ -651,7 +651,7 @@ pub const REL_DIAL: u16 = 0x07;
 pub const REL_WHEEL: u16 = 0x08;
 pub const REL_MISC: u16 = 0x09;
 pub const REL_MAX: u16 = 0x0f;
-pub const REL_CNT: u16 = (REL_MAX + 1);
+pub const REL_CNT: u16 = REL_MAX + 1;
 
 pub const ABS_X: u16 = 0x00;
 pub const ABS_Y: u16 = 0x01;
@@ -699,7 +699,7 @@ pub const ABS_MT_TOOL_X: u16 = 0x3c;
 pub const ABS_MT_TOOL_Y: u16 = 0x3d;
 
 pub const ABS_MAX: u16 = 0x3f;
-pub const ABS_CNT: u16 = (ABS_MAX + 1);
+pub const ABS_CNT: u16 = ABS_MAX + 1;
 
 pub const MSC_SERIAL: u16 = 0x00;
 pub const MSC_PULSELED: u16 = 0x01;
@@ -708,7 +708,7 @@ pub const MSC_RAW: u16 = 0x03;
 pub const MSC_SCAN: u16 = 0x04;
 pub const MSC_TIMESTAMP: u16 = 0x05;
 pub const MSC_MAX: u16 = 0x07;
-pub const MSC_CNT: u16 = (MSC_MAX + 1);
+pub const MSC_CNT: u16 = MSC_MAX + 1;
 
 pub const LED_NUML: u16 = 0x00;
 pub const LED_CAPSL: u16 = 0x01;
@@ -722,12 +722,12 @@ pub const LED_MISC: u16 = 0x08;
 pub const LED_MAIL: u16 = 0x09;
 pub const LED_CHARGING: u16 = 0x0a;
 pub const LED_MAX: u16 = 0x0f;
-pub const LED_CNT: u16 = (LED_MAX + 1);
+pub const LED_CNT: u16 = LED_MAX + 1;
 
 pub const REP_DELAY: u16 = 0x00;
 pub const REP_PERIOD: u16 = 0x01;
 pub const REP_MAX: u16 = 0x01;
-pub const REP_CNT: u16 = (REP_MAX + 1);
+pub const REP_CNT: u16 = REP_MAX + 1;
 
 // Should match linux/virtio_input.h
 pub const VIRTIO_INPUT_CFG_UNSET: u8 = 0x00;
diff --git a/devices/src/virtio/net.rs b/devices/src/virtio/net.rs
index a15ab03..8e9aa9e 100644
--- a/devices/src/virtio/net.rs
+++ b/devices/src/virtio/net.rs
@@ -13,7 +13,6 @@ use std::sync::Arc;
 use std::thread;
 
 use data_model::{DataInit, Le16, Le64};
-use net_sys;
 use net_util::{Error as TapError, MacAddress, TapT};
 use sys_util::Error as SysError;
 use sys_util::{error, warn, EventFd, GuestMemory, PollContext, PollToken, WatchingEvents};
diff --git a/devices/src/virtio/p9.rs b/devices/src/virtio/p9.rs
index 5e483c3..7854c39 100644
--- a/devices/src/virtio/p9.rs
+++ b/devices/src/virtio/p9.rs
@@ -10,7 +10,6 @@ use std::path::{Path, PathBuf};
 use std::result;
 use std::thread;
 
-use p9;
 use sys_util::{error, warn, Error as SysError, EventFd, GuestMemory, PollContext, PollToken};
 use virtio_sys::vhost::VIRTIO_F_VERSION_1;
 
diff --git a/devices/src/virtio/pmem.rs b/devices/src/virtio/pmem.rs
index 499e110..cbeecc3 100644
--- a/devices/src/virtio/pmem.rs
+++ b/devices/src/virtio/pmem.rs
@@ -8,8 +8,8 @@ use std::io;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::thread;
 
-use sys_util::Result as SysResult;
 use sys_util::{error, EventFd, GuestAddress, GuestMemory, PollContext, PollToken};
+use sys_util::{Error as SysError, Result as SysResult};
 
 use data_model::{DataInit, Le32, Le64};
 
@@ -89,6 +89,7 @@ struct Worker {
     memory: GuestMemory,
     pmem_device_socket: VmMsyncRequestSocket,
     mapping_arena_slot: u32,
+    mapping_size: usize,
 }
 
 impl Worker {
@@ -98,6 +99,7 @@ impl Worker {
                 let request = VmMsyncRequest::MsyncArena {
                     slot: self.mapping_arena_slot,
                     offset: 0, // The pmem backing file is always at offset 0 in the arena.
+                    size: self.mapping_size,
                 };
 
                 if let Err(e) = self.pmem_device_socket.send(&request) {
@@ -235,6 +237,10 @@ impl Pmem {
         mapping_size: u64,
         pmem_device_socket: Option<VmMsyncRequestSocket>,
     ) -> SysResult<Pmem> {
+        if mapping_size > usize::max_value() as u64 {
+            return Err(SysError::new(libc::EOVERFLOW));
+        }
+
         Ok(Pmem {
             kill_event: None,
             worker_thread: None,
@@ -308,6 +314,8 @@ impl VirtioDevice for Pmem {
         let queue_event = queue_events.remove(0);
 
         let mapping_arena_slot = self.mapping_arena_slot;
+        // We checked that this fits in a usize in `Pmem::new`.
+        let mapping_size = self.mapping_size as usize;
 
         if let Some(pmem_device_socket) = self.pmem_device_socket.take() {
             let (self_kill_event, kill_event) =
@@ -329,6 +337,7 @@ impl VirtioDevice for Pmem {
                         queue,
                         pmem_device_socket,
                         mapping_arena_slot,
+                        mapping_size,
                     };
                     worker.run(queue_event, kill_event);
                 });
diff --git a/devices/src/virtio/rng.rs b/devices/src/virtio/rng.rs
index 9ce8dae..3897ee3 100644
--- a/devices/src/virtio/rng.rs
+++ b/devices/src/virtio/rng.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::io;
diff --git a/devices/src/virtio/tpm.rs b/devices/src/virtio/tpm.rs
index 95f7092..727c634 100644
--- a/devices/src/virtio/tpm.rs
+++ b/devices/src/virtio/tpm.rs
@@ -12,7 +12,6 @@ use std::path::PathBuf;
 use std::thread;
 
 use sys_util::{error, EventFd, GuestMemory, PollContext, PollToken};
-use tpm2;
 
 use super::{
     DescriptorChain, DescriptorError, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_TPM,
diff --git a/devices/src/virtio/vhost/mod.rs b/devices/src/virtio/vhost/mod.rs
index 86ed81e..4d7623f 100644
--- a/devices/src/virtio/vhost/mod.rs
+++ b/devices/src/virtio/vhost/mod.rs
@@ -4,7 +4,6 @@
 
 //! Implements vhost-based virtio devices.
 
-use std;
 use std::fmt::{self, Display};
 
 use net_util::Error as TapError;
diff --git a/devices/src/virtio/vhost/net.rs b/devices/src/virtio/vhost/net.rs
index 681cfe8..ce13226 100644
--- a/devices/src/virtio/vhost/net.rs
+++ b/devices/src/virtio/vhost/net.rs
@@ -7,7 +7,6 @@ use std::net::Ipv4Addr;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::thread;
 
-use net_sys;
 use net_util::{MacAddress, TapT};
 
 use sys_util::{error, warn, EventFd, GuestMemory};
diff --git a/devices/src/virtio/virtio_device.rs b/devices/src/virtio/virtio_device.rs
index 6eb5548..7c07651 100644
--- a/devices/src/virtio/virtio_device.rs
+++ b/devices/src/virtio/virtio_device.rs
@@ -7,7 +7,7 @@ use std::os::unix::io::RawFd;
 use sys_util::{EventFd, GuestMemory};
 
 use super::*;
-use crate::pci::{MsixStatus, PciBarConfiguration, PciCapability};
+use crate::pci::{MsixStatus, PciAddress, PciBarConfiguration, PciCapability};
 
 /// Trait for virtio devices to be driven by a virtio transport.
 ///
@@ -75,7 +75,7 @@ pub trait VirtioDevice: Send {
     }
 
     /// Returns any additional BAR configuration required by the device.
-    fn get_device_bars(&mut self, _bus: u8, _dev: u8) -> Vec<PciBarConfiguration> {
+    fn get_device_bars(&mut self, _address: PciAddress) -> Vec<PciBarConfiguration> {
         Vec::new()
     }
 
diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs
index e63abe9..eb91e58 100644
--- a/devices/src/virtio/virtio_pci_device.rs
+++ b/devices/src/virtio/virtio_pci_device.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Arc;
@@ -16,10 +15,10 @@ use sys_util::{warn, EventFd, GuestMemory, Result};
 
 use super::*;
 use crate::pci::{
-    MsixCap, MsixConfig, PciBarConfiguration, PciCapability, PciCapabilityID, PciClassCode,
-    PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciInterruptPin, PciSubclass,
+    MsixCap, MsixConfig, PciAddress, PciBarConfiguration, PciCapability, PciCapabilityID,
+    PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciInterruptPin,
+    PciSubclass,
 };
-
 use vm_control::VmIrqRequestSocket;
 
 use self::virtio_pci_common_config::VirtioPciCommonConfig;
@@ -212,7 +211,7 @@ const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040; // Add to device type to get devi
 /// transport for virtio devices.
 pub struct VirtioPciDevice {
     config_regs: PciConfiguration,
-    pci_bus_dev: Option<(u8, u8)>,
+    pci_address: Option<PciAddress>,
 
     device: Box<dyn VirtioDevice>,
     device_activated: bool,
@@ -270,7 +269,7 @@ impl VirtioPciDevice {
 
         Ok(VirtioPciDevice {
             config_regs,
-            pci_bus_dev: None,
+            pci_address: None,
             device,
             device_activated: false,
             interrupt_status: Arc::new(AtomicUsize::new(0)),
@@ -393,8 +392,8 @@ impl PciDevice for VirtioPciDevice {
         format!("virtio-pci ({})", self.device.debug_label())
     }
 
-    fn assign_bus_dev(&mut self, bus: u8, device: u8) {
-        self.pci_bus_dev = Some((bus, device));
+    fn assign_address(&mut self, address: PciAddress) {
+        self.pci_address = Some(address);
     }
 
     fn keep_fds(&self) -> Vec<RawFd> {
@@ -426,16 +425,21 @@ impl PciDevice for VirtioPciDevice {
         &mut self,
         resources: &mut SystemAllocator,
     ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> {
-        let (bus, dev) = self
-            .pci_bus_dev
-            .expect("assign_bus_dev must be called prior to allocate_io_bars");
+        let address = self
+            .pci_address
+            .expect("assign_address must be called prior to allocate_io_bars");
         // Allocate one bar for the structures pointed to by the capability structures.
         let mut ranges = Vec::new();
         let settings_config_addr = resources
             .mmio_allocator(MmioType::Low)
             .allocate_with_align(
                 CAPABILITY_BAR_SIZE,
-                Alloc::PciBar { bus, dev, bar: 0 },
+                Alloc::PciBar {
+                    bus: address.bus,
+                    dev: address.dev,
+                    func: address.func,
+                    bar: 0,
+                },
                 format!(
                     "virtio-{}-cap_bar",
                     type_to_str(self.device.device_type()).unwrap_or("?")
@@ -464,18 +468,19 @@ impl PciDevice for VirtioPciDevice {
         &mut self,
         resources: &mut SystemAllocator,
     ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> {
-        let (bus, dev) = self
-            .pci_bus_dev
-            .expect("assign_bus_dev must be called prior to allocate_device_bars");
+        let address = self
+            .pci_address
+            .expect("assign_address must be called prior to allocate_device_bars");
         let mut ranges = Vec::new();
-        for config in self.device.get_device_bars(bus, dev) {
+        for config in self.device.get_device_bars(address) {
             let device_addr = resources
                 .mmio_allocator(MmioType::High)
                 .allocate_with_align(
                     config.get_size(),
                     Alloc::PciBar {
-                        bus,
-                        dev,
+                        bus: address.bus,
+                        dev: address.dev,
+                        func: address.func,
                         bar: config.get_register_index() as u8,
                     },
                     format!(
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs
index 95075b4..517a22e 100644
--- a/devices/src/virtio/wl.rs
+++ b/devices/src/virtio/wl.rs
@@ -47,7 +47,7 @@ use std::thread;
 use std::time::Duration;
 
 #[cfg(feature = "wl-dmabuf")]
-use libc::{dup, EBADF, EINVAL};
+use libc::{EBADF, EINVAL};
 
 use data_model::VolatileMemoryError;
 use data_model::*;
@@ -543,9 +543,6 @@ impl fmt::Debug for WlVfd {
 impl WlVfd {
     fn connect<P: AsRef<Path>>(path: P) -> WlResult<WlVfd> {
         let socket = UnixStream::connect(path).map_err(WlError::SocketConnect)?;
-        socket
-            .set_nonblocking(true)
-            .map_err(WlError::SocketNonBlock)?;
         let mut vfd = WlVfd::default();
         vfd.socket = Some(socket);
         Ok(vfd)
@@ -587,15 +584,13 @@ impl WlVfd {
             })?;
         match allocate_and_register_gpu_memory_response {
             VmMemoryResponse::AllocateAndRegisterGpuMemory {
-                fd,
+                fd: MaybeOwnedFd::Owned(file),
                 pfn,
                 slot,
                 desc,
             } => {
                 let mut vfd = WlVfd::default();
-                // Duplicate FD for shared memory instance.
-                let raw_fd = unsafe { File::from_raw_fd(dup(fd.as_raw_fd())) };
-                let vfd_shm = SharedMemory::from_raw_fd(raw_fd).map_err(WlError::NewAlloc)?;
+                let vfd_shm = SharedMemory::from_file(file).map_err(WlError::NewAlloc)?;
                 vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm.into()));
                 vfd.slot = Some((slot, pfn, vm));
                 vfd.is_dmabuf = true;
diff --git a/disk/src/qcow/refcount.rs b/disk/src/qcow/refcount.rs
index 438de5b..ab399c3 100644
--- a/disk/src/qcow/refcount.rs
+++ b/disk/src/qcow/refcount.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::fmt::{self, Display};
 use std::io;
 
diff --git a/disk/src/qcow/vec_cache.rs b/disk/src/qcow/vec_cache.rs
index ecd7673..18cfc69 100644
--- a/disk/src/qcow/vec_cache.rs
+++ b/disk/src/qcow/vec_cache.rs
@@ -120,7 +120,7 @@ impl<T: Cacheable> CacheMap<T> {
     {
         if self.map.len() == self.capacity {
             // TODO(dgreid) - smarter eviction strategy.
-            let to_evict = *self.map.iter().nth(0).unwrap().0;
+            let to_evict = *self.map.iter().next().unwrap().0;
             if let Some(evicted) = self.map.remove(&to_evict) {
                 if evicted.dirty() {
                     write_callback(to_evict, evicted)?;
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 1bd1086..ecafd0a 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -36,16 +36,15 @@ RUN apt-get update && apt-get install -y \
 ENV RUSTUP_HOME=/usr/local/rustup \
     CARGO_HOME=/usr/local/cargo \
     PATH=/usr/local/cargo/bin:$PATH \
-    RUST_VERSION=1.41.0 \
+    RUST_VERSION=1.42.0 \
     RUSTFLAGS='--cfg hermetic'
 
 # Debian usually has an old rust version in the repository. Instead of using that, we use rustup to
 # pull in a toolchain versions of our choosing.
-RUN curl -LO "https://static.rust-lang.org/rustup/archive/1.14.0/x86_64-unknown-linux-gnu/rustup-init" \
-    && echo "0077ff9c19f722e2be202698c037413099e1188c0c233c12a2297bf18e9ff6e7 *rustup-init" | sha256sum -c - \
+RUN curl -LO "https://static.rust-lang.org/rustup/archive/1.21.1/x86_64-unknown-linux-gnu/rustup-init" \
+    && echo "ad1f8b5199b3b9e231472ed7aa08d2e5d1d539198a15c5b1e53c746aad81d27b *rustup-init" | sha256sum -c - \
     && chmod +x rustup-init \
     && ./rustup-init -y --no-modify-path --default-toolchain $RUST_VERSION \
-    && rustup component add rustfmt-preview \
     && rm rustup-init \
     && chmod -R a+w $RUSTUP_HOME $CARGO_HOME \
     && rustup --version \
@@ -124,17 +123,20 @@ RUN git clone https://chromium.googlesource.com/chromiumos/platform2 $PLATFORM2_
     && cp librendernodehost.a /lib \
     && git clean -f
 
-# Create a dummy pc file for libvda to run 'cargo check' with video features.
-RUN echo "Name: libvda_pc\nDescription:\nVersion:0.1\nLibs: -lvda" > /usr/lib/pkgconfig/libvda.pc
-
 # Set up sysroot from which system_api proto files are built.
 ENV SYSROOT=/sysroot
 RUN mkdir -p $SYSROOT/usr/include/chromeos/dbus/trunks \
     && cp $PLATFORM2_ROOT/trunks/interface.proto \
         $SYSROOT/usr/include/chromeos/dbus/trunks
+# Copy it under rustc's sysroot as well for cargo clippy.
+RUN export RUST_SYSROOT=$(rustc --print sysroot); echo $RUST_SYSROOT
+RUN mkdir -p $RUST_SYSROOT/usr/include/chromeos/dbus/trunks \
+  && cp $PLATFORM2_ROOT/trunks/interface.proto \
+        $RUST_SYSROOT/usr/include/chromeos/dbus/trunks
 
 # Inform pkg-config where libraries we install are placed.
-COPY pkgconfig/* /usr/lib/pkgconfig
+# Also, copy a dummy libvda.pc to compile crosvm with video features.
+COPY pkgconfig/* /usr/lib/pkgconfig/
 
 # Reduces image size and prevents accidentally using /scratch files
 RUN rm -r /scratch /usr/bin/meson
diff --git a/docker/Dockerfile.crosvm b/docker/Dockerfile.crosvm
index 00649ba..60ba5a9 100644
--- a/docker/Dockerfile.crosvm
+++ b/docker/Dockerfile.crosvm
@@ -2,10 +2,6 @@ FROM crosvm-base
 
 COPY . /platform/crosvm
 
-# Compile crosvm with all features, including experimental and/or platform specific ones that
-# are disabled at 'cargo install' below.
-RUN cargo check --all-features
-
 RUN cargo install --features 'default-no-sandbox wl-dmabuf gpu x' --path . --root /usr
 
 ARG UID=1000
diff --git a/docker/checkout_commits.env b/docker/checkout_commits.env
index 60aecc1..712b5f3 100644
--- a/docker/checkout_commits.env
+++ b/docker/checkout_commits.env
@@ -1,6 +1,6 @@
 MESON_COMMIT=a1a8772034aef90e8d58230d8bcfce54ab27bf6a
 LIBEPOXY_COMMIT=af38a466caf9c2ae49b8acda4ff842ae44d57f78
 TPM2_COMMIT=a9bc45bb7fafc65ea8a787894434d409f533b1f1
-PLATFORM2_COMMIT=9239a43f2dc2e98e57e9d77aac72fa3ce8169e5f
-ADHD_COMMIT=db796cecdea7013b8679f90dfae34915edc9246f
+PLATFORM2_COMMIT=fabad43f1182bf71b3771735b4488180d08f3d59
+ADHD_COMMIT=8405d713c2c293646723a424c218af4a72e598f2
 DRM_COMMIT=00320d7d68ddc7d815d073bb7c92d9a1f9bb8c31
diff --git a/docker/pkgconfig/libvda.pc b/docker/pkgconfig/libvda.pc
new file mode 100644
index 0000000..ee57864
--- /dev/null
+++ b/docker/pkgconfig/libvda.pc
@@ -0,0 +1,4 @@
+Name: libvda
+Description: CrOS VDA Connection Library
+Version: 0.1
+Libs: -lvda
diff --git a/gpu_buffer/src/raw.rs b/gpu_buffer/src/raw.rs
index 6f5686a..ba07259 100644
--- a/gpu_buffer/src/raw.rs
+++ b/gpu_buffer/src/raw.rs
@@ -6,7 +6,7 @@
 // Then modified manually
 
 /* Added below line manually */
-#![allow(dead_code)]
+#![allow(dead_code, non_camel_case_types)]
 
 /* Added below line manually */
 use std::os::raw::{c_char, c_int, c_uint, c_void};
diff --git a/gpu_display/src/gpu_display_stub.rs b/gpu_display/src/gpu_display_stub.rs
index 9b33a96..605e009 100644
--- a/gpu_display/src/gpu_display_stub.rs
+++ b/gpu_display/src/gpu_display_stub.rs
@@ -13,7 +13,7 @@ use sys_util::EventFd;
 
 type SurfaceId = NonZeroU32;
 
-#[allow(unused_variables)]
+#[allow(dead_code)]
 struct Buffer {
     width: u32,
     height: u32,
diff --git a/gpu_display/src/gpu_display_x.rs b/gpu_display/src/gpu_display_x.rs
index 2940f40..0882dc9 100644
--- a/gpu_display/src/gpu_display_x.rs
+++ b/gpu_display/src/gpu_display_x.rs
@@ -20,7 +20,7 @@ use std::num::NonZeroU32;
 use std::os::raw::c_ulong;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::ptr::{null, null_mut, NonNull};
-use std::rc::{Rc, Weak};
+use std::rc::Rc;
 use std::time::Duration;
 
 use libc::{shmat, shmctl, shmdt, shmget, IPC_CREAT, IPC_PRIVATE, IPC_RMID};
diff --git a/gpu_display/src/keycode_converter/mod.rs b/gpu_display/src/keycode_converter/mod.rs
index 236e65d..00549ed 100644
--- a/gpu_display/src/keycode_converter/mod.rs
+++ b/gpu_display/src/keycode_converter/mod.rs
@@ -8,6 +8,7 @@ use data::{MapEntry, KEYCODE_MAP};
 use std::collections::HashMap;
 
 /// Specifies which type of scancode to convert *from* in the KeycodeTranslator.
+#[allow(dead_code)]
 pub enum KeycodeTypes {
     XkbScancode,
     WindowsScancode,
diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml
new file mode 100644
index 0000000..de82105
--- /dev/null
+++ b/hypervisor/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "hypervisor"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+libc = "*"
+kvm_sys = { path = "../kvm_sys" }
+sys_util = { path = "../sys_util" }
\ No newline at end of file
diff --git a/hypervisor/src/caps.rs b/hypervisor/src/caps.rs
new file mode 100644
index 0000000..d088ca7
--- /dev/null
+++ b/hypervisor/src/caps.rs
@@ -0,0 +1,6 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// An enumeration of different hypervisor capabilities.
+pub enum HypervisorCap {}
diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs
new file mode 100644
index 0000000..7058921
--- /dev/null
+++ b/hypervisor/src/kvm/mod.rs
@@ -0,0 +1,62 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::{CpuId, Hypervisor, HypervisorCap};
+use libc::{open, O_CLOEXEC, O_RDWR};
+use std::os::raw::c_char;
+use sys_util::{
+    errno_result, AsRawDescriptor, FromRawDescriptor, RawDescriptor, Result, SafeDescriptor,
+};
+
+pub struct Kvm {
+    kvm: SafeDescriptor,
+}
+
+impl Kvm {
+    /// Opens `/dev/kvm/` and returns a Kvm object on success.
+    pub fn new() -> Result<Kvm> {
+        // Open calls are safe because we give a constant nul-terminated string and verify the
+        // result.
+        let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, O_RDWR | O_CLOEXEC) };
+        if ret < 0 {
+            return errno_result();
+        }
+        // Safe because we verify that ret is valid and we own the fd.
+        Ok(Kvm {
+            kvm: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
+        })
+    }
+}
+
+impl AsRawDescriptor for Kvm {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.kvm.as_raw_descriptor()
+    }
+}
+
+impl Hypervisor for Kvm {
+    fn check_capability(&self, _cap: &HypervisorCap) -> bool {
+        unimplemented!("check_capability for Kvm is not yet implemented");
+    }
+
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn get_supported_cpuid(&self) -> Result<CpuId> {
+        unimplemented!("get_supported_cpuid for Kvm is not yet implemented");
+    }
+
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn get_emulated_cpuid(&self) -> Result<CpuId> {
+        unimplemented!("get_emulated_cpuid for Kvm is not yet implemented");
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Kvm;
+
+    #[test]
+    fn new() {
+        Kvm::new().unwrap();
+    }
+}
diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs
new file mode 100644
index 0000000..056070b
--- /dev/null
+++ b/hypervisor/src/lib.rs
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! A crate for abstracting the underlying kernel hypervisor used in crosvm.
+pub mod caps;
+pub mod kvm;
+pub mod types;
+
+use sys_util::Result;
+
+pub use crate::caps::*;
+pub use crate::types::*;
+
+/// A trait for managing the underlying cpu information for the hypervisor and to check its capabilities.
+trait Hypervisor {
+    // Checks if a particular `HypervisorCap` is available.
+    fn check_capability(&self, cap: &HypervisorCap) -> bool;
+    // Get the system supported CPUID values.
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn get_supported_cpuid(&self) -> Result<CpuId>;
+    // Get the system emulated CPUID values.
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn get_emulated_cpuid(&self) -> Result<CpuId>;
+}
diff --git a/hypervisor/src/types/mod.rs b/hypervisor/src/types/mod.rs
new file mode 100644
index 0000000..69fa9e4
--- /dev/null
+++ b/hypervisor/src/types/mod.rs
@@ -0,0 +1,9 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub mod x86;
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub use self::x86::*;
diff --git a/hypervisor/src/types/x86.rs b/hypervisor/src/types/x86.rs
new file mode 100644
index 0000000..cd5236a
--- /dev/null
+++ b/hypervisor/src/types/x86.rs
@@ -0,0 +1,10 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use kvm_sys::kvm_cpuid_entry2;
+
+pub type CpuIdEntry = kvm_cpuid_entry2;
+pub struct CpuId {
+    _cpu_id_entries: Vec<CpuIdEntry>,
+}
diff --git a/io_jail/src/lib.rs b/io_jail/src/lib.rs
index a927cdb..7a12307 100644
--- a/io_jail/src/lib.rs
+++ b/io_jail/src/lib.rs
@@ -68,6 +68,8 @@ pub enum Error {
     PreservingFd(i32),
     /// Program size is too large
     ProgramTooLarge,
+    /// Alignment of file should be divisible by the alignment of sock_filter.
+    WrongProgramAlignment,
     /// File size should be non-zero and a multiple of sock_filter
     WrongProgramSize,
 }
@@ -148,6 +150,10 @@ impl Display for Error {
             ProcFd(s) => write!(f, "an entry in /proc/self/fd is not an integer: {}", s),
             PreservingFd(e) => write!(f, "fork failed in minijail_preserve_fd with error {}", e),
             ProgramTooLarge => write!(f, "bpf program is too large (max 64K instructions)"),
+            WrongProgramAlignment => write!(
+                f,
+                "the alignment of bpf file was not a multiple of that of sock_filter"
+            ),
             WrongProgramSize => write!(f, "bpf file was empty or not a multiple of sock_filter"),
         }
     }
@@ -287,6 +293,13 @@ impl Minijail {
         if count > (!0 as u16) as usize {
             return Err(Error::ProgramTooLarge);
         }
+        if buffer.as_ptr() as usize % std::mem::align_of::<sock_filter>() != 0 {
+            return Err(Error::WrongProgramAlignment);
+        }
+
+        // Safe cast because we checked that the buffer address is divisible by the alignment of
+        // sock_filter.
+        #[allow(clippy::cast_ptr_alignment)]
         let header = sock_fprog {
             len: count as c_ushort,
             filter: buffer.as_ptr() as *mut sock_filter,
diff --git a/kvm/src/cap.rs b/kvm/src/cap.rs
index 7dfd965..ff65b59 100644
--- a/kvm/src/cap.rs
+++ b/kvm/src/cap.rs
@@ -119,4 +119,5 @@ pub enum Cap {
     CheckExtensionVm = KVM_CAP_CHECK_EXTENSION_VM,
     S390UserSigp = KVM_CAP_S390_USER_SIGP,
     ImmediateExit = KVM_CAP_IMMEDIATE_EXIT,
+    ArmPmuV3 = KVM_CAP_ARM_PMU_V3,
 }
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index b8cd12b..8b98b7c 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -1763,6 +1763,50 @@ impl Vcpu {
         if ret < 0 {
             return errno_result();
         }
+
+        Ok(())
+    }
+
+    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+    pub fn arm_pmu_init(&self, irq: u64) -> Result<()> {
+        let irq_addr = &irq as *const u64;
+
+        // The in-kernel PMU virtualization is initialized by setting the irq
+        // with KVM_ARM_VCPU_PMU_V3_IRQ and then by KVM_ARM_VCPU_PMU_V3_INIT.
+
+        let irq_attr = kvm_device_attr {
+            group: kvm_sys::KVM_ARM_VCPU_PMU_V3_CTRL,
+            attr: kvm_sys::KVM_ARM_VCPU_PMU_V3_IRQ as u64,
+            addr: irq_addr as u64,
+            flags: 0,
+        };
+        // safe becuase we allocated the struct and we know the kernel will read
+        // exactly the size of the struct
+        let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_HAS_DEVICE_ATTR(), &irq_attr) };
+        if ret < 0 {
+            return errno_result();
+        }
+
+        // safe becuase we allocated the struct and we know the kernel will read
+        // exactly the size of the struct
+        let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_SET_DEVICE_ATTR(), &irq_attr) };
+        if ret < 0 {
+            return errno_result();
+        }
+
+        let init_attr = kvm_device_attr {
+            group: kvm_sys::KVM_ARM_VCPU_PMU_V3_CTRL,
+            attr: kvm_sys::KVM_ARM_VCPU_PMU_V3_INIT as u64,
+            addr: 0,
+            flags: 0,
+        };
+        // safe becuase we allocated the struct and we know the kernel will read
+        // exactly the size of the struct
+        let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_SET_DEVICE_ATTR(), &init_attr) };
+        if ret < 0 {
+            return errno_result();
+        }
+
         Ok(())
     }
 
diff --git a/kvm_sys/src/aarch64/bindings.rs b/kvm_sys/src/aarch64/bindings.rs
index 084fc13..0dd7f5a 100644
--- a/kvm_sys/src/aarch64/bindings.rs
+++ b/kvm_sys/src/aarch64/bindings.rs
@@ -157,6 +157,10 @@ pub const KVM_VGIC_V3_ADDR_TYPE_REDIST: ::std::os::raw::c_uint = 3;
 pub const KVM_ARM_VCPU_POWER_OFF: ::std::os::raw::c_uint = 0;
 pub const KVM_ARM_VCPU_EL1_32BIT: ::std::os::raw::c_uint = 1;
 pub const KVM_ARM_VCPU_PSCI_0_2: ::std::os::raw::c_uint = 2;
+pub const KVM_ARM_VCPU_PMU_V3: ::std::os::raw::c_uint = 3;
+pub const KVM_ARM_VCPU_PMU_V3_CTRL: ::std::os::raw::c_uint = 0;
+pub const KVM_ARM_VCPU_PMU_V3_IRQ: ::std::os::raw::c_uint = 0;
+pub const KVM_ARM_VCPU_PMU_V3_INIT: ::std::os::raw::c_uint = 1;
 pub const KVM_ARM_MAX_DBG_REGS: ::std::os::raw::c_uint = 16;
 pub const KVM_GUESTDBG_USE_SW_BP: ::std::os::raw::c_uint = 65536;
 pub const KVM_GUESTDBG_USE_HW: ::std::os::raw::c_uint = 131072;
@@ -439,6 +443,7 @@ pub const KVM_CAP_GUEST_DEBUG_HW_BPS: ::std::os::raw::c_uint = 119;
 pub const KVM_CAP_GUEST_DEBUG_HW_WPS: ::std::os::raw::c_uint = 120;
 pub const KVM_CAP_SPLIT_IRQCHIP: ::std::os::raw::c_uint = 121;
 pub const KVM_CAP_IOEVENTFD_ANY_LENGTH: ::std::os::raw::c_uint = 122;
+pub const KVM_CAP_ARM_PMU_V3: ::std::os::raw::c_uint = 126;
 pub const KVM_CAP_IMMEDIATE_EXIT: ::std::os::raw::c_uint = 136;
 pub const KVM_IRQ_ROUTING_IRQCHIP: ::std::os::raw::c_uint = 1;
 pub const KVM_IRQ_ROUTING_MSI: ::std::os::raw::c_uint = 2;
diff --git a/kvm_sys/src/x86/bindings.rs b/kvm_sys/src/x86/bindings.rs
index 7236611..629d83a 100644
--- a/kvm_sys/src/x86/bindings.rs
+++ b/kvm_sys/src/x86/bindings.rs
@@ -426,6 +426,7 @@ pub const KVM_CAP_GUEST_DEBUG_HW_WPS: ::std::os::raw::c_uint = 120;
 pub const KVM_CAP_SPLIT_IRQCHIP: ::std::os::raw::c_uint = 121;
 pub const KVM_CAP_IOEVENTFD_ANY_LENGTH: ::std::os::raw::c_uint = 122;
 pub const KVM_CAP_HYPERV_SYNIC: ::std::os::raw::c_uint = 123;
+pub const KVM_CAP_ARM_PMU_V3: ::std::os::raw::c_uint = 126;
 pub const KVM_CAP_IMMEDIATE_EXIT: ::std::os::raw::c_uint = 136;
 pub const KVM_CAP_HYPERV_SYNIC2: ::std::os::raw::c_uint = 148;
 pub const KVM_IRQ_ROUTING_IRQCHIP: ::std::os::raw::c_uint = 1;
diff --git a/msg_socket/Cargo.toml b/msg_socket/Cargo.toml
index c803bed..80eba0b 100644
--- a/msg_socket/Cargo.toml
+++ b/msg_socket/Cargo.toml
@@ -11,3 +11,4 @@ futures = "*"
 libc = "*"
 msg_on_socket_derive = { path = "msg_on_socket_derive" }
 sys_util = { path = "../sys_util"  }
+sync = { path = "../sync"  }
diff --git a/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs b/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs
index 7b16546..a5f31f8 100644
--- a/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs
+++ b/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs
@@ -9,11 +9,12 @@ use std::vec::Vec;
 use proc_macro2::{Span, TokenStream};
 use quote::{format_ident, quote};
 use syn::{
-    parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Fields, Ident, Index, Member, Type,
+    parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Fields, Ident, Index, Member, Meta,
+    NestedMeta, Type,
 };
 
 /// The function that derives the recursive implementation for struct or enum.
-#[proc_macro_derive(MsgOnSocket)]
+#[proc_macro_derive(MsgOnSocket, attributes(msg_on_socket))]
 pub fn msg_on_socket_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
     let impl_for_input = msg_socket_impl(input);
@@ -46,6 +47,13 @@ fn is_named_struct(ds: &DataStruct) -> bool {
 }
 
 /************************** Named Struct Impls ********************************************/
+
+struct StructField {
+    member: Member,
+    ty: Type,
+    skipped: bool,
+}
+
 fn impl_for_named_struct(name: Ident, ds: DataStruct) -> TokenStream {
     let fields = get_struct_fields(ds);
     let uses_fd_impl = define_uses_fd_for_struct(&fields);
@@ -64,7 +72,7 @@ fn impl_for_named_struct(name: Ident, ds: DataStruct) -> TokenStream {
 }
 
 // Flatten struct fields.
-fn get_struct_fields(ds: DataStruct) -> Vec<(Member, Type)> {
+fn get_struct_fields(ds: DataStruct) -> Vec<StructField> {
     let fields = match ds.fields {
         Fields::Named(fields_named) => fields_named.named,
         _ => {
@@ -78,17 +86,48 @@ fn get_struct_fields(ds: DataStruct) -> Vec<(Member, Type)> {
             None => panic!("Unknown Error."),
         };
         let ty = field.ty;
-        vec.push((member, ty));
+        let mut skipped = false;
+        for attr in field
+            .attrs
+            .iter()
+            .filter(|attr| attr.path.is_ident("msg_on_socket"))
+        {
+            match attr.parse_meta().unwrap() {
+                Meta::List(meta) => {
+                    for nested in meta.nested {
+                        match nested {
+                            NestedMeta::Meta(Meta::Path(meta_path))
+                                if meta_path.is_ident("skip") =>
+                            {
+                                skipped = true;
+                            }
+                            _ => panic!("unrecognized attribute meta `{}`", quote! { #nested }),
+                        }
+                    }
+                }
+                _ => panic!("unrecognized attribute `{}`", quote! { #attr }),
+            }
+        }
+        vec.push(StructField {
+            member,
+            ty,
+            skipped,
+        });
     }
     vec
 }
 
-fn define_uses_fd_for_struct(fields: &[(Member, Type)]) -> TokenStream {
-    if fields.len() == 0 {
+fn define_uses_fd_for_struct(fields: &[StructField]) -> TokenStream {
+    let field_types: Vec<_> = fields
+        .iter()
+        .filter(|f| !f.skipped)
+        .map(|f| &f.ty)
+        .collect();
+
+    if field_types.is_empty() {
         return quote!();
     }
 
-    let field_types = fields.iter().map(|(_, ty)| ty);
     quote! {
         fn uses_fd() -> bool {
             #(<#field_types>::uses_fd())||*
@@ -96,7 +135,7 @@ fn define_uses_fd_for_struct(fields: &[(Member, Type)]) -> TokenStream {
     }
 }
 
-fn define_buffer_size_for_struct(fields: &[(Member, Type)]) -> TokenStream {
+fn define_buffer_size_for_struct(fields: &[StructField]) -> TokenStream {
     let (msg_size, fd_count) = get_fields_buffer_size_sum(fields);
     quote! {
         fn msg_size(&self) -> usize {
@@ -108,17 +147,24 @@ fn define_buffer_size_for_struct(fields: &[(Member, Type)]) -> TokenStream {
     }
 }
 
-fn define_read_buffer_for_struct(_name: &Ident, fields: &[(Member, Type)]) -> TokenStream {
+fn define_read_buffer_for_struct(_name: &Ident, fields: &[StructField]) -> TokenStream {
     let mut read_fields = Vec::new();
     let mut init_fields = Vec::new();
-    for (field_member, field_ty) in fields {
-        let ident = match field_member {
+    for field in fields {
+        let ident = match &field.member {
             Member::Named(ident) => ident,
             Member::Unnamed(_) => unreachable!(),
         };
-        let read_field = read_from_buffer_and_move_offset(&ident, &field_ty);
-        read_fields.push(read_field);
         let name = ident.clone();
+        if field.skipped {
+            let ty = &field.ty;
+            init_fields.push(quote! {
+                #name: <#ty>::default()
+            });
+            continue;
+        }
+        let read_field = read_from_buffer_and_move_offset(&ident, &field.ty);
+        read_fields.push(read_field);
         init_fields.push(quote!(#name));
     }
     quote! {
@@ -139,10 +185,13 @@ fn define_read_buffer_for_struct(_name: &Ident, fields: &[(Member, Type)]) -> To
     }
 }
 
-fn define_write_buffer_for_struct(_name: &Ident, fields: &[(Member, Type)]) -> TokenStream {
+fn define_write_buffer_for_struct(_name: &Ident, fields: &[StructField]) -> TokenStream {
     let mut write_fields = Vec::new();
-    for (field_member, _) in fields {
-        let ident = match field_member {
+    for field in fields {
+        if field.skipped {
+            continue;
+        }
+        let ident = match &field.member {
             Member::Named(ident) => ident,
             Member::Unnamed(_) => unreachable!(),
         };
@@ -187,17 +236,13 @@ fn define_uses_fd_for_enum(de: &DataEnum) -> TokenStream {
         }
     }
 
-    if variant_field_types.is_empty() {
-        quote! {
-            fn uses_fd() -> bool {
-                false
-            }
-        }
-    } else {
-        quote! {
-            fn uses_fd() -> bool {
-                #(<#variant_field_types>::uses_fd())||*
-            }
+    if variant_field_types.len() == 0 {
+        return quote!();
+    }
+
+    quote! {
+        fn uses_fd() -> bool {
+            #(<#variant_field_types>::uses_fd())||*
         }
     }
 }
@@ -438,7 +483,7 @@ fn impl_for_tuple_struct(name: Ident, ds: DataStruct) -> TokenStream {
     }
 }
 
-fn get_tuple_fields(ds: DataStruct) -> Vec<(Member, Type)> {
+fn get_tuple_fields(ds: DataStruct) -> Vec<StructField> {
     let mut field_idents = Vec::new();
     let fields = match ds.fields {
         Fields::Unnamed(fields_unnamed) => fields_unnamed.unnamed,
@@ -449,17 +494,21 @@ fn get_tuple_fields(ds: DataStruct) -> Vec<(Member, Type)> {
     for (idx, field) in fields.iter().enumerate() {
         let member = Member::Unnamed(Index::from(idx));
         let ty = field.ty.clone();
-        field_idents.push((member, ty));
+        field_idents.push(StructField {
+            member,
+            ty,
+            skipped: false,
+        });
     }
     field_idents
 }
 
-fn define_uses_fd_for_tuples(fields: &[(Member, Type)]) -> TokenStream {
+fn define_uses_fd_for_tuples(fields: &[StructField]) -> TokenStream {
     if fields.len() == 0 {
         return quote!();
     }
 
-    let field_types = fields.iter().map(|(_, ty)| ty);
+    let field_types = fields.iter().map(|f| &f.ty);
     quote! {
         fn uses_fd() -> bool {
             #(<#field_types>::uses_fd())||*
@@ -467,13 +516,13 @@ fn define_uses_fd_for_tuples(fields: &[(Member, Type)]) -> TokenStream {
     }
 }
 
-fn define_read_buffer_for_tuples(name: &Ident, fields: &[(Member, Type)]) -> TokenStream {
+fn define_read_buffer_for_tuples(name: &Ident, fields: &[StructField]) -> TokenStream {
     let mut read_fields = Vec::new();
     let mut init_fields = Vec::new();
-    for (idx, (_, field_ty)) in fields.iter().enumerate() {
+    for (idx, field) in fields.iter().enumerate() {
         let tmp_name = format!("tuple_tmp{}", idx);
         let tmp_name = Ident::new(&tmp_name, Span::call_site());
-        let read_field = read_from_buffer_and_move_offset(&tmp_name, field_ty);
+        let read_field = read_from_buffer_and_move_offset(&tmp_name, &field.ty);
         read_fields.push(read_field);
         init_fields.push(quote!(#tmp_name));
     }
@@ -496,7 +545,7 @@ fn define_read_buffer_for_tuples(name: &Ident, fields: &[(Member, Type)]) -> Tok
     }
 }
 
-fn define_write_buffer_for_tuples(name: &Ident, fields: &[(Member, Type)]) -> TokenStream {
+fn define_write_buffer_for_tuples(name: &Ident, fields: &[StructField]) -> TokenStream {
     let mut write_fields = Vec::new();
     let mut tmp_names = Vec::new();
     for idx in 0..fields.len() {
@@ -520,8 +569,12 @@ fn define_write_buffer_for_tuples(name: &Ident, fields: &[(Member, Type)]) -> To
     }
 }
 /************************** Helpers ********************************************/
-fn get_fields_buffer_size_sum(fields: &[(Member, Type)]) -> (TokenStream, TokenStream) {
-    let fields: Vec<_> = fields.iter().map(|(m, _)| m).collect();
+fn get_fields_buffer_size_sum(fields: &[StructField]) -> (TokenStream, TokenStream) {
+    let fields: Vec<_> = fields
+        .iter()
+        .filter(|f| !f.skipped)
+        .map(|f| &f.member)
+        .collect();
     if fields.len() > 0 {
         (
             quote! {
@@ -832,4 +885,45 @@ mod tests {
 
         assert_eq!(msg_socket_impl(input).to_string(), expected.to_string());
     }
+
+    #[test]
+    fn end_to_end_struct_skip_test() {
+        let input: DeriveInput = parse_quote! {
+            struct MyMsg {
+                #[msg_on_socket(skip)]
+                a: u8,
+            }
+        };
+
+        let expected = quote! {
+            impl msg_socket::MsgOnSocket for MyMsg {
+                fn msg_size(&self) -> usize {
+                    0
+                }
+                fn fd_count(&self) -> usize {
+                    0
+                }
+                unsafe fn read_from_buffer(
+                    buffer: &[u8],
+                    fds: &[std::os::unix::io::RawFd],
+                ) -> msg_socket::MsgResult<(Self, usize)> {
+                    let mut __offset = 0usize;
+                    let mut __fd_offset = 0usize;
+                    Ok((Self { a: <u8>::default() }, __fd_offset))
+                }
+                fn write_to_buffer(
+                    &self,
+                    buffer: &mut [u8],
+                    fds: &mut [std::os::unix::io::RawFd],
+                ) -> msg_socket::MsgResult<usize> {
+                    let mut __offset = 0usize;
+                    let mut __fd_offset = 0usize;
+                    Ok(__fd_offset)
+                }
+            }
+
+        };
+
+        assert_eq!(socket_msg_impl(input).to_string(), expected.to_string());
+    }
 }
diff --git a/msg_socket/src/lib.rs b/msg_socket/src/lib.rs
index 5871735..7f5d1a6 100644
--- a/msg_socket/src/lib.rs
+++ b/msg_socket/src/lib.rs
@@ -13,7 +13,7 @@ use std::task::{Context, Poll};
 use futures::Stream;
 use libc::{EWOULDBLOCK, O_NONBLOCK};
 
-use cros_async::fd_executor::add_read_waker;
+use cros_async::add_read_waker;
 use sys_util::{
     add_fd_flags, clear_fd_flags, error, handle_eintr, net::UnixSeqpacket, Error as SysError,
     ScmSocket,
@@ -50,7 +50,7 @@ impl<I: MsgOnSocket, O: MsgOnSocket> MsgSocket<I, O> {
     }
 
     // Creates an async receiver that implements `futures::Stream`.
-    pub fn async_receiver(&mut self) -> MsgResult<AsyncReceiver<I, O>> {
+    pub fn async_receiver(&self) -> MsgResult<AsyncReceiver<I, O>> {
         AsyncReceiver::new(self)
     }
 }
@@ -164,6 +164,20 @@ pub trait MsgReceiver: AsRef<UnixSeqpacket> {
                 )
             }
         };
+
+        if msg_buffer.len() == 0 && Self::M::fixed_size() != Some(0) {
+            return Err(MsgError::RecvZero);
+        }
+
+        if let Some(fixed_size) = Self::M::fixed_size() {
+            if fixed_size != msg_buffer.len() {
+                return Err(MsgError::BadRecvSize {
+                    expected: fixed_size,
+                    actual: msg_buffer.len(),
+                });
+            }
+        }
+
         // Safe because fd buffer is read from socket.
         let (v, read_fd_size) = unsafe { Self::M::read_from_buffer(&msg_buffer, &fd_buffer)? };
         if fd_buffer.len() != read_fd_size {
@@ -189,12 +203,12 @@ impl<O: MsgOnSocket> MsgReceiver for Receiver<O> {
 
 /// Asynchronous adaptor for `MsgSocket`.
 pub struct AsyncReceiver<'a, I: MsgOnSocket, O: MsgOnSocket> {
-    inner: &'a mut MsgSocket<I, O>,
+    inner: &'a MsgSocket<I, O>,
     done: bool, // Have hit an error and the Stream should return null when polled.
 }
 
 impl<'a, I: MsgOnSocket, O: MsgOnSocket> AsyncReceiver<'a, I, O> {
-    fn new(msg_socket: &mut MsgSocket<I, O>) -> MsgResult<AsyncReceiver<I, O>> {
+    fn new(msg_socket: &MsgSocket<I, O>) -> MsgResult<AsyncReceiver<I, O>> {
         add_fd_flags(msg_socket.as_raw_fd(), O_NONBLOCK).map_err(MsgError::SettingFdFlags)?;
         Ok(AsyncReceiver {
             inner: msg_socket,
diff --git a/msg_socket/src/msg_on_socket.rs b/msg_socket/src/msg_on_socket.rs
index 624d514..67d26aa 100644
--- a/msg_socket/src/msg_on_socket.rs
+++ b/msg_socket/src/msg_on_socket.rs
@@ -10,15 +10,17 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
 use std::ptr::drop_in_place;
 use std::result;
+use std::sync::Arc;
 
 use data_model::*;
+use sync::Mutex;
 use sys_util::{Error as SysError, EventFd};
 
 #[derive(Debug, PartialEq)]
 /// An error during transaction or serialization/deserialization.
 pub enum MsgError {
     /// Error adding a waker for async read.
-    AddingWaker(cros_async::fd_executor::Error),
+    AddingWaker(cros_async::Error),
     /// Error while sending a request or response.
     Send(SysError),
     /// Error while receiving a request or response.
@@ -28,6 +30,8 @@ pub enum MsgError {
     /// There was not the expected amount of data when receiving a message. The inner
     /// value is how much data is expected and how much data was actually received.
     BadRecvSize { expected: usize, actual: usize },
+    /// There was no data received when the socket `recv`-ed.
+    RecvZero,
     /// There was no associated file descriptor received for a request that expected it.
     ExpectFd,
     /// There was some associated file descriptor received but not used when deserialize.
@@ -58,6 +62,7 @@ impl Display for MsgError {
                 "wrong amount of data received; expected {} bytes; got {} bytes",
                 expected, actual
             ),
+            RecvZero => write!(f, "received zero data"),
             ExpectFd => write!(f, "missing associated file descriptor for request"),
             NotExpectFd => write!(f, "unexpected file descriptor is unused"),
             SettingFdFlags(e) => write!(f, "failed setting flags on the message FD: {}", e),
@@ -207,6 +212,50 @@ impl<T: MsgOnSocket> MsgOnSocket for Option<T> {
     }
 }
 
+impl<T: MsgOnSocket> MsgOnSocket for Mutex<T> {
+    fn uses_fd() -> bool {
+        T::uses_fd()
+    }
+
+    fn msg_size(&self) -> usize {
+        self.lock().msg_size()
+    }
+
+    fn fd_count(&self) -> usize {
+        self.lock().fd_count()
+    }
+
+    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+        T::read_from_buffer(buffer, fds).map(|(v, count)| (Mutex::new(v), count))
+    }
+
+    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+        self.lock().write_to_buffer(buffer, fds)
+    }
+}
+
+impl<T: MsgOnSocket> MsgOnSocket for Arc<T> {
+    fn uses_fd() -> bool {
+        T::uses_fd()
+    }
+
+    fn msg_size(&self) -> usize {
+        (**self).msg_size()
+    }
+
+    fn fd_count(&self) -> usize {
+        (**self).fd_count()
+    }
+
+    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+        T::read_from_buffer(buffer, fds).map(|(v, count)| (Arc::new(v), count))
+    }
+
+    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+        (**self).write_to_buffer(buffer, fds)
+    }
+}
+
 impl MsgOnSocket for () {
     fn fixed_size() -> Option<usize> {
         Some(0)
diff --git a/p9/src/protocol/wire_format.rs b/p9/src/protocol/wire_format.rs
index 84408e2..afc1231 100644
--- a/p9/src/protocol/wire_format.rs
+++ b/p9/src/protocol/wire_format.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::fmt;
 use std::io;
 use std::io::{ErrorKind, Read, Write};
diff --git a/p9/src/server.rs b/p9/src/server.rs
index e1ffd16..957923d 100644
--- a/p9/src/server.rs
+++ b/p9/src/server.rs
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use libc;
-
 use std::cmp::min;
 use std::collections::{btree_map, BTreeMap};
 use std::ffi::CString;
diff --git a/resources/src/gpu_allocator.rs b/resources/src/gpu_allocator.rs
index 478d0d4..cbf21a2 100644
--- a/resources/src/gpu_allocator.rs
+++ b/resources/src/gpu_allocator.rs
@@ -8,10 +8,7 @@ use std::fs::File;
 #[cfg(feature = "wl-dmabuf")]
 use libc::EINVAL;
 
-#[cfg(feature = "wl-dmabuf")]
-use gpu_buffer;
 use msg_socket::MsgOnSocket;
-use sys_util;
 
 #[allow(dead_code)]
 #[derive(Debug, Eq, PartialEq)]
diff --git a/resources/src/lib.rs b/resources/src/lib.rs
index a6beceb..cf36cc1 100644
--- a/resources/src/lib.rs
+++ b/resources/src/lib.rs
@@ -24,8 +24,8 @@ pub enum Alloc {
     /// Should only be instantiated through `SystemAllocator::get_anon_alloc()`.
     /// Avoid using these. Instead, use / create a more descriptive Alloc variant.
     Anon(usize),
-    /// A PCI BAR region with associated bus, device, and bar numbers.
-    PciBar { bus: u8, dev: u8, bar: u8 },
+    /// A PCI BAR region with associated bus, device, function and bar numbers.
+    PciBar { bus: u8, dev: u8, func: u8, bar: u8 },
     /// GPU render node region.
     GpuRenderNode,
     /// Pmem device region with associated device index.
diff --git a/resources/src/system_allocator.rs b/resources/src/system_allocator.rs
index 984bc51..889fe01 100644
--- a/resources/src/system_allocator.rs
+++ b/resources/src/system_allocator.rs
@@ -25,13 +25,14 @@ use crate::{Alloc, Error, Result};
 ///           a.mmio_allocator(MmioType::High)
 ///              .allocate(
 ///                  0x100,
-///                  Alloc::PciBar { bus: 0, dev: 0, bar: 0 },
+///                  Alloc::PciBar { bus: 0, dev: 0, func: 0, bar: 0 },
 ///                  "bar0".to_string()
 ///              ),
 ///           Ok(0x10000000)
 ///       );
 ///       assert_eq!(
-///           a.mmio_allocator(MmioType::High).get(&Alloc::PciBar { bus: 0, dev: 0, bar: 0 }),
+///           a.mmio_allocator(MmioType::High)
+///              .get(&Alloc::PciBar { bus: 0, dev: 0, func: 0, bar: 0 }),
 ///           Some(&(0x10000000, 0x100, "bar0".to_string()))
 ///       );
 ///   }
diff --git a/seccomp/aarch64/gpu_device.policy b/seccomp/aarch64/gpu_device.policy
index 7ef95b2..3eaae05 100644
--- a/seccomp/aarch64/gpu_device.policy
+++ b/seccomp/aarch64/gpu_device.policy
@@ -52,6 +52,7 @@ statx: 1
 fstat: 1
 newfstatat: 1
 getdents64: 1
+sysinfo: 1
 
 # 0x6400 == DRM_IOCTL_BASE, 0x8000 = KBASE_IOCTL_TYPE (mali)
 ioctl: arg1 & 0x6400 || arg1 & 0x8000
diff --git a/seccomp/arm/gpu_device.policy b/seccomp/arm/gpu_device.policy
index 4e3a052..1e42302 100644
--- a/seccomp/arm/gpu_device.policy
+++ b/seccomp/arm/gpu_device.policy
@@ -54,6 +54,7 @@ stat64: 1
 fstat64: 1
 getdents: 1
 getdents64: 1
+sysinfo: 1
 
 # 0x6400 == DRM_IOCTL_BASE, 0x8000 = KBASE_IOCTL_TYPE (mali)
 ioctl: arg1 & 0x6400 || arg1 & 0x8000
diff --git a/seccomp/x86_64/gpu_device.policy b/seccomp/x86_64/gpu_device.policy
index 23b6b6c..331fc49 100644
--- a/seccomp/x86_64/gpu_device.policy
+++ b/seccomp/x86_64/gpu_device.policy
@@ -75,3 +75,4 @@ sysinfo: 1
 uname: 1
 sched_setscheduler: 1
 sched_setaffinity: 1
+kcmp: 1
diff --git a/src/argument.rs b/src/argument.rs
index 0d0e142..26950a9 100644
--- a/src/argument.rs
+++ b/src/argument.rs
@@ -292,14 +292,12 @@ where
                     State::Positional
                 }
                 State::Value { name } => {
-                    if arg.starts_with("-") {
+                    if arg.starts_with('-') {
                         arg_consumed = false;
                         f(&name, None)?;
-                    } else {
-                        if let Err(e) = f(&name, Some(&arg)) {
-                            arg_consumed = false;
-                            f(&name, None).map_err(|_| e)?;
-                        }
+                    } else if let Err(e) = f(&name, Some(&arg)) {
+                        arg_consumed = false;
+                        f(&name, None).map_err(|_| e)?;
                     }
                     State::Top
                 }
diff --git a/src/crosvm.rs b/src/crosvm.rs
index a55d2e4..49a08c0 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -16,11 +16,11 @@ use std::os::unix::io::RawFd;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
-use arch::Pstore;
+use arch::{Pstore, SerialHardware, SerialParameters};
 use devices::virtio::fs::passthrough;
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::GpuParameters;
-use devices::{Ac97Parameters, SerialParameters};
+use devices::Ac97Parameters;
 use libc::{getegid, geteuid};
 
 static SECCOMP_POLICY_DIR: &str = "/usr/share/policy/crosvm";
@@ -193,7 +193,7 @@ pub struct Config {
     pub display_window_keyboard: bool,
     pub display_window_mouse: bool,
     pub ac97_parameters: Vec<Ac97Parameters>,
-    pub serial_parameters: BTreeMap<u8, SerialParameters>,
+    pub serial_parameters: BTreeMap<(SerialHardware, u8), SerialParameters>,
     pub syslog_tag: Option<String>,
     pub virtio_single_touch: Option<TouchDeviceOption>,
     pub virtio_trackpad: Option<TouchDeviceOption>,
diff --git a/src/linux.rs b/src/linux.rs
index 9dbdb5c..e480a4c 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::cmp::max;
 use std::convert::TryFrom;
 use std::error::Error as StdError;
@@ -29,7 +28,7 @@ use libc::{self, c_int, gid_t, uid_t};
 
 #[cfg(feature = "gpu")]
 use devices::virtio::EventDevice;
-use devices::virtio::{self, VirtioDevice};
+use devices::virtio::{self, Console, VirtioDevice};
 use devices::{
     self, Ac97Backend, Ac97Dev, HostBackendDeviceProvider, PciDevice, VfioContainer, VfioDevice,
     VfioPciDevice, VirtioPciDevice, XhciController,
@@ -50,7 +49,6 @@ use sys_util::{
     Killable, MemoryMappingArena, PollContext, PollToken, Protection, ScopedEvent, SignalFd,
     Terminal, TimerFd, WatchingEvents, SIGRTMIN,
 };
-use vhost;
 use vm_control::{
     BalloonControlCommand, BalloonControlRequestSocket, BalloonControlResponseSocket,
     BalloonControlResult, DiskControlCommand, DiskControlRequestSocket, DiskControlResponseSocket,
@@ -61,7 +59,10 @@ use vm_control::{
 };
 
 use crate::{Config, DiskOption, Executable, SharedDir, SharedDirKind, TouchDeviceOption};
-use arch::{self, LinuxArch, RunnableLinuxVm, VirtioDeviceStub, VmComponents, VmImage};
+use arch::{
+    self, LinuxArch, RunnableLinuxVm, SerialHardware, SerialParameters, VirtioDeviceStub,
+    VmComponents, VmImage,
+};
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 use aarch64::AArch64 as Arch;
@@ -82,6 +83,7 @@ pub enum Error {
     ChownTpmStorage(sys_util::Error),
     CloneEventFd(sys_util::Error),
     CreateAc97(devices::PciDeviceError),
+    CreateConsole(arch::serial::Error),
     CreateDiskError(disk::Error),
     CreateEventFd(sys_util::Error),
     CreatePollContext(sys_util::Error),
@@ -166,6 +168,7 @@ impl Display for Error {
             ChownTpmStorage(e) => write!(f, "failed to chown tpm storage: {}", e),
             CloneEventFd(e) => write!(f, "failed to clone eventfd: {}", e),
             CreateAc97(e) => write!(f, "failed to create ac97 device: {}", e),
+            CreateConsole(e) => write!(f, "failed to create console device: {}", e),
             CreateDiskError(e) => write!(f, "failed to create virtual disk: {}", e),
             CreateEventFd(e) => write!(f, "failed to create eventfd: {}", e),
             CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
@@ -816,7 +819,12 @@ fn create_fs_device(
             log_failures: cfg.seccomp_log_failures,
             seccomp_policy: &seccomp_policy,
         };
-        create_base_minijail(src, Some(max_open_files), Some(&config))?
+        let mut jail = create_base_minijail(src, Some(max_open_files), Some(&config))?;
+        // We want bind mounts from the parent namespaces to propagate into the fs device's
+        // namespace.
+        jail.set_remount_mode(libc::MS_SLAVE);
+
+        jail
     } else {
         create_base_minijail(src, Some(max_open_files), None)?
     };
@@ -885,7 +893,7 @@ fn create_pmem_device(
         .open(&disk.path)
         .map_err(|e| Error::Disk(disk.path.to_path_buf(), e))?;
 
-    let (disk_size, arena_size) = {
+    let arena_size = {
         let metadata =
             std::fs::metadata(&disk.path).map_err(|e| Error::Disk(disk.path.to_path_buf(), e))?;
         let disk_len = metadata.len();
@@ -900,12 +908,9 @@ fn create_pmem_device(
         } else {
             0
         };
-        (
-            disk_len,
-            disk_len
-                .checked_add(align_adjust)
-                .ok_or(Error::PmemDeviceImageTooBig)?,
-        )
+        disk_len
+            .checked_add(align_adjust)
+            .ok_or(Error::PmemDeviceImageTooBig)?
     };
 
     let protection = {
@@ -919,11 +924,10 @@ fn create_pmem_device(
     let arena = {
         // Conversion from u64 to usize may fail on 32bit system.
         let arena_size = usize::try_from(arena_size).map_err(|_| Error::PmemDeviceImageTooBig)?;
-        let disk_size = usize::try_from(disk_size).map_err(|_| Error::PmemDeviceImageTooBig)?;
 
         let mut arena = MemoryMappingArena::new(arena_size).map_err(Error::ReservePmemMemory)?;
         arena
-            .add_fd_offset_protection(0, disk_size, &fd, 0, protection)
+            .add_fd_offset_protection(0, arena_size, &fd, 0, protection)
             .map_err(Error::ReservePmemMemory)?;
         arena
     };
@@ -963,6 +967,19 @@ fn create_pmem_device(
     })
 }
 
+fn create_console_device(cfg: &Config, param: &SerialParameters) -> DeviceResult {
+    let mut keep_fds = Vec::new();
+    let evt = EventFd::new().map_err(Error::CreateEventFd)?;
+    let dev = param
+        .create_serial_device::<Console>(&evt, &mut keep_fds)
+        .map_err(Error::CreateConsole)?;
+
+    Ok(VirtioDeviceStub {
+        dev: Box::new(dev),
+        jail: simple_jail(&cfg, "serial")?, // TODO(dverkamp): use a separate policy for console?
+    })
+}
+
 // gpu_device_socket is not used when GPU support is disabled.
 #[cfg_attr(not(feature = "gpu"), allow(unused_variables))]
 fn create_virtio_devices(
@@ -979,6 +996,15 @@ fn create_virtio_devices(
 ) -> DeviceResult<Vec<VirtioDeviceStub>> {
     let mut devs = Vec::new();
 
+    for (_, param) in cfg
+        .serial_parameters
+        .iter()
+        .filter(|(_k, v)| v.hardware == SerialHardware::VirtioConsole)
+    {
+        let dev = create_console_device(cfg, param)?;
+        devs.push(dev);
+    }
+
     for disk in &cfg.disks {
         let disk_device_socket = disk_device_sockets.remove(0);
         devs.push(create_block_device(cfg, disk, disk_device_socket)?);
@@ -1398,7 +1424,7 @@ fn run_vcpu(
             // implementation accomplishes that.
             let _scoped_exit_evt = ScopedEvent::from(exit_evt);
 
-            if vcpu_affinity.len() != 0 {
+            if !vcpu_affinity.is_empty() {
                 if let Err(e) = set_cpu_affinity(vcpu_affinity) {
                     error!("Failed to set CPU affinity: {}", e);
                 }
@@ -1740,7 +1766,7 @@ fn run_control(
     }
 
     // Balance available memory between guest and host every second.
-    let mut balancemem_timer = TimerFd::new().map_err(Error::CreateTimerFd)?;
+    let balancemem_timer = TimerFd::new().map_err(Error::CreateTimerFd)?;
     if Path::new(LOWMEM_AVAILABLE).exists() {
         // Create timer request balloon stats every 1s.
         poll_ctx
@@ -2024,7 +2050,7 @@ fn run_control(
                                     }
                                 }
                                 Err(e) => {
-                                    if let MsgError::BadRecvSize { actual: 0, .. } = e {
+                                    if let MsgError::RecvZero = e {
                                         vm_control_indices_to_remove.push(index);
                                     } else {
                                         error!("failed to recv VmRequest: {}", e);
@@ -2040,7 +2066,7 @@ fn run_control(
                                     }
                                 }
                                 Err(e) => {
-                                    if let MsgError::BadRecvSize { actual: 0, .. } = e {
+                                    if let MsgError::RecvZero = e {
                                         vm_control_indices_to_remove.push(index);
                                     } else {
                                         error!("failed to recv VmMemoryControlRequest: {}", e);
@@ -2056,7 +2082,7 @@ fn run_control(
                                     }
                                 }
                                 Err(e) => {
-                                    if let MsgError::BadRecvSize { actual: 0, .. } = e {
+                                    if let MsgError::RecvZero = e {
                                         vm_control_indices_to_remove.push(index);
                                     } else {
                                         error!("failed to recv VmIrqRequest: {}", e);
diff --git a/src/main.rs b/src/main.rs
index e33be66..7fd3eca 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,7 +17,7 @@ use std::string::String;
 use std::thread::sleep;
 use std::time::Duration;
 
-use arch::Pstore;
+use arch::{set_default_serial_parameters, Pstore, SerialHardware, SerialParameters, SerialType};
 use audio_streams::StreamEffect;
 use crosvm::{
     argument::{self, print_help, set_arguments, Argument},
@@ -25,7 +25,7 @@ use crosvm::{
 };
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::{GpuMode, GpuParameters};
-use devices::{Ac97Backend, Ac97Parameters, SerialParameters, SerialType};
+use devices::{Ac97Backend, Ac97Parameters};
 use disk::QcowFile;
 use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
 use sys_util::{
@@ -74,7 +74,7 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
     let mut cpuset = Vec::new();
     for part in s.split(',') {
         let range: Vec<&str> = part.split('-').collect();
-        if range.len() == 0 || range.len() > 2 {
+        if range.is_empty() || range.len() > 2 {
             return Err(argument::Error::InvalidValue {
                 value: part.to_owned(),
                 expected: String::from("invalid list syntax"),
@@ -117,8 +117,8 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
 
     if let Some(s) = s {
         let opts = s
-            .split(",")
-            .map(|frag| frag.split("="))
+            .split(',')
+            .map(|frag| frag.split('='))
             .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
         for (k, v) in opts {
@@ -252,8 +252,8 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
     let mut ac97_params: Ac97Parameters = Default::default();
 
     let opts = s
-        .split(",")
-        .map(|frag| frag.split("="))
+        .split(',')
+        .map(|frag| frag.split('='))
         .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
     for (k, v) in opts {
@@ -273,7 +273,7 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
             }
             "capture_effects" => {
                 ac97_params.capture_effects = v
-                    .split("|")
+                    .split('|')
                     .map(|val| {
                         val.parse::<StreamEffect>()
                             .map_err(|e| argument::Error::InvalidValue {
@@ -298,19 +298,27 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
 fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
     let mut serial_setting = SerialParameters {
         type_: SerialType::Sink,
+        hardware: SerialHardware::Serial,
         path: None,
+        input: None,
         num: 1,
         console: false,
+        earlycon: false,
         stdin: false,
     };
 
     let opts = s
-        .split(",")
-        .map(|frag| frag.split("="))
+        .split(',')
+        .map(|frag| frag.split('='))
         .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
     for (k, v) in opts {
         match k {
+            "hardware" => {
+                serial_setting.hardware = v
+                    .parse::<SerialHardware>()
+                    .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
+            }
             "type" => {
                 serial_setting.type_ = v
                     .parse::<SerialType>()
@@ -336,12 +344,33 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
                     ))
                 })?
             }
+            "earlycon" => {
+                serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
+                    argument::Error::Syntax(format!(
+                        "serial device earlycon is not parseable: {}",
+                        e,
+                    ))
+                })?
+            }
             "stdin" => {
                 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
                     argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
-                })?
+                })?;
+                if serial_setting.stdin && serial_setting.input.is_some() {
+                    return Err(argument::Error::TooManyArguments(
+                        "Cannot specify both stdin and input options".to_string(),
+                    ));
+                }
             }
             "path" => serial_setting.path = Some(PathBuf::from(v)),
+            "input" => {
+                if serial_setting.stdin {
+                    return Err(argument::Error::TooManyArguments(
+                        "Cannot specify both stdin and input options".to_string(),
+                    ));
+                }
+                serial_setting.input = Some(PathBuf::from(v));
+            }
             _ => {
                 return Err(argument::Error::UnknownArgument(format!(
                     "serial parameter {}",
@@ -355,7 +384,7 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
 }
 
 fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
-    let components: Vec<&str> = value.split(":").collect();
+    let components: Vec<&str> = value.split(':').collect();
     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
@@ -402,7 +431,7 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
 }
 
 fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
-    let components: Vec<&str> = value.split(":").collect();
+    let components: Vec<&str> = value.split(':').collect();
     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
@@ -501,7 +530,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 )
         }
         "cpu-affinity" => {
-            if cfg.vcpu_affinity.len() != 0 {
+            if !cfg.vcpu_affinity.is_empty() {
                 return Err(argument::Error::TooManyArguments(
                     "`cpu-affinity` already given".to_owned(),
                 ));
@@ -532,10 +561,11 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
         "serial" => {
             let serial_params = parse_serial_options(value.unwrap())?;
             let num = serial_params.num;
-            if cfg.serial_parameters.contains_key(&num) {
+            let key = (serial_params.hardware, num);
+            if cfg.serial_parameters.contains_key(&key) {
                 return Err(argument::Error::TooManyArguments(format!(
-                    "serial num {}",
-                    num
+                    "serial hardware {} num {}",
+                    serial_params.hardware, num,
                 )));
             }
 
@@ -543,8 +573,29 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 for params in cfg.serial_parameters.values() {
                     if params.console {
                         return Err(argument::Error::TooManyArguments(format!(
-                            "serial device {} already set as console",
-                            params.num
+                            "{} device {} already set as console",
+                            params.hardware, params.num,
+                        )));
+                    }
+                }
+            }
+
+            if serial_params.earlycon {
+                // Only SerialHardware::Serial supports earlycon= currently.
+                match serial_params.hardware {
+                    SerialHardware::Serial => {}
+                    _ => {
+                        return Err(argument::Error::InvalidValue {
+                            value: serial_params.hardware.to_string().to_owned(),
+                            expected: String::from("earlycon not supported for hardware"),
+                        });
+                    }
+                }
+                for params in cfg.serial_parameters.values() {
+                    if params.earlycon {
+                        return Err(argument::Error::TooManyArguments(format!(
+                            "{} device {} already set as earlycon",
+                            params.hardware, params.num,
                         )));
                     }
                 }
@@ -553,13 +604,13 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if serial_params.stdin {
                 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
                     return Err(argument::Error::TooManyArguments(format!(
-                        "serial device {} already connected to standard input",
-                        previous_stdin.num
+                        "{} device {} already connected to standard input",
+                        previous_stdin.hardware, previous_stdin.num,
                     )));
                 }
             }
 
-            cfg.serial_parameters.insert(num, serial_params);
+            cfg.serial_parameters.insert(key, serial_params);
         }
         "syslog-tag" => {
             if cfg.syslog_tag.is_some() {
@@ -1030,7 +1081,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             let reader = BufReader::new(file);
             for l in reader.lines() {
                 let line = l.unwrap();
-                let trimmed_line = line.splitn(2, '#').nth(0).unwrap().trim();
+                let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
                 if !trimmed_line.is_empty() {
                     let mount = parse_plugin_mount_option(trimmed_line)?;
                     cfg.plugin_mounts.push(mount);
@@ -1049,7 +1100,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             let reader = BufReader::new(file);
             for l in reader.lines() {
                 let line = l.unwrap();
-                let trimmed_line = line.splitn(2, '#').nth(0).unwrap().trim();
+                let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
                 if !trimmed_line.is_empty() {
                     let map = parse_plugin_gid_map_option(trimmed_line)?;
                     cfg.plugin_gid_maps.push(map);
@@ -1084,7 +1135,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     "`single-touch` already given".to_owned(),
                 ));
             }
-            let mut it = value.unwrap().split(":");
+            let mut it = value.unwrap().split(':');
 
             let mut single_touch_spec =
                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
@@ -1102,7 +1153,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     "`trackpad` already given".to_owned(),
                 ));
             }
-            let mut it = value.unwrap().split(":");
+            let mut it = value.unwrap().split(':');
 
             let mut trackpad_spec =
                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
@@ -1214,6 +1265,7 @@ fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Err
             }
         }
     }
+    set_default_serial_parameters(&mut cfg.serial_parameters);
     Ok(())
 }
 
@@ -1326,10 +1378,9 @@ Possible key values:
     effect value now is EchoCancellation or aec.
 "#,
         ),
-
         Argument::value(
             "serial",
-            "type=TYPE,[num=NUM,path=PATH,console,stdin]",
+            "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
             "\
 Comma separated key=value pairs for setting up serial devices. Can be given
 more than once.
@@ -1339,16 +1390,26 @@ Possible key values:
   type=(stdout,syslog,sink,file)
     Where to route the serial device.
 
+  hardware=(serial,virtio-console)
+    Which type of serial hardware to emulate.  Defaults to 8250 UART
+    (serial).
+
   num=(1,2,3,4)
     Serial Device Number.  If not provided, num will default to 1.
 
   path=PATH
     The path to the file to write to when type=file.
 
+  input=PATH
+    The path to the file to read from when not stdin.
+
   console
     Use this serial device as the guest console.  Can only be given once.
     Will default to first serial port if not provided.
 
+  earlycon
+    Use this serial device as the early console.  Can only be given once.
+
   stdin
     Direct standard input to this serial device.  Can only be given once.
     Will default to first serial port if not provided.
@@ -1738,7 +1799,7 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
         println!("Set the balloon size of the crosvm instance to `SIZE` bytes.");
         return Err(());
     }
-    let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
+    let num_bytes = match args.next().unwrap().parse::<u64>() {
         Ok(n) => n,
         Err(_) => {
             error!("Failed to parse number of bytes");
@@ -1750,6 +1811,19 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
     vms_request(&VmRequest::BalloonCommand(command), args)
 }
 
+fn balloon_stats(args: std::env::Args) -> std::result::Result<(), ()> {
+    if args.len() != 1 {
+        print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
+        println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
+        return Err(());
+    }
+    let command = BalloonControlCommand::Stats {};
+    let request = &VmRequest::BalloonCommand(command);
+    let response = handle_request(request, args)?;
+    println!("{}", response);
+    Ok(())
+}
+
 fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
     let arguments = [
         Argument::positional("PATH", "where to create the qcow2 image"),
@@ -1796,7 +1870,7 @@ fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
     .map_err(|e| {
         error!("Unable to parse command line arguments: {}", e);
     })?;
-    if file_path.len() == 0 || !(size.is_some() ^ backing_file.is_some()) {
+    if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
         print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
         println!(
             "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
@@ -1837,11 +1911,11 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
         println!("  resize DISK_INDEX NEW_SIZE VM_SOCKET");
         return Err(());
     }
-    let subcommand: &str = &args.nth(0).unwrap();
+    let subcommand: &str = &args.next().unwrap();
 
     let request = match subcommand {
         "resize" => {
-            let disk_index = match args.nth(0).unwrap().parse::<usize>() {
+            let disk_index = match args.next().unwrap().parse::<usize>() {
                 Ok(n) => n,
                 Err(_) => {
                     error!("Failed to parse disk index");
@@ -1849,7 +1923,7 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
                 }
             };
 
-            let new_size = match args.nth(0).unwrap().parse::<u64>() {
+            let new_size = match args.next().unwrap().parse::<u64>() {
                 Ok(n) => n,
                 Err(_) => {
                     error!("Failed to parse disk size");
@@ -1911,7 +1985,7 @@ type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
 
 fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
     debug!("parse_bus_id_addr: {}", v);
-    let mut ids = v.split(":");
+    let mut ids = v.split(':');
     match (ids.next(), ids.next(), ids.next(), ids.next()) {
         (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
             let bus_id = bus_id
@@ -2069,7 +2143,7 @@ fn pkg_version() -> std::result::Result<(), ()> {
     print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
     match PKG_VERSION {
         Some(v) => println!("-{}", v),
-        None => println!(""),
+        None => println!(),
     }
     Ok(())
 }
@@ -2099,6 +2173,7 @@ fn crosvm_main() -> std::result::Result<(), ()> {
         Some("resume") => resume_vms(args),
         Some("run") => run_vm(args),
         Some("balloon") => balloon_vms(args),
+        Some("balloon_stats") => balloon_stats(args),
         Some("create_qcow2") => create_qcow2(args),
         Some("disk") => disk_cmd(args),
         Some("usb") => modify_usb(args),
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index ae7e19c..470d5f0 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -8,7 +8,7 @@ mod vcpu;
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::io;
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
+use std::os::unix::io::{AsRawFd, FromRawFd};
 use std::os::unix::net::UnixDatagram;
 use std::path::Path;
 use std::result;
@@ -181,10 +181,6 @@ impl Display for Error {
 
 type Result<T> = result::Result<T, Error>;
 
-fn downcast_file<F: IntoRawFd>(f: F) -> File {
-    unsafe { File::from_raw_fd(f.into_raw_fd()) }
-}
-
 fn new_seqpacket_pair() -> SysResult<(UnixDatagram, UnixDatagram)> {
     let mut fds = [0, 0];
     unsafe {
diff --git a/src/plugin/process.rs b/src/plugin/process.rs
index 51fc892..783239a 100644
--- a/src/plugin/process.rs
+++ b/src/plugin/process.rs
@@ -7,19 +7,17 @@ use std::env::set_var;
 use std::fs::File;
 use std::io::Write;
 use std::mem::transmute;
-use std::os::unix::io::{AsRawFd, RawFd};
+use std::os::unix::io::{IntoRawFd, RawFd};
 use std::os::unix::net::UnixDatagram;
 use std::path::Path;
 use std::process::Command;
 use std::sync::{Arc, RwLock};
 use std::thread::JoinHandle;
 
-use net_util;
 use net_util::Error as NetError;
 
 use libc::{pid_t, waitpid, EINVAL, ENODATA, ENOTTY, WEXITSTATUS, WIFEXITED, WNOHANG, WTERMSIG};
 
-use protobuf;
 use protobuf::Message;
 
 use io_jail::Minijail;
@@ -348,7 +346,7 @@ impl Process {
         read_only: bool,
         dirty_log: bool,
     ) -> SysResult<()> {
-        let shm = SharedMemory::from_raw_fd(memfd)?;
+        let shm = SharedMemory::from_file(memfd)?;
         // Checking the seals ensures the plugin process won't shrink the mmapped file, causing us
         // to SIGBUS in the future.
         let seals = shm.get_seals()?;
@@ -517,7 +515,14 @@ impl Process {
         let request = protobuf::parse_from_bytes::<MainRequest>(&self.request_buffer[..msg_size])
             .map_err(Error::DecodeRequest)?;
 
-        let mut response_files = Vec::new();
+        /// Use this to make it easier to stuff various kinds of File-like objects into the
+        /// `boxed_fds` list.
+        fn box_owned_fd<F: IntoRawFd + 'static>(f: F) -> Box<dyn IntoRawFd> {
+            Box::new(f)
+        }
+
+        // This vec is used to extend ownership of certain FDs until the end of this function.
+        let mut boxed_fds = Vec::new();
         let mut response_fds = Vec::new();
         let mut response = MainResponse::new();
         let res = if request.has_create() {
@@ -559,7 +564,7 @@ impl Process {
                                 Ok(()) => {
                                     response_fds.push(evt.as_raw_fd());
                                     response_fds.push(resample_evt.as_raw_fd());
-                                    response_files.push(downcast_file(resample_evt));
+                                    boxed_fds.push(box_owned_fd(resample_evt));
                                     entry.insert(PluginObject::IrqEvent {
                                         irq_id: irq_event.irq_id,
                                         evt,
@@ -588,7 +593,7 @@ impl Process {
                 Ok((request_socket, child_socket)) => {
                     self.request_sockets.push(request_socket);
                     response_fds.push(child_socket.as_raw_fd());
-                    response_files.push(downcast_file(child_socket));
+                    boxed_fds.push(box_owned_fd(child_socket));
                     Ok(())
                 }
                 Err(e) => Err(e),
diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs
index a5bf73c..4bd3ea8 100644
--- a/src/plugin/vcpu.rs
+++ b/src/plugin/vcpu.rs
@@ -13,7 +13,6 @@ use std::sync::{Arc, RwLock};
 
 use libc::{EINVAL, ENOENT, ENOTTY, EPERM, EPIPE, EPROTO};
 
-use protobuf;
 use protobuf::Message;
 
 use assertions::const_assert;
diff --git a/sys_util/src/descriptor.rs b/sys_util/src/descriptor.rs
new file mode 100644
index 0000000..1af72fc
--- /dev/null
+++ b/sys_util/src/descriptor.rs
@@ -0,0 +1,88 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::unix::io::RawFd;
+
+use crate::{errno_result, Result};
+use std::mem;
+use std::ops::Drop;
+
+pub type RawDescriptor = RawFd;
+
+/// Trait for forfeiting ownership of the current raw descriptor, and returning the raw descriptor
+pub trait IntoRawDescriptor {
+    fn into_raw_descriptor(self) -> RawDescriptor;
+}
+
+/// Trait for returning the underlying raw descriptor, without giving up ownership of the
+/// descriptor.
+pub trait AsRawDescriptor {
+    fn as_raw_descriptor(&self) -> RawDescriptor;
+}
+
+pub trait FromRawDescriptor {
+    /// # Safety
+    /// Safe only if the caller ensures nothing has access to the descriptor after passing it to
+    /// `from_raw_descriptor`
+    unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self;
+}
+
+/// Wraps a RawDescriptor and safely closes it when self falls out of scope.
+#[derive(Debug, PartialEq)]
+pub struct SafeDescriptor {
+    descriptor: RawDescriptor,
+}
+
+impl Drop for SafeDescriptor {
+    fn drop(&mut self) {
+        let _ = unsafe { libc::close(self.descriptor) };
+    }
+}
+
+impl AsRawDescriptor for SafeDescriptor {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.descriptor
+    }
+}
+
+impl IntoRawDescriptor for SafeDescriptor {
+    fn into_raw_descriptor(self) -> RawDescriptor {
+        let descriptor = self.descriptor;
+        mem::forget(self);
+        descriptor
+    }
+}
+
+impl FromRawDescriptor for SafeDescriptor {
+    unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
+        SafeDescriptor { descriptor }
+    }
+}
+
+impl SafeDescriptor {
+    /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
+    /// share the same underlying count within the kernel.
+    pub fn try_clone(&self) -> Result<SafeDescriptor> {
+        // Safe because self.as_raw_descriptor() returns a valid value
+        let copy_fd = unsafe { libc::dup(self.as_raw_descriptor()) };
+        if copy_fd < 0 {
+            return errno_result();
+        }
+        // Safe becuase we just successfully duplicated and this object will uniquely
+        // own the raw descriptor.
+        Ok(unsafe { SafeDescriptor::from_raw_descriptor(copy_fd) })
+    }
+}
+
+/// For use cases where a simple wrapper around a RawDescriptor is needed.
+/// This is a simply a wrapper and does not manage the lifetime of the descriptor.
+/// Most usages should prefer SafeDescriptor or using a RawDescriptor directly
+#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
+#[repr(transparent)]
+pub struct Descriptor(pub RawDescriptor);
+impl AsRawDescriptor for Descriptor {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.0
+    }
+}
diff --git a/sys_util/src/ioctl.rs b/sys_util/src/ioctl.rs
index a0309ff..f8c7604 100644
--- a/sys_util/src/ioctl.rs
+++ b/sys_util/src/ioctl.rs
@@ -7,8 +7,6 @@
 use std::os::raw::*;
 use std::os::unix::io::AsRawFd;
 
-use libc;
-
 /// Raw macro to declare the expression that calculates an ioctl number
 #[macro_export]
 macro_rules! ioctl_expr {
diff --git a/sys_util/src/lib.rs b/sys_util/src/lib.rs
index 357e6b4..98c9dc6 100644
--- a/sys_util/src/lib.rs
+++ b/sys_util/src/lib.rs
@@ -14,6 +14,7 @@ pub mod ioctl;
 pub mod syslog;
 mod capabilities;
 mod clock;
+mod descriptor;
 mod errno;
 mod eventfd;
 mod file_flags;
@@ -41,8 +42,8 @@ pub use crate::affinity::*;
 pub use crate::alloc::LayoutAllocation;
 pub use crate::capabilities::drop_capabilities;
 pub use crate::clock::{Clock, FakeClock};
-use crate::errno::errno_result;
-pub use crate::errno::{Error, Result};
+pub use crate::descriptor::*;
+pub use crate::errno::{errno_result, Error, Result};
 pub use crate::eventfd::*;
 pub use crate::file_flags::*;
 pub use crate::fork::*;
diff --git a/sys_util/src/mmap.rs b/sys_util/src/mmap.rs
index 006b0a8..c6a52ea 100644
--- a/sys_util/src/mmap.rs
+++ b/sys_util/src/mmap.rs
@@ -6,10 +6,9 @@
 //! mmap object leaves scope.
 
 use std::cmp::min;
-use std::collections::BTreeMap;
 use std::fmt::{self, Display};
 use std::io;
-use std::mem::{size_of, ManuallyDrop};
+use std::mem::size_of;
 use std::os::unix::io::AsRawFd;
 use std::ptr::{copy_nonoverlapping, null_mut, read_unaligned, write_unaligned};
 
@@ -28,8 +27,6 @@ pub enum Error {
     InvalidOffset,
     /// Requested mapping is not page aligned
     NotPageAligned,
-    /// Overlapping regions
-    Overlapping(usize, usize),
     /// Requested memory range spans past the end of the region.
     InvalidRange(usize, usize, usize),
     /// `mmap` returned the given error.
@@ -49,11 +46,6 @@ impl Display for Error {
             InvalidAddress => write!(f, "requested memory out of range"),
             InvalidOffset => write!(f, "requested offset is out of range of off_t"),
             NotPageAligned => write!(f, "requested memory is not page aligned"),
-            Overlapping(offset, count) => write!(
-                f,
-                "requested memory range overlaps with existing region: offset={} size={}",
-                offset, count
-            ),
             InvalidRange(offset, count, region_size) => write!(
                 f,
                 "requested memory range spans past the end of the region: offset={} count={} region_size={}",
@@ -643,13 +635,6 @@ impl Drop for MemoryMapping {
 pub struct MemoryMappingArena {
     addr: *mut u8,
     size: usize,
-    // When doing in-place swaps of MemoryMappings, the BTreeMap returns a owned
-    // instance of the old MemoryMapping. When the old MemoryMapping falls out
-    // of scope, it calls munmap on the same region as the new MemoryMapping
-    // that was just mapped in. To avoid accidentally munmapping the new,
-    // MemoryMapping, all mappings are wrapped in a ManuallyDrop, and then
-    // "forgotten" when removed from the BTreeMap
-    maps: BTreeMap<usize, ManuallyDrop<MemoryMapping>>,
 }
 
 // Send and Sync aren't automatically inherited for the raw address pointer.
@@ -666,17 +651,7 @@ impl MemoryMappingArena {
     /// * `size` - Size of memory region in bytes.
     pub fn new(size: usize) -> Result<MemoryMappingArena> {
         // Reserve the arena's memory using an anonymous read-only mmap.
-        // The actual MemoryMapping object is forgotten, with
-        // MemoryMappingArena manually calling munmap on drop.
-        let mmap = MemoryMapping::new_protection(size, Protection::none().set_read())?;
-        let addr = mmap.as_ptr();
-        let size = mmap.size();
-        std::mem::forget(mmap);
-        Ok(MemoryMappingArena {
-            addr,
-            size,
-            maps: BTreeMap::new(),
-        })
+        MemoryMapping::new_protection(size, Protection::none().set_read()).map(From::from)
     }
 
     /// Anonymously maps `size` bytes at `offset` bytes from the start of the arena.
@@ -755,58 +730,43 @@ impl MemoryMappingArena {
         let mmap = unsafe {
             match fd {
                 Some((fd, fd_offset)) => MemoryMapping::from_fd_offset_protection_fixed(
-                    (self.addr as usize + offset) as *mut u8,
+                    self.addr.add(offset),
                     fd,
                     size,
                     fd_offset,
                     prot,
                 )?,
-                None => MemoryMapping::new_protection_fixed(
-                    (self.addr as usize + offset) as *mut u8,
-                    size,
-                    prot,
-                )?,
+                None => MemoryMapping::new_protection_fixed(self.addr.add(offset), size, prot)?,
             }
         };
 
-        self.maps.insert(offset, ManuallyDrop::new(mmap));
+        // This mapping will get automatically removed when we drop the whole arena.
+        std::mem::forget(mmap);
         Ok(())
     }
 
-    /// Removes a mapping at `offset` from the start of the arena.
-    /// Returns a boolean indicating if there was a mapping present at `offset`.
-    /// If none was present, this method is a noop.
-    pub fn remove(&mut self, offset: usize) -> Result<bool> {
-        if let Some(mmap) = self.maps.remove(&offset) {
-            // Instead of munmapping the memory map, leaving an unprotected hole
-            // in the arena, swap this mmap with an anonymous protection.
-            // This is safe since the memory mapping perfectly overlaps with an
-            // existing, known good memory mapping.
-            let mmap = unsafe {
-                MemoryMapping::new_protection_fixed(
-                    mmap.as_ptr(),
-                    mmap.size(),
-                    Protection::none().set_read(),
-                )?
-            };
-            self.maps.insert(offset, ManuallyDrop::new(mmap));
-            Ok(true)
-        } else {
-            Ok(false)
-        }
+    /// Removes `size` bytes at `offset` bytes from the start of the arena. `offset` must be page
+    /// aligned.
+    ///
+    /// # Arguments
+    /// * `offset` - Page aligned offset into the arena in bytes.
+    /// * `size` - Size of memory region in bytes.
+    pub fn remove(&mut self, offset: usize, size: usize) -> Result<()> {
+        self.try_add(offset, size, Protection::read(), None)
     }
 
-    /// Calls msync with MS_SYNC on the mapping at `offset` from the start of
-    /// the arena.
-    /// Returns a boolean indicating if there was a mapping present at `offset`.
-    /// If none was present, this method is a noop.
-    pub fn msync(&self, offset: usize) -> Result<bool> {
-        if let Some(mmap) = self.maps.get(&offset) {
-            mmap.msync()?;
-            Ok(true)
-        } else {
-            Ok(false)
+    /// Calls msync with MS_SYNC on a mapping of `size` bytes starting at `offset` from the start of
+    /// the arena. `offset` must be page aligned.
+    pub fn msync(&self, offset: usize, size: usize) -> Result<()> {
+        self.validate_range(offset, size)?;
+
+        // Safe because we've validated that this memory range is owned by this `MemoryMappingArena`.
+        let ret =
+            unsafe { libc::msync(self.addr as *mut libc::c_void, self.size(), libc::MS_SYNC) };
+        if ret == -1 {
+            return Err(Error::SystemCallFailed(errno::Error::last()));
         }
+        Ok(())
     }
 
     /// Returns a pointer to the beginning of the memory region.  Should only be
@@ -836,32 +796,26 @@ impl MemoryMappingArena {
         if end_offset > self.size {
             return Err(Error::InvalidAddress);
         }
-        // Ensure offset..offset+size doesn't overlap with existing regions
-        // Find the offset + size of the first mapping before the desired offset
-        let (prev_offset, prev_size) = match self.maps.range(..offset).rev().next() {
-            Some((offset, mmap)) => (*offset, mmap.size()),
-            None => {
-                // Empty map
-                return Ok(());
-            }
-        };
-        if offset == prev_offset {
-            // Perfectly overlapping regions are allowed
-            if size != prev_size {
-                return Err(Error::Overlapping(offset, size));
-            }
-        } else if offset < (prev_offset + prev_size) {
-            return Err(Error::Overlapping(offset, size));
-        }
-
         Ok(())
     }
 }
 
+impl From<MemoryMapping> for MemoryMappingArena {
+    fn from(mmap: MemoryMapping) -> Self {
+        let addr = mmap.as_ptr();
+        let size = mmap.size();
+
+        // Forget the original mapping because the `MemoryMappingArena` will take care of calling
+        // `munmap` when it is dropped.
+        std::mem::forget(mmap);
+        MemoryMappingArena { addr, size }
+    }
+}
+
 impl Drop for MemoryMappingArena {
     fn drop(&mut self) {
-        // This is safe because we mmap the area at addr ourselves, and nobody
-        // else is holding a reference to it.
+        // This is safe because we own this memory range, and nobody else is holding a reference to
+        // it.
         unsafe {
             libc::munmap(self.addr as *mut libc::c_void, self.size);
         }
@@ -977,22 +931,8 @@ mod tests {
     fn arena_remove() {
         let mut m = MemoryMappingArena::new(0x40000).unwrap();
         assert!(m.add_anon(0, pagesize() * 4).is_ok());
-        assert!(m.remove(0).unwrap(), true);
-        assert!(m.remove(0).unwrap(), false);
-    }
-
-    #[test]
-    fn arena_add_overlap_error() {
-        let page = pagesize();
-        let mut m = MemoryMappingArena::new(page * 4).unwrap();
-        assert!(m.add_anon(0, page * 4).is_ok());
-        let res = m.add_anon(page, page).unwrap_err();
-        match res {
-            Error::Overlapping(a, o) => {
-                assert_eq!((a, o), (page, page));
-            }
-            e => panic!("unexpected error: {}", e),
-        }
+        assert!(m.remove(0, pagesize()).is_ok());
+        assert!(m.remove(0, pagesize() * 2).is_ok());
     }
 
     #[test]
@@ -1015,4 +955,62 @@ mod tests {
             e => panic!("unexpected error: {}", e),
         }
     }
+
+    #[test]
+    fn arena_add_overlapping() {
+        let ps = pagesize();
+        let mut m =
+            MemoryMappingArena::new(12 * ps).expect("failed to create `MemoryMappingArena`");
+        m.add_anon(ps * 4, ps * 4)
+            .expect("failed to add sub-mapping");
+
+        // Overlap in the front.
+        m.add_anon(ps * 2, ps * 3)
+            .expect("failed to add front overlapping sub-mapping");
+
+        // Overlap in the back.
+        m.add_anon(ps * 7, ps * 3)
+            .expect("failed to add back overlapping sub-mapping");
+
+        // Overlap the back of the first mapping, all of the middle mapping, and the front of the
+        // last mapping.
+        m.add_anon(ps * 3, ps * 6)
+            .expect("failed to add mapping that overlaps several mappings");
+    }
+
+    #[test]
+    fn arena_remove_overlapping() {
+        let ps = pagesize();
+        let mut m =
+            MemoryMappingArena::new(12 * ps).expect("failed to create `MemoryMappingArena`");
+        m.add_anon(ps * 4, ps * 4)
+            .expect("failed to add sub-mapping");
+        m.add_anon(ps * 2, ps * 2)
+            .expect("failed to add front overlapping sub-mapping");
+        m.add_anon(ps * 8, ps * 2)
+            .expect("failed to add back overlapping sub-mapping");
+
+        // Remove the back of the first mapping and the front of the second.
+        m.remove(ps * 3, ps * 2)
+            .expect("failed to remove front overlapping mapping");
+
+        // Remove the back of the second mapping and the front of the third.
+        m.remove(ps * 7, ps * 2)
+            .expect("failed to remove back overlapping mapping");
+
+        // Remove a mapping that completely overlaps the middle mapping.
+        m.remove(ps * 5, ps * 2)
+            .expect("failed to remove fully overlapping mapping");
+    }
+
+    #[test]
+    fn arena_remove_unaligned() {
+        let ps = pagesize();
+        let mut m =
+            MemoryMappingArena::new(12 * ps).expect("failed to create `MemoryMappingArena`");
+
+        m.add_anon(0, ps).expect("failed to add mapping");
+        m.remove(0, ps - 1)
+            .expect("failed to remove unaligned mapping");
+    }
 }
diff --git a/sys_util/src/shm.rs b/sys_util/src/shm.rs
index d158230..ee5f5a3 100644
--- a/sys_util/src/shm.rs
+++ b/sys_util/src/shm.rs
@@ -5,7 +5,7 @@
 use std::ffi::{CStr, CString};
 use std::fs::{read_link, File};
 use std::io::{self, Read, Seek, SeekFrom, Write};
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 
 use libc::{
     self, c_char, c_int, c_long, c_uint, close, fcntl, ftruncate64, off64_t, syscall, EINVAL,
@@ -136,13 +136,11 @@ impl SharedMemory {
         Ok(SharedMemory { fd: file, size: 0 })
     }
 
-    /// Constructs a `SharedMemory` instance from a file descriptor that represents shared memory.
+    /// Constructs a `SharedMemory` instance from a `File` that represents shared memory.
     ///
     /// The size of the resulting shared memory will be determined using `File::seek`. If the given
     /// file's size can not be determined this way, this will return an error.
-    pub fn from_raw_fd<T: IntoRawFd>(fd: T) -> Result<SharedMemory> {
-        // Safe because the IntoRawFd trait indicates fd has unique ownership.
-        let mut file = unsafe { File::from_raw_fd(fd.into_raw_fd()) };
+    pub fn from_file(mut file: File) -> Result<SharedMemory> {
         let file_size = file.seek(SeekFrom::End(0))?;
         Ok(SharedMemory {
             fd: file,
diff --git a/sys_util/src/struct_util.rs b/sys_util/src/struct_util.rs
index 551204e..aa2ca39 100644
--- a/sys_util/src/struct_util.rs
+++ b/sys_util/src/struct_util.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::io::Read;
 use std::mem::size_of;
 
diff --git a/sys_util/src/timerfd.rs b/sys_util/src/timerfd.rs
index 8e5d91b..f2a3ff8 100644
--- a/sys_util/src/timerfd.rs
+++ b/sys_util/src/timerfd.rs
@@ -34,7 +34,7 @@ impl TimerFd {
     /// Sets the timer to expire after `dur`.  If `interval` is not `None` it represents
     /// the period for repeated expirations after the initial expiration.  Otherwise
     /// the timer will expire just once.  Cancels any existing duration and repeating interval.
-    pub fn reset(&mut self, dur: Duration, interval: Option<Duration>) -> Result<()> {
+    pub fn reset(&self, dur: Duration, interval: Option<Duration>) -> Result<()> {
         // Safe because we are zero-initializing a struct with only primitive member fields.
         let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
         spec.it_value.tv_sec = dur.as_secs() as libc::time_t;
@@ -61,7 +61,7 @@ impl TimerFd {
     /// Waits until the timer expires.  The return value represents the number of times the timer
     /// has expired since the last time `wait` was called.  If the timer has not yet expired once
     /// this call will block until it does.
-    pub fn wait(&mut self) -> Result<u64> {
+    pub fn wait(&self) -> Result<u64> {
         let mut count = 0u64;
 
         // Safe because this will only modify |buf| and we check the return value.
@@ -96,7 +96,7 @@ impl TimerFd {
     }
 
     /// Disarms the timer.
-    pub fn clear(&mut self) -> Result<()> {
+    pub fn clear(&self) -> Result<()> {
         // Safe because we are zero-initializing a struct with only primitive member fields.
         let spec: libc::itimerspec = unsafe { mem::zeroed() };
 
@@ -222,7 +222,7 @@ mod tests {
 
     #[test]
     fn one_shot() {
-        let mut tfd = TimerFd::new().expect("failed to create timerfd");
+        let tfd = TimerFd::new().expect("failed to create timerfd");
         assert_eq!(tfd.is_armed().unwrap(), false);
 
         let dur = Duration::from_millis(200);
@@ -239,7 +239,7 @@ mod tests {
 
     #[test]
     fn repeating() {
-        let mut tfd = TimerFd::new().expect("failed to create timerfd");
+        let tfd = TimerFd::new().expect("failed to create timerfd");
 
         let dur = Duration::from_millis(200);
         let interval = Duration::from_millis(100);
diff --git a/tests/boot.rs b/tests/boot.rs
index b4e38e1..9c2da3c 100644
--- a/tests/boot.rs
+++ b/tests/boot.rs
@@ -13,8 +13,9 @@ use std::sync::Once;
 
 use libc::{cpu_set_t, sched_getaffinity};
 
+use arch::{set_default_serial_parameters, SerialHardware, SerialParameters, SerialType};
 use crosvm::{linux, Config, Executable};
-use devices::{SerialParameters, SerialType};
+use sys_util::syslog;
 
 const CHROOT_KERNEL_PATH: &str = "/mnt/host/source/src/third_party/kernel/v4.19/";
 const CONTAINER_VM_DEFCONFIG: &str = "arch/x86/configs/chromiumos-container-vm-x86_64_defconfig";
@@ -220,20 +221,26 @@ fn prepare_kernel() -> PathBuf {
 
 #[test]
 fn boot() {
+    syslog::init().unwrap();
+
     let kernel_path = prepare_kernel();
 
     let mut c = Config::default();
     c.sandbox = false;
     c.serial_parameters.insert(
-        1,
+        (SerialHardware::Serial, 1),
         SerialParameters {
             type_: SerialType::Sink,
+            hardware: SerialHardware::Serial,
             path: None,
+            input: None,
             num: 1,
             console: false,
+            earlycon: false,
             stdin: false,
         },
     );
+    set_default_serial_parameters(&mut c.serial_parameters);
     c.executable_path = Some(Executable::Kernel(kernel_path));
 
     let r = linux::run_config(c);
diff --git a/tests/plugins.rs b/tests/plugins.rs
index c45096f..1675a75 100644
--- a/tests/plugins.rs
+++ b/tests/plugins.rs
@@ -75,7 +75,7 @@ fn build_plugin(src: &str) -> RemovePath {
     let status = child.wait().expect("failed to wait for compiler");
     assert!(status.success(), "failed to build plugin");
 
-    RemovePath(PathBuf::from(out_bin))
+    RemovePath(out_bin)
 }
 
 fn run_plugin(bin_path: &Path, with_sandbox: bool) {
diff --git a/usb_util/src/error.rs b/usb_util/src/error.rs
index 24d8036..409be98 100644
--- a/usb_util/src/error.rs
+++ b/usb_util/src/error.rs
@@ -4,7 +4,6 @@
 
 use libc::c_ulong;
 use remain::sorted;
-use std;
 use std::fmt::{self, Display};
 use std::io;
 use std::num;
diff --git a/vhost/src/net.rs b/vhost/src/net.rs
index 7d49f17..9b59cf2 100644
--- a/vhost/src/net.rs
+++ b/vhost/src/net.rs
@@ -2,13 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use libc;
 use net_util::TapT;
 use std::fs::{File, OpenOptions};
 use std::marker::PhantomData;
 use std::os::unix::fs::OpenOptionsExt;
 use std::os::unix::io::{AsRawFd, RawFd};
-use virtio_sys;
 
 use sys_util::{ioctl_with_ref, GuestMemory};
 
diff --git a/vhost/src/vsock.rs b/vhost/src/vsock.rs
index f453027..8bd27e7 100644
--- a/vhost/src/vsock.rs
+++ b/vhost/src/vsock.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use libc;
 use std::fs::{File, OpenOptions};
 use std::os::unix::fs::OpenOptionsExt;
 use std::os::unix::io::{AsRawFd, RawFd};
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
index b01f4af..a1d2964 100644
--- a/vm_control/src/lib.rs
+++ b/vm_control/src/lib.rs
@@ -123,6 +123,43 @@ pub struct BalloonStats {
     pub hugetlb_failures: Option<u64>,
 }
 
+impl Display for BalloonStats {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{{")?;
+        if let Some(swap_in) = self.swap_in {
+            write!(f, "\n    swap_in: {}", swap_in)?;
+        }
+        if let Some(swap_out) = self.swap_out {
+            write!(f, "\n    swap_out: {}", swap_out)?;
+        }
+        if let Some(major_faults) = self.major_faults {
+            write!(f, "\n    major_faults: {}", major_faults)?;
+        }
+        if let Some(minor_faults) = self.minor_faults {
+            write!(f, "\n    minor_faults: {}", minor_faults)?;
+        }
+        if let Some(free_memory) = self.free_memory {
+            write!(f, "\n    free_memory: {}", free_memory)?;
+        }
+        if let Some(total_memory) = self.total_memory {
+            write!(f, "\n    total_memory: {}", total_memory)?;
+        }
+        if let Some(available_memory) = self.available_memory {
+            write!(f, "\n    available_memory: {}", available_memory)?;
+        }
+        if let Some(disk_caches) = self.disk_caches {
+            write!(f, "\n    disk_caches: {}", disk_caches)?;
+        }
+        if let Some(hugetlb_allocations) = self.hugetlb_allocations {
+            write!(f, "\n    hugetlb_allocations: {}", hugetlb_allocations)?;
+        }
+        if let Some(hugetlb_failures) = self.hugetlb_failures {
+            write!(f, "\n    hugetlb_failures: {}", hugetlb_failures)?;
+        }
+        write!(f, "\n}}")
+    }
+}
+
 #[derive(MsgOnSocket, Debug)]
 pub enum BalloonControlResult {
     Stats {
@@ -413,7 +450,12 @@ pub enum VmMsyncRequest {
     /// Flush the content of a memory mapping to its backing file.
     /// `slot` selects the arena (as returned by `Vm::add_mmap_arena`).
     /// `offset` is the offset of the mapping to sync within the arena.
-    MsyncArena { slot: u32, offset: usize },
+    /// `size` is the size of the mapping to sync within the arena.
+    MsyncArena {
+        slot: u32,
+        offset: usize,
+        size: usize,
+    },
 }
 
 #[derive(MsgOnSocket, Debug)]
@@ -434,11 +476,10 @@ impl VmMsyncRequest {
     pub fn execute(&self, vm: &mut Vm) -> VmMsyncResponse {
         use self::VmMsyncRequest::*;
         match *self {
-            MsyncArena { slot, offset } => {
+            MsyncArena { slot, offset, size } => {
                 if let Some(arena) = vm.get_mmap_arena(slot) {
-                    match arena.msync(offset) {
-                        Ok(true) => VmMsyncResponse::Ok,
-                        Ok(false) => VmMsyncResponse::Err(SysError::new(EINVAL)),
+                    match arena.msync(offset, size) {
+                        Ok(()) => VmMsyncResponse::Ok,
                         Err(e) => match e {
                             MmapError::SystemCallFailed(errno) => VmMsyncResponse::Err(errno),
                             _ => VmMsyncResponse::Err(SysError::new(EINVAL)),
@@ -509,11 +550,23 @@ fn register_memory(
     };
 
     let addr = match allocation {
-        Some((Alloc::PciBar { bus, dev, bar }, offset)) => {
+        Some((
+            Alloc::PciBar {
+                bus,
+                dev,
+                func,
+                bar,
+            },
+            offset,
+        )) => {
             match allocator
                 .mmio_allocator(MmioType::High)
-                .get(&Alloc::PciBar { bus, dev, bar })
-            {
+                .get(&Alloc::PciBar {
+                    bus,
+                    dev,
+                    func,
+                    bar,
+                }) {
                 Some((start_addr, length, _)) => {
                     let address = *start_addr + offset;
                     let range = *start_addr..*start_addr + *length;
@@ -574,10 +627,30 @@ impl VmRequest {
                 *run_mode = Some(VmRunMode::Running);
                 VmResponse::Ok
             }
-            VmRequest::BalloonCommand(ref command) => match balloon_host_socket.send(command) {
-                Ok(_) => VmResponse::Ok,
-                Err(_) => VmResponse::Err(SysError::last()),
-            },
+            VmRequest::BalloonCommand(BalloonControlCommand::Adjust { num_bytes }) => {
+                match balloon_host_socket.send(&BalloonControlCommand::Adjust { num_bytes }) {
+                    Ok(_) => VmResponse::Ok,
+                    Err(_) => VmResponse::Err(SysError::last()),
+                }
+            }
+            VmRequest::BalloonCommand(BalloonControlCommand::Stats) => {
+                match balloon_host_socket.send(&BalloonControlCommand::Stats {}) {
+                    Ok(_) => match balloon_host_socket.recv() {
+                        Ok(BalloonControlResult::Stats {
+                            stats,
+                            balloon_actual,
+                        }) => VmResponse::BalloonStats {
+                            stats,
+                            balloon_actual,
+                        },
+                        Err(e) => {
+                            error!("balloon socket recv failed: {}", e);
+                            VmResponse::Err(SysError::last())
+                        }
+                    },
+                    Err(_) => VmResponse::Err(SysError::last()),
+                }
+            }
             VmRequest::DiskCommand {
                 disk_index,
                 ref command,
@@ -639,6 +712,11 @@ pub enum VmResponse {
         slot: u32,
         desc: GpuMemoryDesc,
     },
+    /// Results of balloon control commands.
+    BalloonStats {
+        stats: BalloonStats,
+        balloon_actual: u64,
+    },
     /// Results of usb control commands.
     UsbResponse(UsbControlResult),
 }
@@ -660,6 +738,14 @@ impl Display for VmResponse {
                 "gpu memory allocated and registered to page frame number {:#x} and memory slot {}",
                 pfn, slot
             ),
+            BalloonStats {
+                stats,
+                balloon_actual,
+            } => write!(
+                f,
+                "balloon size: {}\nballoon stats: {}",
+                balloon_actual, stats
+            ),
             UsbResponse(result) => write!(f, "usb control request get result {:?}", result),
         }
     }
diff --git a/x86_64/src/acpi.rs b/x86_64/src/acpi.rs
index 3600d16..14b21a7 100644
--- a/x86_64/src/acpi.rs
+++ b/x86_64/src/acpi.rs
@@ -40,8 +40,8 @@ const FADT_LEN: u32 = 276;
 const FADT_REVISION: u8 = 6;
 const FADT_MINOR_REVISION: u8 = 3;
 // FADT flags
-const FADT_POWER_BUTTON: u32 = (1 << 4);
-const FADT_SLEEP_BUTTON: u32 = (1 << 5);
+const FADT_POWER_BUTTON: u32 = 1 << 4;
+const FADT_SLEEP_BUTTON: u32 = 1 << 5;
 // FADT fields offset
 const FADT_FIELD_SCI_INTERRUPT: usize = 46;
 const FADT_FIELD_PM1A_EVENT_BLK_ADDR: usize = 56;
diff --git a/x86_64/src/cpuid.rs b/x86_64/src/cpuid.rs
index 46294b2..b32298c 100644
--- a/x86_64/src/cpuid.rs
+++ b/x86_64/src/cpuid.rs
@@ -6,9 +6,6 @@ use std::arch::x86_64::{__cpuid, __cpuid_count};
 use std::fmt::{self, Display};
 use std::result;
 
-use kvm;
-use sys_util;
-
 #[derive(Debug, PartialEq)]
 pub enum Error {
     GetSupportedCpusFailed(sys_util::Error),
diff --git a/x86_64/src/interrupts.rs b/x86_64/src/interrupts.rs
index 5fba859..3be9940 100644
--- a/x86_64/src/interrupts.rs
+++ b/x86_64/src/interrupts.rs
@@ -7,9 +7,7 @@ use std::fmt::{self, Display};
 use std::mem;
 use std::result;
 
-use kvm;
 use kvm_sys::kvm_lapic_state;
-use sys_util;
 
 #[derive(Debug)]
 pub enum Error {
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index a4ba444..a6a02bc 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -54,11 +54,14 @@ use std::mem;
 use std::sync::Arc;
 
 use crate::bootparam::boot_params;
-use arch::{RunnableLinuxVm, VmComponents, VmImage};
+use arch::{
+    get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware, SerialParameters,
+    VmComponents, VmImage,
+};
 use devices::split_irqchip_common::GsiRelay;
 use devices::{
-    get_serial_tty_string, Ioapic, PciConfigIo, PciDevice, PciInterruptPin, Pic, SerialParameters,
-    IOAPIC_BASE_ADDRESS, IOAPIC_MEM_LENGTH_BYTES,
+    Ioapic, PciAddress, PciConfigIo, PciDevice, PciInterruptPin, Pic, IOAPIC_BASE_ADDRESS,
+    IOAPIC_MEM_LENGTH_BYTES,
 };
 use io_jail::Minijail;
 use kvm::*;
@@ -90,6 +93,7 @@ pub enum Error {
     CreateVm(sys_util::Error),
     E820Configuration,
     EnableSplitIrqchip(sys_util::Error),
+    GetSerialCmdline(GetSerialCmdlineError),
     KernelOffsetPastEnd,
     LoadBios(io::Error),
     LoadBzImage(bzimage::Error),
@@ -139,6 +143,7 @@ impl Display for Error {
             CreateVm(e) => write!(f, "failed to create VM: {}", e),
             E820Configuration => write!(f, "invalid e820 setup params"),
             EnableSplitIrqchip(e) => write!(f, "failed to enable split irqchip: {}", e),
+            GetSerialCmdline(e) => write!(f, "failed to get serial cmdline: {}", e),
             KernelOffsetPastEnd => write!(f, "the kernel extends past the end of RAM"),
             LoadBios(e) => write!(f, "error loading bios: {}", e),
             LoadBzImage(e) => write!(f, "error loading kernel bzImage: {}", e),
@@ -172,8 +177,8 @@ pub struct X8664arch;
 
 const BOOT_STACK_POINTER: u64 = 0x8000;
 // Make sure it align to 256MB for MTRR convenient
-const MEM_32BIT_GAP_SIZE: u64 = (768 << 20);
-const FIRST_ADDR_PAST_32BITS: u64 = (1 << 32);
+const MEM_32BIT_GAP_SIZE: u64 = 768 << 20;
+const FIRST_ADDR_PAST_32BITS: u64 = 1 << 32;
 const END_ADDR_BEFORE_32BITS: u64 = FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE;
 const MMIO_SIZE: u64 = MEM_32BIT_GAP_SIZE - 0x8000000;
 const KERNEL_64BIT_ENTRY_OFFSET: u64 = 0x200;
@@ -207,7 +212,7 @@ fn configure_system(
     cmdline_addr: GuestAddress,
     cmdline_size: usize,
     num_cpus: u8,
-    pci_irqs: Vec<(u32, PciInterruptPin)>,
+    pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
     setup_data: Option<GuestAddress>,
     initrd: Option<(GuestAddress, usize)>,
     mut params: boot_params,
@@ -331,7 +336,7 @@ impl arch::LinuxArch for X8664arch {
         mut components: VmComponents,
         split_irqchip: bool,
         ioapic_device_socket: VmIrqRequestSocket,
-        serial_parameters: &BTreeMap<u8, SerialParameters>,
+        serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
         create_devices: F,
     ) -> Result<RunnableLinuxVm>
@@ -421,7 +426,7 @@ impl arch::LinuxArch for X8664arch {
             suspend_evt.try_clone().map_err(Error::CloneEventFd)?,
         )?;
 
-        let stdio_serial_num = Self::setup_serial_devices(
+        Self::setup_serial_devices(
             &mut vm,
             &mut io_bus,
             &mut gsi_relay,
@@ -459,7 +464,11 @@ impl arch::LinuxArch for X8664arch {
         match components.vm_image {
             VmImage::Bios(ref mut bios) => Self::load_bios(&mem, bios)?,
             VmImage::Kernel(ref mut kernel_image) => {
-                let mut cmdline = Self::get_base_linux_cmdline(stdio_serial_num);
+                let mut cmdline = Self::get_base_linux_cmdline();
+
+                get_serial_cmdline(&mut cmdline, serial_parameters, "io")
+                    .map_err(Error::GetSerialCmdline)?;
+
                 for param in components.extra_kernel_params {
                     cmdline.insert_str(&param).map_err(Error::Cmdline)?;
                 }
@@ -579,7 +588,7 @@ impl X8664arch {
         vcpu_count: u32,
         cmdline: &CStr,
         initrd_file: Option<File>,
-        pci_irqs: Vec<(u32, PciInterruptPin)>,
+        pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
         android_fstab: Option<File>,
         kernel_end: u64,
         params: boot_params,
@@ -719,12 +728,8 @@ impl X8664arch {
     }
 
     /// This returns a minimal kernel command for this architecture
-    fn get_base_linux_cmdline(stdio_serial_num: Option<u8>) -> kernel_cmdline::Cmdline {
+    fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
         let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE as usize);
-        if let Some(stdio_serial_num) = stdio_serial_num {
-            let tty_string = get_serial_tty_string(stdio_serial_num);
-            cmdline.insert("console", &tty_string).unwrap();
-        }
         cmdline.insert_str("pci=noacpi reboot=k panic=-1").unwrap();
 
         cmdline
@@ -848,13 +853,13 @@ impl X8664arch {
         vm: &mut Vm,
         io_bus: &mut devices::Bus,
         gsi_relay: &mut Option<GsiRelay>,
-        serial_parameters: &BTreeMap<u8, SerialParameters>,
+        serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
-    ) -> Result<Option<u8>> {
+    ) -> Result<()> {
         let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
         let com_evt_2_4 = EventFd::new().map_err(Error::CreateEventFd)?;
 
-        let stdio_serial_num = arch::add_serial_devices(
+        arch::add_serial_devices(
             io_bus,
             &com_evt_1_3,
             &com_evt_2_4,
@@ -873,7 +878,7 @@ impl X8664arch {
                 .map_err(Error::RegisterIrqfd)?;
         }
 
-        Ok(stdio_serial_num)
+        Ok(())
     }
 
     /// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
diff --git a/x86_64/src/mptable.rs b/x86_64/src/mptable.rs
index cac9e58..218f561 100644
--- a/x86_64/src/mptable.rs
+++ b/x86_64/src/mptable.rs
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use std::convert::TryFrom;
 use std::fmt::{self, Display};
 use std::mem;
 use std::result;
@@ -10,7 +11,7 @@ use std::slice;
 use libc::c_char;
 
 use data_model::VolatileMemory;
-use devices::PciInterruptPin;
+use devices::{PciAddress, PciInterruptPin};
 use sys_util::{GuestAddress, GuestMemory};
 
 use crate::mpspec::*;
@@ -117,14 +118,16 @@ fn compute_mp_size(num_cpus: u8) -> usize {
 pub fn setup_mptable(
     mem: &GuestMemory,
     num_cpus: u8,
-    pci_irqs: Vec<(u32, PciInterruptPin)>,
+    pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
 ) -> Result<()> {
-    const PCI_BUS_ID: u8 = 0;
-    const ISA_BUS_ID: u8 = 1;
-
     // Used to keep track of the next base pointer into the MP table.
     let mut base_mp = GuestAddress(MPTABLE_START);
 
+    // Calculate ISA bus number in the system, report at least one PCI bus.
+    let isa_bus_id = match pci_irqs.iter().max_by_key(|v| v.0.bus) {
+        Some(pci_irq) => pci_irq.0.bus + 1,
+        _ => 1,
+    };
     let mp_size = compute_mp_size(num_cpus);
 
     // The checked_add here ensures the all of the following base_mp.unchecked_add's will be without
@@ -194,11 +197,11 @@ pub fn setup_mptable(
         base_mp = base_mp.unchecked_add(size as u64);
         checksum = checksum.wrapping_add(compute_checksum(&mpc_ioapic));
     }
-    {
+    for pci_bus_id in 0..isa_bus_id {
         let size = mem::size_of::<mpc_bus>();
         let mut mpc_bus = mpc_bus::default();
         mpc_bus.type_ = MP_BUS as u8;
-        mpc_bus.busid = PCI_BUS_ID;
+        mpc_bus.busid = pci_bus_id;
         mpc_bus.bustype = BUS_TYPE_PCI;
         mem.write_obj_at_addr(mpc_bus, base_mp)
             .map_err(|_| Error::WriteMpcBus)?;
@@ -209,7 +212,7 @@ pub fn setup_mptable(
         let size = mem::size_of::<mpc_bus>();
         let mut mpc_bus = mpc_bus::default();
         mpc_bus.type_ = MP_BUS as u8;
-        mpc_bus.busid = ISA_BUS_ID;
+        mpc_bus.busid = isa_bus_id;
         mpc_bus.bustype = BUS_TYPE_ISA;
         mem.write_obj_at_addr(mpc_bus, base_mp)
             .map_err(|_| Error::WriteMpcBus)?;
@@ -222,7 +225,7 @@ pub fn setup_mptable(
         mpc_intsrc.type_ = MP_INTSRC as u8;
         mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
         mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
-        mpc_intsrc.srcbus = ISA_BUS_ID;
+        mpc_intsrc.srcbus = isa_bus_id;
         mpc_intsrc.srcbusirq = 0;
         mpc_intsrc.dstapic = 0;
         mpc_intsrc.dstirq = 0;
@@ -239,7 +242,7 @@ pub fn setup_mptable(
         mpc_intsrc.type_ = MP_INTSRC as u8;
         mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
         mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
-        mpc_intsrc.srcbus = ISA_BUS_ID;
+        mpc_intsrc.srcbus = isa_bus_id;
         mpc_intsrc.srcbusirq = i;
         mpc_intsrc.dstapic = ioapicid;
         mpc_intsrc.dstirq = i;
@@ -257,7 +260,7 @@ pub fn setup_mptable(
         mpc_intsrc.type_ = MP_INTSRC as u8;
         mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
         mpc_intsrc.irqflag = (MP_IRQDIR_HIGH | MP_LEVEL_TRIGGER) as u16;
-        mpc_intsrc.srcbus = ISA_BUS_ID;
+        mpc_intsrc.srcbus = isa_bus_id;
         mpc_intsrc.srcbusirq = sci_irq;
         mpc_intsrc.dstapic = ioapicid;
         mpc_intsrc.dstirq = sci_irq;
@@ -268,16 +271,16 @@ pub fn setup_mptable(
     }
     let pci_irq_base = super::X86_64_IRQ_BASE as u8;
     // Insert PCI interrupts after platform IRQs.
-    for (i, pci_irq) in pci_irqs.iter().enumerate() {
+    for (address, irq_num, irq_pin) in pci_irqs.iter() {
         let size = mem::size_of::<mpc_intsrc>();
         let mut mpc_intsrc = mpc_intsrc::default();
         mpc_intsrc.type_ = MP_INTSRC as u8;
         mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
         mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
-        mpc_intsrc.srcbus = PCI_BUS_ID;
-        mpc_intsrc.srcbusirq = (pci_irq.0 as u8 + 1) << 2 | pci_irq.1.to_mask() as u8;
+        mpc_intsrc.srcbus = address.bus;
+        mpc_intsrc.srcbusirq = address.dev << 2 | irq_pin.to_mask() as u8;
         mpc_intsrc.dstapic = ioapicid;
-        mpc_intsrc.dstirq = pci_irq_base + i as u8;
+        mpc_intsrc.dstirq = u8::try_from(*irq_num).map_err(|_| Error::WriteMpcIntsrc)?;
         mem.write_obj_at_addr(mpc_intsrc, base_mp)
             .map_err(|_| Error::WriteMpcIntsrc)?;
         base_mp = base_mp.unchecked_add(size as u64);
@@ -290,7 +293,7 @@ pub fn setup_mptable(
         mpc_intsrc.type_ = MP_INTSRC as u8;
         mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
         mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
-        mpc_intsrc.srcbus = ISA_BUS_ID;
+        mpc_intsrc.srcbus = isa_bus_id;
         mpc_intsrc.srcbusirq = i as u8;
         mpc_intsrc.dstapic = ioapicid;
         mpc_intsrc.dstirq = i as u8;
@@ -305,7 +308,7 @@ pub fn setup_mptable(
         mpc_lintsrc.type_ = MP_LINTSRC as u8;
         mpc_lintsrc.irqtype = mp_irq_source_types_mp_ExtINT as u8;
         mpc_lintsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
-        mpc_lintsrc.srcbusid = ISA_BUS_ID;
+        mpc_lintsrc.srcbusid = isa_bus_id;
         mpc_lintsrc.srcbusirq = 0;
         mpc_lintsrc.destapic = 0;
         mpc_lintsrc.destapiclint = 0;
@@ -320,7 +323,7 @@ pub fn setup_mptable(
         mpc_lintsrc.type_ = MP_LINTSRC as u8;
         mpc_lintsrc.irqtype = mp_irq_source_types_mp_NMI as u8;
         mpc_lintsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
-        mpc_lintsrc.srcbusid = ISA_BUS_ID;
+        mpc_lintsrc.srcbusid = isa_bus_id;
         mpc_lintsrc.srcbusirq = 0;
         mpc_lintsrc.destapic = 0xFF; // Per SeaBIOS
         mpc_lintsrc.destapiclint = 1;
diff --git a/x86_64/src/regs.rs b/x86_64/src/regs.rs
index 344878a..1f5c816 100644
--- a/x86_64/src/regs.rs
+++ b/x86_64/src/regs.rs
@@ -7,7 +7,6 @@ use std::fmt::{self, Display};
 use std::{mem, result};
 
 use assertions::const_assert;
-use kvm;
 use kvm_sys::kvm_fpu;
 use kvm_sys::kvm_msr_entry;
 use kvm_sys::kvm_msrs;