summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2020-03-26 12:26:18 +0000
committerAlyssa Ross <hi@alyssa.is>2020-03-26 12:26:18 +0000
commitdbd5f925b6b2f4cb1bf87563be4d24c8ed35689a (patch)
tree5a51c0af87b6a8bb3dfe43d599cabf96523a7f6f
parent88b7821302043b7ad871fcc0c7748573d0f140e2 (diff)
parent22964eab8874d41cf0eadf03dfeb1ffb653283e5 (diff)
downloadcrosvm-dbd5f925b6b2f4cb1bf87563be4d24c8ed35689a.tar
crosvm-dbd5f925b6b2f4cb1bf87563be4d24c8ed35689a.tar.gz
crosvm-dbd5f925b6b2f4cb1bf87563be4d24c8ed35689a.tar.bz2
crosvm-dbd5f925b6b2f4cb1bf87563be4d24c8ed35689a.tar.lz
crosvm-dbd5f925b6b2f4cb1bf87563be4d24c8ed35689a.tar.xz
crosvm-dbd5f925b6b2f4cb1bf87563be4d24c8ed35689a.tar.zst
crosvm-dbd5f925b6b2f4cb1bf87563be4d24c8ed35689a.zip
Merge remote-tracking branch 'origin/master'
-rw-r--r--Cargo.lock1
-rw-r--r--README.md52
-rw-r--r--devices/Cargo.toml1
-rw-r--r--devices/src/lib.rs4
-rw-r--r--devices/src/pci/ac97.rs88
-rw-r--r--devices/src/pci/ac97_bus_master.rs11
-rw-r--r--devices/src/pci/mod.rs4
-rw-r--r--devices/src/pci/msix.rs42
-rw-r--r--devices/src/pci/pci_device.rs3
-rw-r--r--devices/src/pit.rs2
-rw-r--r--devices/src/serial.rs44
-rw-r--r--devices/src/usb/host_backend/host_device.rs40
-rw-r--r--devices/src/virtio/console.rs449
-rw-r--r--devices/src/virtio/descriptor_utils.rs15
-rw-r--r--devices/src/virtio/fs/filesystem.rs30
-rw-r--r--devices/src/virtio/fs/fuse.rs91
-rw-r--r--devices/src/virtio/fs/passthrough.rs67
-rw-r--r--devices/src/virtio/fs/server.rs39
-rw-r--r--devices/src/virtio/gpu/mod.rs131
-rw-r--r--devices/src/virtio/gpu/protocol.rs120
-rw-r--r--devices/src/virtio/gpu/virtio_3d_backend.rs269
-rw-r--r--devices/src/virtio/gpu/virtio_gfxstream_backend.rs6
-rw-r--r--devices/src/virtio/interrupt.rs2
-rw-r--r--devices/src/virtio/mod.rs2
-rw-r--r--devices/src/virtio/vhost/control_socket.rs36
-rw-r--r--devices/src/virtio/vhost/mod.rs2
-rw-r--r--devices/src/virtio/vhost/net.rs66
-rw-r--r--devices/src/virtio/vhost/vsock.rs1
-rw-r--r--devices/src/virtio/vhost/worker.rs135
-rw-r--r--devices/src/virtio/virtio_device.rs4
-rw-r--r--devices/src/virtio/virtio_pci_device.rs7
-rw-r--r--docker/checkout_commits.env2
-rw-r--r--gpu_display/src/keycode_converter/mod.rs2
-rw-r--r--gpu_renderer/src/generated/virglrenderer.rs27
-rw-r--r--gpu_renderer/src/lib.rs93
-rw-r--r--gpu_renderer/src/vsnprintf.rs33
-rw-r--r--p9/src/server.rs2
-rw-r--r--seccomp/aarch64/fs_device.policy1
-rw-r--r--seccomp/arm/fs_device.policy1
-rw-r--r--seccomp/x86_64/common_device.frequency45
-rw-r--r--seccomp/x86_64/common_device.policy1
-rw-r--r--seccomp/x86_64/fs_device.policy1
-rw-r--r--src/argument.rs11
-rw-r--r--src/crosvm.rs10
-rw-r--r--src/linux.rs39
-rw-r--r--src/main.rs275
-rw-r--r--usb_util/src/device.rs13
-rw-r--r--vm_control/src/lib.rs9
-rw-r--r--x86_64/src/gdt.rs8
-rw-r--r--x86_64/src/interrupts.rs2
-rw-r--r--x86_64/src/lib.rs20
-rw-r--r--x86_64/src/mpspec.rs1
-rw-r--r--x86_64/src/mptable.rs26
53 files changed, 1802 insertions, 584 deletions
diff --git a/Cargo.lock b/Cargo.lock
index bb75235..e9da7f9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -180,6 +180,7 @@ dependencies = [
  "kvm 0.1.0",
  "kvm_sys 0.1.0",
  "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libcras 0.1.0",
  "linux_input_sys 0.1.0",
  "msg_on_socket_derive 0.1.0",
  "msg_socket 0.1.0",
diff --git a/README.md b/README.md
index 09e8c1c..5756dbf 100644
--- a/README.md
+++ b/README.md
@@ -11,11 +11,61 @@ an exploit in the devices.
 The channel #crosvm on [freenode](https://webchat.freenode.net/#crosvm) is used
 for technical discussion related to crosvm development and integration.
 
-## Building with Docker
+## Getting started
+
+### Building for CrOS
+
+crosvm on Chromium OS is built with Portage, so it follows the same general
+workflow as any `cros_workon` package. The full package name is
+`chromeos-base/crosvm`.
+
+See the [Chromium OS developer guide] for more on how to build and deploy with
+Portage.
+
+[Chromium OS developer guide]: https://chromium.googlesource.com/chromiumos/docs/+/master/developer_guide.md
+
+### Building with Docker
 
 See the [README](docker/README.md) from the `docker` subdirectory to learn how
 to build crosvm in enviroments outside of the Chrome OS chroot.
 
+### Building for Linux
+
+>**NOTE:** Building for Linux natively is new and not fully supported.
+
+First, [set up depot_tools] and use `repo` to sync down the crosvm source
+tree. This is a subset of the entire Chromium OS manifest with just enough repos
+to build crosvm.
+
+```sh
+mkdir crosvm
+cd crosvm
+repo init -g crosvm -u https://chromium.googlesource.com/chromiumos/manifest.git --repo-url=https://chromium.googlesource.com/external/repo.git
+repo sync
+```
+
+A basic crosvm build links against `libcap` and `libfdt`. On a Debian-based system,
+you can install `libcap-dev` and `libfdt-dev`.
+
+Handy Debian one-liner for all build and runtime deps, particularly if you're
+running Crostini:
+```sh
+sudo apt install build-essential libcap-dev libfdt-dev pkg-config python
+```
+
+Known issues:
+*   Seccomp policy files have hardcoded absolute paths. You can either fix up
+    the paths locally, or set up an awesome hacky symlink: `sudo mkdir
+    /usr/share/policy && sudo ln -s /path/to/crosvm/seccomp/x86_64
+    /usr/share/policy/crosvm`. We'll eventually build the precompiled
+    policies [into the crosvm binary](http://crbug.com/1052126).
+*   Devices can't be jailed if `/var/empty` doesn't exist. `sudo mkdir -p
+    /var/empty` to work around this for now.
+
+And that's it! You should be able to `cargo build/run/test`.
+
+[set up depot_tools]: https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up
+
 ## Usage
 
 To see the usage information for your version of crosvm, run `crosvm` or `crosvm
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index 83aa406..629f29b 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -25,6 +25,7 @@ io_jail = { path = "../io_jail" }
 kvm = { path = "../kvm" }
 kvm_sys = { path = "../kvm_sys" }
 libc = "*"
+libcras = "*"
 linux_input_sys = { path = "../linux_input_sys" }
 msg_on_socket_derive = { path = "../msg_socket/msg_on_socket_derive" }
 msg_socket = { path = "../msg_socket" }
diff --git a/devices/src/lib.rs b/devices/src/lib.rs
index 1ecfc9c..9ed8417 100644
--- a/devices/src/lib.rs
+++ b/devices/src/lib.rs
@@ -30,8 +30,8 @@ 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::{
-    Ac97Dev, PciConfigIo, PciConfigMmio, PciDevice, PciDeviceError, PciInterruptPin, PciRoot,
-    VfioPciDevice,
+    Ac97Backend, Ac97Dev, Ac97Parameters, PciConfigIo, PciConfigMmio, PciDevice, PciDeviceError,
+    PciInterruptPin, PciRoot, VfioPciDevice,
 };
 pub use self::pic::Pic;
 pub use self::pit::{Pit, PitError};
diff --git a/devices/src/pci/ac97.rs b/devices/src/pci/ac97.rs
index 792df24..5f59165 100644
--- a/devices/src/pci/ac97.rs
+++ b/devices/src/pci/ac97.rs
@@ -2,9 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use std::default::Default;
+use std::error;
+use std::fmt::{self, Display};
 use std::os::unix::io::RawFd;
+use std::str::FromStr;
 
-use audio_streams::shm_streams::ShmStreamSource;
+use audio_streams::{
+    shm_streams::{NullShmStreamSource, ShmStreamSource},
+    StreamEffect,
+};
+use libcras::CrasClient;
 use resources::{Alloc, MmioType, SystemAllocator};
 use sys_util::{error, EventFd, GuestMemory};
 
@@ -25,6 +33,53 @@ const PCI_DEVICE_ID_INTEL_82801AA_5: u16 = 0x2415;
 /// Internally the `Ac97BusMaster` and `Ac97Mixer` structs are used to emulated the bus master and
 /// mixer registers respectively. `Ac97BusMaster` handles moving smaples between guest memory and
 /// the audio backend.
+#[derive(Debug, Clone)]
+pub enum Ac97Backend {
+    NULL,
+    CRAS,
+}
+
+impl Default for Ac97Backend {
+    fn default() -> Self {
+        Ac97Backend::NULL
+    }
+}
+
+/// Errors that are possible from a `Ac97`.
+#[derive(Debug)]
+pub enum Ac97Error {
+    InvalidBackend,
+}
+
+impl error::Error for Ac97Error {}
+
+impl Display for Ac97Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Ac97Error::InvalidBackend => write!(f, "Must be cras or null"),
+        }
+    }
+}
+
+impl FromStr for Ac97Backend {
+    type Err = Ac97Error;
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        match s {
+            "cras" => Ok(Ac97Backend::CRAS),
+            "null" => Ok(Ac97Backend::NULL),
+            _ => Err(Ac97Error::InvalidBackend),
+        }
+    }
+}
+
+/// Holds the parameters for a AC97 device
+#[derive(Default, Debug, Clone)]
+pub struct Ac97Parameters {
+    pub backend: Ac97Backend,
+    pub capture: bool,
+    pub capture_effects: Vec<StreamEffect>,
+}
+
 pub struct Ac97Dev {
     config_regs: PciConfiguration,
     pci_bus_dev: Option<(u8, u8)>,
@@ -61,6 +116,37 @@ 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))?);
+        if params.capture {
+            server.enable_cras_capture();
+        }
+
+        let mut cras_audio = Ac97Dev::new(mem, server);
+        cras_audio.set_capture_effects(params.capture_effects);
+        Ok(cras_audio)
+    }
+
+    fn create_null_audio_device(mem: GuestMemory) -> Result<Ac97Dev> {
+        let server = Box::new(NullShmStreamSource::new());
+        let null_audio = Ac97Dev::new(mem, server);
+        Ok(null_audio)
+    }
+
+    /// Creates an 'Ac97Dev' with suitable audio server inside based on Ac97Parameters
+    pub fn try_new(mem: GuestMemory, param: Ac97Parameters) -> Result<Ac97Dev> {
+        match param.backend {
+            Ac97Backend::CRAS => Ac97Dev::create_cras_audio_device(param, mem),
+            Ac97Backend::NULL => Ac97Dev::create_null_audio_device(mem),
+        }
+    }
+
+    /// Provides the effect needed in capture stream creation
+    pub fn set_capture_effects(&mut self, effect: Vec<StreamEffect>) {
+        self.bus_master.set_capture_effects(effect);
+    }
+
     fn read_mixer(&mut self, offset: u64, data: &mut [u8]) {
         match data.len() {
             // The mixer is only accessed with 16-bit words.
diff --git a/devices/src/pci/ac97_bus_master.rs b/devices/src/pci/ac97_bus_master.rs
index cb28c5a..5f4ca75 100644
--- a/devices/src/pci/ac97_bus_master.rs
+++ b/devices/src/pci/ac97_bus_master.rs
@@ -165,6 +165,9 @@ pub struct Ac97BusMaster {
     po_info: AudioThreadInfo,
     pi_info: AudioThreadInfo,
 
+    // Audio effect
+    capture_effects: Vec<StreamEffect>,
+
     // Audio server used to create playback or capture streams.
     audio_server: Box<dyn ShmStreamSource>,
 
@@ -184,12 +187,18 @@ impl Ac97BusMaster {
             po_info: AudioThreadInfo::new(),
             pi_info: AudioThreadInfo::new(),
 
+            capture_effects: Vec::new(),
             audio_server,
 
             irq_resample_thread: None,
         }
     }
 
+    /// Provides the effect needed in capture stream creation.
+    pub fn set_capture_effects(&mut self, effects: Vec<StreamEffect>) {
+        self.capture_effects = effects;
+    }
+
     /// Returns any file descriptors that need to be kept open when entering a jail.
     pub fn keep_fds(&self) -> Option<Vec<RawFd>> {
         let mut fds = self.audio_server.keep_fds();
@@ -518,7 +527,7 @@ impl Ac97BusMaster {
                 SampleFormat::S16LE,
                 DEVICE_SAMPLE_RATE,
                 buffer_frames,
-                StreamEffect::NoEffect,
+                self.capture_effects.as_slice(),
                 self.mem.as_ref(),
                 starting_offsets,
             )
diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs
index a9e8749..b9ebc19 100644
--- a/devices/src/pci/mod.rs
+++ b/devices/src/pci/mod.rs
@@ -14,8 +14,8 @@ mod pci_device;
 mod pci_root;
 mod vfio_pci;
 
-pub use self::ac97::Ac97Dev;
-pub use self::msix::{MsixCap, MsixConfig};
+pub use self::ac97::{Ac97Backend, Ac97Dev, Ac97Parameters};
+pub use self::msix::{MsixCap, MsixConfig, MsixStatus};
 pub use self::pci_configuration::{
     PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityID,
     PciClassCode, PciConfiguration, PciHeaderType, PciProgrammingInterface, PciSerialBusSubClass,
diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs
index 6c79b4a..b69114b 100644
--- a/devices/src/pci/msix.rs
+++ b/devices/src/pci/msix.rs
@@ -89,6 +89,12 @@ impl Display for MsixError {
 
 type MsixResult<T> = std::result::Result<T, MsixError>;
 
+pub enum MsixStatus {
+    Changed,
+    EntryChanged(usize),
+    NothingToDo,
+}
+
 impl MsixConfig {
     pub fn new(msix_vectors: u16, vm_socket: Arc<VmIrqRequestSocket>) -> Self {
         assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE);
@@ -123,6 +129,18 @@ impl MsixConfig {
         self.masked
     }
 
+    /// Check whether the Function Mask bit in MSIX table Message Control
+    /// word in set or not.
+    /// If true, the vector is masked.
+    /// If false, the vector is unmasked.
+    pub fn table_masked(&self, index: usize) -> bool {
+        if index >= self.table_entries.len() {
+            true
+        } else {
+            self.table_entries[index].masked()
+        }
+    }
+
     /// Check whether the MSI-X Enable bit in Message Control word in set or not.
     /// if 1, the function is permitted to use MSI-X to request service.
     pub fn enabled(&self) -> bool {
@@ -147,7 +165,7 @@ impl MsixConfig {
 
     /// Write to the MSI-X Capability Structure.
     /// Only the top 2 bits in Message Control Word are writable.
-    pub fn write_msix_capability(&mut self, offset: u64, data: &[u8]) {
+    pub fn write_msix_capability(&mut self, offset: u64, data: &[u8]) -> MsixStatus {
         if offset == 2 && data.len() == 2 {
             let reg = u16::from_le_bytes([data[0], data[1]]);
             let old_masked = self.masked;
@@ -173,6 +191,9 @@ impl MsixConfig {
                         self.inject_msix_and_clear_pba(index);
                     }
                 }
+                return MsixStatus::Changed;
+            } else if !old_masked && self.masked {
+                return MsixStatus::Changed;
             }
         } else {
             error!(
@@ -180,6 +201,7 @@ impl MsixConfig {
                 offset
             );
         }
+        MsixStatus::NothingToDo
     }
 
     fn add_msi_route(&self, index: u16, gsi: u32) -> MsixResult<()> {
@@ -304,7 +326,7 @@ impl MsixConfig {
     /// Vector Control: only bit 0 (Mask Bit) is not reserved: when this bit
     ///     is set, the function is prohibited from sending a message using
     ///     this MSI-X Table entry.
-    pub fn write_msix_table(&mut self, offset: u64, data: &[u8]) {
+    pub fn write_msix_table(&mut self, offset: u64, data: &[u8]) -> MsixStatus {
         let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize;
         let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
 
@@ -360,13 +382,17 @@ impl MsixConfig {
         // device.
 
         // Check if bit has been flipped
-        if !self.masked()
-            && old_entry.masked()
-            && !self.table_entries[index].masked()
-            && self.get_pba_bit(index as u16) == 1
-        {
-            self.inject_msix_and_clear_pba(index);
+        if !self.masked() {
+            if old_entry.masked() && !self.table_entries[index].masked() {
+                if self.get_pba_bit(index as u16) == 1 {
+                    self.inject_msix_and_clear_pba(index);
+                }
+                return MsixStatus::EntryChanged(index);
+            } else if !old_entry.masked() && self.table_entries[index].masked() {
+                return MsixStatus::EntryChanged(index);
+            }
         }
+        MsixStatus::NothingToDo
     }
 
     /// Read PBA Entries
diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs
index 4c62e05..a1a3cca 100644
--- a/devices/src/pci/pci_device.rs
+++ b/devices/src/pci/pci_device.rs
@@ -22,6 +22,8 @@ pub enum Error {
     IoAllocationFailed(u64, SystemAllocatorFaliure),
     /// Registering an IO BAR failed.
     IoRegistrationFailed(u64, pci_configuration::Error),
+    /// Create cras client failed.
+    CreateCrasClientFailed(libcras::Error),
 }
 pub type Result<T> = std::result::Result<T, Error>;
 
@@ -31,6 +33,7 @@ impl Display for Error {
 
         match self {
             CapabilitiesSetup(e) => write!(f, "failed to add capability {}", e),
+            CreateCrasClientFailed(e) => write!(f, "failed to create CRAS Client: {}", e),
             IoAllocationFailed(size, e) => write!(
                 f,
                 "failed to allocate space for an IO BAR, size={}: {}",
diff --git a/devices/src/pit.rs b/devices/src/pit.rs
index 86ad4c5..68ed6e0 100644
--- a/devices/src/pit.rs
+++ b/devices/src/pit.rs
@@ -691,7 +691,7 @@ impl PitCounter {
             Some(t) => {
                 let dur = self.clock.lock().now().duration_since(t);
                 let dur_ns: u64 = dur.as_secs() * NANOS_PER_SEC + u64::from(dur.subsec_nanos());
-                (dur_ns * FREQUENCY_HZ / NANOS_PER_SEC)
+                dur_ns * FREQUENCY_HZ / NANOS_PER_SEC
             }
         }
     }
diff --git a/devices/src/serial.rs b/devices/src/serial.rs
index 98f39dc..6629d7e 100644
--- a/devices/src/serial.rs
+++ b/devices/src/serial.rs
@@ -149,37 +149,35 @@ impl SerialParameters {
     ) -> 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());
-                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())
-                        }
-                    }
-                    Ok(Serial::new_in_out(
-                        evt_fd,
-                        Box::new(StdinWrapper),
-                        Box::new(stdout()),
-                    ))
-                } else {
-                    Ok(Serial::new_out(evt_fd, Box::new(stdout())))
-                }
+                Ok(Serial::new(evt_fd, input, Some(Box::new(stdout()))))
             }
-            SerialType::Sink => Ok(Serial::new_sink(evt_fd)),
+            SerialType::Sink => Ok(Serial::new(evt_fd, input, None)),
             SerialType::Syslog => {
                 syslog::push_fds(keep_fds);
-                Ok(Serial::new_out(
+                Ok(Serial::new(
                     evt_fd,
-                    Box::new(syslog::Syslogger::new(
+                    input,
+                    Some(Box::new(syslog::Syslogger::new(
                         syslog::Priority::Info,
                         syslog::Facility::Daemon,
-                    )),
+                    ))),
                 ))
             }
             SerialType::File => match &self.path {
@@ -187,7 +185,7 @@ impl SerialParameters {
                 Some(path) => {
                     let file = File::create(path.as_path()).map_err(Error::FileError)?;
                     keep_fds.push(file.as_raw_fd());
-                    Ok(Serial::new_out(evt_fd, Box::new(file)))
+                    Ok(Serial::new(evt_fd, input, Some(Box::new(file))))
                 }
             },
             SerialType::UnixSocket => Err(Error::Unimplemented(SerialType::UnixSocket)),
diff --git a/devices/src/usb/host_backend/host_device.rs b/devices/src/usb/host_backend/host_device.rs
index 3b85ea2..196fa50 100644
--- a/devices/src/usb/host_backend/host_device.rs
+++ b/devices/src/usb/host_backend/host_device.rs
@@ -305,22 +305,31 @@ impl HostDevice {
             config
         );
         self.release_interfaces();
-        let cur_config = self
-            .device
-            .lock()
-            .get_active_configuration()
-            .map_err(Error::GetActiveConfig)?;
-        usb_debug!("current config is: {}", cur_config);
-        if config != cur_config {
-            self.device
-                .lock()
-                .set_active_configuration(config)
-                .map_err(Error::SetActiveConfig)?;
+        if self.device.lock().get_num_configurations() > 1 {
+            let cur_config = match self.device.lock().get_active_configuration() {
+                Ok(c) => Some(c),
+                Err(e) => {
+                    // The device may be in the default state, in which case
+                    // GET_CONFIGURATION may fail.  Assume the device needs to be
+                    // reconfigured.
+                    usb_debug!("Failed to get active configuration: {}", e);
+                    error!("Failed to get active configuration: {}", e);
+                    None
+                }
+            };
+            if Some(config) != cur_config {
+                self.device
+                    .lock()
+                    .set_active_configuration(config)
+                    .map_err(Error::SetActiveConfig)?;
+            }
+        } else {
+            usb_debug!("Only one configuration - not calling set_active_configuration");
         }
         let config_descriptor = self
             .device
             .lock()
-            .get_active_config_descriptor()
+            .get_config_descriptor(config)
             .map_err(Error::GetActiveConfig)?;
         self.claim_interfaces(&config_descriptor);
         self.create_endpoints(&config_descriptor)?;
@@ -337,10 +346,15 @@ impl HostDevice {
             .set_interface_alt_setting(interface, alt_setting)
             .map_err(Error::SetInterfaceAltSetting)?;
         self.alt_settings.insert(interface, alt_setting);
+        let config = self
+            .device
+            .lock()
+            .get_active_configuration()
+            .map_err(Error::GetActiveConfig)?;
         let config_descriptor = self
             .device
             .lock()
-            .get_active_config_descriptor()
+            .get_config_descriptor(config)
             .map_err(Error::GetActiveConfig)?;
         self.create_endpoints(&config_descriptor)?;
         Ok(TransferStatus::Completed)
diff --git a/devices/src/virtio/console.rs b/devices/src/virtio/console.rs
new file mode 100644
index 0000000..38f5bf1
--- /dev/null
+++ b/devices/src/virtio/console.rs
@@ -0,0 +1,449 @@
+// 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::{self, Read};
+use std::os::unix::io::RawFd;
+use std::sync::mpsc::{channel, Receiver, TryRecvError};
+use std::thread;
+
+use data_model::{DataInit, Le16, Le32};
+use sys_util::{error, EventFd, GuestMemory, PollContext, PollToken};
+
+use super::{
+    copy_config, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_CONSOLE, VIRTIO_F_VERSION_1,
+};
+
+const QUEUE_SIZE: u16 = 256;
+
+// For now, just implement port 0 (receiveq and transmitq).
+// If VIRTIO_CONSOLE_F_MULTIPORT is implemented, more queues will be needed.
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE, QUEUE_SIZE];
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_console_config {
+    cols: Le16,
+    rows: Le16,
+    max_nr_ports: Le32,
+    emerg_wr: Le32,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_console_config {}
+
+struct Worker {
+    mem: GuestMemory,
+    interrupt: Interrupt,
+    input: Option<Box<dyn io::Read + Send>>,
+    output: Option<Box<dyn io::Write + Send>>,
+}
+
+fn write_output(output: &mut Box<dyn io::Write>, data: &[u8]) -> io::Result<()> {
+    output.write_all(&data)?;
+    output.flush()
+}
+
+impl Worker {
+    fn process_transmit_request(
+        mut reader: Reader,
+        output: &mut Box<dyn io::Write>,
+    ) -> io::Result<u32> {
+        let len = reader.available_bytes();
+        let mut data = vec![0u8; len];
+        reader.read_exact(&mut data)?;
+        write_output(output, &data)?;
+        Ok(0)
+    }
+
+    fn process_transmit_queue(
+        &mut self,
+        transmit_queue: &mut Queue,
+        output: &mut Box<dyn io::Write>,
+    ) {
+        let mut needs_interrupt = false;
+        while let Some(avail_desc) = transmit_queue.pop(&self.mem) {
+            let desc_index = avail_desc.index;
+
+            let reader = match Reader::new(&self.mem, avail_desc) {
+                Ok(r) => r,
+                Err(e) => {
+                    error!("console: failed to create reader: {}", e);
+                    transmit_queue.add_used(&self.mem, desc_index, 0);
+                    needs_interrupt = true;
+                    continue;
+                }
+            };
+
+            let len = match Self::process_transmit_request(reader, output) {
+                Ok(written) => written,
+                Err(e) => {
+                    error!("console: process_transmit_request failed: {}", e);
+                    0
+                }
+            };
+
+            transmit_queue.add_used(&self.mem, desc_index, len);
+            needs_interrupt = true;
+        }
+
+        if needs_interrupt {
+            self.interrupt.signal_used_queue(transmit_queue.vector);
+        }
+    }
+
+    // Start a thread that reads self.input and sends the input back via the returned channel.
+    //
+    // `in_avail_evt` will be triggered by the thread when new input is available.
+    fn spawn_input_thread(&mut self, in_avail_evt: &EventFd) -> Option<Receiver<u8>> {
+        let mut rx = match self.input.take() {
+            Some(input) => input,
+            None => return None,
+        };
+
+        let (send_channel, recv_channel) = channel();
+
+        let thread_in_avail_evt = match in_avail_evt.try_clone() {
+            Ok(evt) => evt,
+            Err(e) => {
+                error!("failed to clone in_avail_evt: {}", e);
+                return None;
+            }
+        };
+
+        // The input thread runs in detached mode and will exit when channel is disconnected because
+        // the console device has been dropped.
+        let res = thread::Builder::new()
+            .name(format!("console_input"))
+            .spawn(move || {
+                let mut rx_buf = [0u8; 1];
+                loop {
+                    match rx.read(&mut rx_buf) {
+                        Ok(0) => break, // Assume the stream of input has ended.
+                        Ok(_) => {
+                            if send_channel.send(rx_buf[0]).is_err() {
+                                // The receiver has disconnected.
+                                break;
+                            }
+                            thread_in_avail_evt.write(1).unwrap();
+                        }
+                        Err(e) => {
+                            // Being interrupted is not an error, but everything else is.
+                            if e.kind() != io::ErrorKind::Interrupted {
+                                error!(
+                                    "failed to read for bytes to queue into console device: {}",
+                                    e
+                                );
+                                break;
+                            }
+                        }
+                    }
+                }
+            });
+        if let Err(e) = res {
+            error!("failed to spawn input thread: {}", e);
+            return None;
+        }
+        Some(recv_channel)
+    }
+
+    // Check for input from `in_channel_opt` and transfer it to the receive queue, if any.
+    fn handle_input(
+        &mut self,
+        in_channel_opt: &mut Option<Receiver<u8>>,
+        receive_queue: &mut Queue,
+    ) {
+        let in_channel = match in_channel_opt.as_ref() {
+            Some(v) => v,
+            None => return,
+        };
+
+        while let Some(desc) = receive_queue.peek(&self.mem) {
+            let desc_index = desc.index;
+            let mut writer = match Writer::new(&self.mem, desc) {
+                Ok(w) => w,
+                Err(e) => {
+                    error!("console: failed to create Writer: {}", e);
+                    break;
+                }
+            };
+
+            let mut disconnected = false;
+            while writer.available_bytes() > 0 {
+                match in_channel.try_recv() {
+                    Ok(byte) => {
+                        writer.write_obj(byte).unwrap();
+                    }
+                    Err(TryRecvError::Empty) => break,
+                    Err(TryRecvError::Disconnected) => {
+                        disconnected = true;
+                        break;
+                    }
+                }
+            }
+
+            let bytes_written = writer.bytes_written() as u32;
+
+            if bytes_written > 0 {
+                receive_queue.pop_peeked(&self.mem);
+                receive_queue.add_used(&self.mem, desc_index, bytes_written);
+                self.interrupt.signal_used_queue(receive_queue.vector);
+            }
+
+            if disconnected {
+                // Set in_channel to None so that future handle_input calls exit early.
+                in_channel_opt.take();
+                return;
+            }
+
+            if bytes_written == 0 {
+                break;
+            }
+        }
+    }
+
+    fn run(&mut self, mut queues: Vec<Queue>, mut queue_evts: Vec<EventFd>, kill_evt: EventFd) {
+        #[derive(PollToken)]
+        enum Token {
+            ReceiveQueueAvailable,
+            TransmitQueueAvailable,
+            InputAvailable,
+            InterruptResample,
+            Kill,
+        }
+
+        // Device -> driver
+        let (mut receive_queue, receive_evt) = (queues.remove(0), queue_evts.remove(0));
+
+        // Driver -> device
+        let (mut transmit_queue, transmit_evt) = (queues.remove(0), queue_evts.remove(0));
+
+        let in_avail_evt = match EventFd::new() {
+            Ok(evt) => evt,
+            Err(e) => {
+                error!("failed creating EventFd: {}", e);
+                return;
+            }
+        };
+
+        // Spawn a separate thread to poll self.input.
+        // A thread is used because io::Read only provides a blocking interface, and there is no
+        // generic way to add an io::Read instance to a poll context (it may not be backed by a file
+        // descriptor).  Moving the blocking read call to a separate thread and sending data back to
+        // the main worker thread with an event for notification bridges this gap.
+        let mut in_channel = self.spawn_input_thread(&in_avail_evt);
+
+        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+            (&transmit_evt, Token::TransmitQueueAvailable),
+            (&receive_evt, Token::ReceiveQueueAvailable),
+            (&in_avail_evt, Token::InputAvailable),
+            (self.interrupt.get_resample_evt(), Token::InterruptResample),
+            (&kill_evt, Token::Kill),
+        ]) {
+            Ok(pc) => pc,
+            Err(e) => {
+                error!("failed creating PollContext: {}", e);
+                return;
+            }
+        };
+
+        let mut output: Box<dyn io::Write> = match self.output.take() {
+            Some(o) => o,
+            None => Box::new(io::sink()),
+        };
+
+        'poll: loop {
+            let events = match poll_ctx.wait() {
+                Ok(v) => v,
+                Err(e) => {
+                    error!("failed polling for events: {}", e);
+                    break;
+                }
+            };
+
+            for event in events.iter_readable() {
+                match event.token() {
+                    Token::TransmitQueueAvailable => {
+                        if let Err(e) = transmit_evt.read() {
+                            error!("failed reading transmit queue EventFd: {}", e);
+                            break 'poll;
+                        }
+                        self.process_transmit_queue(&mut transmit_queue, &mut output);
+                    }
+                    Token::ReceiveQueueAvailable => {
+                        if let Err(e) = receive_evt.read() {
+                            error!("failed reading receive queue EventFd: {}", e);
+                            break 'poll;
+                        }
+                        self.handle_input(&mut in_channel, &mut receive_queue);
+                    }
+                    Token::InputAvailable => {
+                        if let Err(e) = in_avail_evt.read() {
+                            error!("failed reading in_avail_evt: {}", e);
+                            break 'poll;
+                        }
+                        self.handle_input(&mut in_channel, &mut receive_queue);
+                    }
+                    Token::InterruptResample => {
+                        self.interrupt.interrupt_resample();
+                    }
+                    Token::Kill => break 'poll,
+                }
+            }
+        }
+    }
+}
+
+/// Virtio console device.
+pub struct Console {
+    kill_evt: Option<EventFd>,
+    worker_thread: Option<thread::JoinHandle<Worker>>,
+    input: Option<Box<dyn io::Read + Send>>,
+    output: Option<Box<dyn io::Write + Send>>,
+    keep_fds: Vec<RawFd>,
+}
+
+impl Console {
+    fn new(
+        input: Option<Box<dyn io::Read + Send>>,
+        output: Option<Box<dyn io::Write + Send>>,
+        keep_fds: Vec<RawFd>,
+    ) -> Console {
+        Console {
+            kill_evt: None,
+            worker_thread: None,
+            input,
+            output,
+            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 {
+    fn drop(&mut self) {
+        if let Some(kill_evt) = self.kill_evt.take() {
+            // Ignore the result because there is nothing we can do about it.
+            let _ = kill_evt.write(1);
+        }
+
+        if let Some(worker_thread) = self.worker_thread.take() {
+            let _ = worker_thread.join();
+        }
+    }
+}
+
+impl VirtioDevice for Console {
+    fn keep_fds(&self) -> Vec<RawFd> {
+        self.keep_fds.clone()
+    }
+
+    fn features(&self) -> u64 {
+        1 << VIRTIO_F_VERSION_1
+    }
+
+    fn device_type(&self) -> u32 {
+        TYPE_CONSOLE
+    }
+
+    fn queue_max_sizes(&self) -> &[u16] {
+        QUEUE_SIZES
+    }
+
+    fn read_config(&self, offset: u64, data: &mut [u8]) {
+        let config = virtio_console_config {
+            max_nr_ports: 1.into(),
+            ..Default::default()
+        };
+        copy_config(data, 0, config.as_slice(), offset);
+    }
+
+    fn activate(
+        &mut self,
+        mem: GuestMemory,
+        interrupt: Interrupt,
+        queues: Vec<Queue>,
+        queue_evts: Vec<EventFd>,
+    ) {
+        if queues.len() < 2 || queue_evts.len() < 2 {
+            return;
+        }
+
+        let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+            Ok(v) => v,
+            Err(e) => {
+                error!("failed creating kill EventFd pair: {}", e);
+                return;
+            }
+        };
+        self.kill_evt = Some(self_kill_evt);
+
+        let input = self.input.take();
+        let output = self.output.take();
+
+        let worker_result = thread::Builder::new()
+            .name("virtio_console".to_string())
+            .spawn(move || {
+                let mut worker = Worker {
+                    mem,
+                    interrupt,
+                    input,
+                    output,
+                };
+                worker.run(queues, queue_evts, kill_evt);
+                worker
+            });
+
+        match worker_result {
+            Err(e) => {
+                error!("failed to spawn virtio_console worker: {}", e);
+                return;
+            }
+            Ok(join_handle) => {
+                self.worker_thread = Some(join_handle);
+            }
+        }
+    }
+
+    fn reset(&mut self) -> bool {
+        if let Some(kill_evt) = self.kill_evt.take() {
+            if kill_evt.write(1).is_err() {
+                error!("{}: failed to notify the kill event", self.debug_label());
+                return false;
+            }
+        }
+
+        if let Some(worker_thread) = self.worker_thread.take() {
+            match worker_thread.join() {
+                Err(_) => {
+                    error!("{}: failed to get back resources", self.debug_label());
+                    return false;
+                }
+                Ok(worker) => {
+                    self.input = worker.input;
+                    self.output = worker.output;
+                    return true;
+                }
+            }
+        }
+        false
+    }
+}
diff --git a/devices/src/virtio/descriptor_utils.rs b/devices/src/virtio/descriptor_utils.rs
index 723b0c6..990f147 100644
--- a/devices/src/virtio/descriptor_utils.rs
+++ b/devices/src/virtio/descriptor_utils.rs
@@ -216,8 +216,8 @@ pub struct Reader<'a> {
     buffer: DescriptorChainConsumer<'a>,
 }
 
-// An iterator over `DataInit` objects on readable descriptors in the descriptor chain.
-struct ReaderIterator<'a, T: DataInit> {
+/// An iterator over `DataInit` objects on readable descriptors in the descriptor chain.
+pub struct ReaderIterator<'a, T: DataInit> {
     reader: &'a mut Reader<'a>,
     phantom: PhantomData<T>,
 }
@@ -283,10 +283,17 @@ impl<'a> Reader<'a> {
     /// them as a collection. Returns an error if the size of the remaining data is indivisible by
     /// the size of an object of type `T`.
     pub fn collect<C: FromIterator<io::Result<T>>, T: DataInit>(&'a mut self) -> C {
-        C::from_iter(ReaderIterator {
+        C::from_iter(self.iter())
+    }
+
+    /// Creates an iterator for sequentially reading `DataInit` objects from the `Reader`. Unlike
+    /// `collect`, this doesn't consume all the remaining data in the `Reader` and doesn't require
+    /// the objects to be stored in a separate collection.
+    pub fn iter<T: DataInit>(&'a mut self) -> ReaderIterator<'a, T> {
+        ReaderIterator {
             reader: self,
             phantom: PhantomData,
-        })
+        }
     }
 
     /// Reads data from the descriptor chain buffer into a file descriptor.
diff --git a/devices/src/virtio/fs/filesystem.rs b/devices/src/virtio/fs/filesystem.rs
index 232ff99..eb9726c 100644
--- a/devices/src/virtio/fs/filesystem.rs
+++ b/devices/src/virtio/fs/filesystem.rs
@@ -1139,4 +1139,34 @@ pub trait FileSystem {
     fn lseek(&self) -> io::Result<()> {
         Err(io::Error::from_raw_os_error(libc::ENOSYS))
     }
+
+    /// Copy a range of data from one file to another
+    ///
+    /// Performs an optimized copy between two file descriptors without the additional cost of
+    /// transferring data through the kernel module to user space (glibc) and then back into
+    /// the file system again.
+    ///
+    /// In case this method is not implemented, glibc falls back to reading data from the source and
+    /// writing to the destination.
+    ///
+    /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
+    /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `copy_file_range`
+    /// without forwarding them to the file system.
+    ///
+    /// All values accepted by the `copy_file_range(2)` system call are valid values for `flags` and
+    /// must be handled by the file system.
+    fn copy_file_range(
+        &self,
+        ctx: Context,
+        inode_src: Self::Inode,
+        handle_src: Self::Handle,
+        offset_src: u64,
+        inode_dst: Self::Inode,
+        handle_dst: Self::Handle,
+        offset_dst: u64,
+        length: u64,
+        flags: u64,
+    ) -> io::Result<usize> {
+        Err(io::Error::from_raw_os_error(libc::ENOSYS))
+    }
 }
diff --git a/devices/src/virtio/fs/fuse.rs b/devices/src/virtio/fs/fuse.rs
index 612202b..5c531cf 100644
--- a/devices/src/virtio/fs/fuse.rs
+++ b/devices/src/virtio/fs/fuse.rs
@@ -12,8 +12,11 @@ use libc;
 /// Version number of this interface.
 pub const KERNEL_VERSION: u32 = 7;
 
+/// Oldest supported minor version of the fuse interface.
+pub const OLDEST_SUPPORTED_KERNEL_MINOR_VERSION: u32 = 27;
+
 /// Minor version number of this interface.
-pub const KERNEL_MINOR_VERSION: u32 = 27;
+pub const KERNEL_MINOR_VERSION: u32 = 31;
 
 /// The ID of the inode corresponding to the root directory of the file system.
 pub const ROOT_ID: u64 = 1;
@@ -56,6 +59,12 @@ const FOPEN_KEEP_CACHE: u32 = 2;
 /// The file is not seekable.
 const FOPEN_NONSEEKABLE: u32 = 4;
 
+/// Allow caching the directory entries.
+const FOPEN_CACHE_DIR: u32 = 8;
+
+/// This file is stream-like (i.e., no file position).
+const FOPEN_STREAM: u32 = 16;
+
 bitflags! {
     /// Options controlling the behavior of files opened by the server in response
     /// to an open or create request.
@@ -63,6 +72,8 @@ bitflags! {
         const DIRECT_IO = FOPEN_DIRECT_IO;
         const KEEP_CACHE = FOPEN_KEEP_CACHE;
         const NONSEEKABLE = FOPEN_NONSEEKABLE;
+        const CACHE_DIR = FOPEN_CACHE_DIR;
+        const STREAM = FOPEN_STREAM;
     }
 }
 
@@ -131,6 +142,24 @@ const HANDLE_KILLPRIV: u32 = 524288;
 /// FileSystem supports posix acls.
 const POSIX_ACL: u32 = 1048576;
 
+/// Reading the device after an abort returns `ECONNABORTED`.
+const ABORT_ERROR: u32 = 2097152;
+
+/// The reply to the `init` message contains the max number of request pages.
+const MAX_PAGES: u32 = 4194304;
+
+/// Cache `readlink` responses.
+const CACHE_SYMLINKS: u32 = 8388608;
+
+/// Kernel supports zero-message opens for directories.
+const NO_OPENDIR_SUPPORT: u32 = 16777216;
+
+/// Kernel supports explicit cache invalidation.
+const EXPLICIT_INVAL_DATA: u32 = 33554432;
+
+/// The `map_alignment` field of the `InitOut` struct is valid.
+const MAP_ALIGNMENT: u32 = 67108864;
+
 bitflags! {
     /// A bitfield passed in as a parameter to and returned from the `init` method of the
     /// `FileSystem` trait.
@@ -297,6 +326,37 @@ bitflags! {
         ///
         /// This feature is disabled by default.
         const POSIX_ACL = POSIX_ACL;
+
+        /// Indicates that the kernel may cache responses to `readlink` calls.
+        const CACHE_SYMLINKS = CACHE_SYMLINKS;
+
+        /// Indicates support for zero-message opens for directories. If this flag is set in the
+        /// `capable` parameter of the `init` trait method, then the file system may return `ENOSYS`
+        /// from the opendir() handler to indicate success. Further attempts to open directories
+        /// will be handled in the kernel. (If this flag is not set, returning ENOSYS will be
+        /// treated as an error and signaled to the caller).
+        ///
+        /// Setting (or not setting) the field in the `FsOptions` returned from the `init` method
+        /// has no effect.
+        const ZERO_MESSAGE_OPENDIR = NO_OPENDIR_SUPPORT;
+
+        /// Indicates support for invalidating cached pages only on explicit request.
+        ///
+        /// If this flag is set in the `capable` parameter of the `init` trait method, then the FUSE
+        /// kernel module supports invalidating cached pages only on explicit request by the
+        /// filesystem.
+        ///
+        /// By setting this flag in the return value of the `init` trait method, the filesystem is
+        /// responsible for invalidating cached pages through explicit requests to the kernel.
+        ///
+        /// Note that setting this flag does not prevent the cached pages from being flushed by OS
+        /// itself and/or through user actions.
+        ///
+        /// Note that if both EXPLICIT_INVAL_DATA and AUTO_INVAL_DATA are set in the `capable`
+        /// parameter of the `init` trait method then AUTO_INVAL_DATA takes precedence.
+        ///
+        /// This feature is disabled by default.
+        const EXPLICIT_INVAL_DATA = EXPLICIT_INVAL_DATA;
     }
 }
 
@@ -318,6 +378,9 @@ pub const WRITE_CACHE: u32 = 1;
 /// `lock_owner` field is valid.
 pub const WRITE_LOCKOWNER: u32 = 2;
 
+/// Kill the suid and sgid bits.
+pub const WRITE_KILL_PRIV: u32 = 3;
+
 // Read flags.
 pub const READ_LOCKOWNER: u32 = 2;
 
@@ -338,6 +401,9 @@ const IOCTL_32BIT: u32 = 8;
 /// Is a directory
 const IOCTL_DIR: u32 = 16;
 
+/// 32-bit compat ioctl on 64-bit machine with 64-bit time_t.
+const IOCTL_COMPAT_X32: u32 = 32;
+
 /// Maximum of in_iovecs + out_iovecs
 pub const IOCTL_MAX_IOV: usize = 256;
 
@@ -357,6 +423,9 @@ bitflags! {
 
         /// Is a directory
         const DIR = IOCTL_DIR;
+
+        /// 32-bit compat ioctl on 64-bit machine with 64-bit time_t.
+        const COMPAT_X32 = IOCTL_COMPAT_X32;
     }
 }
 
@@ -511,6 +580,9 @@ pub enum Opcode {
     Readdirplus = 44,
     Rename2 = 45,
     Lseek = 46,
+    CopyFileRange = 47,
+    SetUpMapping = 48,
+    RemoveMapping = 49,
 }
 
 #[repr(u32)]
@@ -830,7 +902,9 @@ pub struct InitOut {
     pub congestion_threshold: u16,
     pub max_write: u32,
     pub time_gran: u32,
-    pub unused: [u32; 9],
+    pub max_pages: u16,
+    pub map_alignment: u16,
+    pub unused: [u32; 8],
 }
 unsafe impl DataInit for InitOut {}
 
@@ -1049,3 +1123,16 @@ pub struct LseekOut {
     pub offset: u64,
 }
 unsafe impl DataInit for LseekOut {}
+
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct CopyFileRangeIn {
+    pub fh_src: u64,
+    pub off_src: u64,
+    pub nodeid_dst: u64,
+    pub fh_dst: u64,
+    pub off_dst: u64,
+    pub len: u64,
+    pub flags: u64,
+}
+unsafe impl DataInit for CopyFileRangeIn {}
diff --git a/devices/src/virtio/fs/passthrough.rs b/devices/src/virtio/fs/passthrough.rs
index bdc1c6f..419d466 100644
--- a/devices/src/virtio/fs/passthrough.rs
+++ b/devices/src/virtio/fs/passthrough.rs
@@ -395,7 +395,7 @@ impl PassthroughFs {
             libc::openat(
                 self.proc.as_raw_fd(),
                 pathname.as_ptr(),
-                (flags | libc::O_CLOEXEC) & (!libc::O_NOFOLLOW),
+                (flags | libc::O_CLOEXEC) & !(libc::O_NOFOLLOW | libc::O_DIRECT),
             )
         };
         if fd < 0 {
@@ -592,7 +592,13 @@ impl PassthroughFs {
                 OpenOptions::DIRECT_IO,
                 flags & (libc::O_DIRECTORY as u32) == 0,
             ),
-            CachePolicy::Always => opts |= OpenOptions::KEEP_CACHE,
+            CachePolicy::Always => {
+                opts |= if flags & (libc::O_DIRECTORY as u32) == 0 {
+                    OpenOptions::KEEP_CACHE
+                } else {
+                    OpenOptions::CACHE_DIR
+                }
+            }
             _ => {}
         };
 
@@ -965,7 +971,8 @@ impl FileSystem for PassthroughFs {
             libc::openat(
                 data.file.as_raw_fd(),
                 name.as_ptr(),
-                flags as i32 | libc::O_CREAT | libc::O_CLOEXEC | libc::O_NOFOLLOW,
+                (flags as i32 | libc::O_CREAT | libc::O_CLOEXEC | libc::O_NOFOLLOW)
+                    & !libc::O_DIRECT,
                 mode & !(umask & 0o777),
             )
         };
@@ -1689,4 +1696,58 @@ impl FileSystem for PassthroughFs {
             Err(io::Error::from_raw_os_error(libc::ENOTTY))
         }
     }
+
+    fn copy_file_range(
+        &self,
+        ctx: Context,
+        inode_src: Inode,
+        handle_src: Handle,
+        offset_src: u64,
+        inode_dst: Inode,
+        handle_dst: Handle,
+        offset_dst: u64,
+        length: u64,
+        flags: u64,
+    ) -> io::Result<usize> {
+        // We need to change credentials during a write so that the kernel will remove setuid or
+        // setgid bits from the file if it was written to by someone other than the owner.
+        let (_uid, _gid) = set_creds(ctx.uid, ctx.gid)?;
+        let src_data = self
+            .handles
+            .read()
+            .unwrap()
+            .get(&handle_src)
+            .filter(|hd| hd.inode == inode_src)
+            .map(Arc::clone)
+            .ok_or_else(ebadf)?;
+        let dst_data = self
+            .handles
+            .read()
+            .unwrap()
+            .get(&handle_dst)
+            .filter(|hd| hd.inode == inode_dst)
+            .map(Arc::clone)
+            .ok_or_else(ebadf)?;
+
+        let src = src_data.file.lock().as_raw_fd();
+        let dst = dst_data.file.lock().as_raw_fd();
+
+        let res = unsafe {
+            libc::syscall(
+                libc::SYS_copy_file_range,
+                src,
+                &offset_src,
+                dst,
+                offset_dst,
+                length,
+                flags,
+            )
+        };
+
+        if res >= 0 {
+            Ok(res as usize)
+        } else {
+            Err(io::Error::last_os_error())
+        }
+    }
 }
diff --git a/devices/src/virtio/fs/server.rs b/devices/src/virtio/fs/server.rs
index 32b5008..c9025f2 100644
--- a/devices/src/virtio/fs/server.rs
+++ b/devices/src/virtio/fs/server.rs
@@ -118,7 +118,8 @@ impl<F: FileSystem + Sync> Server<F> {
             Some(Opcode::Readdirplus) => self.readdirplus(in_header, r, w),
             Some(Opcode::Rename2) => self.rename2(in_header, r, w),
             Some(Opcode::Lseek) => self.lseek(in_header, r, w),
-            None => reply_error(
+            Some(Opcode::CopyFileRange) => self.copy_file_range(in_header, r, w),
+            Some(Opcode::SetUpMapping) | Some(Opcode::RemoveMapping) | None => reply_error(
                 io::Error::from_raw_os_error(libc::ENOSYS),
                 in_header.unique,
                 w,
@@ -809,7 +810,7 @@ impl<F: FileSystem + Sync> Server<F> {
             return reply_ok(Some(out), None, in_header.unique, w);
         }
 
-        if minor < KERNEL_MINOR_VERSION {
+        if minor < OLDEST_SUPPORTED_KERNEL_MINOR_VERSION {
             error!(
                 "Unsupported fuse protocol minor version: {}.{}",
                 major, minor
@@ -1208,6 +1209,40 @@ impl<F: FileSystem + Sync> Server<F> {
             Ok(0)
         }
     }
+
+    fn copy_file_range(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+        let CopyFileRangeIn {
+            fh_src,
+            off_src,
+            nodeid_dst,
+            fh_dst,
+            off_dst,
+            len,
+            flags,
+        } = r.read_obj().map_err(Error::DecodeMessage)?;
+
+        match self.fs.copy_file_range(
+            Context::from(in_header),
+            in_header.nodeid.into(),
+            fh_src.into(),
+            off_src,
+            nodeid_dst.into(),
+            fh_dst.into(),
+            off_dst,
+            len,
+            flags,
+        ) {
+            Ok(count) => {
+                let out = WriteOut {
+                    size: count as u32,
+                    ..Default::default()
+                };
+
+                reply_ok(Some(out), None, in_header.unique, w)
+            }
+            Err(e) => reply_error(e, in_header.unique, w),
+        }
+    }
 }
 
 fn retry_ioctl(
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index 8cc211f..93d004f 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -277,30 +277,24 @@ trait Backend {
         GpuResponse::ErrUnspec
     }
 
-    fn allocation_metadata(
-        &mut self,
-        _request_id: u32,
-        _request: Vec<u8>,
-        mut _response: Vec<u8>,
-    ) -> GpuResponse {
-        GpuResponse::ErrUnspec
-    }
-
     fn resource_create_v2(
         &mut self,
         _resource_id: u32,
-        _guest_memory_type: u32,
-        _guest_caching_type: u32,
+        _ctx_id: u32,
+        _flags: u32,
         _size: u64,
-        _pci_addr: u64,
-        _mem: &GuestMemory,
+        _memory_id: u64,
         _vecs: Vec<(GuestAddress, usize)>,
-        _args: Vec<u8>,
+        _mem: &GuestMemory,
     ) -> GpuResponse {
         GpuResponse::ErrUnspec
     }
 
-    fn resource_v2_unref(&mut self, _resource_id: u32) -> GpuResponse {
+    fn resource_map(&mut self, _resource_id: u32, _pci_addr: u64) -> GpuResponse {
+        GpuResponse::ErrUnspec
+    }
+
+    fn resource_unmap(&mut self, _resource_id: u32) -> GpuResponse {
         GpuResponse::ErrUnspec
     }
 }
@@ -474,19 +468,19 @@ impl Frontend {
                 let available_bytes = reader.available_bytes();
                 if available_bytes != 0 {
                     let entry_count = info.nr_entries.to_native() as usize;
-                    let mut iovecs = Vec::with_capacity(entry_count);
+                    let mut vecs = Vec::with_capacity(entry_count);
                     for _ in 0..entry_count {
                         match reader.read_obj::<virtio_gpu_mem_entry>() {
                             Ok(entry) => {
                                 let addr = GuestAddress(entry.addr.to_native());
                                 let len = entry.length.to_native() as usize;
-                                iovecs.push((addr, len))
+                                vecs.push((addr, len))
                             }
                             Err(_) => return GpuResponse::ErrUnspec,
                         }
                     }
                     self.backend
-                        .attach_backing(info.resource_id.to_native(), mem, iovecs)
+                        .attach_backing(info.resource_id.to_native(), mem, vecs)
                 } else {
                     error!("missing data for command {:?}", cmd);
                     GpuResponse::ErrUnspec
@@ -610,78 +604,49 @@ impl Frontend {
                     GpuResponse::OkNoData
                 }
             }
-            GpuCommand::AllocationMetadata(info) => {
-                if reader.available_bytes() != 0 {
-                    let id = info.request_id.to_native();
-                    let request_size = info.request_size.to_native();
-                    let response_size = info.response_size.to_native();
-                    if request_size > VIRTIO_GPU_MAX_BLOB_ARGUMENT_SIZE
-                        || response_size > VIRTIO_GPU_MAX_BLOB_ARGUMENT_SIZE
-                    {
-                        return GpuResponse::ErrUnspec;
-                    }
-
-                    let mut request_buf = vec![0; request_size as usize];
-                    let response_buf = vec![0; response_size as usize];
-                    if reader.read_exact(&mut request_buf[..]).is_ok() {
-                        self.backend
-                            .allocation_metadata(id, request_buf, response_buf)
-                    } else {
-                        GpuResponse::ErrInvalidParameter
-                    }
-                } else {
-                    GpuResponse::ErrUnspec
-                }
-            }
             GpuCommand::ResourceCreateV2(info) => {
-                if reader.available_bytes() != 0 {
-                    let resource_id = info.resource_id.to_native();
-                    let guest_memory_type = info.guest_memory_type.to_native();
-                    let size = info.size.to_native();
-                    let guest_caching_type = info.guest_caching_type.to_native();
-                    let pci_addr = info.pci_addr.to_native();
-                    let entry_count = info.nr_entries.to_native();
-                    let args_size = info.args_size.to_native();
-                    if args_size > VIRTIO_GPU_MAX_BLOB_ARGUMENT_SIZE
-                        || entry_count > VIRTIO_GPU_MAX_IOVEC_ENTRIES
-                    {
-                        return GpuResponse::ErrUnspec;
-                    }
-
-                    let mut iovecs = Vec::with_capacity(entry_count as usize);
-                    let mut args = vec![0; args_size as usize];
+                let resource_id = info.resource_id.to_native();
+                let ctx_id = info.hdr.ctx_id.to_native();
+                let flags = info.flags.to_native();
+                let size = info.size.to_native();
+                let memory_id = info.memory_id.to_native();
+                let entry_count = info.nr_entries.to_native();
+                if entry_count > VIRTIO_GPU_MAX_IOVEC_ENTRIES
+                    || (reader.available_bytes() == 0 && entry_count > 0)
+                {
+                    return GpuResponse::ErrUnspec;
+                }
 
-                    for _ in 0..entry_count {
-                        match reader.read_obj::<virtio_gpu_mem_entry>() {
-                            Ok(entry) => {
-                                let addr = GuestAddress(entry.addr.to_native());
-                                let len = entry.length.to_native() as usize;
-                                iovecs.push((addr, len))
-                            }
-                            Err(_) => return GpuResponse::ErrUnspec,
+                let mut vecs = Vec::with_capacity(entry_count as usize);
+                for _ in 0..entry_count {
+                    match reader.read_obj::<virtio_gpu_mem_entry>() {
+                        Ok(entry) => {
+                            let addr = GuestAddress(entry.addr.to_native());
+                            let len = entry.length.to_native() as usize;
+                            vecs.push((addr, len))
                         }
+                        Err(_) => return GpuResponse::ErrUnspec,
                     }
-
-                    match reader.read_exact(&mut args[..]) {
-                        Ok(_) => self.backend.resource_create_v2(
-                            resource_id,
-                            guest_memory_type,
-                            guest_caching_type,
-                            size,
-                            pci_addr,
-                            mem,
-                            iovecs,
-                            args,
-                        ),
-                        Err(_) => GpuResponse::ErrUnspec,
-                    }
-                } else {
-                    GpuResponse::ErrUnspec
                 }
+
+                self.backend.resource_create_v2(
+                    resource_id,
+                    ctx_id,
+                    flags,
+                    size,
+                    memory_id,
+                    vecs,
+                    mem,
+                )
+            }
+            GpuCommand::ResourceMap(info) => {
+                let resource_id = info.resource_id.to_native();
+                let offset = info.offset.to_native();
+                self.backend.resource_map(resource_id, offset)
             }
-            GpuCommand::ResourceV2Unref(info) => {
+            GpuCommand::ResourceUnmap(info) => {
                 let resource_id = info.resource_id.to_native();
-                self.backend.resource_v2_unref(resource_id)
+                self.backend.resource_unmap(resource_id)
             }
         }
     }
diff --git a/devices/src/virtio/gpu/protocol.rs b/devices/src/virtio/gpu/protocol.rs
index b4610b2..c98e289 100644
--- a/devices/src/virtio/gpu/protocol.rs
+++ b/devices/src/virtio/gpu/protocol.rs
@@ -19,9 +19,10 @@ use data_model::{DataInit, Le32, Le64};
 pub const VIRTIO_GPU_F_VIRGL: u32 = 0;
 pub const VIRTIO_GPU_F_EDID: u32 = 1;
 /* The following capabilities are not upstreamed. */
-pub const VIRTIO_GPU_F_MEMORY: u32 = 2;
-pub const VIRTIO_GPU_F_SHARED_GUEST: u32 = 3;
-pub const VIRTIO_GPU_F_HOST_COHERENT: u32 = 4;
+pub const VIRTIO_GPU_F_RESOURCE_UUID: u32 = 2;
+pub const VIRTIO_GPU_F_RESOURCE_V2: u32 = 3;
+pub const VIRTIO_GPU_F_HOST_VISIBLE: u32 = 4;
+pub const VIRTIO_GPU_F_VULKAN: u32 = 5;
 
 pub const VIRTIO_GPU_UNDEFINED: u32 = 0x0;
 
@@ -49,8 +50,8 @@ pub const VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: u32 = 0x206;
 pub const VIRTIO_GPU_CMD_SUBMIT_3D: u32 = 0x207;
 /* The following hypercalls are not upstreamed. */
 pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_V2: u32 = 0x208;
-pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_V2_UNREF: u32 = 0x209;
-pub const VIRTIO_GPU_CMD_ALLOCATION_METADATA: u32 = 0x20a;
+pub const VIRTIO_GPU_CMD_RESOURCE_MAP: u32 = 0x209;
+pub const VIRTIO_GPU_CMD_RESOURCE_UNMAP: u32 = 0x20a;
 
 /* cursor commands */
 pub const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x300;
@@ -63,7 +64,6 @@ pub const VIRTIO_GPU_RESP_OK_CAPSET_INFO: u32 = 0x1102;
 pub const VIRTIO_GPU_RESP_OK_CAPSET: u32 = 0x1103;
 pub const VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO: u32 = 0x1104;
 pub const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1105;
-pub const VIRTIO_GPU_RESP_OK_ALLOCATION_METADATA: u32 = 0x1106;
 
 /* error responses */
 pub const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200;
@@ -73,20 +73,22 @@ pub const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203;
 pub const VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID: u32 = 0x1204;
 pub const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205;
 
-/* guest memory types (not upstreamed) */
-pub const VIRTIO_GPU_MEMORY_UNDEFINED: u32 = 0;
-pub const VIRTIO_GPU_MEMORY_TRANSFER: u32 = 1;
-pub const VIRTIO_GPU_MEMORY_SHARED_GUEST: u32 = 2;
-pub const VIRTIO_GPU_MEMORY_HOST_COHERENT: u32 = 3;
+/* Resource flags (not upstreamed) */
+pub const VIRTIO_GPU_RESOURCE_GUEST_NONE: u32 = 0x0000;
+pub const VIRTIO_GPU_RESOURCE_GUEST_MASK: u32 = 0x000f;
+pub const VIRTIO_GPU_RESOURCE_GUEST_SYSTEM: u32 = 0x0001;
 
-/* guest caching types (not upstreamed) */
-pub const VIRTIO_GPU_UNDEFINED_CACHING: u32 = 0;
-pub const VIRTIO_GPU_CACHED: u32 = 1;
-pub const VIRTIO_GPU_WRITE_COMBINE: u32 = 2;
-pub const VIRTIO_GPU_UNCACHED: u32 = 3;
+pub const VIRTIO_GPU_RESOURCE_HOST_NONE: u32 = 0x0000;
+pub const VIRTIO_GPU_RESOURCE_HOST_MASK: u32 = 0x00f0;
+pub const VIRTIO_GPU_RESOURCE_HOST: u32 = 0x0010;
+pub const VIRTIO_GPU_RESOURCE_HOST_FROM_GUEST: u32 = 0x0020;
+
+pub const VIRTIO_GPU_RESOURCE_USE_NONE: u32 = 0x0000;
+pub const VIRTIO_GPU_RESOURCE_USE_MASK: u32 = 0x0f00;
+pub const VIRTIO_GPU_RESOURCE_USE_MAPPABLE: u32 = 0x0100;
+pub const VIRTIO_GPU_RESOURCE_USE_SHAREABLE: u32 = 0x0200;
+pub const VIRTIO_GPU_RESOURCE_USE_CROSS_DEVICE: u32 = 0x0400;
 
-/* Limits on virtio-gpu stream (not upstreamed) */
-pub const VIRTIO_GPU_MAX_BLOB_ARGUMENT_SIZE: u32 = 4096;
 /* This matches the limit in udmabuf.c */
 pub const VIRTIO_GPU_MAX_IOVEC_ENTRIES: u32 = 1024;
 
@@ -112,8 +114,8 @@ pub fn virtio_gpu_cmd_str(cmd: u32) -> &'static str {
         VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => "VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D",
         VIRTIO_GPU_CMD_SUBMIT_3D => "VIRTIO_GPU_CMD_SUBMIT_3D",
         VIRTIO_GPU_CMD_RESOURCE_CREATE_V2 => "VIRTIO_GPU_CMD_RESOURCE_CREATE_V2",
-        VIRTIO_GPU_CMD_RESOURCE_CREATE_V2_UNREF => "VIRTIO_GPU_CMD_RESOURCE_CREATE_V2_UNREF",
-        VIRTIO_GPU_CMD_ALLOCATION_METADATA => "VIRTIO_GPU_CMD_ALLOCATION_METADATA",
+        VIRTIO_GPU_CMD_RESOURCE_MAP => "VIRTIO_GPU_RESOURCE_MAP",
+        VIRTIO_GPU_CMD_RESOURCE_UNMAP => "VIRTIO_GPU_RESOURCE_UNMAP",
         VIRTIO_GPU_CMD_UPDATE_CURSOR => "VIRTIO_GPU_CMD_UPDATE_CURSOR",
         VIRTIO_GPU_CMD_MOVE_CURSOR => "VIRTIO_GPU_CMD_MOVE_CURSOR",
         VIRTIO_GPU_RESP_OK_NODATA => "VIRTIO_GPU_RESP_OK_NODATA",
@@ -497,52 +499,38 @@ unsafe impl DataInit for virtio_gpu_config {}
 
 #[derive(Copy, Clone, Debug, Default)]
 #[repr(C)]
-pub struct virtio_gpu_allocation_metadata {
+pub struct virtio_gpu_resource_create_v2 {
     pub hdr: virtio_gpu_ctrl_hdr,
-    pub request_id: Le32,
+    pub resource_id: Le32,
+    pub flags: Le32,
+    pub size: Le64,
+    pub memory_id: Le64,
+    pub nr_entries: Le32,
     pub padding: Le32,
-    pub request_size: Le32,
-    pub response_size: Le32,
 }
 
-unsafe impl DataInit for virtio_gpu_allocation_metadata {}
-
-/* VIRTIO_GPU_RESP_OK_ALLOCATION_METADATA */
-#[derive(Copy, Clone, Debug, Default)]
-#[repr(C)]
-pub struct virtio_gpu_resp_allocation_metadata {
-    pub hdr: virtio_gpu_ctrl_hdr,
-    pub request_id: Le32,
-    pub response_size: Le32,
-}
-
-unsafe impl DataInit for virtio_gpu_resp_allocation_metadata {}
+unsafe impl DataInit for virtio_gpu_resource_create_v2 {}
 
 #[derive(Copy, Clone, Debug, Default)]
 #[repr(C)]
-pub struct virtio_gpu_resource_create_v2 {
+pub struct virtio_gpu_resource_map {
     pub hdr: virtio_gpu_ctrl_hdr,
     pub resource_id: Le32,
-    pub guest_memory_type: Le32,
-    pub guest_caching_type: Le32,
-    pub padding: Le32,
-    pub size: Le64,
-    pub pci_addr: Le64,
-    pub args_size: Le32,
-    pub nr_entries: Le32,
+    pub map_flags: Le32,
+    pub offset: Le64,
 }
 
-unsafe impl DataInit for virtio_gpu_resource_create_v2 {}
+unsafe impl DataInit for virtio_gpu_resource_map {}
 
 #[derive(Copy, Clone, Debug, Default)]
 #[repr(C)]
-pub struct virtio_gpu_resource_v2_unref {
+pub struct virtio_gpu_resource_unmap {
     pub hdr: virtio_gpu_ctrl_hdr,
     pub resource_id: Le32,
     pub padding: Le32,
 }
 
-unsafe impl DataInit for virtio_gpu_resource_v2_unref {}
+unsafe impl DataInit for virtio_gpu_resource_unmap {}
 
 /* simple formats for fbcon/X use */
 pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
@@ -576,8 +564,8 @@ pub enum GpuCommand {
     TransferFromHost3d(virtio_gpu_transfer_host_3d),
     CmdSubmit3d(virtio_gpu_cmd_submit),
     ResourceCreateV2(virtio_gpu_resource_create_v2),
-    ResourceV2Unref(virtio_gpu_resource_v2_unref),
-    AllocationMetadata(virtio_gpu_allocation_metadata),
+    ResourceMap(virtio_gpu_resource_map),
+    ResourceUnmap(virtio_gpu_resource_unmap),
     UpdateCursor(virtio_gpu_update_cursor),
     MoveCursor(virtio_gpu_update_cursor),
 }
@@ -645,8 +633,8 @@ impl fmt::Debug for GpuCommand {
             TransferFromHost3d(_info) => f.debug_struct("TransferFromHost3d").finish(),
             CmdSubmit3d(_info) => f.debug_struct("CmdSubmit3d").finish(),
             ResourceCreateV2(_info) => f.debug_struct("ResourceCreateV2").finish(),
-            ResourceV2Unref(_info) => f.debug_struct("ResourceV2Unref").finish(),
-            AllocationMetadata(_info) => f.debug_struct("AllocationMetadata").finish(),
+            ResourceMap(_info) => f.debug_struct("ResourceMap").finish(),
+            ResourceUnmap(_info) => f.debug_struct("ResourceUnmap").finish(),
             UpdateCursor(_info) => f.debug_struct("UpdateCursor").finish(),
             MoveCursor(_info) => f.debug_struct("MoveCursor").finish(),
         }
@@ -678,8 +666,8 @@ impl GpuCommand {
             VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => TransferFromHost3d(cmd.read_obj()?),
             VIRTIO_GPU_CMD_SUBMIT_3D => CmdSubmit3d(cmd.read_obj()?),
             VIRTIO_GPU_CMD_RESOURCE_CREATE_V2 => ResourceCreateV2(cmd.read_obj()?),
-            VIRTIO_GPU_CMD_RESOURCE_CREATE_V2_UNREF => ResourceV2Unref(cmd.read_obj()?),
-            VIRTIO_GPU_CMD_ALLOCATION_METADATA => AllocationMetadata(cmd.read_obj()?),
+            VIRTIO_GPU_CMD_RESOURCE_MAP => ResourceMap(cmd.read_obj()?),
+            VIRTIO_GPU_CMD_RESOURCE_UNMAP => ResourceUnmap(cmd.read_obj()?),
             VIRTIO_GPU_CMD_UPDATE_CURSOR => UpdateCursor(cmd.read_obj()?),
             VIRTIO_GPU_CMD_MOVE_CURSOR => MoveCursor(cmd.read_obj()?),
             _ => return Err(GpuCommandDecodeError::InvalidType(hdr.type_.into())),
@@ -709,8 +697,8 @@ impl GpuCommand {
             TransferFromHost3d(info) => &info.hdr,
             CmdSubmit3d(info) => &info.hdr,
             ResourceCreateV2(info) => &info.hdr,
-            ResourceV2Unref(info) => &info.hdr,
-            AllocationMetadata(info) => &info.hdr,
+            ResourceMap(info) => &info.hdr,
+            ResourceUnmap(info) => &info.hdr,
             UpdateCursor(info) => &info.hdr,
             MoveCursor(info) => &info.hdr,
         }
@@ -723,12 +711,6 @@ pub struct GpuResponsePlaneInfo {
     pub offset: u32,
 }
 
-#[derive(Default, Debug, PartialEq)]
-pub struct AllocationMetadataResponse {
-    pub request_id: u32,
-    pub response: Vec<u8>,
-}
-
 /// A response to a `GpuCommand`. These correspond to `VIRTIO_GPU_RESP_*`.
 #[derive(Debug, PartialEq)]
 pub enum GpuResponse {
@@ -744,9 +726,6 @@ pub enum GpuResponse {
         format_modifier: u64,
         plane_info: Vec<GpuResponsePlaneInfo>,
     },
-    OkAllocationMetadata {
-        res_info: AllocationMetadataResponse,
-    },
     ErrUnspec,
     ErrOutOfMemory,
     ErrInvalidScanoutId,
@@ -880,17 +859,6 @@ impl GpuResponse {
                     size_of_val(&hdr)
                 }
             }
-            GpuResponse::OkAllocationMetadata { ref res_info } => {
-                let resp_info = virtio_gpu_resp_allocation_metadata {
-                    hdr,
-                    request_id: Le32::from(res_info.request_id),
-                    response_size: Le32::from(res_info.response.len() as u32),
-                };
-
-                resp.write_obj(resp_info)?;
-                resp.write_all(&res_info.response)?;
-                size_of_val(&resp_info) + res_info.response.len()
-            }
             _ => {
                 resp.write_obj(hdr)?;
                 size_of_val(&hdr)
@@ -907,7 +875,6 @@ impl GpuResponse {
             GpuResponse::OkCapsetInfo { .. } => VIRTIO_GPU_RESP_OK_CAPSET_INFO,
             GpuResponse::OkCapset(_) => VIRTIO_GPU_RESP_OK_CAPSET,
             GpuResponse::OkResourcePlaneInfo { .. } => VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO,
-            GpuResponse::OkAllocationMetadata { .. } => VIRTIO_GPU_RESP_OK_ALLOCATION_METADATA,
             GpuResponse::ErrUnspec => VIRTIO_GPU_RESP_ERR_UNSPEC,
             GpuResponse::ErrOutOfMemory => VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
             GpuResponse::ErrInvalidScanoutId => VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
@@ -925,7 +892,6 @@ impl GpuResponse {
             GpuResponse::OkCapsetInfo { .. } => true,
             GpuResponse::OkCapset(_) => true,
             GpuResponse::OkResourcePlaneInfo { .. } => true,
-            GpuResponse::OkAllocationMetadata { .. } => true,
             _ => false,
         }
     }
diff --git a/devices/src/virtio/gpu/virtio_3d_backend.rs b/devices/src/virtio/gpu/virtio_3d_backend.rs
index 7ae044c..3ede8a6 100644
--- a/devices/src/virtio/gpu/virtio_3d_backend.rs
+++ b/devices/src/virtio/gpu/virtio_3d_backend.rs
@@ -26,13 +26,13 @@ use gpu_renderer::{
 };
 
 use super::protocol::{
-    AllocationMetadataResponse, GpuResponse, GpuResponsePlaneInfo, VIRTIO_GPU_CAPSET3,
-    VIRTIO_GPU_CAPSET_VIRGL, VIRTIO_GPU_CAPSET_VIRGL2, VIRTIO_GPU_MEMORY_HOST_COHERENT,
+    GpuResponse, GpuResponsePlaneInfo, VIRTIO_GPU_CAPSET3, VIRTIO_GPU_CAPSET_VIRGL,
+    VIRTIO_GPU_CAPSET_VIRGL2, VIRTIO_GPU_RESOURCE_USE_MAPPABLE, VIRTIO_GPU_RESOURCE_USE_MASK,
 };
 pub use crate::virtio::gpu::virtio_backend::{VirtioBackend, VirtioResource};
 use crate::virtio::gpu::{
-    Backend, DisplayBackend, VIRTIO_F_VERSION_1, VIRTIO_GPU_F_HOST_COHERENT, VIRTIO_GPU_F_MEMORY,
-    VIRTIO_GPU_F_VIRGL,
+    Backend, DisplayBackend, VIRTIO_F_VERSION_1, VIRTIO_GPU_F_HOST_VISIBLE,
+    VIRTIO_GPU_F_RESOURCE_UUID, VIRTIO_GPU_F_RESOURCE_V2, VIRTIO_GPU_F_VIRGL, VIRTIO_GPU_F_VULKAN,
 };
 use crate::virtio::resource_bridge::{PlaneInfo, ResourceInfo, ResourceResponse};
 
@@ -44,6 +44,8 @@ struct Virtio3DResource {
     gpu_resource: GpuRendererResource,
     display_import: Option<(Rc<RefCell<GpuDisplay>>, u32)>,
     kvm_slot: Option<u32>,
+    size: u64,
+    flags: u32,
 }
 
 impl Virtio3DResource {
@@ -54,27 +56,38 @@ impl Virtio3DResource {
             gpu_resource,
             display_import: None,
             kvm_slot: None,
+            flags: 0,
+            // The size of the host resource isn't really zero, but it's undefined by
+            // virtio_gpu_resource_create_3d
+            size: 0,
         }
     }
 
     pub fn v2_new(
         width: u32,
         height: u32,
-        kvm_slot: u32,
         gpu_resource: GpuRendererResource,
+        flags: u32,
+        size: u64,
     ) -> Virtio3DResource {
         Virtio3DResource {
             width,
             height,
             gpu_resource,
             display_import: None,
-            kvm_slot: Some(kvm_slot),
+            kvm_slot: None,
+            flags,
+            size,
         }
     }
 
     fn as_mut(&mut self) -> &mut dyn VirtioResource {
         self
     }
+
+    fn use_flags(&self) -> u32 {
+        self.flags & VIRTIO_GPU_RESOURCE_USE_MASK
+    }
 }
 
 impl VirtioResource for Virtio3DResource {
@@ -225,8 +238,10 @@ impl Backend for Virtio3DBackend {
     fn features() -> u64 {
         1 << VIRTIO_GPU_F_VIRGL
             | 1 << VIRTIO_F_VERSION_1
-            | 1 << VIRTIO_GPU_F_MEMORY
-            | 1 << VIRTIO_GPU_F_HOST_COHERENT
+            | 1 << VIRTIO_GPU_F_RESOURCE_UUID
+            | 1 << VIRTIO_GPU_F_RESOURCE_V2
+            | 1 << VIRTIO_GPU_F_HOST_VISIBLE
+            | 1 << VIRTIO_GPU_F_VULKAN
     }
 
     /// Returns the underlying Backend.
@@ -757,51 +772,26 @@ impl Backend for Virtio3DBackend {
         }
     }
 
-    fn allocation_metadata(
-        &mut self,
-        request_id: u32,
-        request: Vec<u8>,
-        mut response: Vec<u8>,
-    ) -> GpuResponse {
-        let res = self.renderer.allocation_metadata(&request, &mut response);
-
-        match res {
-            Ok(_) => {
-                let res_info = AllocationMetadataResponse {
-                    request_id,
-                    response,
-                };
-
-                GpuResponse::OkAllocationMetadata { res_info }
-            }
-            Err(_) => {
-                error!("failed to get metadata");
-                GpuResponse::ErrUnspec
-            }
-        }
-    }
-
     fn resource_create_v2(
         &mut self,
         resource_id: u32,
-        guest_memory_type: u32,
-        guest_caching_type: u32,
+        ctx_id: u32,
+        flags: u32,
         size: u64,
-        pci_addr: u64,
-        mem: &GuestMemory,
+        memory_id: u64,
         vecs: Vec<(GuestAddress, usize)>,
-        args: Vec<u8>,
+        mem: &GuestMemory,
     ) -> GpuResponse {
         match self.resources.entry(resource_id) {
             Entry::Vacant(entry) => {
                 let resource = match self.renderer.resource_create_v2(
                     resource_id,
-                    guest_memory_type,
-                    guest_caching_type,
+                    ctx_id,
+                    flags,
                     size,
-                    mem,
+                    memory_id,
                     &vecs,
-                    &args,
+                    mem,
                 ) {
                     Ok(resource) => resource,
                     Err(e) => {
@@ -810,102 +800,119 @@ impl Backend for Virtio3DBackend {
                     }
                 };
 
-                match guest_memory_type {
-                    VIRTIO_GPU_MEMORY_HOST_COHERENT => {
-                        let dma_buf_fd = match resource.export() {
-                            Ok(export) => export.1,
-                            Err(e) => {
-                                error!("failed to export plane fd: {}", e);
-                                return GpuResponse::ErrUnspec;
-                            }
-                        };
-
-                        let request = VmMemoryRequest::RegisterMemoryAtAddress(
-                            self.pci_bar,
-                            MaybeOwnedFd::Borrowed(dma_buf_fd.as_raw_fd()),
-                            size as usize,
-                            pci_addr,
-                        );
-
-                        match self.gpu_device_socket.send(&request) {
-                            Ok(_resq) => match self.gpu_device_socket.recv() {
-                                Ok(response) => match response {
-                                    VmMemoryResponse::RegisterMemory { pfn: _, slot } => {
-                                        entry.insert(Virtio3DResource::v2_new(
-                                            self.base.display_width,
-                                            self.base.display_height,
-                                            slot,
-                                            resource,
-                                        ));
-                                        GpuResponse::OkNoData
-                                    }
-                                    VmMemoryResponse::Err(e) => {
-                                        error!("received an error: {}", e);
-                                        GpuResponse::ErrUnspec
-                                    }
-                                    _ => {
-                                        error!("recieved an unexpected response");
-                                        GpuResponse::ErrUnspec
-                                    }
-                                },
-                                Err(e) => {
-                                    error!("failed to receive data: {}", e);
-                                    GpuResponse::ErrUnspec
-                                }
-                            },
-                            Err(e) => {
-                                error!("failed to send request: {}", e);
-                                GpuResponse::ErrUnspec
-                            }
-                        }
-                    }
-                    _ => {
-                        entry.insert(Virtio3DResource::new(
-                            self.base.display_width,
-                            self.base.display_height,
-                            resource,
-                        ));
+                entry.insert(Virtio3DResource::v2_new(
+                    self.base.display_width,
+                    self.base.display_height,
+                    resource,
+                    flags,
+                    size,
+                ));
 
-                        GpuResponse::OkNoData
-                    }
-                }
+                GpuResponse::OkNoData
             }
             Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
         }
     }
 
-    fn resource_v2_unref(&mut self, resource_id: u32) -> GpuResponse {
-        match self.resources.remove(&resource_id) {
-            Some(entry) => match entry.kvm_slot {
-                Some(kvm_slot) => {
-                    let request = VmMemoryRequest::UnregisterMemory(kvm_slot);
-                    match self.gpu_device_socket.send(&request) {
-                        Ok(_resq) => match self.gpu_device_socket.recv() {
-                            Ok(response) => match response {
-                                VmMemoryResponse::Ok => GpuResponse::OkNoData,
-                                VmMemoryResponse::Err(e) => {
-                                    error!("received an error: {}", e);
-                                    GpuResponse::ErrUnspec
-                                }
-                                _ => {
-                                    error!("recieved an unexpected response");
-                                    GpuResponse::ErrUnspec
-                                }
-                            },
-                            Err(e) => {
-                                error!("failed to receive data: {}", e);
-                                GpuResponse::ErrUnspec
-                            }
-                        },
-                        Err(e) => {
-                            error!("failed to send request: {}", e);
-                            GpuResponse::ErrUnspec
-                        }
-                    }
-                }
-                None => GpuResponse::OkNoData,
-            },
-            None => GpuResponse::ErrInvalidResourceId,
+    fn resource_map(&mut self, resource_id: u32, offset: u64) -> GpuResponse {
+        let resource = match self.resources.get_mut(&resource_id) {
+            Some(r) => r,
+            None => return GpuResponse::ErrInvalidResourceId,
+        };
+
+        if resource.use_flags() & VIRTIO_GPU_RESOURCE_USE_MAPPABLE == 0 {
+            error!("resource not mappable");
+            return GpuResponse::ErrUnspec;
+        }
+
+        let dma_buf_fd = match resource.gpu_resource.export() {
+            Ok(export) => export.1,
+            Err(e) => {
+                error!("failed to export plane fd: {}", e);
+                return GpuResponse::ErrUnspec;
+            }
+        };
+
+        let request = VmMemoryRequest::RegisterFdAtPciBarOffset(
+            self.pci_bar,
+            MaybeOwnedFd::Borrowed(dma_buf_fd.as_raw_fd()),
+            resource.size as usize,
+            offset,
+        );
+
+        match self.gpu_device_socket.send(&request) {
+            Ok(_) => (),
+            Err(e) => {
+                error!("failed to send request: {}", e);
+                return GpuResponse::ErrUnspec;
+            }
+        }
+
+        let response = match self.gpu_device_socket.recv() {
+            Ok(response) => response,
+            Err(e) => {
+                error!("failed to receive data: {}", e);
+                return GpuResponse::ErrUnspec;
+            }
+        };
+
+        match response {
+            VmMemoryResponse::RegisterMemory { pfn: _, slot } => {
+                resource.kvm_slot = Some(slot);
+                GpuResponse::OkNoData
+            }
+            VmMemoryResponse::Err(e) => {
+                error!("received an error: {}", e);
+                GpuResponse::ErrUnspec
+            }
+            _ => {
+                error!("recieved an unexpected response");
+                GpuResponse::ErrUnspec
+            }
+        }
+    }
+
+    fn resource_unmap(&mut self, resource_id: u32) -> GpuResponse {
+        let resource = match self.resources.get_mut(&resource_id) {
+            Some(r) => r,
+            None => return GpuResponse::ErrInvalidResourceId,
+        };
+
+        let kvm_slot = match resource.kvm_slot {
+            Some(kvm_slot) => kvm_slot,
+            None => return GpuResponse::ErrUnspec,
+        };
+
+        let request = VmMemoryRequest::UnregisterMemory(kvm_slot);
+        match self.gpu_device_socket.send(&request) {
+            Ok(_) => (),
+            Err(e) => {
+                error!("failed to send request: {}", e);
+                return GpuResponse::ErrUnspec;
+            }
+        }
+
+        let response = match self.gpu_device_socket.recv() {
+            Ok(response) => response,
+            Err(e) => {
+                error!("failed to receive data: {}", e);
+                return GpuResponse::ErrUnspec;
+            }
+        };
+
+        match response {
+            VmMemoryResponse::Ok => {
+                resource.kvm_slot = None;
+                GpuResponse::OkNoData
+            }
+            VmMemoryResponse::Err(e) => {
+                error!("received an error: {}", e);
+                GpuResponse::ErrUnspec
+            }
+            _ => {
+                error!("recieved an unexpected response");
+                GpuResponse::ErrUnspec
+            }
         }
     }
 }
diff --git a/devices/src/virtio/gpu/virtio_gfxstream_backend.rs b/devices/src/virtio/gpu/virtio_gfxstream_backend.rs
index aa02e15..2a49da8 100644
--- a/devices/src/virtio/gpu/virtio_gfxstream_backend.rs
+++ b/devices/src/virtio/gpu/virtio_gfxstream_backend.rs
@@ -10,7 +10,6 @@
 use std::cell::RefCell;
 use std::collections::btree_map::Entry;
 use std::collections::BTreeMap as Map;
-use std::fs::File;
 use std::mem::transmute;
 use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void};
 use std::panic;
@@ -27,6 +26,7 @@ use vm_control::VmMemoryControlRequestSocket;
 use super::protocol::GpuResponse;
 pub use super::virtio_backend::{VirtioBackend, VirtioResource};
 use crate::virtio::gpu::{Backend, DisplayBackend, VIRTIO_F_VERSION_1, VIRTIO_GPU_F_VIRGL};
+use crate::virtio::resource_bridge::ResourceResponse;
 
 // C definitions related to gfxstream
 // In gfxstream, only write_fence is used
@@ -340,8 +340,8 @@ impl Backend for VirtioGfxStreamBackend {
     }
 
     /// If supported, export the resource with the given id to a file.
-    fn export_resource(&mut self, _id: u32) -> Option<File> {
-        None
+    fn export_resource(&mut self, _id: u32) -> ResourceResponse {
+        ResourceResponse::Invalid
     }
 
     /// Creates a fence with the given id that can be used to determine when the previous command
diff --git a/devices/src/virtio/interrupt.rs b/devices/src/virtio/interrupt.rs
index f808d84..91a3942 100644
--- a/devices/src/virtio/interrupt.rs
+++ b/devices/src/virtio/interrupt.rs
@@ -13,7 +13,7 @@ pub struct Interrupt {
     interrupt_status: Arc<AtomicUsize>,
     interrupt_evt: EventFd,
     interrupt_resample_evt: EventFd,
-    msix_config: Option<Arc<Mutex<MsixConfig>>>,
+    pub msix_config: Option<Arc<Mutex<MsixConfig>>>,
     config_msix_vector: u16,
 }
 
diff --git a/devices/src/virtio/mod.rs b/devices/src/virtio/mod.rs
index 7716fe0..4d5d2cb 100644
--- a/devices/src/virtio/mod.rs
+++ b/devices/src/virtio/mod.rs
@@ -6,6 +6,7 @@
 
 mod balloon;
 mod block;
+mod console;
 mod descriptor_utils;
 mod input;
 mod interrupt;
@@ -29,6 +30,7 @@ pub mod vhost;
 
 pub use self::balloon::*;
 pub use self::block::*;
+pub use self::console::*;
 pub use self::descriptor_utils::Error as DescriptorError;
 pub use self::descriptor_utils::*;
 #[cfg(feature = "gpu")]
diff --git a/devices/src/virtio/vhost/control_socket.rs b/devices/src/virtio/vhost/control_socket.rs
new file mode 100644
index 0000000..a1ccfaf
--- /dev/null
+++ b/devices/src/virtio/vhost/control_socket.rs
@@ -0,0 +1,36 @@
+// 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 msg_socket::{MsgOnSocket, MsgSocket};
+use sys_util::Error as SysError;
+
+#[derive(MsgOnSocket, Debug)]
+pub enum VhostDevRequest {
+    /// Mask or unmask all the MSI entries for a Virtio Vhost device.
+    MsixChanged,
+    /// Mask or unmask a MSI entry for a Virtio Vhost device.
+    MsixEntryChanged(usize),
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum VhostDevResponse {
+    Ok,
+    Err(SysError),
+}
+
+pub type VhostDevRequestSocket = MsgSocket<VhostDevRequest, VhostDevResponse>;
+pub type VhostDevResponseSocket = MsgSocket<VhostDevResponse, VhostDevRequest>;
+
+/// Create control socket pair. This pair is used to communicate with the
+/// virtio device process.
+/// Mainly between the virtio and activate thread.
+pub fn create_control_sockets() -> (
+    Option<VhostDevRequestSocket>,
+    Option<VhostDevResponseSocket>,
+) {
+    match msg_socket::pair::<VhostDevRequest, VhostDevResponse>() {
+        Ok((request, response)) => (Some(request), Some(response)),
+        _ => (None, None),
+    }
+}
diff --git a/devices/src/virtio/vhost/mod.rs b/devices/src/virtio/vhost/mod.rs
index 66c62d0..86ed81e 100644
--- a/devices/src/virtio/vhost/mod.rs
+++ b/devices/src/virtio/vhost/mod.rs
@@ -12,10 +12,12 @@ use remain::sorted;
 use sys_util::Error as SysError;
 use vhost::Error as VhostError;
 
+mod control_socket;
 mod net;
 mod vsock;
 mod worker;
 
+pub use self::control_socket::*;
 pub use self::net::Net;
 pub use self::vsock::Vsock;
 
diff --git a/devices/src/virtio/vhost/net.rs b/devices/src/virtio/vhost/net.rs
index ff72970..542423a 100644
--- a/devices/src/virtio/vhost/net.rs
+++ b/devices/src/virtio/vhost/net.rs
@@ -14,9 +14,12 @@ use sys_util::{error, warn, EventFd, GuestMemory};
 use vhost::NetT as VhostNetT;
 use virtio_sys::virtio_net;
 
+use super::control_socket::*;
 use super::worker::Worker;
 use super::{Error, Result};
+use crate::pci::MsixStatus;
 use crate::virtio::{Interrupt, Queue, VirtioDevice, TYPE_NET};
+use msg_socket::{MsgReceiver, MsgSender};
 
 const QUEUE_SIZE: u16 = 256;
 const NUM_QUEUES: usize = 2;
@@ -31,6 +34,8 @@ pub struct Net<T: TapT, U: VhostNetT<T>> {
     vhost_interrupt: Option<Vec<EventFd>>,
     avail_features: u64,
     acked_features: u64,
+    request_socket: Option<VhostDevRequestSocket>,
+    response_socket: Option<VhostDevResponseSocket>,
 }
 
 impl<T, U> Net<T, U>
@@ -85,6 +90,8 @@ where
             vhost_interrupt.push(EventFd::new().map_err(Error::VhostIrqCreate)?);
         }
 
+        let (request_socket, response_socket) = create_control_sockets();
+
         Ok(Net {
             workers_kill_evt: Some(kill_evt.try_clone().map_err(Error::CloneKillEventFd)?),
             kill_evt,
@@ -94,6 +101,8 @@ where
             vhost_interrupt: Some(vhost_interrupt),
             avail_features,
             acked_features: 0u64,
+            request_socket,
+            response_socket,
         })
     }
 }
@@ -143,6 +152,14 @@ where
         }
         keep_fds.push(self.kill_evt.as_raw_fd());
 
+        if let Some(request_socket) = &self.request_socket {
+            keep_fds.push(request_socket.as_raw_fd());
+        }
+
+        if let Some(response_socket) = &self.response_socket {
+            keep_fds.push(response_socket.as_raw_fd());
+        }
+
         keep_fds
     }
 
@@ -189,6 +206,11 @@ where
                 if let Some(vhost_interrupt) = self.vhost_interrupt.take() {
                     if let Some(kill_evt) = self.workers_kill_evt.take() {
                         let acked_features = self.acked_features;
+                        let socket = if self.response_socket.is_some() {
+                            self.response_socket.take()
+                        } else {
+                            None
+                        };
                         let worker_result = thread::Builder::new()
                             .name("vhost_net".to_string())
                             .spawn(move || {
@@ -199,6 +221,7 @@ where
                                     interrupt,
                                     acked_features,
                                     kill_evt,
+                                    socket,
                                 );
                                 let activate_vqs = |handle: &U| -> Result<()> {
                                     for idx in 0..NUM_QUEUES {
@@ -251,6 +274,48 @@ where
         }
     }
 
+    fn control_notify(&self, behavior: MsixStatus) {
+        if self.worker_thread.is_none() || self.request_socket.is_none() {
+            return;
+        }
+        if let Some(socket) = &self.request_socket {
+            match behavior {
+                MsixStatus::EntryChanged(index) => {
+                    if let Err(e) = socket.send(&VhostDevRequest::MsixEntryChanged(index)) {
+                        error!(
+                            "{} failed to send VhostMsixEntryChanged request for entry {}: {:?}",
+                            self.debug_label(),
+                            index,
+                            e
+                        );
+                        return;
+                    }
+                    if let Err(e) = socket.recv() {
+                        error!("{} failed to receive VhostMsixEntryChanged response for entry {}: {:?}", self.debug_label(), index, e);
+                    }
+                }
+                MsixStatus::Changed => {
+                    if let Err(e) = socket.send(&VhostDevRequest::MsixChanged) {
+                        error!(
+                            "{} failed to send VhostMsixChanged request: {:?}",
+                            self.debug_label(),
+                            e
+                        );
+                        return;
+                    }
+                    if let Err(e) = socket.recv() {
+                        error!(
+                            "{} failed to receive VhostMsixChanged response {:?}",
+                            self.debug_label(),
+                            e
+                        );
+                    }
+                }
+                _ => {}
+            }
+        }
+    }
+
     fn reset(&mut self) -> bool {
         // Only kill the child if it claimed its eventfd.
         if self.workers_kill_evt.is_none() && self.kill_evt.write(1).is_err() {
@@ -269,6 +334,7 @@ where
                     self.tap = Some(tap);
                     self.vhost_interrupt = Some(worker.vhost_interrupt);
                     self.workers_kill_evt = Some(worker.kill_evt);
+                    self.response_socket = worker.response_socket;
                     return true;
                 }
             }
diff --git a/devices/src/virtio/vhost/vsock.rs b/devices/src/virtio/vhost/vsock.rs
index 03826ff..390c857 100644
--- a/devices/src/virtio/vhost/vsock.rs
+++ b/devices/src/virtio/vhost/vsock.rs
@@ -169,6 +169,7 @@ impl VirtioDevice for Vsock {
                                 interrupt,
                                 acked_features,
                                 kill_evt,
+                                None,
                             );
                             let activate_vqs = |handle: &VhostVsockHandle| -> Result<()> {
                                 handle.set_cid(cid).map_err(Error::VhostVsockSetCid)?;
diff --git a/devices/src/virtio/vhost/worker.rs b/devices/src/virtio/vhost/worker.rs
index 1eff01f..ca02a63 100644
--- a/devices/src/virtio/vhost/worker.rs
+++ b/devices/src/virtio/vhost/worker.rs
@@ -4,16 +4,16 @@
 
 use std::os::raw::c_ulonglong;
 
-use sys_util::{EventFd, PollContext, PollToken};
+use sys_util::{error, Error as SysError, EventFd, PollContext, PollToken};
 use vhost::Vhost;
 
+use super::control_socket::{VhostDevRequest, VhostDevResponse, VhostDevResponseSocket};
 use super::{Error, Result};
 use crate::virtio::{Interrupt, Queue};
+use libc::EIO;
+use msg_socket::{MsgReceiver, MsgSender};
 
-/// Worker that takes care of running the vhost device.  This mainly involves forwarding interrupts
-/// from the vhost driver to the guest VM because crosvm only supports the virtio-mmio transport,
-/// which requires a bit to be set in the interrupt status register before triggering the interrupt
-/// and the vhost driver doesn't do this for us.
+/// Worker that takes care of running the vhost device.
 pub struct Worker<T: Vhost> {
     interrupt: Interrupt,
     queues: Vec<Queue>,
@@ -21,6 +21,7 @@ pub struct Worker<T: Vhost> {
     pub vhost_interrupt: Vec<EventFd>,
     acked_features: u64,
     pub kill_evt: EventFd,
+    pub response_socket: Option<VhostDevResponseSocket>,
 }
 
 impl<T: Vhost> Worker<T> {
@@ -31,6 +32,7 @@ impl<T: Vhost> Worker<T> {
         interrupt: Interrupt,
         acked_features: u64,
         kill_evt: EventFd,
+        response_socket: Option<VhostDevResponseSocket>,
     ) -> Worker<T> {
         Worker {
             interrupt,
@@ -39,6 +41,7 @@ impl<T: Vhost> Worker<T> {
             vhost_interrupt,
             acked_features,
             kill_evt,
+            response_socket,
         }
     }
 
@@ -87,9 +90,7 @@ impl<T: Vhost> Worker<T> {
             self.vhost_handle
                 .set_vring_base(queue_index, 0)
                 .map_err(Error::VhostSetVringBase)?;
-            self.vhost_handle
-                .set_vring_call(queue_index, &self.vhost_interrupt[queue_index])
-                .map_err(Error::VhostSetVringCall)?;
+            self.set_vring_call_for_entry(queue_index, queue.vector as usize)?;
             self.vhost_handle
                 .set_vring_kick(queue_index, &queue_evts[queue_index])
                 .map_err(Error::VhostSetVringKick)?;
@@ -102,6 +103,7 @@ impl<T: Vhost> Worker<T> {
             VhostIrqi { index: usize },
             InterruptResample,
             Kill,
+            ControlNotify,
         }
 
         let poll_ctx: PollContext<Token> = PollContext::build_with(&[
@@ -115,6 +117,11 @@ impl<T: Vhost> Worker<T> {
                 .add(vhost_int, Token::VhostIrqi { index })
                 .map_err(Error::CreatePollContext)?;
         }
+        if let Some(socket) = &self.response_socket {
+            poll_ctx
+                .add(socket, Token::ControlNotify)
+                .map_err(Error::CreatePollContext)?;
+        }
 
         'poll: loop {
             let events = poll_ctx.wait().map_err(Error::PollError)?;
@@ -134,10 +141,122 @@ impl<T: Vhost> Worker<T> {
                         let _ = self.kill_evt.read();
                         break 'poll;
                     }
+                    Token::ControlNotify => {
+                        if let Some(socket) = &self.response_socket {
+                            match socket.recv() {
+                                Ok(VhostDevRequest::MsixEntryChanged(index)) => {
+                                    let mut qindex = 0;
+                                    for (queue_index, queue) in self.queues.iter().enumerate() {
+                                        if queue.vector == index as u16 {
+                                            qindex = queue_index;
+                                            break;
+                                        }
+                                    }
+                                    let response =
+                                        match self.set_vring_call_for_entry(qindex, index) {
+                                            Ok(()) => VhostDevResponse::Ok,
+                                            Err(e) => {
+                                                error!(
+                                                "Set vring call failed for masked entry {}: {:?}",
+                                                index, e
+                                            );
+                                                VhostDevResponse::Err(SysError::new(EIO))
+                                            }
+                                        };
+                                    if let Err(e) = socket.send(&response) {
+                                        error!("Vhost failed to send VhostMsixEntryMasked Response for entry {}: {:?}", index, e);
+                                    }
+                                }
+                                Ok(VhostDevRequest::MsixChanged) => {
+                                    let response = match self.set_vring_calls() {
+                                        Ok(()) => VhostDevResponse::Ok,
+                                        Err(e) => {
+                                            error!("Set vring calls failed: {:?}", e);
+                                            VhostDevResponse::Err(SysError::new(EIO))
+                                        }
+                                    };
+                                    if let Err(e) = socket.send(&response) {
+                                        error!(
+                                            "Vhost failed to send VhostMsixMasked Response: {:?}",
+                                            e
+                                        );
+                                    }
+                                }
+                                Err(e) => {
+                                    error!("Vhost failed to receive Control request: {:?}", e);
+                                }
+                            }
+                        }
+                    }
                 }
             }
         }
         cleanup_vqs(&self.vhost_handle)?;
         Ok(())
     }
+
+    fn set_vring_call_for_entry(&self, queue_index: usize, vector: usize) -> Result<()> {
+        // No response_socket means it doesn't have any control related
+        // with the msix. Due to this, cannot use the direct irq fd but
+        // should fall back to indirect irq fd.
+        if self.response_socket.is_some() {
+            if let Some(msix_config) = &self.interrupt.msix_config {
+                let msix_config = msix_config.lock();
+                let msix_masked = msix_config.masked();
+                if msix_masked {
+                    return Ok(());
+                }
+                if !msix_config.table_masked(vector) {
+                    if let Some(irqfd) = msix_config.get_irqfd(vector) {
+                        self.vhost_handle
+                            .set_vring_call(queue_index, irqfd)
+                            .map_err(Error::VhostSetVringCall)?;
+                    } else {
+                        self.vhost_handle
+                            .set_vring_call(queue_index, &self.vhost_interrupt[queue_index])
+                            .map_err(Error::VhostSetVringCall)?;
+                    }
+                    return Ok(());
+                }
+            }
+        }
+
+        self.vhost_handle
+            .set_vring_call(queue_index, &self.vhost_interrupt[queue_index])
+            .map_err(Error::VhostSetVringCall)?;
+        Ok(())
+    }
+
+    fn set_vring_calls(&self) -> Result<()> {
+        if let Some(msix_config) = &self.interrupt.msix_config {
+            let msix_config = msix_config.lock();
+            if msix_config.masked() {
+                for (queue_index, _) in self.queues.iter().enumerate() {
+                    self.vhost_handle
+                        .set_vring_call(queue_index, &self.vhost_interrupt[queue_index])
+                        .map_err(Error::VhostSetVringCall)?;
+                }
+            } else {
+                for (queue_index, queue) in self.queues.iter().enumerate() {
+                    let vector = queue.vector as usize;
+                    if !msix_config.table_masked(vector) {
+                        if let Some(irqfd) = msix_config.get_irqfd(vector) {
+                            self.vhost_handle
+                                .set_vring_call(queue_index, irqfd)
+                                .map_err(Error::VhostSetVringCall)?;
+                        } else {
+                            self.vhost_handle
+                                .set_vring_call(queue_index, &self.vhost_interrupt[queue_index])
+                                .map_err(Error::VhostSetVringCall)?;
+                        }
+                    } else {
+                        self.vhost_handle
+                            .set_vring_call(queue_index, &self.vhost_interrupt[queue_index])
+                            .map_err(Error::VhostSetVringCall)?;
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
 }
diff --git a/devices/src/virtio/virtio_device.rs b/devices/src/virtio/virtio_device.rs
index 806a98f..6eb5548 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::{PciBarConfiguration, PciCapability};
+use crate::pci::{MsixStatus, PciBarConfiguration, PciCapability};
 
 /// Trait for virtio devices to be driven by a virtio transport.
 ///
@@ -86,4 +86,6 @@ pub trait VirtioDevice: Send {
 
     /// Invoked when the device is sandboxed.
     fn on_device_sandboxed(&mut self) {}
+
+    fn control_notify(&self, _behavior: MsixStatus) {}
 }
diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs
index c6d6786..e63abe9 100644
--- a/devices/src/virtio/virtio_pci_device.rs
+++ b/devices/src/virtio/virtio_pci_device.rs
@@ -535,7 +535,8 @@ impl PciDevice for VirtioPciDevice {
     fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
         if let Some(msix_cap_reg_idx) = self.msix_cap_reg_idx {
             if msix_cap_reg_idx == reg_idx {
-                self.msix_config.lock().write_msix_capability(offset, data);
+                let behavior = self.msix_config.lock().write_msix_capability(offset, data);
+                self.device.control_notify(behavior);
             }
         }
 
@@ -626,9 +627,11 @@ impl PciDevice for VirtioPciDevice {
                 // Handled with ioeventfds.
             }
             o if MSIX_TABLE_BAR_OFFSET <= o && o < MSIX_TABLE_BAR_OFFSET + MSIX_TABLE_SIZE => {
-                self.msix_config
+                let behavior = self
+                    .msix_config
                     .lock()
                     .write_msix_table(o - MSIX_TABLE_BAR_OFFSET, data);
+                self.device.control_notify(behavior);
             }
             o if MSIX_PBA_BAR_OFFSET <= o && o < MSIX_PBA_BAR_OFFSET + MSIX_PBA_SIZE => {
                 self.msix_config
diff --git a/docker/checkout_commits.env b/docker/checkout_commits.env
index 92586bc..60aecc1 100644
--- a/docker/checkout_commits.env
+++ b/docker/checkout_commits.env
@@ -2,5 +2,5 @@ MESON_COMMIT=a1a8772034aef90e8d58230d8bcfce54ab27bf6a
 LIBEPOXY_COMMIT=af38a466caf9c2ae49b8acda4ff842ae44d57f78
 TPM2_COMMIT=a9bc45bb7fafc65ea8a787894434d409f533b1f1
 PLATFORM2_COMMIT=9239a43f2dc2e98e57e9d77aac72fa3ce8169e5f
-ADHD_COMMIT=d99fa2312e03594c1de236e7ac74b332b0ddc329
+ADHD_COMMIT=db796cecdea7013b8679f90dfae34915edc9246f
 DRM_COMMIT=00320d7d68ddc7d815d073bb7c92d9a1f9bb8c31
diff --git a/gpu_display/src/keycode_converter/mod.rs b/gpu_display/src/keycode_converter/mod.rs
index 40fdb95..236e65d 100644
--- a/gpu_display/src/keycode_converter/mod.rs
+++ b/gpu_display/src/keycode_converter/mod.rs
@@ -23,7 +23,7 @@ impl KeycodeTranslator {
     /// Create a new KeycodeTranslator that translates from the `from_type` type to Linux keycodes.
     pub fn new(from_type: KeycodeTypes) -> KeycodeTranslator {
         let mut kcm: HashMap<u32, MapEntry> = HashMap::new();
-        for entry in KEYCODE_MAP.into_iter() {
+        for entry in KEYCODE_MAP.iter() {
             kcm.insert(
                 match from_type {
                     KeycodeTypes::XkbScancode => entry.xkb,
diff --git a/gpu_renderer/src/generated/virglrenderer.rs b/gpu_renderer/src/generated/virglrenderer.rs
index 01bf219..020d69f 100644
--- a/gpu_renderer/src/generated/virglrenderer.rs
+++ b/gpu_renderer/src/generated/virglrenderer.rs
@@ -307,24 +307,21 @@ extern "C" {
         execute_size: u32,
     ) -> ::std::os::raw::c_int;
 }
-extern "C" {
-    pub fn virgl_renderer_allocation_metadata(
-        request: *const ::std::os::raw::c_void,
-        response: *mut ::std::os::raw::c_void,
-        request_size: u32,
-        response_size: u32,
-    ) -> ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct virgl_renderer_resource_create_v2_args {
+    pub version: u32,
+    pub res_handle: u32,
+    pub ctx_id: u32,
+    pub flags: u32,
+    pub size: u64,
+    pub memory_id: u64,
+    pub iovecs: *mut iovec,
+    pub num_iovs: u32,
 }
 extern "C" {
     pub fn virgl_renderer_resource_create_v2(
-        resource_id: u32,
-        guest_memory_type: u32,
-        guest_caching_type: u32,
-        size: u64,
-        iovec: *const iovec,
-        num_iovs: u32,
-        args: *const ::std::os::raw::c_void,
-        args_size: u32,
+        args: *mut virgl_renderer_resource_create_v2_args,
     ) -> ::std::os::raw::c_int;
 }
 pub type __builtin_va_list = [__va_list_tag; 1usize];
diff --git a/gpu_renderer/src/lib.rs b/gpu_renderer/src/lib.rs
index be8c17a..5e5ab95 100644
--- a/gpu_renderer/src/lib.rs
+++ b/gpu_renderer/src/lib.rs
@@ -6,8 +6,11 @@
 
 mod command_buffer;
 mod generated;
+mod vsnprintf;
 
 use std::cell::RefCell;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+use std::ffi::CString;
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::marker::PhantomData;
@@ -22,7 +25,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
 use libc::close;
 
 use data_model::{VolatileMemory, VolatileSlice};
-use sys_util::{GuestAddress, GuestMemory};
+use sys_util::{debug, GuestAddress, GuestMemory};
 
 use crate::generated::p_defines::{
     PIPE_BIND_RENDER_TARGET, PIPE_BIND_SAMPLER_VIEW, PIPE_TEXTURE_1D, PIPE_TEXTURE_2D,
@@ -31,8 +34,10 @@ use crate::generated::p_format::PIPE_FORMAT_B8G8R8X8_UNORM;
 use crate::generated::virglrenderer::*;
 
 pub use crate::command_buffer::CommandBufferBuilder;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub use crate::vsnprintf::vsnprintf;
 
-/// Arguments used in `Renderer::create_resource`..
+/// Arguments used in `Renderer::create_resource`.
 pub type ResourceCreateArgs = virgl_renderer_resource_create_args;
 /// Some of the information returned from `Resource::export_query`.
 pub type Query = virgl_renderer_export_query;
@@ -245,6 +250,11 @@ impl Renderer {
             fence_state: Rc::clone(&fence_state),
         }));
 
+        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+        unsafe {
+            virgl_set_debug_callback(Some(Renderer::debug_callback))
+        };
+
         // Safe because a valid cookie and set of callbacks is used and the result is checked for
         // error.
         let ret = unsafe {
@@ -396,71 +406,52 @@ impl Renderer {
     }
 
     #[allow(unused_variables)]
-    pub fn allocation_metadata(&self, request: &[u8], response: &mut Vec<u8>) -> Result<()> {
-        #[cfg(feature = "virtio-gpu-next")]
-        {
-            let ret = unsafe {
-                virgl_renderer_allocation_metadata(
-                    request.as_ptr() as *const c_void,
-                    response.as_mut_ptr() as *mut c_void,
-                    request.len() as u32,
-                    response.len() as u32,
-                )
-            };
-            ret_to_res(ret)
-        }
-        #[cfg(not(feature = "virtio-gpu-next"))]
-        Err(Error::Unsupported)
-    }
-
-    #[allow(unused_variables)]
     pub fn resource_create_v2(
         &self,
         resource_id: u32,
-        guest_memory_type: u32,
-        guest_caching_type: u32,
+        ctx_id: u32,
+        flags: u32,
         size: u64,
+        memory_id: u64,
+        vecs: &[(GuestAddress, usize)],
         mem: &GuestMemory,
-        iovecs: &[(GuestAddress, usize)],
-        args: &[u8],
     ) -> Result<Resource> {
         #[cfg(feature = "virtio-gpu-next")]
         {
-            if iovecs
+            if vecs
                 .iter()
                 .any(|&(addr, len)| mem.get_slice(addr.offset(), len as u64).is_err())
             {
                 return Err(Error::InvalidIovec);
             }
 
-            let mut vecs = Vec::new();
-            for &(addr, len) in iovecs {
+            let mut iovecs = Vec::new();
+            for &(addr, len) in vecs {
                 // Unwrap will not panic because we already checked the slices.
                 let slice = mem.get_slice(addr.offset(), len as u64).unwrap();
-                vecs.push(VirglVec {
+                iovecs.push(VirglVec {
                     base: slice.as_ptr() as *mut c_void,
                     len,
                 });
             }
 
-            let ret = unsafe {
-                virgl_renderer_resource_create_v2(
-                    resource_id,
-                    guest_memory_type,
-                    guest_caching_type,
-                    size,
-                    vecs.as_ptr() as *const iovec,
-                    vecs.len() as u32,
-                    args.as_ptr() as *const c_void,
-                    args.len() as u32,
-                )
+            let mut resource_create_args = virgl_renderer_resource_create_v2_args {
+                version: 1,
+                res_handle: resource_id,
+                ctx_id,
+                flags,
+                size,
+                memory_id,
+                iovecs: iovecs.as_mut_ptr() as *mut iovec,
+                num_iovs: iovecs.len() as u32,
             };
 
+            let ret = unsafe { virgl_renderer_resource_create_v2(&mut resource_create_args) };
             ret_to_res(ret)?;
 
             Ok(Resource {
                 id: resource_id,
-                backing_iovecs: vecs,
+                backing_iovecs: iovecs,
                 backing_mem: None,
                 no_sync_send: PhantomData,
             })
@@ -468,6 +459,28 @@ impl Renderer {
         #[cfg(not(feature = "virtio-gpu-next"))]
         Err(Error::Unsupported)
     }
+
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    extern "C" fn debug_callback(
+        fmt: *const ::std::os::raw::c_char,
+        ap: *mut generated::virglrenderer::__va_list_tag,
+    ) {
+        let len: u32 = 256;
+        let mut c_str = CString::new(vec![' ' as u8; len as usize]).unwrap();
+        unsafe {
+            let mut varargs = vsnprintf::__va_list_tag {
+                gp_offset: (*ap).gp_offset,
+                fp_offset: (*ap).fp_offset,
+                overflow_arg_area: (*ap).overflow_arg_area,
+                reg_save_area: (*ap).reg_save_area,
+            };
+
+            let raw = c_str.into_raw();
+            vsnprintf(raw, len.into(), fmt, &mut varargs);
+            c_str = CString::from_raw(raw);
+        }
+        debug!("{}", c_str.to_string_lossy());
+    }
 }
 
 /// A context in which resources can be attached/detached and commands can be submitted.
diff --git a/gpu_renderer/src/vsnprintf.rs b/gpu_renderer/src/vsnprintf.rs
new file mode 100644
index 0000000..ec121ad
--- /dev/null
+++ b/gpu_renderer/src/vsnprintf.rs
@@ -0,0 +1,33 @@
+// 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.
+
+#![allow(dead_code, non_snake_case, non_camel_case_types)]
+
+/*
+ * automatically generated by rust-bindgen
+ * $ bindgen /usr/include/stdio.h \
+ *       --no-layout-tests \
+ *       --whitelist-function vsnprintf \
+ *       -o vsnprintf.rs
+ */
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+extern "C" {
+    pub fn vsnprintf(
+        __s: *mut ::std::os::raw::c_char,
+        __maxlen: ::std::os::raw::c_ulong,
+        __format: *const ::std::os::raw::c_char,
+        __arg: *mut __va_list_tag,
+    ) -> ::std::os::raw::c_int;
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __va_list_tag {
+    pub gp_offset: ::std::os::raw::c_uint,
+    pub fp_offset: ::std::os::raw::c_uint,
+    pub overflow_arg_area: *mut ::std::os::raw::c_void,
+    pub reg_save_area: *mut ::std::os::raw::c_void,
+}
diff --git a/p9/src/server.rs b/p9/src/server.rs
index b8a026f..e1ffd16 100644
--- a/p9/src/server.rs
+++ b/p9/src/server.rs
@@ -44,7 +44,7 @@ const MAPPED_FLAGS: [(u32, i32); 10] = [
     (P9_NONBLOCK, libc::O_NONBLOCK),
     (P9_DSYNC, libc::O_DSYNC),
     (P9_FASYNC, 0), // Unsupported
-    (P9_DIRECT, libc::O_DIRECT),
+    (P9_DIRECT, 0), // Unsupported
     (P9_LARGEFILE, libc::O_LARGEFILE),
     (P9_DIRECTORY, libc::O_DIRECTORY),
     (P9_NOFOLLOW, libc::O_NOFOLLOW),
diff --git a/seccomp/aarch64/fs_device.policy b/seccomp/aarch64/fs_device.policy
index 9fd4c8b..ec9d155 100644
--- a/seccomp/aarch64/fs_device.policy
+++ b/seccomp/aarch64/fs_device.policy
@@ -4,6 +4,7 @@
 
 @include /usr/share/policy/crosvm/common_device.policy
 
+copy_file_range: 1
 fallocate: 1
 fchmodat: 1
 fchownat: 1
diff --git a/seccomp/arm/fs_device.policy b/seccomp/arm/fs_device.policy
index eb9df16..4078f41 100644
--- a/seccomp/arm/fs_device.policy
+++ b/seccomp/arm/fs_device.policy
@@ -4,6 +4,7 @@
 
 @include /usr/share/policy/crosvm/common_device.policy
 
+copy_file_range: 1
 fallocate: 1
 fchmodat: 1
 fchownat: 1
diff --git a/seccomp/x86_64/common_device.frequency b/seccomp/x86_64/common_device.frequency
new file mode 100644
index 0000000..618c44d
--- /dev/null
+++ b/seccomp/x86_64/common_device.frequency
@@ -0,0 +1,45 @@
+# 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.
+
+capget: 30
+capset: 30
+chdir: 30
+chroot: 15
+clone: 30
+close: 1185
+dup: 50
+dup2: 160
+epoll_ctl: 25
+epoll_wait: 90
+eventfd2: 75
+exit: 15
+exit_group: 15
+fchdir: 30
+fstat: 90
+futex: 20
+getdents: 55
+ioctl: 350
+mmap: 95
+mount: 45
+mprotect: 45
+openat: 515
+pipe: 15
+pivot_root: 15
+prctl: 570
+prlimit64: 15
+read: 82415
+recvmsg: 85
+restart_syscall: 15
+rt_sigaction: 20
+rt_sigreturn: 15
+seccomp: 25
+sendmsg: 390
+setsockopt: 30
+socket: 20
+socketpair: 30
+stat: 30
+umount2: 15
+unshare: 30
+wait4: 20
+write: 56100
diff --git a/seccomp/x86_64/common_device.policy b/seccomp/x86_64/common_device.policy
index 8464c4b..453719d 100644
--- a/seccomp/x86_64/common_device.policy
+++ b/seccomp/x86_64/common_device.policy
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+@frequency ./common_device.frequency
 brk: 1
 clone: arg0 & CLONE_THREAD
 close: 1
diff --git a/seccomp/x86_64/fs_device.policy b/seccomp/x86_64/fs_device.policy
index ddb2a51..eb5a1c4 100644
--- a/seccomp/x86_64/fs_device.policy
+++ b/seccomp/x86_64/fs_device.policy
@@ -4,6 +4,7 @@
 
 @include /usr/share/policy/crosvm/common_device.policy
 
+copy_file_range: 1
 fallocate: 1
 fchmodat: 1
 fchownat: 1
diff --git a/src/argument.rs b/src/argument.rs
index 9ef9d4d..0d0e142 100644
--- a/src/argument.rs
+++ b/src/argument.rs
@@ -25,7 +25,7 @@
 //!             let v: u32 = value.unwrap().parse().map_err(|_| {
 //!                 Error::InvalidValue {
 //!                     value: value.unwrap().to_owned(),
-//!                     expected: "this value for `cpus` needs to be integer",
+//!                     expected: String::from("this value for `cpus` needs to be integer"),
 //!                 }
 //!             })?;
 //!         }
@@ -56,10 +56,7 @@ pub enum Error {
     /// The argument was required.
     ExpectedArgument(String),
     /// The argument's given value is invalid.
-    InvalidValue {
-        value: String,
-        expected: &'static str,
-    },
+    InvalidValue { value: String, expected: String },
     /// The argument was already given and none more are expected.
     TooManyArguments(String),
     /// The argument expects a value.
@@ -453,7 +450,7 @@ mod tests {
                     "cpus" => {
                         let c: u32 = value.unwrap().parse().map_err(|_| Error::InvalidValue {
                             value: value.unwrap().to_owned(),
-                            expected: "this value for `cpus` needs to be integer",
+                            expected: String::from("this value for `cpus` needs to be integer"),
                         })?;
                         assert_eq!(c, 3);
                     }
@@ -527,7 +524,7 @@ mod tests {
                                     _ => {
                                         return Err(Error::InvalidValue {
                                             value: v.to_string(),
-                                            expected: "2D or 3D",
+                                            expected: String::from("2D or 3D"),
                                         })
                                     }
                                 }
diff --git a/src/crosvm.rs b/src/crosvm.rs
index b3a9233..81344c3 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -20,7 +20,7 @@ use arch::Pstore;
 use devices::virtio::fs::passthrough;
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::GpuParameters;
-use devices::SerialParameters;
+use devices::{Ac97Parameters, SerialParameters};
 use libc::{getegid, geteuid};
 
 static SECCOMP_POLICY_DIR: &str = "/usr/share/policy/crosvm";
@@ -189,11 +189,9 @@ pub struct Config {
     #[cfg(feature = "gpu")]
     pub gpu_parameters: Option<GpuParameters>,
     pub software_tpm: bool,
-    pub cras_audio: bool,
-    pub cras_capture: bool,
-    pub null_audio: bool,
     pub display_window_keyboard: bool,
     pub display_window_mouse: bool,
+    pub ac97_parameters: Vec<Ac97Parameters>,
     pub serial_parameters: BTreeMap<u8, SerialParameters>,
     pub syslog_tag: Option<String>,
     pub virtio_single_touch: Option<TouchDeviceOption>,
@@ -240,9 +238,7 @@ impl Default for Config {
             sandbox: !cfg!(feature = "default-no-sandbox"),
             seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
             seccomp_log_failures: false,
-            cras_audio: false,
-            cras_capture: false,
-            null_audio: false,
+            ac97_parameters: Vec::new(),
             serial_parameters: BTreeMap::new(),
             syslog_tag: None,
             virtio_single_touch: None,
diff --git a/src/linux.rs b/src/linux.rs
index 2de1aaa..130644d 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -27,17 +27,15 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
 
 use libc::{self, c_int, gid_t, uid_t};
 
-use audio_streams::shm_streams::NullShmStreamSource;
 #[cfg(feature = "gpu")]
 use devices::virtio::EventDevice;
 use devices::virtio::{self, VirtioDevice};
 use devices::{
-    self, HostBackendDeviceProvider, PciDevice, VfioContainer, VfioDevice, VfioPciDevice,
-    VirtioPciDevice, XhciController,
+    self, Ac97Backend, Ac97Dev, HostBackendDeviceProvider, PciDevice, VfioContainer, VfioDevice,
+    VfioPciDevice, VirtioPciDevice, XhciController,
 };
 use io_jail::{self, Minijail};
 use kvm::*;
-use libcras::CrasClient;
 use msg_socket::{MsgError, MsgReceiver, MsgSender, MsgSocket};
 use net_util::{Error as NetError, MacAddress, Tap};
 use rand_ish::SimpleRng;
@@ -83,7 +81,7 @@ pub enum Error {
     BuildVm(<Arch as LinuxArch>::Error),
     ChownTpmStorage(sys_util::Error),
     CloneEventFd(sys_util::Error),
-    CreateCrasClient(libcras::Error),
+    CreateAc97(devices::PciDeviceError),
     CreateDiskError(disk::Error),
     CreateEventFd(sys_util::Error),
     CreatePollContext(sys_util::Error),
@@ -168,7 +166,7 @@ impl Display for Error {
             BuildVm(e) => write!(f, "The architecture failed to build the vm: {}", e),
             ChownTpmStorage(e) => write!(f, "failed to chown tpm storage: {}", e),
             CloneEventFd(e) => write!(f, "failed to clone eventfd: {}", e),
-            CreateCrasClient(e) => write!(f, "failed to create cras client: {}", e),
+            CreateAc97(e) => write!(f, "failed to create ac97 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),
@@ -1150,27 +1148,14 @@ fn create_devices(
         pci_devices.push((dev, stub.jail));
     }
 
-    if cfg.cras_audio {
-        let mut server = Box::new(CrasClient::new().map_err(Error::CreateCrasClient)?);
-        if cfg.cras_capture {
-            server.enable_cras_capture();
-        }
-        let cras_audio = devices::Ac97Dev::new(mem.clone(), server);
-
-        pci_devices.push((
-            Box::new(cras_audio),
-            simple_jail(&cfg, "cras_audio_device")?,
-        ));
-    }
-
-    if cfg.null_audio {
-        let server = Box::new(NullShmStreamSource::new());
-        let null_audio = devices::Ac97Dev::new(mem.clone(), server);
+    for ac97_param in &cfg.ac97_parameters {
+        let dev = Ac97Dev::try_new(mem.clone(), ac97_param.clone()).map_err(Error::CreateAc97)?;
+        let policy = match ac97_param.backend {
+            Ac97Backend::CRAS => "cras_audio_device",
+            Ac97Backend::NULL => "null_audio_device",
+        };
 
-        pci_devices.push((
-            Box::new(null_audio),
-            simple_jail(&cfg, "null_audio_device")?,
-        ));
+        pci_devices.push((Box::new(dev), simple_jail(&cfg, &policy)?));
     }
     // Create xhci controller.
     let usb_controller = Box::new(XhciController::new(mem.clone(), usb_provider));
@@ -1751,7 +1736,7 @@ fn run_control(
         .map_err(Error::PollContextAdd)?;
 
     if let Some(gsi_relay) = &linux.gsi_relay {
-        for (gsi, evt) in gsi_relay.irqfd.into_iter().enumerate() {
+        for (gsi, evt) in gsi_relay.irqfd.iter().enumerate() {
             if let Some(evt) = evt {
                 poll_ctx
                     .add(evt, Token::IrqFd { gsi })
diff --git a/src/main.rs b/src/main.rs
index 1804028..d385db3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@
 
 pub mod panic_hook;
 
+use std::default::Default;
 use std::fmt;
 use std::fs::{File, OpenOptions};
 use std::io::{BufRead, BufReader};
@@ -17,13 +18,14 @@ use std::thread::sleep;
 use std::time::Duration;
 
 use arch::Pstore;
+use audio_streams::StreamEffect;
 use crosvm::{
     argument::{self, print_help, set_arguments, Argument},
     linux, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
 };
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::{GpuMode, GpuParameters};
-use devices::{SerialParameters, SerialType};
+use devices::{Ac97Backend, Ac97Parameters, SerialParameters, SerialType};
 use disk::QcowFile;
 use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
 use sys_util::{
@@ -78,21 +80,21 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
         if range.len() == 0 || range.len() > 2 {
             return Err(argument::Error::InvalidValue {
                 value: part.to_owned(),
-                expected: "invalid list syntax",
+                expected: String::from("invalid list syntax"),
             });
         }
         let first_cpu: usize = range[0]
             .parse()
             .map_err(|_| argument::Error::InvalidValue {
                 value: part.to_owned(),
-                expected: "CPU index must be a non-negative integer",
+                expected: String::from("CPU index must be a non-negative integer"),
             })?;
         let last_cpu: usize = if range.len() == 2 {
             range[1]
                 .parse()
                 .map_err(|_| argument::Error::InvalidValue {
                     value: part.to_owned(),
-                    expected: "CPU index must be a non-negative integer",
+                    expected: String::from("CPU index must be a non-negative integer"),
                 })?
         } else {
             first_cpu
@@ -101,7 +103,7 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
         if last_cpu < first_cpu {
             return Err(argument::Error::InvalidValue {
                 value: part.to_owned(),
-                expected: "CPU ranges must be from low to high",
+                expected: String::from("CPU ranges must be from low to high"),
             });
         }
 
@@ -151,7 +153,9 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: v.to_string(),
-                            expected: "gpu parameter 'backend' should be one of (2d|3d|gfxstream)",
+                            expected: String::from(
+                                "gpu parameter 'backend' should be one of (2d|3d|gfxstream)",
+                            ),
                         });
                     }
                 },
@@ -165,7 +169,7 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: v.to_string(),
-                            expected: "gpu parameter 'egl' should be a boolean",
+                            expected: String::from("gpu parameter 'egl' should be a boolean"),
                         });
                     }
                 },
@@ -179,7 +183,7 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: v.to_string(),
-                            expected: "gpu parameter 'gles' should be a boolean",
+                            expected: String::from("gpu parameter 'gles' should be a boolean"),
                         });
                     }
                 },
@@ -193,7 +197,7 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: v.to_string(),
-                            expected: "gpu parameter 'glx' should be a boolean",
+                            expected: String::from("gpu parameter 'glx' should be a boolean"),
                         });
                     }
                 },
@@ -207,7 +211,9 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: v.to_string(),
-                            expected: "gpu parameter 'surfaceless' should be a boolean",
+                            expected: String::from(
+                                "gpu parameter 'surfaceless' should be a boolean",
+                            ),
                         });
                     }
                 },
@@ -216,7 +222,9 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
                         v.parse::<u32>()
                             .map_err(|_| argument::Error::InvalidValue {
                                 value: v.to_string(),
-                                expected: "gpu parameter 'width' must be a valid integer",
+                                expected: String::from(
+                                    "gpu parameter 'width' must be a valid integer",
+                                ),
                             })?;
                 }
                 "height" => {
@@ -224,7 +232,9 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
                         v.parse::<u32>()
                             .map_err(|_| argument::Error::InvalidValue {
                                 value: v.to_string(),
-                                expected: "gpu parameter 'height' must be a valid integer",
+                                expected: String::from(
+                                    "gpu parameter 'height' must be a valid integer",
+                                ),
                             })?;
                 }
                 "" => {}
@@ -241,6 +251,53 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
     Ok(gpu_params)
 }
 
+fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
+    let mut ac97_params: Ac97Parameters = Default::default();
+
+    let opts = s
+        .split(",")
+        .map(|frag| frag.split("="))
+        .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
+
+    for (k, v) in opts {
+        match k {
+            "backend" => {
+                ac97_params.backend =
+                    v.parse::<Ac97Backend>()
+                        .map_err(|e| argument::Error::InvalidValue {
+                            value: v.to_string(),
+                            expected: e.to_string(),
+                        })?;
+            }
+            "capture" => {
+                ac97_params.capture = v.parse::<bool>().map_err(|e| {
+                    argument::Error::Syntax(format!("invalid capture option: {}", e))
+                })?;
+            }
+            "capture_effects" => {
+                ac97_params.capture_effects = v
+                    .split("|")
+                    .map(|val| {
+                        val.parse::<StreamEffect>()
+                            .map_err(|e| argument::Error::InvalidValue {
+                                value: val.to_string(),
+                                expected: e.to_string(),
+                            })
+                    })
+                    .collect::<argument::Result<Vec<_>>>()?;
+            }
+            _ => {
+                return Err(argument::Error::UnknownArgument(format!(
+                    "unknown ac97 parameter {}",
+                    k
+                )));
+            }
+        }
+    }
+
+    Ok(ac97_params)
+}
+
 fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
     let mut serial_setting = SerialParameters {
         type_: SerialType::Sink,
@@ -269,7 +326,7 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
                 if num < 1 || num > 4 {
                     return Err(argument::Error::InvalidValue {
                         value: num.to_string(),
-                        expected: "Serial port num must be between 1 - 4",
+                        expected: String::from("Serial port num must be between 1 - 4"),
                     });
                 }
                 serial_setting.num = num;
@@ -305,7 +362,9 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
-            expected: "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
+            expected: String::from(
+                "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
+            ),
         });
     }
 
@@ -313,13 +372,13 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
     if src.is_relative() {
         return Err(argument::Error::InvalidValue {
             value: components[0].to_owned(),
-            expected: "the source path for `plugin-mount` must be absolute",
+            expected: String::from("the source path for `plugin-mount` must be absolute"),
         });
     }
     if !src.exists() {
         return Err(argument::Error::InvalidValue {
             value: components[0].to_owned(),
-            expected: "the source path for `plugin-mount` does not exist",
+            expected: String::from("the source path for `plugin-mount` does not exist"),
         });
     }
 
@@ -330,7 +389,7 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
     if dst.is_relative() {
         return Err(argument::Error::InvalidValue {
             value: components[1].to_owned(),
-            expected: "the destination path for `plugin-mount` must be absolute",
+            expected: String::from("the destination path for `plugin-mount` must be absolute"),
         });
     }
 
@@ -338,7 +397,7 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
         None => false,
         Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
             value: components[2].to_owned(),
-            expected: "the <writable> component for `plugin-mount` is not valid bool",
+            expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
         })?,
     };
 
@@ -350,8 +409,9 @@ fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
-            expected:
+            expected: String::from(
                 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
+            ),
         });
     }
 
@@ -359,14 +419,14 @@ fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
         .parse()
         .map_err(|_| argument::Error::InvalidValue {
             value: components[0].to_owned(),
-            expected: "the <inner> component for `plugin-gid-map` is not valid gid",
+            expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
         })?;
 
     let outer: libc::gid_t = match components.get(1) {
         None | Some(&"") => inner,
         Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
             value: components[1].to_owned(),
-            expected: "the <outer> component for `plugin-gid-map` is not valid gid",
+            expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
         })?,
     };
 
@@ -374,7 +434,9 @@ fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
         None => 1,
         Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
             value: components[2].to_owned(),
-            expected: "the <count> component for `plugin-gid-map` is not valid number",
+            expected: String::from(
+                "the <count> component for `plugin-gid-map` is not valid number",
+            ),
         })?,
     };
 
@@ -398,7 +460,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if !kernel_path.exists() {
                 return Err(argument::Error::InvalidValue {
                     value: value.unwrap().to_owned(),
-                    expected: "this kernel path does not exist",
+                    expected: String::from("this kernel path does not exist"),
                 });
             }
             cfg.executable_path = Some(Executable::Kernel(kernel_path));
@@ -415,7 +477,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 if !android_fstab.exists() {
                     return Err(argument::Error::InvalidValue {
                         value: value.unwrap().to_owned(),
-                        expected: "this android fstab path does not exist",
+                        expected: String::from("this android fstab path does not exist"),
                     });
                 }
                 cfg.android_fstab = Some(android_fstab);
@@ -437,7 +499,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         .parse()
                         .map_err(|_| argument::Error::InvalidValue {
                             value: value.unwrap().to_owned(),
-                            expected: "this value for `cpus` needs to be integer",
+                            expected: String::from("this value for `cpus` needs to be integer"),
                         })?,
                 )
         }
@@ -462,18 +524,13 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         .parse()
                         .map_err(|_| argument::Error::InvalidValue {
                             value: value.unwrap().to_owned(),
-                            expected: "this value for `mem` needs to be integer",
+                            expected: String::from("this value for `mem` needs to be integer"),
                         })?,
                 )
         }
-        "cras-audio" => {
-            cfg.cras_audio = true;
-        }
-        "cras-capture" => {
-            cfg.cras_capture = true;
-        }
-        "null-audio" => {
-            cfg.null_audio = true;
+        "ac97" => {
+            let ac97_params = parse_ac97_options(value.unwrap())?;
+            cfg.ac97_parameters.push(ac97_params);
         }
         "serial" => {
             let serial_params = parse_serial_options(value.unwrap())?;
@@ -526,13 +583,13 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         .next()
                         .ok_or_else(|| argument::Error::InvalidValue {
                             value: param.to_owned(),
-                            expected: "missing disk path",
+                            expected: String::from("missing disk path"),
                         })?,
                 );
             if !disk_path.exists() {
                 return Err(argument::Error::InvalidValue {
                     value: param.to_owned(),
-                    expected: "this disk path does not exist",
+                    expected: String::from("this disk path does not exist"),
                 });
             }
             if name.ends_with("root") {
@@ -559,18 +616,18 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 let mut o = opt.splitn(2, '=');
                 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
                     value: opt.to_owned(),
-                    expected: "disk options must not be empty",
+                    expected: String::from("disk options must not be empty"),
                 })?;
                 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
                     value: opt.to_owned(),
-                    expected: "disk options must be of the form `kind=value`",
+                    expected: String::from("disk options must be of the form `kind=value`"),
                 })?;
 
                 match kind {
                     "sparse" => {
                         let sparse = value.parse().map_err(|_| argument::Error::InvalidValue {
                             value: value.to_owned(),
-                            expected: "`sparse` must be a boolean",
+                            expected: String::from("`sparse` must be a boolean"),
                         })?;
                         disk.sparse = sparse;
                     }
@@ -578,14 +635,14 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         let block_size =
                             value.parse().map_err(|_| argument::Error::InvalidValue {
                                 value: value.to_owned(),
-                                expected: "`block_size` must be an integer",
+                                expected: String::from("`block_size` must be an integer"),
                             })?;
                         disk.block_size = block_size;
                     }
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: kind.to_owned(),
-                            expected: "unrecognized disk option",
+                            expected: String::from("unrecognized disk option"),
                         });
                     }
                 }
@@ -598,7 +655,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if !disk_path.exists() {
                 return Err(argument::Error::InvalidValue {
                     value: value.unwrap().to_owned(),
-                    expected: "this disk path does not exist",
+                    expected: String::from("this disk path does not exist"),
                 });
             }
 
@@ -621,7 +678,9 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if components.len() != 2 {
                 return Err(argument::Error::InvalidValue {
                     value: value.to_owned(),
-                    expected: "pstore must have exactly 2 components: path=<path>,size=<size>",
+                    expected: String::from(
+                        "pstore must have exactly 2 components: path=<path>,size=<size>",
+                    ),
                 });
             }
             cfg.pstore = Some(Pstore {
@@ -629,7 +688,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     if components[0].len() <= 5 || !components[0].starts_with("path=") {
                         return Err(argument::Error::InvalidValue {
                             value: components[0].to_owned(),
-                            expected: "pstore path must follow with `path=`",
+                            expected: String::from("pstore path must follow with `path=`"),
                         });
                     };
                     PathBuf::from(&components[0][5..])
@@ -638,14 +697,14 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     if components[1].len() <= 5 || !components[1].starts_with("size=") {
                         return Err(argument::Error::InvalidValue {
                             value: components[1].to_owned(),
-                            expected: "pstore size must follow with `size=`",
+                            expected: String::from("pstore size must follow with `size=`"),
                         });
                     };
                     components[1][5..]
                         .parse()
                         .map_err(|_| argument::Error::InvalidValue {
                             value: value.to_owned(),
-                            expected: "pstore size must be an integer",
+                            expected: String::from("pstore size must be an integer"),
                         })?
                 },
             });
@@ -663,7 +722,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         .parse()
                         .map_err(|_| argument::Error::InvalidValue {
                             value: value.unwrap().to_owned(),
-                            expected: "`host_ip` needs to be in the form \"x.x.x.x\"",
+                            expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
                         })?,
                 )
         }
@@ -680,7 +739,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         .parse()
                         .map_err(|_| argument::Error::InvalidValue {
                             value: value.unwrap().to_owned(),
-                            expected: "`netmask` needs to be in the form \"x.x.x.x\"",
+                            expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
                         })?,
                 )
         }
@@ -697,7 +756,9 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         .parse()
                         .map_err(|_| argument::Error::InvalidValue {
                             value: value.unwrap().to_owned(),
-                            expected: "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
+                            expected: String::from(
+                                "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
+                            ),
                         })?,
                 )
         }
@@ -709,7 +770,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         .next()
                         .ok_or_else(|| argument::Error::InvalidValue {
                             value: value.unwrap().to_owned(),
-                            expected: "missing socket path",
+                            expected: String::from("missing socket path"),
                         })?,
                 );
             let mut name = "";
@@ -720,7 +781,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: c.to_owned(),
-                            expected: "option must be of the form `kind=value`",
+                            expected: String::from("option must be of the form `kind=value`"),
                         })
                     }
                 };
@@ -729,7 +790,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: kind.to_owned(),
-                            expected: "unrecognized option",
+                            expected: String::from("unrecognized option"),
                         })
                     }
                 }
@@ -771,7 +832,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if socket_path.exists() {
                 return Err(argument::Error::InvalidValue {
                     value: socket_path.to_string_lossy().into_owned(),
-                    expected: "this socket path already exists",
+                    expected: String::from("this socket path already exists"),
                 });
             }
             cfg.socket_path = Some(socket_path);
@@ -791,7 +852,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     .parse()
                     .map_err(|_| argument::Error::InvalidValue {
                         value: value.unwrap().to_owned(),
-                        expected: "this value for `cid` must be an unsigned integer",
+                        expected: String::from("this value for `cid` must be an unsigned integer"),
                     })?,
             );
         }
@@ -816,21 +877,21 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         .next()
                         .ok_or_else(|| argument::Error::InvalidValue {
                             value: param.to_owned(),
-                            expected: "missing source path for `shared-dir`",
+                            expected: String::from("missing source path for `shared-dir`"),
                         })?,
                 );
             let tag = components
                 .next()
                 .ok_or_else(|| argument::Error::InvalidValue {
                     value: param.to_owned(),
-                    expected: "missing tag for `shared-dir`",
+                    expected: String::from("missing tag for `shared-dir`"),
                 })?
                 .to_owned();
 
             if !src.is_dir() {
                 return Err(argument::Error::InvalidValue {
                     value: param.to_owned(),
-                    expected: "source path for `shared-dir` must be a directory",
+                    expected: String::from("source path for `shared-dir` must be a directory"),
                 });
             }
 
@@ -843,11 +904,11 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 let mut o = opt.splitn(2, '=');
                 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
                     value: opt.to_owned(),
-                    expected: "`shared-dir` options must not be empty",
+                    expected: String::from("`shared-dir` options must not be empty"),
                 })?;
                 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
                     value: opt.to_owned(),
-                    expected: "`shared-dir` options must be of the form `kind=value`",
+                    expected: String::from("`shared-dir` options must be of the form `kind=value`"),
                 })?;
 
                 match kind {
@@ -855,7 +916,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         shared_dir.kind =
                             value.parse().map_err(|_| argument::Error::InvalidValue {
                                 value: value.to_owned(),
-                                expected: "`type` must be one of `fs` or `9p`",
+                                expected: String::from("`type` must be one of `fs` or `9p`"),
                             })?
                     }
                     "uidmap" => shared_dir.uid_map = value.into(),
@@ -863,7 +924,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     "timeout" => {
                         let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
                             value: value.to_owned(),
-                            expected: "`timeout` must be an integer",
+                            expected: String::from("`timeout` must be an integer"),
                         })?;
 
                         let dur = Duration::from_secs(seconds);
@@ -873,7 +934,9 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     "cache" => {
                         let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
                             value: value.to_owned(),
-                            expected: "`cache` must be one of `never`, `always`, or `auto`",
+                            expected: String::from(
+                                "`cache` must be one of `never`, `always`, or `auto`",
+                            ),
                         })?;
                         shared_dir.cfg.cache_policy = policy;
                     }
@@ -881,14 +944,14 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                         let writeback =
                             value.parse().map_err(|_| argument::Error::InvalidValue {
                                 value: value.to_owned(),
-                                expected: "`writeback` must be a boolean",
+                                expected: String::from("`writeback` must be a boolean"),
                             })?;
                         shared_dir.cfg.writeback = writeback;
                     }
                     _ => {
                         return Err(argument::Error::InvalidValue {
                             value: kind.to_owned(),
-                            expected: "unrecognized option for `shared-dir`",
+                            expected: String::from("unrecognized option for `shared-dir`"),
                         })
                     }
                 }
@@ -930,7 +993,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if plugin.is_relative() {
                 return Err(argument::Error::InvalidValue {
                     value: plugin.to_string_lossy().into_owned(),
-                    expected: "the plugin path must be an absolute path",
+                    expected: String::from("the plugin path must be an absolute path"),
                 });
             }
             cfg.executable_path = Some(Executable::Plugin(plugin));
@@ -945,7 +1008,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
         "plugin-mount-file" => {
             let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
                 value: value.unwrap().to_owned(),
-                expected: "unable to open `plugin-mount-file` file",
+                expected: String::from("unable to open `plugin-mount-file` file"),
             })?;
             let reader = BufReader::new(file);
             for l in reader.lines() {
@@ -964,7 +1027,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
         "plugin-gid-map-file" => {
             let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
                 value: value.unwrap().to_owned(),
-                expected: "unable to open `plugin-gid-map-file` file",
+                expected: String::from("unable to open `plugin-gid-map-file` file"),
             })?;
             let reader = BufReader::new(file);
             for l in reader.lines() {
@@ -984,7 +1047,9 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     .parse()
                     .map_err(|_| argument::Error::InvalidValue {
                         value: value.unwrap().to_owned(),
-                        expected: "this value for `tap-fd` must be an unsigned integer",
+                        expected: String::from(
+                            "this value for `tap-fd` must be an unsigned integer",
+                        ),
                     })?,
             );
         }
@@ -1053,7 +1118,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if !dev_path.exists() {
                 return Err(argument::Error::InvalidValue {
                     value: value.unwrap().to_owned(),
-                    expected: "this input device path does not exist",
+                    expected: String::from("this input device path does not exist"),
                 });
             }
             cfg.virtio_input_evdevs.push(dev_path);
@@ -1078,13 +1143,13 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if !vfio_path.exists() {
                 return Err(argument::Error::InvalidValue {
                     value: value.unwrap().to_owned(),
-                    expected: "the vfio path does not exist",
+                    expected: String::from("the vfio path does not exist"),
                 });
             }
             if !vfio_path.is_dir() {
                 return Err(argument::Error::InvalidValue {
                     value: value.unwrap().to_owned(),
-                    expected: "the vfio path should be directory",
+                    expected: String::from("the vfio path should be directory"),
                 });
             }
 
@@ -1223,24 +1288,28 @@ Path to pstore buffer backend file follewed by size.
         ),
         Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
         Argument::value("mac", "MAC", "MAC address for VM."),
-        Argument::flag(
-            "cras-audio",
-            "\
-Add an audio device to the VM that plays samples through CRAS server.
-",
-        ),
-        Argument::flag(
-            "cras-capture",
-            "\
-Enable capturing audio from CRAS server to the cras-audio device.
-",
-        ),
-        Argument::flag(
-            "null-audio",
-            "\
-Add an audio device to the VM that plays samples to /dev/null.
-",
+        Argument::value(
+            "ac97",
+            "[backend=BACKEND,capture=true,capture_effect=EFFECT]",
+            r#"Comma separated key=value pairs for setting up Ac97 devices. Can be
+given more than once.
+
+Possible key values:
+
+  backend=(null,cras)
+    Where to route the audio device.  "null" for /dev/null, and "cras"
+    for CRAS server.
+    (default: null)
+
+  capture
+    Enable audio capture.
+
+  capture_effects
+    Separated effects to be enabled for recording.  The only supported
+    effect value now is EchoCancellation or aec.
+"#,
         ),
+
         Argument::value(
             "serial",
             "type=TYPE,[num=NUM,path=PATH,console,stdin]",
@@ -1683,7 +1752,7 @@ fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
                 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
                     argument::Error::InvalidValue {
                         value: value.unwrap().to_owned(),
-                        expected: "SIZE should be a nonnegative integer",
+                        expected: String::from("SIZE should be a nonnegative integer"),
                     }
                 })?);
             }
@@ -2100,6 +2169,34 @@ mod tests {
     }
 
     #[test]
+    fn parse_ac97_vaild() {
+        parse_ac97_options("backend=cras").expect("parse should have succeded");
+    }
+
+    #[test]
+    fn parse_ac97_null_vaild() {
+        parse_ac97_options("backend=null").expect("parse should have succeded");
+    }
+
+    #[test]
+    fn parse_ac97_dup_effect_vaild() {
+        parse_ac97_options("backend=cras,capture=true,capture_effects=aec|aec")
+            .expect("parse should have succeded");
+    }
+
+    #[test]
+    fn parse_ac97_effect_invaild() {
+        parse_ac97_options("backend=cras,capture=true,capture_effects=abc")
+            .expect_err("parse should have failed");
+    }
+
+    #[test]
+    fn parse_ac97_effect_vaild() {
+        parse_ac97_options("backend=cras,capture=true,capture_effects=aec")
+            .expect("parse should have succeded");
+    }
+
+    #[test]
     fn parse_serial_vaild() {
         parse_serial_options("type=syslog,num=1,console=true,stdin=true")
             .expect("parse should have succeded");
diff --git a/usb_util/src/device.rs b/usb_util/src/device.rs
index aad77db..3ac1403 100644
--- a/usb_util/src/device.rs
+++ b/usb_util/src/device.rs
@@ -261,12 +261,8 @@ impl Device {
     }
 
     /// Get active config descriptor of this device.
-    pub fn get_active_config_descriptor(&self) -> Result<ConfigDescriptorTree> {
-        let active_config = self.get_active_configuration()?;
-        match self
-            .device_descriptor_tree
-            .get_config_descriptor(active_config)
-        {
+    pub fn get_config_descriptor(&self, config: u8) -> Result<ConfigDescriptorTree> {
+        match self.device_descriptor_tree.get_config_descriptor(config) {
             Some(config_descriptor) => Ok(config_descriptor.clone()),
             None => Err(Error::NoSuchDescriptor),
         }
@@ -297,6 +293,11 @@ impl Device {
         Ok(active_config)
     }
 
+    /// Get the total number of configurations for this device.
+    pub fn get_num_configurations(&self) -> u8 {
+        self.device_descriptor_tree.bNumConfigurations
+    }
+
     /// Clear the halt/stall condition for an endpoint.
     pub fn clear_halt(&self, ep_addr: u8) -> Result<()> {
         let endpoint: c_uint = ep_addr.into();
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
index 8a7b9ec..0216a90 100644
--- a/vm_control/src/lib.rs
+++ b/vm_control/src/lib.rs
@@ -192,7 +192,7 @@ pub enum VmMemoryRequest {
     RegisterMemory(MaybeOwnedFd, usize),
     /// Similiar to `VmMemoryRequest::RegisterMemory`, but doesn't allocate new address space.
     /// Useful for cases where the address space is already allocated (PCI regions).
-    RegisterMemoryAtAddress(Alloc, MaybeOwnedFd, usize, u64),
+    RegisterFdAtPciBarOffset(Alloc, MaybeOwnedFd, usize, u64),
     /// Unregister the given memory slot that was previously registereed with `RegisterMemory`.
     UnregisterMemory(u32),
     /// Allocate GPU buffer of a given size/format and register the memory into guest address space.
@@ -230,8 +230,8 @@ impl VmMemoryRequest {
                     Err(e) => VmMemoryResponse::Err(e),
                 }
             }
-            RegisterMemoryAtAddress(alloc, ref fd, size, guest_addr) => {
-                match register_memory(vm, sys_allocator, fd, size, Some((alloc, guest_addr))) {
+            RegisterFdAtPciBarOffset(alloc, ref fd, size, offset) => {
+                match register_memory(vm, sys_allocator, fd, size, Some((alloc, offset))) {
                     Ok((pfn, slot)) => VmMemoryResponse::RegisterMemory { pfn, slot },
                     Err(e) => VmMemoryResponse::Err(e),
                 }
@@ -433,12 +433,13 @@ fn register_memory(
     };
 
     let addr = match allocation {
-        Some((Alloc::PciBar { bus, dev, bar }, address)) => {
+        Some((Alloc::PciBar { bus, dev, bar }, offset)) => {
             match allocator
                 .mmio_allocator(MmioType::High)
                 .get(&Alloc::PciBar { bus, dev, bar })
             {
                 Some((start_addr, length, _)) => {
+                    let address = *start_addr + offset;
                     let range = *start_addr..*start_addr + *length;
                     let end = address + (size as u64);
                     match (range.contains(&address), range.contains(&end)) {
diff --git a/x86_64/src/gdt.rs b/x86_64/src/gdt.rs
index 7eb1ff7..a37ddd4 100644
--- a/x86_64/src/gdt.rs
+++ b/x86_64/src/gdt.rs
@@ -8,17 +8,17 @@ use kvm_sys::kvm_segment;
 
 /// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
 pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
-    ((((base as u64) & 0xff000000u64) << (56 - 24))
+    (((base as u64) & 0xff000000u64) << (56 - 24))
         | (((flags as u64) & 0x0000f0ffu64) << 40)
         | (((limit as u64) & 0x000f0000u64) << (48 - 16))
         | (((base as u64) & 0x00ffffffu64) << 16)
-        | ((limit as u64) & 0x0000ffffu64))
+        | ((limit as u64) & 0x0000ffffu64)
 }
 
 fn get_base(entry: u64) -> u64 {
-    ((((entry) & 0xFF00000000000000) >> 32)
+    (((entry) & 0xFF00000000000000) >> 32)
         | (((entry) & 0x000000FF00000000) >> 16)
-        | (((entry) & 0x00000000FFFF0000) >> 16))
+        | (((entry) & 0x00000000FFFF0000) >> 16)
 }
 
 fn get_limit(entry: u64) -> u32 {
diff --git a/x86_64/src/interrupts.rs b/x86_64/src/interrupts.rs
index a2b0f1c..5fba859 100644
--- a/x86_64/src/interrupts.rs
+++ b/x86_64/src/interrupts.rs
@@ -57,7 +57,7 @@ fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
 }
 
 fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
-    (((reg) & !0x700) | ((mode) << 8))
+    ((reg) & !0x700) | ((mode) << 8)
 }
 
 /// Configures LAPICs.  LAPIC0 is set for external interrupts, LAPIC1 is set for NMI.
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index a912edd..a4ba444 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -189,7 +189,15 @@ const CMDLINE_OFFSET: u64 = 0x20000;
 const CMDLINE_MAX_SIZE: u64 = KERNEL_START_OFFSET - CMDLINE_OFFSET;
 const X86_64_SERIAL_1_3_IRQ: u32 = 4;
 const X86_64_SERIAL_2_4_IRQ: u32 = 3;
-const X86_64_IRQ_BASE: u32 = 5;
+// X86_64_SCI_IRQ is used to fill the ACPI FACP table.
+// The sci_irq number is better to be a legacy
+// IRQ number which is less than 16(actually most of the
+// platforms have fixed IRQ number 9). So we can
+// reserve the IRQ number 5 for SCI and let the
+// the other devices starts from next.
+pub const X86_64_SCI_IRQ: u32 = 5;
+// So the IRQ_BASE start from SCI_IRQ + 1
+pub const X86_64_IRQ_BASE: u32 = X86_64_SCI_IRQ + 1;
 const ACPI_HI_RSDP_WINDOW_BASE: u64 = 0x000E0000;
 
 fn configure_system(
@@ -203,7 +211,6 @@ fn configure_system(
     setup_data: Option<GuestAddress>,
     initrd: Option<(GuestAddress, usize)>,
     mut params: boot_params,
-    sci_irq: u32,
 ) -> Result<()> {
     const EBDA_START: u64 = 0x0009fc00;
     const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
@@ -267,7 +274,7 @@ fn configure_system(
         .write_obj_at_addr(params, zero_page_addr)
         .map_err(|_| Error::ZeroPageSetup)?;
 
-    let rsdp_addr = acpi::create_acpi_tables(guest_mem, num_cpus, sci_irq);
+    let rsdp_addr = acpi::create_acpi_tables(guest_mem, num_cpus, X86_64_SCI_IRQ);
     params.acpi_rsdp_addr = rsdp_addr.0;
 
     Ok(())
@@ -404,8 +411,6 @@ impl arch::LinuxArch for X8664arch {
 
         // Event used to notify crosvm that guest OS is trying to suspend.
         let suspend_evt = EventFd::new().map_err(Error::CreateEventFd)?;
-        // allocate sci_irq to fill the ACPI FACP table
-        let sci_irq = resources.allocate_irq().ok_or(Error::AllocateIrq)?;
 
         let mut io_bus = Self::setup_io_bus(
             &mut vm,
@@ -492,7 +497,6 @@ impl arch::LinuxArch for X8664arch {
                     components.android_fstab,
                     kernel_end,
                     params,
-                    sci_irq,
                 )?;
             }
         }
@@ -579,7 +583,6 @@ impl X8664arch {
         android_fstab: Option<File>,
         kernel_end: u64,
         params: boot_params,
-        sci_irq: u32,
     ) -> Result<()> {
         kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)
             .map_err(Error::LoadCmdline)?;
@@ -641,7 +644,6 @@ impl X8664arch {
             setup_data,
             initrd,
             params,
-            sci_irq,
         )?;
         Ok(())
     }
@@ -723,7 +725,7 @@ impl X8664arch {
             let tty_string = get_serial_tty_string(stdio_serial_num);
             cmdline.insert("console", &tty_string).unwrap();
         }
-        cmdline.insert_str("acpi=off reboot=k panic=-1").unwrap();
+        cmdline.insert_str("pci=noacpi reboot=k panic=-1").unwrap();
 
         cmdline
     }
diff --git a/x86_64/src/mpspec.rs b/x86_64/src/mpspec.rs
index ab7af51..5340d9e 100644
--- a/x86_64/src/mpspec.rs
+++ b/x86_64/src/mpspec.rs
@@ -38,6 +38,7 @@ pub const MPC_APIC_USABLE: ::std::os::raw::c_uint = 1;
 pub const MP_IRQDIR_DEFAULT: ::std::os::raw::c_uint = 0;
 pub const MP_IRQDIR_HIGH: ::std::os::raw::c_uint = 1;
 pub const MP_IRQDIR_LOW: ::std::os::raw::c_uint = 3;
+pub const MP_LEVEL_TRIGGER: ::std::os::raw::c_uint = 0xc;
 pub const MP_APIC_ALL: ::std::os::raw::c_uint = 255;
 pub const MPC_OEM_SIGNATURE: &'static [u8; 5usize] = b"_OEM\x00";
 #[repr(C)]
diff --git a/x86_64/src/mptable.rs b/x86_64/src/mptable.rs
index 9aded3f..cac9e58 100644
--- a/x86_64/src/mptable.rs
+++ b/x86_64/src/mptable.rs
@@ -231,8 +231,9 @@ pub fn setup_mptable(
         base_mp = base_mp.unchecked_add(size as u64);
         checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
     }
+    let sci_irq = super::X86_64_SCI_IRQ as u8;
     // Per kvm_setup_default_irq_routing() in kernel
-    for i in 0..5 {
+    for i in 0..sci_irq {
         let size = mem::size_of::<mpc_intsrc>();
         let mut mpc_intsrc = mpc_intsrc::default();
         mpc_intsrc.type_ = MP_INTSRC as u8;
@@ -247,6 +248,25 @@ pub fn setup_mptable(
         base_mp = base_mp.unchecked_add(size as u64);
         checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
     }
+    // Insert SCI interrupt before PCI interrupts. Set the SCI interrupt
+    // to be the default trigger/polarity of PCI bus, which is level/low.
+    // This setting can be changed in future if necessary.
+    {
+        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_HIGH | MP_LEVEL_TRIGGER) as u16;
+        mpc_intsrc.srcbus = ISA_BUS_ID;
+        mpc_intsrc.srcbusirq = sci_irq;
+        mpc_intsrc.dstapic = ioapicid;
+        mpc_intsrc.dstirq = sci_irq;
+        mem.write_obj_at_addr(mpc_intsrc, base_mp)
+            .map_err(|_| Error::WriteMpcIntsrc)?;
+        base_mp = base_mp.unchecked_add(size as u64);
+        checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
+    }
+    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() {
         let size = mem::size_of::<mpc_intsrc>();
@@ -257,14 +277,14 @@ pub fn setup_mptable(
         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.dstapic = ioapicid;
-        mpc_intsrc.dstirq = 5 + i as u8;
+        mpc_intsrc.dstirq = pci_irq_base + i as u8;
         mem.write_obj_at_addr(mpc_intsrc, base_mp)
             .map_err(|_| Error::WriteMpcIntsrc)?;
         base_mp = base_mp.unchecked_add(size as u64);
         checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
     }
     // Finally insert ISA interrupts.
-    for i in 5 + pci_irqs.len()..16 {
+    for i in pci_irq_base + pci_irqs.len() as u8..16 {
         let size = mem::size_of::<mpc_intsrc>();
         let mut mpc_intsrc = mpc_intsrc::default();
         mpc_intsrc.type_ = MP_INTSRC as u8;