Merge 19 commits from origin/main (2022-03-10, cq-depend)

ee60fe49 crosvm: Change cargo resolver to 1 to fix issues with chromeos and b* builds
4d88ee98 Upstream data_model
cd75771e x86_64: acpi: always use virtual reset register
3fbf6c21 acpi: support GPE injection in command line
eb16dd51 acpi: support vGPE
6ca0323c acpi: support fixed power button event in command line
072c103b acpi: support fixed power button in vPM1
8f833c1f x86_64: acpi: always use vPM1 registers
80e6d5bf acpi: refactor PM1 virtualization
90922be6 crosvm: Enable plugin feature in CI builds
fb1df154 crosvm: Fix running plugins integration tests
5586ff50 linux: punch holes in guest memory for file mappings
de4d729c linux: allow file-backed mappings outside of MMIO regions
808bb0f5 resources: return overlapping Alloc in allocate_at
578e7cce base: remove wildcard and export specific sys_util symbols
449dd6b7 Don't add ramoops parameters except addr and size
4a3341ce crosvm: vvu: proxy: Handle sibling disconnect
bed40ad5 crosvm: migrate to Rust 2021 edition
6fd89263 crosvm: pci: prefer to/from le_bytes instead of manual byte manipulation

https://chromium.googlesource.com/chromiumos/platform/crosvm/+log/3e0a93a1e1fbd9e53c6d1dab94f9e219eb4d9f2a..ee60fe491fe15a1d8f5c7336175fab6284ca7f04

BUG=b:188011323
BUG=b:194136484
BUG=b:199383670
BUG=b:218891911
BUG=b:223855233,b:223821596
TEST=CQ

Cq-Depend: chromium:3491214
Change-Id: I29f1cca23bfacb60f9b58790e5c0b30217b54c80
diff --git a/Cargo.toml b/Cargo.toml
index 39c1ab6..16d39de 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,8 +2,10 @@
 name = "crosvm"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 default-run = "crosvm"
+# b:223855233
+resolver = "1"
 
 [lib]
 path = "src/crosvm.rs"
@@ -102,12 +104,6 @@
     "virgl_renderer",
     "x",
     ]
-all-linux-armhf = [
-    "composite-disk",
-    "default",
-    "gdb",
-    "tpm",
-    ]
 audio = ["devices/audio"]
 audio_cras = ["devices/audio_cras"]
 chromeos = ["base/chromeos", "audio_cras", "devices/chromeos"]
@@ -119,6 +115,14 @@
 gfxstream = ["devices/gfxstream"]
 gpu = ["devices/gpu"]
 libvda = ["devices/libvda"]
+linux-armhf = [
+    "composite-disk",
+    "default",
+    "gdb",
+    "tpm",
+    ]
+linux-x86_64 = ["all-linux", "plugin"]
+linux-aarch64 = ["all-linux"]
 plugin = ["protos/plugin", "crosvm_plugin", "kvm", "kvm_sys", "protobuf"]
 power-monitor-powerd = ["arch/power-monitor-powerd"]
 tpm = ["devices/tpm"]
diff --git a/aarch64/Cargo.toml b/aarch64/Cargo.toml
index 7ad86c2..3fb0c23 100644
--- a/aarch64/Cargo.toml
+++ b/aarch64/Cargo.toml
@@ -2,7 +2,7 @@
 name = "aarch64"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 arch = { path = "../arch" }
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
index 170227a..39a0e00 100644
--- a/aarch64/src/lib.rs
+++ b/aarch64/src/lib.rs
@@ -482,6 +482,7 @@
             rt_cpus: components.rt_cpus,
             delay_rt: components.delay_rt,
             bat_control: None,
+            pm: None,
             resume_notify_devices: Vec::new(),
             root_config: pci_root,
             hotplug_bus: Vec::new(),
diff --git a/acpi_tables/Cargo.toml b/acpi_tables/Cargo.toml
index 9537960..c6d5917 100644
--- a/acpi_tables/Cargo.toml
+++ b/acpi_tables/Cargo.toml
@@ -2,7 +2,7 @@
 name = "acpi_tables"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 data_model = { path = "../common/data_model" }
diff --git a/acpi_tables/src/sdt.rs b/acpi_tables/src/sdt.rs
index 50e7442..bda2630 100644
--- a/acpi_tables/src/sdt.rs
+++ b/acpi_tables/src/sdt.rs
@@ -96,13 +96,23 @@
         self.write(LENGTH_OFFSET, self.data.len() as u32);
     }
 
+    /// Read a value at the given offset
+    pub fn read<T: DataInit + Default>(&self, offset: usize) -> T {
+        let value_len = std::mem::size_of::<T>();
+        *T::from_slice(
+            self.as_slice()
+                .get(offset..offset + value_len)
+                .unwrap_or(T::default().as_slice()),
+        )
+        .unwrap()
+    }
+
     /// Write a value at the given offset
     pub fn write<T: DataInit>(&mut self, offset: usize, value: T) {
         let value_len = std::mem::size_of::<T>();
         if (offset + value_len) > self.data.len() {
             return;
         }
-
         self.data[offset..offset + value_len].copy_from_slice(value.as_slice());
         self.update_checksum();
     }
diff --git a/arch/Cargo.toml b/arch/Cargo.toml
index 62167d2..3706fa8 100644
--- a/arch/Cargo.toml
+++ b/arch/Cargo.toml
@@ -2,7 +2,7 @@
 name = "arch"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 power-monitor-powerd = ["power_monitor/powerd"]
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index 930abf2..afe259b 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -29,7 +29,7 @@
 use resources::{MmioType, SystemAllocator};
 use sync::Mutex;
 use thiserror::Error;
-use vm_control::{BatControl, BatteryType};
+use vm_control::{BatControl, BatteryType, PmResource};
 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
 
 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
@@ -123,6 +123,7 @@
     pub bat_control: Option<BatControl>,
     #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
     pub gdb: Option<(u32, Tube)>,
+    pub pm: Option<Arc<Mutex<dyn PmResource>>>,
     /// Devices to be notified before the system resumes from the S3 suspended state.
     pub resume_notify_devices: Vec<Arc<Mutex<dyn BusResumeDevice>>>,
     pub root_config: Arc<Mutex<PciRoot>>,
@@ -692,7 +693,7 @@
         create_monitor,
     )
     .map_err(DeviceRegistrationError::RegisterBattery)?;
-    Aml::to_aml_bytes(&goldfish_bat, amls);
+    goldfish_bat.to_aml_bytes(amls);
 
     match battery_jail.as_ref() {
         Some(jail) => {
diff --git a/arch/src/pstore.rs b/arch/src/pstore.rs
index bf90974..a914801 100644
--- a/arch/src/pstore.rs
+++ b/arch/src/pstore.rs
@@ -84,9 +84,6 @@
     let ramoops_opts = [
         ("mem_address", ramoops_region.address),
         ("mem_size", ramoops_region.size as u64),
-        ("console_size", (ramoops_region.size / 4) as u64),
-        ("record_size", (ramoops_region.size / 4) as u64),
-        ("dump_oops", 1_u64),
     ];
     for (name, val) in &ramoops_opts {
         cmdline.insert_str(format!("ramoops.{}={:#x}", name, val))?;
diff --git a/bit_field/Cargo.toml b/bit_field/Cargo.toml
index ac8846c..540412f 100644
--- a/bit_field/Cargo.toml
+++ b/bit_field/Cargo.toml
@@ -2,7 +2,7 @@
 name = "bit_field"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 bit_field_derive = { path = "bit_field_derive" }
diff --git a/bit_field/bit_field_derive/Cargo.toml b/bit_field/bit_field_derive/Cargo.toml
index 4642b93..dddf7b4 100644
--- a/bit_field/bit_field_derive/Cargo.toml
+++ b/bit_field/bit_field_derive/Cargo.toml
@@ -2,7 +2,7 @@
 name = "bit_field_derive"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 proc-macro2 = "^1"
diff --git a/common/assertions/Cargo.toml b/common/assertions/Cargo.toml
index 60b1c5e..6608f09 100644
--- a/common/assertions/Cargo.toml
+++ b/common/assertions/Cargo.toml
@@ -2,7 +2,7 @@
 name = "assertions"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 include = ["src/**/*", "Cargo.toml"]
 
 [workspace]
diff --git a/common/audio_streams/Cargo.toml b/common/audio_streams/Cargo.toml
index e014a6a..960dc4e 100644
--- a/common/audio_streams/Cargo.toml
+++ b/common/audio_streams/Cargo.toml
@@ -2,7 +2,7 @@
 name = "audio_streams"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 path = "src/audio_streams.rs"
diff --git a/common/balloon_control/Cargo.toml b/common/balloon_control/Cargo.toml
index 32384b1..11b8acf 100644
--- a/common/balloon_control/Cargo.toml
+++ b/common/balloon_control/Cargo.toml
@@ -2,7 +2,7 @@
 name = "balloon_control"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 serde = { version = "1", features = [ "derive" ] }
diff --git a/common/base/Cargo.toml b/common/base/Cargo.toml
index ca94518..3a57599 100644
--- a/common/base/Cargo.toml
+++ b/common/base/Cargo.toml
@@ -2,7 +2,7 @@
 name = "base"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 chromeos = ["sys_util/chromeos"]
diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs
index ac341ae..7be4ff5 100644
--- a/common/base/src/lib.rs
+++ b/common/base/src/lib.rs
@@ -2,7 +2,54 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-pub use sys_util::*;
+pub use sys_util::chown;
+pub use sys_util::drop_capabilities;
+pub use sys_util::handle_eintr_errno;
+pub use sys_util::iov_max;
+pub use sys_util::kernel_has_memfd;
+pub use sys_util::pipe;
+pub use sys_util::read_raw_stdin;
+pub use sys_util::syscall;
+pub use sys_util::syslog;
+pub use sys_util::EventFd;
+pub use sys_util::Fd;
+pub use sys_util::SignalFd;
+pub use sys_util::Terminal;
+pub use sys_util::TimerFd;
+pub use sys_util::UnsyncMarker;
+pub use sys_util::{add_fd_flags, clear_fd_flags};
+pub use sys_util::{
+    block_signal, clear_signal, get_blocked_signals, register_rt_signal_handler, signal,
+    unblock_signal, Killable, SIGRTMIN,
+};
+pub use sys_util::{
+    clone_descriptor, get_max_open_files, safe_descriptor_from_path, validate_raw_fd,
+    with_as_descriptor, with_raw_descriptor, AsRawDescriptor, Descriptor, FromRawDescriptor,
+    IntoRawDescriptor, RawDescriptor, SafeDescriptor, INVALID_DESCRIPTOR,
+};
+pub use sys_util::{debug, error, info, warn};
+pub use sys_util::{
+    enable_core_scheduling, get_cpu_affinity, set_cpu_affinity, set_rt_prio_limit,
+    set_rt_round_robin,
+};
+pub use sys_util::{errno_result, Error, Result};
+pub use sys_util::{flock, FlockOperation};
+pub use sys_util::{get_filesystem_type, open_file, FileFlags, PunchHole, WriteZeroes};
+pub use sys_util::{getegid, geteuid};
+pub use sys_util::{getpid, gettid, kill_process_group, reap_child};
+pub use sys_util::{ioctl_io_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr};
+pub use sys_util::{
+    net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener},
+    ScmSocket, UnlinkUnixListener,
+};
+pub use sys_util::{pagesize, round_up_to_page_size};
+pub use sys_util::{Clock, FakeClock};
+pub use sys_util::{EpollContext, EpollEvents};
+pub use sys_util::{ExternalMapping, ExternalMappingError, ExternalMappingResult};
+pub use sys_util::{
+    LayoutAllocation, MappedRegion, MemfdSeals, MemoryMappingArena, MmapError, Protection,
+};
+pub use sys_util::{PollToken, WatchingEvents};
 
 mod async_types;
 mod event;
diff --git a/common/base/src/tube.rs b/common/base/src/tube.rs
index 82c2ad5..934360d 100644
--- a/common/base/src/tube.rs
+++ b/common/base/src/tube.rs
@@ -8,7 +8,7 @@
 use std::os::unix::prelude::{AsRawFd, RawFd};
 use std::time::Duration;
 
-use crate::{net::UnixSeqpacket, FromRawDescriptor, SafeDescriptor, ScmSocket, UnsyncMarker};
+use crate::{FromRawDescriptor, SafeDescriptor, ScmSocket, UnixSeqpacket, UnsyncMarker};
 
 use cros_async::{Executor, IntoAsync, IoSourceExt};
 use remain::sorted;
diff --git a/common/cros-fuzz/Cargo.toml b/common/cros-fuzz/Cargo.toml
index e91baf2..c304d95 100644
--- a/common/cros-fuzz/Cargo.toml
+++ b/common/cros-fuzz/Cargo.toml
@@ -2,7 +2,7 @@
 name = "cros_fuzz"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 include = ["Cargo.toml", "src/*.rs"]
 
 [dependencies]
diff --git a/common/cros_async/Cargo.toml b/common/cros_async/Cargo.toml
index d8ee886..009dbbf 100644
--- a/common/cros_async/Cargo.toml
+++ b/common/cros_async/Cargo.toml
@@ -2,7 +2,7 @@
 name = "cros_async"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 async-trait = "0.1.36"
diff --git a/common/cros_asyncv2/Cargo.toml b/common/cros_asyncv2/Cargo.toml
index 468ed90..0dac52b 100644
--- a/common/cros_asyncv2/Cargo.toml
+++ b/common/cros_asyncv2/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "cros_async"
 version = "0.2.0"
-edition = "2018"
+edition = "2021"
 
 [features]
 uring = ["io-uring"]
diff --git a/common/data_model/Cargo.toml b/common/data_model/Cargo.toml
index 4401e0e..d3e3f3e 100644
--- a/common/data_model/Cargo.toml
+++ b/common/data_model/Cargo.toml
@@ -2,14 +2,21 @@
 name = "data_model"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 include = ["src/**/*", "Cargo.toml"]
 
 [dependencies]
 assertions = { path = "../assertions" } # provided by ebuild
+cfg-if = "1.0.0"
 libc = "*"
 remain = "0.2"
 serde = { version = "1", features = ["derive"] }
 thiserror = "1.0.20"
 
+[target.'cfg(windows)'.dependencies]
+winapi = { version = "*", features = ["everything", "std", "impl-default"] }
+
+[target.'cfg(unix)'.dependencies]
+libc = "*"
+
 [workspace]
diff --git a/common/data_model/src/lib.rs b/common/data_model/src/lib.rs
index 708bed3..4585b19 100644
--- a/common/data_model/src/lib.rs
+++ b/common/data_model/src/lib.rs
@@ -187,4 +187,4 @@
 pub use flexible_array::{vec_with_array_field, FlexibleArray, FlexibleArrayWrapper};
 
 mod sys;
-pub use sys::IoBufMut;
+pub use sys::{create_iobuf, IoBuf, IoBufMut};
diff --git a/common/data_model/src/sys.rs b/common/data_model/src/sys.rs
index 0099e06..2bf1722 100644
--- a/common/data_model/src/sys.rs
+++ b/common/data_model/src/sys.rs
@@ -1,134 +1,16 @@
-// 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.
+// Copyright 2022 The Chromium OS Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+//
+//! A wrapper module for platform dependent code.
 
-use std::ffi::c_void;
-use std::fmt::{self, Debug};
-use std::marker::PhantomData;
-use std::slice;
-
-use libc::iovec;
-
-/// This type is essentialy `std::io::IoBufMut`, and guaranteed to be ABI-compatible with
-/// `libc::iovec`; however, it does NOT automatically deref to `&mut [u8]`, which is critical
-/// because it can point to guest memory. (Guest memory is implicitly mutably borrowed by the
-/// guest, so another mutable borrow would violate Rust assumptions about references.)
-#[derive(Copy, Clone)]
-#[repr(transparent)]
-pub struct IoBufMut<'a> {
-    iov: iovec,
-    phantom: PhantomData<&'a mut [u8]>,
-}
-
-impl<'a> IoBufMut<'a> {
-    pub fn new(buf: &mut [u8]) -> IoBufMut<'a> {
-        // Safe because buf's memory is of the supplied length, and
-        // guaranteed to exist for the lifetime of the returned value.
-        unsafe { Self::from_raw_parts(buf.as_mut_ptr(), buf.len()) }
-    }
-
-    /// Creates a `IoBufMut` from a pointer and a length.
-    ///
-    /// # Safety
-    ///
-    /// In order to use this method safely, `addr` must be valid for reads and writes of `len` bytes
-    /// and should live for the entire duration of lifetime `'a`.
-    pub unsafe fn from_raw_parts(addr: *mut u8, len: usize) -> IoBufMut<'a> {
-        IoBufMut {
-            iov: iovec {
-                iov_base: addr as *mut c_void,
-                iov_len: len,
-            },
-            phantom: PhantomData,
-        }
-    }
-
-    /// Advance the internal position of the buffer.
-    ///
-    /// Panics if `count > self.len()`.
-    pub fn advance(&mut self, count: usize) {
-        assert!(count <= self.len());
-
-        self.iov.iov_len -= count;
-        // Safe because we've checked that `count <= self.len()` so both the starting and resulting
-        // pointer are within the bounds of the allocation.
-        self.iov.iov_base = unsafe { self.iov.iov_base.add(count) };
-    }
-
-    /// Shorten the length of the buffer.
-    ///
-    /// Has no effect if `len > self.len()`.
-    pub fn truncate(&mut self, len: usize) {
-        if len < self.len() {
-            self.iov.iov_len = len;
-        }
-    }
-
-    #[inline]
-    pub fn len(&self) -> usize {
-        self.iov.iov_len as usize
-    }
-
-    #[inline]
-    pub fn is_empty(&self) -> bool {
-        self.iov.iov_len == 0
-    }
-
-    /// Gets a const pointer to this slice's memory.
-    #[inline]
-    pub fn as_ptr(&self) -> *const u8 {
-        self.iov.iov_base as *const u8
-    }
-
-    /// Gets a mutable pointer to this slice's memory.
-    #[inline]
-    pub fn as_mut_ptr(&self) -> *mut u8 {
-        self.iov.iov_base as *mut u8
-    }
-
-    /// Converts a slice of `IoBufMut`s into a slice of `iovec`s.
-    #[allow(clippy::wrong_self_convention)]
-    #[inline]
-    pub fn as_iobufs<'slice>(iovs: &'slice [IoBufMut<'_>]) -> &'slice [iovec] {
-        // Safe because `IoBufMut` is ABI-compatible with `iovec`.
-        unsafe { slice::from_raw_parts(iovs.as_ptr() as *const libc::iovec, iovs.len()) }
-    }
-}
-
-impl<'a> AsRef<libc::iovec> for IoBufMut<'a> {
-    fn as_ref(&self) -> &libc::iovec {
-        &self.iov
-    }
-}
-
-impl<'a> AsMut<libc::iovec> for IoBufMut<'a> {
-    fn as_mut(&mut self) -> &mut libc::iovec {
-        &mut self.iov
-    }
-}
-
-// It's safe to implement Send + Sync for this type for the same reason that `std::io::IoBufMut`
-// is Send + Sync. Internally, it contains a pointer and a length. The integer length is safely Send
-// + Sync.  There's nothing wrong with sending a pointer between threads and de-referencing the
-// pointer requires an unsafe block anyway. See also https://github.com/rust-lang/rust/pull/70342.
-unsafe impl<'a> Send for IoBufMut<'a> {}
-unsafe impl<'a> Sync for IoBufMut<'a> {}
-
-struct DebugIovec(iovec);
-impl Debug for DebugIovec {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.debug_struct("iovec")
-            .field("iov_base", &self.0.iov_base)
-            .field("iov_len", &self.0.iov_len)
-            .finish()
-    }
-}
-
-impl<'a> Debug for IoBufMut<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.debug_struct("IoBufMut")
-            .field("iov", &DebugIovec(self.iov))
-            .field("phantom", &self.phantom)
-            .finish()
-    }
+cfg_if::cfg_if! {
+     if #[cfg(unix)] {
+        mod unix;
+        pub use unix::*;
+     } else if #[cfg(windows)] {
+        mod windows;
+        pub use windows::*;
+     } else {
+        compile_error!("Unsupported platform");
+     }
 }
diff --git a/common/data_model/src/sys/unix.rs b/common/data_model/src/sys/unix.rs
new file mode 100644
index 0000000..312668c
--- /dev/null
+++ b/common/data_model/src/sys/unix.rs
@@ -0,0 +1,158 @@
+// 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::ffi::c_void;
+use std::fmt::{self, Debug};
+use std::marker::PhantomData;
+use std::slice;
+
+use libc::iovec;
+
+/// Cross platform binary compatible iovec.
+pub type IoBuf = iovec;
+
+/// Cross platform stub to create a platform specific IoBuf.
+pub fn create_iobuf(addr: *mut u8, len: usize) -> IoBuf {
+    iovec {
+        iov_base: addr as *mut c_void,
+        iov_len: len,
+    }
+}
+
+/// This type is essentialy `std::io::IoBufMut`, and guaranteed to be ABI-compatible with
+/// `libc::iovec`; however, it does NOT automatically deref to `&mut [u8]`, which is critical
+/// because it can point to guest memory. (Guest memory is implicitly mutably borrowed by the
+/// guest, so another mutable borrow would violate Rust assumptions about references.)
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoBufMut<'a> {
+    iov: iovec,
+    phantom: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoBufMut<'a> {
+    pub fn new(buf: &mut [u8]) -> IoBufMut<'a> {
+        // Safe because buf's memory is of the supplied length, and
+        // guaranteed to exist for the lifetime of the returned value.
+        unsafe { Self::from_raw_parts(buf.as_mut_ptr(), buf.len()) }
+    }
+
+    /// Creates a `IoBufMut` from a pointer and a length.
+    ///
+    /// # Safety
+    ///
+    /// In order to use this method safely, `addr` must be valid for reads and writes of `len` bytes
+    /// and should live for the entire duration of lifetime `'a`.
+    pub unsafe fn from_raw_parts(addr: *mut u8, len: usize) -> IoBufMut<'a> {
+        IoBufMut {
+            iov: iovec {
+                iov_base: addr as *mut c_void,
+                iov_len: len,
+            },
+            phantom: PhantomData,
+        }
+    }
+
+    /// Creates a `IoBufMut` from an IoBuf.
+    ///
+    /// # Safety
+    ///
+    /// In order to use this method safely, `iobuf` must be valid for reads and writes through its
+    /// length and should live for the entire duration of lifetime `'a`.
+    pub unsafe fn from_iobuf(iobuf: IoBuf) -> IoBufMut<'a> {
+        IoBufMut {
+            iov: iobuf,
+            phantom: PhantomData,
+        }
+    }
+
+    /// Advance the internal position of the buffer.
+    ///
+    /// Panics if `count > self.len()`.
+    pub fn advance(&mut self, count: usize) {
+        assert!(count <= self.len());
+
+        self.iov.iov_len -= count;
+        // Safe because we've checked that `count <= self.len()` so both the starting and resulting
+        // pointer are within the bounds of the allocation.
+        self.iov.iov_base = unsafe { self.iov.iov_base.add(count) };
+    }
+
+    /// Shorten the length of the buffer.
+    ///
+    /// Has no effect if `len > self.len()`.
+    pub fn truncate(&mut self, len: usize) {
+        if len < self.len() {
+            self.iov.iov_len = len;
+        }
+    }
+
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.iov.iov_len as usize
+    }
+
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.iov.iov_len == 0
+    }
+
+    /// Gets a const pointer to this slice's memory.
+    #[inline]
+    pub fn as_ptr(&self) -> *const u8 {
+        self.iov.iov_base as *const u8
+    }
+
+    /// Gets a mutable pointer to this slice's memory.
+    #[inline]
+    pub fn as_mut_ptr(&self) -> *mut u8 {
+        self.iov.iov_base as *mut u8
+    }
+
+    /// Converts a slice of `IoBufMut`s into a slice of `iovec`s.
+    #[allow(clippy::wrong_self_convention)]
+    #[inline]
+    pub fn as_iobufs<'slice>(iovs: &'slice [IoBufMut<'_>]) -> &'slice [iovec] {
+        // Safe because `IoBufMut` is ABI-compatible with `iovec`.
+        unsafe { slice::from_raw_parts(iovs.as_ptr() as *const libc::iovec, iovs.len()) }
+    }
+}
+
+impl<'a> AsRef<libc::iovec> for IoBufMut<'a> {
+    fn as_ref(&self) -> &libc::iovec {
+        &self.iov
+    }
+}
+
+impl<'a> AsMut<libc::iovec> for IoBufMut<'a> {
+    fn as_mut(&mut self) -> &mut libc::iovec {
+        &mut self.iov
+    }
+}
+
+// It's safe to implement Send + Sync for this type for the same reason that `std::io::IoBufMut`
+// is Send + Sync. Internally, it contains a pointer and a length. The integer length is safely Send
+// + Sync.  There's nothing wrong with sending a pointer between threads and de-referencing the
+// pointer requires an unsafe block anyway. See also https://github.com/rust-lang/rust/pull/70342.
+unsafe impl<'a> Send for IoBufMut<'a> {}
+unsafe impl<'a> Sync for IoBufMut<'a> {}
+
+struct DebugIovec(iovec);
+impl Debug for DebugIovec {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("iovec")
+            .field("iov_base", &self.0.iov_base)
+            .field("iov_len", &self.0.iov_len)
+            .finish()
+    }
+}
+
+impl<'a> Debug for IoBufMut<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("IoBufMut")
+            .field("iov", &DebugIovec(self.iov))
+            .field("phantom", &self.phantom)
+            .finish()
+    }
+}
diff --git a/common/data_model/src/sys/windows.rs b/common/data_model/src/sys/windows.rs
new file mode 100644
index 0000000..4e3ae83
--- /dev/null
+++ b/common/data_model/src/sys/windows.rs
@@ -0,0 +1,133 @@
+use std::fmt::{self, Debug};
+use std::marker::PhantomData;
+use std::slice;
+
+use winapi::shared::ws2def::WSABUF;
+
+/// Cross platform binary compatible iovec.
+pub type IoBuf = WSABUF;
+
+/// Cross platform stub to create a platform specific IoBuf.
+pub fn create_iobuf(addr: *mut u8, len: usize) -> IoBuf {
+    WSABUF {
+        buf: addr as *mut i8,
+        len: len as u32,
+    }
+}
+
+/// This type is essentialy `std::io::IoBufMut`, and guaranteed to be ABI-compatible with
+/// `WSABUF`; however, it does NOT automatically deref to `&mut [u8]`, which is critical
+/// because it can point to guest memory. (Guest memory is implicitly mutably borrowed by the
+/// guest, so another mutable borrow would violate Rust assumptions about references.)
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoBufMut<'a> {
+    buf: WSABUF,
+    phantom: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoBufMut<'a> {
+    pub fn new(buf: &mut [u8]) -> IoBufMut<'a> {
+        // Safe because buf's memory is of the supplied length, and
+        // guaranteed to exist for the lifetime of the returned value.
+        unsafe { Self::from_raw_parts(buf.as_mut_ptr(), buf.len()) }
+    }
+
+    /// Creates a `IoBufMut` from a pointer and a length.
+    ///
+    /// # Safety
+    ///
+    /// In order to use this method safely, `addr` must be valid for reads and writes of `len` bytes
+    /// and should live for the entire duration of lifetime `'a`.
+    pub unsafe fn from_raw_parts(addr: *mut u8, len: usize) -> IoBufMut<'a> {
+        IoBufMut {
+            buf: WSABUF {
+                buf: addr as *mut i8,
+                len: len as u32,
+            },
+            phantom: PhantomData,
+        }
+    }
+
+    /// Creates a `IoBufMut` from an IoBuf.
+    ///
+    /// # Safety
+    ///
+    /// In order to use this method safely, `iobuf` must be valid for reads and writes through its
+    /// length and should live for the entire duration of lifetime `'a`.
+    pub unsafe fn from_iobuf(iobuf: IoBuf) -> IoBufMut<'a> {
+        IoBufMut {
+            buf: iobuf,
+            phantom: PhantomData,
+        }
+    }
+
+    /// Advance the internal position of the buffer.
+    ///
+    /// Panics if `count > self.len()`.
+    pub fn advance(&mut self, _count: usize) {
+        unimplemented!()
+    }
+
+    /// Shorten the length of the buffer.
+    ///
+    /// Has no effect if `len > self.len()`.
+    pub fn truncate(&mut self, _len: usize) {
+        unimplemented!()
+    }
+
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.buf.len as usize
+    }
+
+    /// Gets a const pointer to this slice's memory.
+    #[inline]
+    pub fn as_ptr(&self) -> *const u8 {
+        self.buf.buf as *const u8
+    }
+
+    /// Gets a mutable pointer to this slice's memory.
+    #[inline]
+    pub fn as_mut_ptr(&self) -> *mut u8 {
+        self.buf.buf as *mut u8
+    }
+
+    /// Converts a slice of `IoBufMut`s into a slice of `iovec`s.
+    #[inline]
+    pub fn as_iobufs<'slice>(iovs: &'slice [IoBufMut<'_>]) -> &'slice [IoBuf] {
+        // Safe because `IoBufMut` is ABI-compatible with `WSABUF`.
+        unsafe { slice::from_raw_parts(iovs.as_ptr() as *const WSABUF, iovs.len()) }
+    }
+}
+
+impl<'a> AsRef<WSABUF> for IoBufMut<'a> {
+    fn as_ref(&self) -> &WSABUF {
+        &self.buf
+    }
+}
+
+impl<'a> AsMut<WSABUF> for IoBufMut<'a> {
+    fn as_mut(&mut self) -> &mut WSABUF {
+        &mut self.buf
+    }
+}
+
+struct DebugWSABUF(WSABUF);
+impl Debug for DebugWSABUF {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("WSABUF")
+            .field("buf", &self.0.buf)
+            .field("len", &self.0.len)
+            .finish()
+    }
+}
+
+impl<'a> Debug for IoBufMut<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("IoBufMut")
+            .field("buf", &DebugWSABUF(self.buf))
+            .field("phantom", &self.phantom)
+            .finish()
+    }
+}
diff --git a/common/io_uring/Cargo.toml b/common/io_uring/Cargo.toml
index d420641..47b3138 100644
--- a/common/io_uring/Cargo.toml
+++ b/common/io_uring/Cargo.toml
@@ -2,7 +2,7 @@
 name = "io_uring"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 data_model = { path = "../data_model" } # provided by ebuild
diff --git a/common/p9/Cargo.toml b/common/p9/Cargo.toml
index 90616f3..45c5d0d 100644
--- a/common/p9/Cargo.toml
+++ b/common/p9/Cargo.toml
@@ -2,7 +2,7 @@
 name = "p9"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 libc = "*"
diff --git a/common/p9/fuzz/Cargo.toml b/common/p9/fuzz/Cargo.toml
index fe9af4a..76ccc60 100644
--- a/common/p9/fuzz/Cargo.toml
+++ b/common/p9/fuzz/Cargo.toml
@@ -2,7 +2,7 @@
 name = "p9-fuzz"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 p9 = { path = "../" }
diff --git a/common/sync/Cargo.toml b/common/sync/Cargo.toml
index af38fd1..953d41b 100644
--- a/common/sync/Cargo.toml
+++ b/common/sync/Cargo.toml
@@ -2,7 +2,7 @@
 name = "sync"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 include = ["src/**/*", "Cargo.toml"]
 
 [workspace]
diff --git a/common/sys_util/Cargo.toml b/common/sys_util/Cargo.toml
index 2971468..933dbda 100644
--- a/common/sys_util/Cargo.toml
+++ b/common/sys_util/Cargo.toml
@@ -2,7 +2,7 @@
 name = "sys_util"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 include = ["src/**/*", "Cargo.toml"]
 
 [dependencies]
diff --git a/common/sys_util/src/signal.rs b/common/sys_util/src/signal.rs
index 97fa64b..3dba86e 100644
--- a/common/sys_util/src/signal.rs
+++ b/common/sys_util/src/signal.rs
@@ -549,19 +549,12 @@
 
 /// Treat some errno's as Ok(()).
 macro_rules! ok_if {
-    ($result:expr, $($errno:pat)|+) => {{
-    let res = $result;
-    match res {
-        Ok(_) => Ok(()),
-        Err(err) => {
-            if matches!(err.errno(), $($errno)|+) {
-                Ok(())
-            } else {
-                Err(err)
-            }
+    ($result:expr, $errno:pat) => {{
+        match $result {
+            Err(err) if !matches!(err.errno(), $errno) => Err(err),
+            _ => Ok(()),
         }
-    }
-    }}
+    }};
 }
 
 /// Terminates and reaps a child process. If the child process is a group leader, its children will
diff --git a/common/sys_util_core/Cargo.toml b/common/sys_util_core/Cargo.toml
index 2bf2cac..2546a51 100644
--- a/common/sys_util_core/Cargo.toml
+++ b/common/sys_util_core/Cargo.toml
@@ -2,7 +2,7 @@
 name = "sys_util_core"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 include = ["src/**/*", "Cargo.toml"]
 
 [dependencies]
diff --git a/common/sys_util_core/poll_token_derive/Cargo.toml b/common/sys_util_core/poll_token_derive/Cargo.toml
index 0b4d41c..6954015 100644
--- a/common/sys_util_core/poll_token_derive/Cargo.toml
+++ b/common/sys_util_core/poll_token_derive/Cargo.toml
@@ -2,7 +2,7 @@
 name = "poll_token_derive"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 include = ["*.rs", "Cargo.toml"]
 
 [lib]
diff --git a/crosvm-fuzz/Cargo.toml b/crosvm-fuzz/Cargo.toml
index e7f8e8b..0b54611 100644
--- a/crosvm-fuzz/Cargo.toml
+++ b/crosvm-fuzz/Cargo.toml
@@ -2,7 +2,7 @@
 name = "crosvm-fuzz"
 version = "0.0.1"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 cros_fuzz = "*"
diff --git a/crosvm_control/Cargo.toml b/crosvm_control/Cargo.toml
index 4554344..7f97143 100644
--- a/crosvm_control/Cargo.toml
+++ b/crosvm_control/Cargo.toml
@@ -2,7 +2,7 @@
 name = "crosvm_control"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 crate-type = ["cdylib"]
diff --git a/crosvm_plugin/Cargo.toml b/crosvm_plugin/Cargo.toml
index 8531cf6..d1a631b 100644
--- a/crosvm_plugin/Cargo.toml
+++ b/crosvm_plugin/Cargo.toml
@@ -2,7 +2,7 @@
 name = "crosvm_plugin"
 version = "0.17.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 stats = []
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index 10a6c91..b49572a 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -2,7 +2,7 @@
 name = "devices"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 audio = []
diff --git a/devices/src/acpi.rs b/devices/src/acpi.rs
index 8160057..e6e1c65 100644
--- a/devices/src/acpi.rs
+++ b/devices/src/acpi.rs
@@ -5,51 +5,145 @@
 use crate::{BusAccessInfo, BusDevice, BusResumeDevice};
 use acpi_tables::{aml, aml::Aml};
 use base::{error, warn, Event};
+use vm_control::PmResource;
 
 /// ACPI PM resource for handling OS suspend/resume request
+#[allow(dead_code)]
 pub struct ACPIPMResource {
+    sci_evt: Event,
     suspend_evt: Event,
     exit_evt: Event,
     pm1_status: u16,
     pm1_enable: u16,
     pm1_control: u16,
-    sleep_control: u8,
-    sleep_status: u8,
+    gpe0_status: [u8; ACPIPM_RESOURCE_GPE0_BLK_LEN as usize / 2],
+    gpe0_enable: [u8; ACPIPM_RESOURCE_GPE0_BLK_LEN as usize / 2],
 }
 
 impl ACPIPMResource {
     /// Constructs ACPI Power Management Resouce.
-    pub fn new(suspend_evt: Event, exit_evt: Event) -> ACPIPMResource {
+    #[allow(dead_code)]
+    pub fn new(sci_evt: Event, suspend_evt: Event, exit_evt: Event) -> ACPIPMResource {
         ACPIPMResource {
+            sci_evt,
             suspend_evt,
             exit_evt,
             pm1_status: 0,
             pm1_enable: 0,
             pm1_control: 0,
-            sleep_control: 0,
-            sleep_status: 0,
+            gpe0_status: Default::default(),
+            gpe0_enable: Default::default(),
+        }
+    }
+
+    fn pm_sci(&self) {
+        if self.pm1_status
+            & self.pm1_enable
+            & (BITMASK_PM1EN_GBL_EN
+                | BITMASK_PM1EN_PWRBTN_EN
+                | BITMASK_PM1EN_SLPBTN_EN
+                | BITMASK_PM1EN_RTC_EN)
+            != 0
+        {
+            if let Err(e) = self.sci_evt.write(1) {
+                error!("ACPIPM: failed to trigger sci event: {}", e);
+            }
+        }
+    }
+
+    fn gpe_sci(&self) {
+        for i in 0..self.gpe0_status.len() {
+            if self.gpe0_status[i] & self.gpe0_enable[i] != 0 {
+                if let Err(e) = self.sci_evt.write(1) {
+                    error!("ACPIPM: failed to trigger sci event: {}", e);
+                }
+                return;
+            }
         }
     }
 }
 
 /// the ACPI PM register length.
-pub const ACPIPM_RESOURCE_LEN: u8 = 8;
 pub const ACPIPM_RESOURCE_EVENTBLK_LEN: u8 = 4;
 pub const ACPIPM_RESOURCE_CONTROLBLK_LEN: u8 = 2;
+pub const ACPIPM_RESOURCE_GPE0_BLK_LEN: u8 = 64;
+pub const ACPIPM_RESOURCE_LEN: u8 = ACPIPM_RESOURCE_EVENTBLK_LEN + 4 + ACPIPM_RESOURCE_GPE0_BLK_LEN;
 
 /// ACPI PM register value definitions
-const PM1_STATUS: u16 = 0;
-const PM1_ENABLE: u16 = 2;
-const PM1_CONTROL: u16 = 4;
-const SLEEP_CONTROL: u16 = 6;
-const SLEEP_STATUS: u16 = 7;
-const BITMASK_PM1CNT_SLEEP_ENABLE: u16 = 0x2000;
-const BITMASK_SLEEPCNT_SLEEP_ENABLE: u8 = 0x20;
-const BITMASK_PM1CNT_WAKE_STATUS: u16 = 0x8000;
-const BITMASK_SLEEPCNT_WAKE_STATUS: u8 = 0x80;
 
+/// 4.8.4.1.1 PM1 Status Registers, ACPI Spec Version 6.4
+/// Register Location: <PM1a_EVT_BLK / PM1b_EVT_BLK> System I/O or Memory Space (defined in FADT)
+/// Size: PM1_EVT_LEN / 2 (defined in FADT)
+const PM1_STATUS: u16 = 0;
+
+/// 4.8.4.1.2 PM1Enable Registers, ACPI Spec Version 6.4
+/// Register Location: <<PM1a_EVT_BLK / PM1b_EVT_BLK> + PM1_EVT_LEN / 2 System I/O or Memory Space
+/// (defined in FADT)
+/// Size: PM1_EVT_LEN / 2 (defined in FADT)
+const PM1_ENABLE: u16 = PM1_STATUS + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2);
+
+/// 4.8.4.2.1 PM1 Control Registers, ACPI Spec Version 6.4
+/// Register Location: <PM1a_CNT_BLK / PM1b_CNT_BLK> System I/O or Memory Space (defined in FADT)
+/// Size: PM1_CNT_LEN (defined in FADT)
+const PM1_CONTROL: u16 = PM1_STATUS + ACPIPM_RESOURCE_EVENTBLK_LEN as u16;
+
+/// 4.8.5.1 General-Purpose Event Register Blocks, ACPI Spec Version 6.4
+/// - Each register block contains two registers: an enable and a status register.
+/// - Each register block is 32-bit aligned.
+/// - Each register in the block is accessed as a byte.
+
+/// 4.8.5.1.1 General-Purpose Event 0 Register Block, ACPI Spec Version 6.4
+/// This register block consists of two registers: The GPE0_STS and the GPE0_EN registers. Each
+/// register’s length is defined to be half the length of the GPE0 register block, and is described
+/// in the ACPI FADT’s GPE0_BLK and GPE0_BLK_LEN operators.
+
+/// 4.8.5.1.1.1 General-Purpose Event 0 Status Register, ACPI Spec Version 6.4
+/// Register Location: <GPE0_STS> System I/O or System Memory Space (defined in FADT)
+/// Size: GPE0_BLK_LEN/2 (defined in FADT)
+const GPE0_STATUS: u16 = PM1_STATUS + ACPIPM_RESOURCE_EVENTBLK_LEN as u16 + 4; // ensure alignment
+
+/// 4.8.5.1.1.2 General-Purpose Event 0 Enable Register, ACPI Spec Version 6.4
+/// Register Location: <GPE0_EN> System I/O or System Memory Space (defined in FADT)
+/// Size: GPE0_BLK_LEN/2 (defined in FADT)
+const GPE0_ENABLE: u16 = GPE0_STATUS + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2);
+
+const BITMASK_PM1STS_PWRBTN_STS: u16 = 1 << 8;
+const BITMASK_PM1EN_GBL_EN: u16 = 1 << 5;
+const BITMASK_PM1EN_PWRBTN_EN: u16 = 1 << 8;
+const BITMASK_PM1EN_SLPBTN_EN: u16 = 1 << 9;
+const BITMASK_PM1EN_RTC_EN: u16 = 1 << 10;
+const BITMASK_PM1CNT_SLEEP_ENABLE: u16 = 0x2000;
+const BITMASK_PM1CNT_WAKE_STATUS: u16 = 0x8000;
+
+#[cfg(not(feature = "direct"))]
 const BITMASK_PM1CNT_SLEEP_TYPE: u16 = 0x1C00;
-const SLEEP_TYPE_S5: u16 = 0;
+#[cfg(not(feature = "direct"))]
+const SLEEP_TYPE_S1: u16 = 1 << 10;
+#[cfg(not(feature = "direct"))]
+const SLEEP_TYPE_S5: u16 = 0 << 10;
+
+impl PmResource for ACPIPMResource {
+    fn pwrbtn_evt(&mut self) {
+        self.pm1_status |= BITMASK_PM1STS_PWRBTN_STS;
+        self.pm_sci();
+    }
+
+    fn gpe_evt(&mut self, gpe: u32) {
+        let byte = gpe as usize / 8;
+        if byte >= self.gpe0_status.len() {
+            error!("gpe_evt: GPE register {} does not exist", byte);
+            return;
+        }
+        self.gpe0_status[byte] |= 1 << (gpe % 8);
+        self.gpe_sci();
+    }
+}
+
+const PM1_STATUS_LAST: u16 = PM1_STATUS + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2) - 1;
+const PM1_ENABLE_LAST: u16 = PM1_ENABLE + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2) - 1;
+const PM1_CONTROL_LAST: u16 = PM1_CONTROL + ACPIPM_RESOURCE_CONTROLBLK_LEN as u16 - 1;
+const GPE0_STATUS_LAST: u16 = GPE0_STATUS + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2) - 1;
+const GPE0_ENABLE_LAST: u16 = GPE0_ENABLE + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2) - 1;
 
 impl BusDevice for ACPIPMResource {
     fn debug_label(&self) -> String {
@@ -57,69 +151,156 @@
     }
 
     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
-        let val = match info.offset as u16 {
-            PM1_STATUS => self.pm1_status,
-            PM1_ENABLE => self.pm1_enable,
-            PM1_CONTROL => self.pm1_control,
-            SLEEP_CONTROL => self.sleep_control as u16,
-            SLEEP_STATUS => self.sleep_status as u16,
+        match info.offset as u16 {
+            // Accesses to the PM1 registers are done through byte or word accesses
+            PM1_STATUS..=PM1_STATUS_LAST => {
+                if data.len() > std::mem::size_of::<u16>()
+                    || info.offset + data.len() as u64 > (PM1_STATUS_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad read size: {}", data.len());
+                    return;
+                }
+                let offset = (info.offset - PM1_STATUS as u64) as usize;
+                data.copy_from_slice(&self.pm1_status.to_ne_bytes()[offset..offset + data.len()]);
+            }
+            PM1_ENABLE..=PM1_ENABLE_LAST => {
+                if data.len() > std::mem::size_of::<u16>()
+                    || info.offset + data.len() as u64 > (PM1_ENABLE_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad read size: {}", data.len());
+                    return;
+                }
+                let offset = (info.offset - PM1_ENABLE as u64) as usize;
+                data.copy_from_slice(&self.pm1_enable.to_ne_bytes()[offset..offset + data.len()]);
+            }
+            PM1_CONTROL..=PM1_CONTROL_LAST => {
+                if data.len() > std::mem::size_of::<u16>()
+                    || info.offset + data.len() as u64 > (PM1_CONTROL_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad read size: {}", data.len());
+                    return;
+                }
+                let offset = (info.offset - PM1_CONTROL as u64) as usize;
+                data.copy_from_slice(&self.pm1_control.to_ne_bytes()[offset..offset + data.len()]);
+            }
+            // OSPM accesses GPE registers through byte accesses (regardless of their length)
+            GPE0_STATUS..=GPE0_STATUS_LAST => {
+                if data.len() > std::mem::size_of::<u8>()
+                    || info.offset + data.len() as u64 > (GPE0_STATUS_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad read size: {}", data.len());
+                    return;
+                }
+                data[0] = self.gpe0_status[(info.offset - GPE0_STATUS as u64) as usize];
+            }
+            GPE0_ENABLE..=GPE0_ENABLE_LAST => {
+                if data.len() > std::mem::size_of::<u8>()
+                    || info.offset + data.len() as u64 > (GPE0_ENABLE_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad read size: {}", data.len());
+                    return;
+                }
+                data[0] = self.gpe0_enable[(info.offset - GPE0_ENABLE as u64) as usize];
+            }
             _ => {
                 warn!("ACPIPM: Bad read from {}", info);
-                return;
-            }
-        };
-
-        let val_arr = val.to_ne_bytes();
-        for i in 0..std::mem::size_of::<u16>() {
-            if i < data.len() {
-                data[i] = val_arr[i];
             }
         }
     }
 
     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
-        let max_bytes = std::mem::size_of::<u16>();
-
-        // only allow maximum max_bytes to write
-        if data.len() > max_bytes {
-            warn!("ACPIPM: bad write size: {}", data.len());
-            return;
-        }
-
-        let mut val_arr = u16::to_ne_bytes(0u16);
-        for i in 0..std::mem::size_of::<u16>() {
-            if i < data.len() {
-                val_arr[i] = data[i];
-            }
-        }
-        let val = u16::from_ne_bytes(val_arr);
-
         match info.offset as u16 {
-            PM1_STATUS => self.pm1_status &= !val,
-            PM1_ENABLE => self.pm1_enable = val,
-            PM1_CONTROL => {
-                if (val & BITMASK_PM1CNT_SLEEP_ENABLE) == BITMASK_PM1CNT_SLEEP_ENABLE {
-                    if val & BITMASK_PM1CNT_SLEEP_TYPE == SLEEP_TYPE_S5 {
-                        if let Err(e) = self.exit_evt.write(1) {
-                            error!("ACPIPM: failed to trigger exit event: {}", e);
+            // Accesses to the PM1 registers are done through byte or word accesses
+            PM1_STATUS..=PM1_STATUS_LAST => {
+                if data.len() > std::mem::size_of::<u16>()
+                    || info.offset + data.len() as u64 > (PM1_STATUS_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad write size: {}", data.len());
+                    return;
+                }
+                let offset = (info.offset - PM1_STATUS as u64) as usize;
+                let mut v = self.pm1_status.to_ne_bytes();
+                for (i, j) in (offset..offset + data.len()).enumerate() {
+                    v[j] &= !data[i];
+                }
+                self.pm1_status = u16::from_ne_bytes(v);
+            }
+            PM1_ENABLE..=PM1_ENABLE_LAST => {
+                if data.len() > std::mem::size_of::<u16>()
+                    || info.offset + data.len() as u64 > (PM1_ENABLE_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad write size: {}", data.len());
+                    return;
+                }
+                let offset = (info.offset - PM1_ENABLE as u64) as usize;
+                let mut v = self.pm1_enable.to_ne_bytes();
+                for (i, j) in (offset..offset + data.len()).enumerate() {
+                    v[j] = data[i];
+                }
+                self.pm1_enable = u16::from_ne_bytes(v);
+                self.pm_sci(); // TODO take care of spurious interrupts
+            }
+            PM1_CONTROL..=PM1_CONTROL_LAST => {
+                if data.len() > std::mem::size_of::<u16>()
+                    || info.offset + data.len() as u64 > (PM1_CONTROL_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad write size: {}", data.len());
+                    return;
+                }
+                let offset = (info.offset - PM1_CONTROL as u64) as usize;
+                let mut v = self.pm1_control.to_ne_bytes();
+                for (i, j) in (offset..offset + data.len()).enumerate() {
+                    v[j] = data[i];
+                }
+                let val = u16::from_ne_bytes(v);
+
+                // SLP_EN is a write-only bit and reads to it always return a zero
+                if (val & BITMASK_PM1CNT_SLEEP_ENABLE) != 0 {
+                    // only support S5 in direct mode
+                    #[cfg(feature = "direct")]
+                    if let Err(e) = self.exit_evt.write(1) {
+                        error!("ACPIPM: failed to trigger exit event: {}", e);
+                    }
+                    #[cfg(not(feature = "direct"))]
+                    match val & BITMASK_PM1CNT_SLEEP_TYPE {
+                        SLEEP_TYPE_S1 => {
+                            if let Err(e) = self.suspend_evt.write(1) {
+                                error!("ACPIPM: failed to trigger suspend event: {}", e);
+                            }
                         }
-                    } else if let Err(e) = self.suspend_evt.write(1) {
-                        error!("ACPIPM: failed to trigger suspend event: {}", e);
+                        SLEEP_TYPE_S5 => {
+                            if let Err(e) = self.exit_evt.write(1) {
+                                error!("ACPIPM: failed to trigger exit event: {}", e);
+                            }
+                        }
+                        _ => error!(
+                            "ACPIPM: unknown SLP_TYP written: {}",
+                            (val & BITMASK_PM1CNT_SLEEP_TYPE) >> 10
+                        ),
                     }
                 }
                 self.pm1_control = val & !BITMASK_PM1CNT_SLEEP_ENABLE;
             }
-            SLEEP_CONTROL => {
-                let sleep_control = val as u8;
-                if (sleep_control & BITMASK_SLEEPCNT_SLEEP_ENABLE) == BITMASK_SLEEPCNT_SLEEP_ENABLE
+            // OSPM accesses GPE registers through byte accesses (regardless of their length)
+            GPE0_STATUS..=GPE0_STATUS_LAST => {
+                if data.len() > std::mem::size_of::<u8>()
+                    || info.offset + data.len() as u64 > (GPE0_STATUS_LAST + 1).into()
                 {
-                    if let Err(e) = self.suspend_evt.write(1) {
-                        error!("ACPIPM: failed to trigger suspend event: {}", e);
-                    }
+                    warn!("ACPIPM: bad write size: {}", data.len());
+                    return;
                 }
-                self.sleep_control = sleep_control as u8 & !BITMASK_SLEEPCNT_SLEEP_ENABLE;
+                self.gpe0_status[(info.offset - GPE0_STATUS as u64) as usize] &= !data[0];
             }
-            SLEEP_STATUS => self.sleep_status &= !val as u8,
+            GPE0_ENABLE..=GPE0_ENABLE_LAST => {
+                if data.len() > std::mem::size_of::<u8>()
+                    || info.offset + data.len() as u64 > (GPE0_ENABLE_LAST + 1).into()
+                {
+                    warn!("ACPIPM: bad write size: {}", data.len());
+                    return;
+                }
+                self.gpe0_enable[(info.offset - GPE0_ENABLE as u64) as usize] = data[0];
+                self.gpe_sci();
+            }
             _ => {
                 warn!("ACPIPM: Bad write to {}", info);
             }
@@ -129,11 +310,7 @@
 
 impl BusResumeDevice for ACPIPMResource {
     fn resume_imminent(&mut self) {
-        let val = self.pm1_status;
-        self.pm1_status = val | BITMASK_PM1CNT_WAKE_STATUS;
-
-        let val = self.sleep_status;
-        self.sleep_status = val | BITMASK_SLEEPCNT_WAKE_STATUS;
+        self.pm1_status |= BITMASK_PM1CNT_WAKE_STATUS;
     }
 }
 
diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs
index d7e3829..0989eb2 100644
--- a/devices/src/pci/pci_root.rs
+++ b/devices/src/pci/pci_root.rs
@@ -456,10 +456,7 @@
                 .lock()
                 .virtual_config_space_read(address, register)
         };
-        data[0] = value as u8;
-        data[1] = (value >> (1 * 8)) as u8;
-        data[2] = (value >> (2 * 8)) as u8;
-        data[3] = (value >> (3 * 8)) as u8;
+        data[0..4].copy_from_slice(&value.to_le_bytes()[..]);
     }
 
     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
@@ -472,10 +469,8 @@
             );
             return;
         }
-        let value = (data[0] as u32)
-            | ((data[1] as u32) << (1 * 8))
-            | ((data[2] as u32) << (2 * 8))
-            | ((data[3] as u32) << (3 * 8));
+        // Unwrap is safe as we verified length above
+        let value = u32::from_le_bytes(data.try_into().unwrap());
         let (address, register) =
             PciAddress::from_config_address(info.offset as u32, self.register_bit_num);
         self.pci_root
diff --git a/devices/src/virtio/snd/vios_backend/shm_vios.rs b/devices/src/virtio/snd/vios_backend/shm_vios.rs
index aeace18..349781f 100644
--- a/devices/src/virtio/snd/vios_backend/shm_vios.rs
+++ b/devices/src/virtio/snd/vios_backend/shm_vios.rs
@@ -6,9 +6,9 @@
 use crate::virtio::snd::layout::*;
 
 use base::{
-    error, net::UnixSeqpacket, AsRawDescriptor, Error as BaseError, Event, FromRawDescriptor,
-    IntoRawDescriptor, MemoryMapping, MemoryMappingBuilder, MmapError, PollToken, SafeDescriptor,
-    ScmSocket, WaitContext,
+    error, AsRawDescriptor, Error as BaseError, Event, FromRawDescriptor, IntoRawDescriptor,
+    MemoryMapping, MemoryMappingBuilder, MmapError, PollToken, SafeDescriptor, ScmSocket,
+    UnixSeqpacket, WaitContext,
 };
 use data_model::{DataInit, VolatileMemory, VolatileMemoryError, VolatileSlice};
 
diff --git a/devices/src/virtio/vhost/user/device/gpu.rs b/devices/src/virtio/vhost/user/device/gpu.rs
index 4a5405d..37da959 100644
--- a/devices/src/virtio/vhost/user/device/gpu.rs
+++ b/devices/src/virtio/vhost/user/device/gpu.rs
@@ -8,9 +8,8 @@
 use argh::FromArgs;
 use async_task::Task;
 use base::{
-    clone_descriptor, error,
-    net::{UnixSeqpacketListener, UnlinkUnixSeqpacketListener},
-    warn, Event, FromRawDescriptor, IntoRawDescriptor, SafeDescriptor, TimerFd, Tube,
+    clone_descriptor, error, warn, Event, FromRawDescriptor, IntoRawDescriptor, SafeDescriptor,
+    TimerFd, Tube, UnixSeqpacketListener, UnlinkUnixSeqpacketListener,
 };
 use cros_async::{AsyncWrapper, EventAsync, Executor, IoSourceExt, TimerAsync};
 use futures::{
diff --git a/devices/src/virtio/vhost/user/device/wl.rs b/devices/src/virtio/vhost/user/device/wl.rs
index 3997850..ac34e85 100644
--- a/devices/src/virtio/vhost/user/device/wl.rs
+++ b/devices/src/virtio/vhost/user/device/wl.rs
@@ -13,9 +13,8 @@
 use anyhow::{anyhow, bail, Context};
 use argh::FromArgs;
 use base::{
-    clone_descriptor, error,
-    net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener},
-    warn, Event, FromRawDescriptor, SafeDescriptor, Tube,
+    clone_descriptor, error, warn, Event, FromRawDescriptor, SafeDescriptor, Tube, UnixSeqpacket,
+    UnixSeqpacketListener, UnlinkUnixSeqpacketListener,
 };
 use cros_async::{AsyncWrapper, EventAsync, Executor, IoSourceExt};
 use futures::future::{AbortHandle, Abortable};
diff --git a/devices/src/virtio/vhost/user/proxy.rs b/devices/src/virtio/vhost/user/proxy.rs
index aeeba07..c4fda6d 100644
--- a/devices/src/virtio/vhost/user/proxy.rs
+++ b/devices/src/virtio/vhost/user/proxy.rs
@@ -16,8 +16,8 @@
 use std::thread;
 
 use base::{
-    error, info, AsRawDescriptor, Event, EventType, FromRawDescriptor, IntoRawDescriptor,
-    PollToken, RawDescriptor, SafeDescriptor, Tube, TubeError, WaitContext,
+    error, AsRawDescriptor, Event, EventType, FromRawDescriptor, IntoRawDescriptor, PollToken,
+    RawDescriptor, SafeDescriptor, Tube, TubeError, WaitContext,
 };
 use data_model::{DataInit, Le32};
 use libc::{recv, MSG_DONTWAIT, MSG_PEEK};
@@ -186,6 +186,9 @@
     /// There are no more available descriptors to receive into.
     #[error("no rx descriptors available")]
     RxDescriptorsExhausted,
+    /// Sibling is disconnected.
+    #[error("sibling disconnected")]
+    SiblingDisconnected,
     /// Failed to receive a memory mapping request from the main process.
     #[error("receiving mapping request from tube failed: {0}")]
     TubeReceiveFailure(TubeError),
@@ -395,6 +398,9 @@
                                     .map_err(Error::WaitContextDisableSiblingVmSocket)?;
                                 sibling_socket_polling_enabled = false;
                             }
+                            // TODO(b/216407443): Handle sibling disconnection. The sibling sends
+                            // 0-length data the proxy device forwards it to the guest so that the
+                            // VVU backend can get notified that the connection is closed.
                             Err(e) => return Err(e),
                         }
                     }
@@ -461,102 +467,95 @@
         // - Parse the Vhost-user sibling message. If it's not an action type message
         //   then copy the message as is to the Rx buffer and forward it to the
         //   device backend.
-        let mut exhausted_queue = false;
+        //
         // Peek if any data is left on the Vhost-user sibling socket. If no, then
         // nothing to forwad to the device backend.
-        while self.is_sibling_data_available() {
-            if let Some(desc) = self.rx_queue.peek(&self.mem) {
-                // To successfully receive attached file descriptors, we need to
-                // receive messages and corresponding attached file descriptors in
-                // this way:
-                // - receive messsage header and optional attached files.
-                // - receive optional message body and payload according size field
-                //   in message header.
-                // - forward it to the device backend.
-                let (hdr, files) = self
-                    .slave_req_helper
-                    .as_mut()
-                    .recv_header()
-                    .map_err(Error::ReadSiblingHeader)?;
-                check_attached_files(&hdr, &files)?;
-                let buf = self.get_sibling_msg_data(&hdr)?;
+        while self.is_sibling_data_available()? {
+            let desc = self
+                .rx_queue
+                .peek(&self.mem)
+                .ok_or(Error::RxDescriptorsExhausted)?;
+            // To successfully receive attached file descriptors, we need to
+            // receive messages and corresponding attached file descriptors in
+            // this way:
+            // - receive messsage header and optional attached files.
+            // - receive optional message body and payload according size field
+            //   in message header.
+            // - forward it to the device backend.
+            let (hdr, files) = self
+                .slave_req_helper
+                .as_mut()
+                .recv_header()
+                .map_err(Error::ReadSiblingHeader)?;
+            check_attached_files(&hdr, &files)?;
+            let buf = self.get_sibling_msg_data(&hdr)?;
 
-                let index = desc.index;
-                let bytes_written = {
-                    if is_action_request(&hdr) {
-                        // TODO(abhishekbh): Implement action messages.
-                        let res = match hdr.get_code() {
-                            MasterReq::SET_MEM_TABLE => {
-                                // Map the sibling memory in this process and forward the sibling
-                                // memory info to the slave. Only if the mapping succeeds send info
-                                // along to the slave, else send a failed Ack back to the master.
-                                self.set_mem_table(&hdr, &buf, files)
-                            }
-                            MasterReq::SET_VRING_CALL => self.set_vring_call(&hdr, &buf, files),
-                            MasterReq::SET_VRING_KICK => {
-                                self.set_vring_kick(wait_ctx, &hdr, &buf, files)
-                            }
-                            _ => {
-                                unimplemented!("unimplemented action message:{:?}", hdr.get_code());
-                            }
-                        };
-
-                        // If the "action" in response to the action messages
-                        // failed then no bytes have been written to the virt
-                        // queue. Else, the action is done. Now forward the
-                        // message to the virt queue and return how many bytes
-                        // were written.
-                        match res {
-                            Ok(()) => self.forward_msg_to_device(desc, &hdr, &buf),
-                            Err(e) => Err(e),
+            let index = desc.index;
+            let bytes_written = {
+                if is_action_request(&hdr) {
+                    // TODO(abhishekbh): Implement action messages.
+                    let res = match hdr.get_code() {
+                        MasterReq::SET_MEM_TABLE => {
+                            // Map the sibling memory in this process and forward the
+                            // sibling memory info to the slave. Only if the mapping
+                            // succeeds send info along to the slave, else send a failed
+                            // Ack back to the master.
+                            self.set_mem_table(&hdr, &buf, files)
                         }
-                    } else {
-                        // If no special processing required. Forward this message as is
-                        // to the device backend.
-                        self.forward_msg_to_device(desc, &hdr, &buf)
-                    }
-                };
-
-                // If some bytes were written to the virt queue, now it's time
-                // to add a used buffer and notify the guest. Else if there was
-                // an error of any sort, we notify the sibling by sending an ACK
-                // with failure.
-                match bytes_written {
-                    Ok(bytes_written) => {
-                        // The driver is able to deal with a descriptor with 0 bytes written.
-                        self.rx_queue.pop_peeked(&self.mem);
-                        self.rx_queue.add_used(&self.mem, index, bytes_written);
-                        if !self.rx_queue.trigger_interrupt(&self.mem, &self.interrupt) {
-                            // This interrupt should always be injected. We'd rather fail fast
-                            // if there is an error.
-                            panic!("failed to send interrupt");
+                        MasterReq::SET_VRING_CALL => self.set_vring_call(&hdr, &buf, files),
+                        MasterReq::SET_VRING_KICK => {
+                            self.set_vring_kick(wait_ctx, &hdr, &buf, files)
                         }
+                        _ => {
+                            unimplemented!("unimplemented action message:{:?}", hdr.get_code());
+                        }
+                    };
+
+                    // If the "action" in response to the action messages
+                    // failed then no bytes have been written to the virt
+                    // queue. Else, the action is done. Now forward the
+                    // message to the virt queue and return how many bytes
+                    // were written.
+                    match res {
+                        Ok(()) => self.forward_msg_to_device(desc, &hdr, &buf),
+                        Err(e) => Err(e),
                     }
-                    Err(e) => {
-                        error!("failed to forward message to the device: {}", e);
-                        self.slave_req_helper
-                            .send_ack_message(&hdr, false)
-                            .map_err(Error::FailedAck)?;
+                } else {
+                    // If no special processing required. Forward this message as is
+                    // to the device backend.
+                    self.forward_msg_to_device(desc, &hdr, &buf)
+                }
+            };
+
+            // If some bytes were written to the virt queue, now it's time
+            // to add a used buffer and notify the guest. Else if there was
+            // an error of any sort, we notify the sibling by sending an ACK
+            // with failure.
+            match bytes_written {
+                Ok(bytes_written) => {
+                    // The driver is able to deal with a descriptor with 0 bytes written.
+                    self.rx_queue.pop_peeked(&self.mem);
+                    self.rx_queue.add_used(&self.mem, index, bytes_written);
+                    if !self.rx_queue.trigger_interrupt(&self.mem, &self.interrupt) {
+                        // This interrupt should always be injected. We'd rather fail
+                        // fast if there is an error.
+                        panic!("failed to send interrupt");
                     }
                 }
-            } else {
-                // No buffer left to fill up. No point processing any data
-                // from the Vhost-user sibling.
-                exhausted_queue = true;
-                break;
+                Err(e) => {
+                    error!("failed to forward message to the device: {}", e);
+                    self.slave_req_helper
+                        .send_ack_message(&hdr, false)
+                        .map_err(Error::FailedAck)?;
+                }
             }
         }
 
-        if exhausted_queue {
-            Err(Error::RxDescriptorsExhausted)
-        } else {
-            info!("no more data available on the sibling socket");
-            Ok(())
-        }
+        Ok(())
     }
 
-    // Returns true iff any data is available on the sibling socket.
-    fn is_sibling_data_available(&self) -> bool {
+    // Returns the sibling connection status.
+    fn is_sibling_data_available(&self) -> Result<bool> {
         // Peek if any data is left on the Vhost-user sibling socket. If no, then
         // nothing to forwad to the device backend.
         let mut peek_buf = [0; 1];
@@ -571,9 +570,16 @@
             )
         };
 
-        // TODO(abhishekbh): Should we log on < 0 ?. Peek should
-        // succeed.
-        peek_ret > 0
+        match peek_ret {
+            0 => Err(Error::SiblingDisconnected),
+            ret if ret < 0 => match base::Error::last() {
+                // EAGAIN means that no data is available. Any other error means that the sibling
+                // has disconnected.
+                e if e.errno() == libc::EAGAIN => Ok(false),
+                _ => Err(Error::SiblingDisconnected),
+            },
+            _ => Ok(true),
+        }
     }
 
     // Returns any data attached to a Vhost-user sibling message.
@@ -1183,7 +1189,10 @@
                 };
                 let rx_queue_evt = activate_params.queue_evts.remove(0);
                 let tx_queue_evt = activate_params.queue_evts.remove(0);
-                let _ = worker.run(rx_queue_evt, tx_queue_evt, main_thread_tube, kill_evt);
+                if let Err(e) = worker.run(rx_queue_evt, tx_queue_evt, main_thread_tube, kill_evt) {
+                    // TODO(b/216407443): Handle sibling reconnect events.
+                    error!("worker thread exited: {}", e);
+                }
                 Ok(worker)
             });
 
diff --git a/disk/Cargo.toml b/disk/Cargo.toml
index f3bbf14..5dff7ff 100644
--- a/disk/Cargo.toml
+++ b/disk/Cargo.toml
@@ -2,7 +2,7 @@
 name = "disk"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 path = "src/disk.rs"
diff --git a/fuse/Cargo.toml b/fuse/Cargo.toml
index 3cbc6bd..7e4bfc9 100644
--- a/fuse/Cargo.toml
+++ b/fuse/Cargo.toml
@@ -2,7 +2,7 @@
 name = "fuse"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 path = "src/lib.rs"
diff --git a/gpu_display/Cargo.toml b/gpu_display/Cargo.toml
index d5aa36d..84e1fc0 100644
--- a/gpu_display/Cargo.toml
+++ b/gpu_display/Cargo.toml
@@ -2,7 +2,7 @@
 name = "gpu_display"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 x = []
diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml
index eea6f26..1bff265 100644
--- a/hypervisor/Cargo.toml
+++ b/hypervisor/Cargo.toml
@@ -2,7 +2,7 @@
 name = "hypervisor"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 bit_field = { path = "../bit_field" }
diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml
index f250cfb..7da71c1 100644
--- a/integration_tests/Cargo.toml
+++ b/integration_tests/Cargo.toml
@@ -2,7 +2,7 @@
 name = "integration_tests"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dev-dependencies]
 tempfile = "3"
diff --git a/kernel_cmdline/Cargo.toml b/kernel_cmdline/Cargo.toml
index bf2a5d6..aacbe05 100644
--- a/kernel_cmdline/Cargo.toml
+++ b/kernel_cmdline/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "kernel_cmdline"
 version = "0.1.0"
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 libc = "*"
diff --git a/kernel_loader/Cargo.toml b/kernel_loader/Cargo.toml
index 9fd1e8c..b57b4d9 100644
--- a/kernel_loader/Cargo.toml
+++ b/kernel_loader/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "kernel_loader"
 version = "0.1.0"
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 data_model = { path = "../common/data_model" }
diff --git a/kvm/Cargo.toml b/kvm/Cargo.toml
index 525e73a..7bdfd17 100644
--- a/kvm/Cargo.toml
+++ b/kvm/Cargo.toml
@@ -2,7 +2,7 @@
 name = "kvm"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 data_model = { path = "../common/data_model" }
diff --git a/kvm_sys/Cargo.toml b/kvm_sys/Cargo.toml
index 0649785..dbac907 100644
--- a/kvm_sys/Cargo.toml
+++ b/kvm_sys/Cargo.toml
@@ -2,7 +2,7 @@
 name = "kvm_sys"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 data_model = { path = "../common/data_model" }
diff --git a/libcras_stub/Cargo.toml b/libcras_stub/Cargo.toml
index 7d40cf2..4b0961c 100644
--- a/libcras_stub/Cargo.toml
+++ b/libcras_stub/Cargo.toml
@@ -2,7 +2,7 @@
 name = "libcras"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 path = "src/libcras.rs"
diff --git a/libvda/Cargo.toml b/libvda/Cargo.toml
index 7de282c..551d558 100644
--- a/libvda/Cargo.toml
+++ b/libvda/Cargo.toml
@@ -2,7 +2,7 @@
 name = "libvda"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 links = "vda"
 
 [dependencies]
diff --git a/linux_input_sys/Cargo.toml b/linux_input_sys/Cargo.toml
index a8181d0..7d57c84 100644
--- a/linux_input_sys/Cargo.toml
+++ b/linux_input_sys/Cargo.toml
@@ -2,7 +2,7 @@
 name = "linux_input_sys"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 data_model = { path = "../common/data_model" }
diff --git a/net_sys/Cargo.toml b/net_sys/Cargo.toml
index 53e3b1a..cd7c095 100644
--- a/net_sys/Cargo.toml
+++ b/net_sys/Cargo.toml
@@ -2,7 +2,7 @@
 name = "net_sys"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 base = { path = "../common/base" }
diff --git a/net_util/Cargo.toml b/net_util/Cargo.toml
index 8dfe1df..951d0e0 100644
--- a/net_util/Cargo.toml
+++ b/net_util/Cargo.toml
@@ -2,7 +2,7 @@
 name = "net_util"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 libc = "*"
diff --git a/power_monitor/Cargo.toml b/power_monitor/Cargo.toml
index e16362c..c7fe4e0 100644
--- a/power_monitor/Cargo.toml
+++ b/power_monitor/Cargo.toml
@@ -2,7 +2,7 @@
 name = "power_monitor"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 powerd = ["dbus", "protobuf", "protoc-rust"]
diff --git a/protos/Cargo.toml b/protos/Cargo.toml
index d4286e6..736d755 100644
--- a/protos/Cargo.toml
+++ b/protos/Cargo.toml
@@ -2,7 +2,7 @@
 name = "protos"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 plugin = ["kvm_sys"]
diff --git a/qcow_utils/Cargo.toml b/qcow_utils/Cargo.toml
index 840bb18..28ade45 100644
--- a/qcow_utils/Cargo.toml
+++ b/qcow_utils/Cargo.toml
@@ -2,7 +2,7 @@
 name = "qcow_utils"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 path = "src/qcow_utils.rs"
diff --git a/resources/Cargo.toml b/resources/Cargo.toml
index f5c7219..6cb4e0d 100644
--- a/resources/Cargo.toml
+++ b/resources/Cargo.toml
@@ -2,7 +2,7 @@
 name = "resources"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 libc = "*"
diff --git a/resources/src/address_allocator.rs b/resources/src/address_allocator.rs
index ffb5152..12fc6c5 100644
--- a/resources/src/address_allocator.rs
+++ b/resources/src/address_allocator.rs
@@ -195,8 +195,8 @@
 
     /// Allocates a range of addresses from the managed region with an optional tag
     /// and required location. Allocation alignment is not enforced.
-    /// Returns OutOfSpace if requested range is not available (e.g. already allocated
-    /// with a different alloc tag).
+    /// Returns OutOfSpace if requested range is not available or ExistingAlloc if the requested
+    /// range overlaps an existing allocation.
     pub fn allocate_at(&mut self, start: u64, size: u64, alloc: Alloc, tag: String) -> Result<()> {
         if self.allocs.contains_key(&alloc) {
             return Err(Error::ExistingAlloc(alloc));
@@ -224,7 +224,13 @@
 
                 Ok(())
             }
-            None => Err(Error::OutOfSpace),
+            None => {
+                if let Some(existing_alloc) = self.find_overlapping(start, size) {
+                    Err(Error::ExistingAlloc(existing_alloc))
+                } else {
+                    Err(Error::OutOfSpace)
+                }
+            }
         }
     }
 
@@ -237,19 +243,29 @@
 
     /// Release a allocation contains the value.
     pub fn release_containing(&mut self, value: u64) -> Result<()> {
-        let mut alloc = None;
-        for (key, val) in self.allocs.iter() {
-            if value >= val.0 && value < val.0 + val.1 {
-                alloc = Some(*key);
-                break;
-            }
+        if let Some(alloc) = self.find_overlapping(value, 1) {
+            self.release(alloc)
+        } else {
+            Err(Error::OutOfSpace)
+        }
+    }
+
+    // Find an existing allocation that overlaps the region defined by `address` and `size`. If more
+    // than one allocation overlaps the given region, any of them may be returned, since the HashMap
+    // iterator is not ordered in any particular way.
+    fn find_overlapping(&self, start: u64, size: u64) -> Option<Alloc> {
+        if size == 0 {
+            return None;
         }
 
-        if let Some(key) = alloc {
-            return self.release(key);
-        }
-
-        Err(Error::OutOfSpace)
+        let end = start.saturating_add(size - 1);
+        self.allocs
+            .iter()
+            .find(|(_, &(alloc_start, alloc_size, _))| {
+                let alloc_end = alloc_start + alloc_size;
+                start < alloc_end && end >= alloc_start
+            })
+            .map(|(&alloc, _)| alloc)
     }
 
     /// Returns allocation associated with `alloc`, or None if no such allocation exists.
@@ -506,7 +522,8 @@
 
     #[test]
     fn allocate_and_split_allocate_at() {
-        let mut pool = AddressAllocator::new(0x1000, 0x1000, Some(0x100), None).unwrap();
+        let mut pool = AddressAllocator::new(0x1000, 0x1000, Some(1), None).unwrap();
+        // 0x1200..0x1a00
         assert_eq!(
             pool.allocate_at(0x1200, 0x800, Alloc::Anon(0), String::from("bar0")),
             Ok(())
@@ -515,14 +532,41 @@
             pool.allocate(0x800, Alloc::Anon(1), String::from("bar1")),
             Err(Error::OutOfSpace)
         );
+        // 0x600..0x2000
         assert_eq!(
             pool.allocate(0x600, Alloc::Anon(2), String::from("bar2")),
             Ok(0x1a00)
         );
+        // 0x1000..0x1200
         assert_eq!(
             pool.allocate(0x200, Alloc::Anon(3), String::from("bar3")),
             Ok(0x1000)
         );
+        // 0x1b00..0x1c00 (overlaps with 0x600..0x2000)
+        assert_eq!(
+            pool.allocate_at(0x1b00, 0x100, Alloc::Anon(4), String::from("bar4")),
+            Err(Error::ExistingAlloc(Alloc::Anon(2)))
+        );
+        // 0x1fff..0x2000 (overlaps with 0x600..0x2000)
+        assert_eq!(
+            pool.allocate_at(0x1fff, 1, Alloc::Anon(5), String::from("bar5")),
+            Err(Error::ExistingAlloc(Alloc::Anon(2)))
+        );
+        // 0x1200..0x1201 (overlaps with 0x1200..0x1a00)
+        assert_eq!(
+            pool.allocate_at(0x1200, 1, Alloc::Anon(6), String::from("bar6")),
+            Err(Error::ExistingAlloc(Alloc::Anon(0)))
+        );
+        // 0x11ff..0x1200 (overlaps with 0x1000..0x1200)
+        assert_eq!(
+            pool.allocate_at(0x11ff, 1, Alloc::Anon(7), String::from("bar7")),
+            Err(Error::ExistingAlloc(Alloc::Anon(3)))
+        );
+        // 0x1100..0x1300 (overlaps with 0x1000..0x1200 and 0x1200..0x1a00)
+        match pool.allocate_at(0x1100, 0x200, Alloc::Anon(8), String::from("bar8")) {
+            Err(Error::ExistingAlloc(Alloc::Anon(0) | Alloc::Anon(3))) => {}
+            x => panic!("unexpected result {:?}", x),
+        }
     }
 
     #[test]
diff --git a/rutabaga_gfx/Cargo.toml b/rutabaga_gfx/Cargo.toml
index 46d56bb..bb02eac 100644
--- a/rutabaga_gfx/Cargo.toml
+++ b/rutabaga_gfx/Cargo.toml
@@ -2,7 +2,7 @@
 name = "rutabaga_gfx"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 gfxstream = []
diff --git a/rutabaga_gfx/ffi/Cargo.toml b/rutabaga_gfx/ffi/Cargo.toml
index 9e10616..3926c4a 100644
--- a/rutabaga_gfx/ffi/Cargo.toml
+++ b/rutabaga_gfx/ffi/Cargo.toml
@@ -2,7 +2,7 @@
 name = "rutabaga_gfx_ffi"
 version = "0.1.0"
 authors = ["The Chromium OS Authors + Android Open Source Project"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 name = "rutabaga_gfx_ffi"
diff --git a/rutabaga_gfx/src/rutabaga_gralloc/Cargo.toml b/rutabaga_gfx/src/rutabaga_gralloc/Cargo.toml
index 1ab814c..a541c9c 100644
--- a/rutabaga_gfx/src/rutabaga_gralloc/Cargo.toml
+++ b/rutabaga_gfx/src/rutabaga_gralloc/Cargo.toml
@@ -2,7 +2,7 @@
 name = "gpu_buffer"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 data_model = { path = "../data_model" }
diff --git a/src/linux/mod.rs b/src/linux/mod.rs
index 568fd85..039d2fd 100644
--- a/src/linux/mod.rs
+++ b/src/linux/mod.rs
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 use std::cmp::{max, Reverse};
-use std::collections::BTreeMap;
+use std::collections::{BTreeMap, BTreeSet};
 use std::convert::TryInto;
 use std::fs::{File, OpenOptions};
 use std::io::prelude::*;
@@ -27,8 +27,8 @@
 use acpi_tables::sdt::SDT;
 
 use anyhow::{anyhow, bail, Context, Result};
-use base::net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener};
 use base::*;
+use base::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener};
 use devices::serial_device::SerialHardware;
 use devices::vfio::{VfioCommonSetup, VfioCommonTrait};
 use devices::virtio::memory_mapper::MemoryMapperTrait;
@@ -55,7 +55,7 @@
 
 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
 use crate::gdb::{gdb_thread, GdbStub};
-use crate::{Config, Executable, SharedDir, SharedDirKind, VfioType};
+use crate::{Config, Executable, FileBackedMappingParameters, SharedDir, SharedDirKind, VfioType};
 use arch::{
     self, LinuxArch, RunnableLinuxVm, VcpuAffinity, VirtioDeviceStub, VmComponents, VmImage,
 };
@@ -640,15 +640,19 @@
             .build()
             .context("failed to map backing file for file-backed mapping")?;
 
-        resources
-            .mmio_allocator_any()
-            .allocate_at(
-                mapping.address,
-                mapping.size,
-                Alloc::FileBacked(mapping.address),
-                "file-backed mapping".to_owned(),
-            )
-            .context("failed to allocate guest address for file-backed mapping")?;
+        match resources.mmio_allocator_any().allocate_at(
+            mapping.address,
+            mapping.size,
+            Alloc::FileBacked(mapping.address),
+            "file-backed mapping".to_owned(),
+        ) {
+            // OutOfSpace just means that this mapping is not in the MMIO regions at all, so don't
+            // consider it an error.
+            // TODO(b/222769529): Reserve this region in a global memory address space allocator once
+            // we have that so nothing else can accidentally overlap with it.
+            Ok(()) | Err(resources::Error::OutOfSpace) => {}
+            e => e.context("failed to allocate guest address for file-backed mapping")?,
+        }
 
         vm.add_memory_region(
             GuestAddress(mapping.address),
@@ -871,11 +875,58 @@
     GuestPanic,
 }
 
+// Remove ranges in `guest_mem_layout` that overlap with ranges in `file_backed_mappings`.
+// Returns the updated guest memory layout.
+fn punch_holes_in_guest_mem_layout_for_mappings(
+    guest_mem_layout: Vec<(GuestAddress, u64)>,
+    file_backed_mappings: &[FileBackedMappingParameters],
+) -> Vec<(GuestAddress, u64)> {
+    // Create a set containing (start, end) pairs with exclusive end (end = start + size; the byte
+    // at end is not included in the range).
+    let mut layout_set = BTreeSet::new();
+    for (addr, size) in &guest_mem_layout {
+        layout_set.insert((addr.offset(), addr.offset() + size));
+    }
+
+    for mapping in file_backed_mappings {
+        let mapping_start = mapping.address;
+        let mapping_end = mapping_start + mapping.size;
+
+        // Repeatedly split overlapping guest memory regions until no overlaps remain.
+        while let Some((range_start, range_end)) = layout_set
+            .iter()
+            .find(|&&(range_start, range_end)| {
+                mapping_start < range_end && mapping_end > range_start
+            })
+            .cloned()
+        {
+            layout_set.remove(&(range_start, range_end));
+
+            if range_start < mapping_start {
+                layout_set.insert((range_start, mapping_start));
+            }
+            if range_end > mapping_end {
+                layout_set.insert((mapping_end, range_end));
+            }
+        }
+    }
+
+    // Build the final guest memory layout from the modified layout_set.
+    layout_set
+        .iter()
+        .map(|(start, end)| (GuestAddress(*start), end - start))
+        .collect()
+}
+
 pub fn run_config(cfg: Config) -> Result<ExitState> {
     let components = setup_vm_components(&cfg)?;
 
     let guest_mem_layout =
         Arch::guest_memory_layout(&components).context("failed to create guest memory layout")?;
+
+    let guest_mem_layout =
+        punch_holes_in_guest_mem_layout_for_mappings(guest_mem_layout, &cfg.file_backed_mappings);
+
     let guest_mem = GuestMemory::new(&guest_mem_layout).context("failed to create guest memory")?;
     let mut mem_policy = MemoryPolicy::empty();
     if components.hugepages {
@@ -1805,6 +1856,7 @@
                                             balloon_host_tube.as_ref(),
                                             &mut balloon_stats_id,
                                             disk_host_tubes,
+                                            &mut linux.pm,
                                             #[cfg(feature = "usb")]
                                             Some(&usb_control_tube),
                                             #[cfg(not(feature = "usb"))]
@@ -2035,3 +2087,161 @@
 
     Ok(exit_state)
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    // Create a file-backed mapping parameters struct with the given `address` and `size` and other
+    // parameters set to default values.
+    fn test_file_backed_mapping(address: u64, size: u64) -> FileBackedMappingParameters {
+        FileBackedMappingParameters {
+            address,
+            size,
+            path: PathBuf::new(),
+            offset: 0,
+            writable: false,
+            sync: false,
+        }
+    }
+
+    #[test]
+    fn guest_mem_file_backed_mappings_overlap() {
+        // Base case: no file mappings; output layout should be identical.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[]
+            ),
+            vec![
+                (GuestAddress(0), 0xD000_0000),
+                (GuestAddress(0x1_0000_0000), 0x8_0000),
+            ]
+        );
+
+        // File mapping that does not overlap guest memory.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[test_file_backed_mapping(0xD000_0000, 0x1000)]
+            ),
+            vec![
+                (GuestAddress(0), 0xD000_0000),
+                (GuestAddress(0x1_0000_0000), 0x8_0000),
+            ]
+        );
+
+        // File mapping at the start of the low address space region.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[test_file_backed_mapping(0, 0x2000)]
+            ),
+            vec![
+                (GuestAddress(0x2000), 0xD000_0000 - 0x2000),
+                (GuestAddress(0x1_0000_0000), 0x8_0000),
+            ]
+        );
+
+        // File mapping at the end of the low address space region.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[test_file_backed_mapping(0xD000_0000 - 0x2000, 0x2000)]
+            ),
+            vec![
+                (GuestAddress(0), 0xD000_0000 - 0x2000),
+                (GuestAddress(0x1_0000_0000), 0x8_0000),
+            ]
+        );
+
+        // File mapping fully contained within the middle of the low address space region.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[test_file_backed_mapping(0x1000, 0x2000)]
+            ),
+            vec![
+                (GuestAddress(0), 0x1000),
+                (GuestAddress(0x3000), 0xD000_0000 - 0x3000),
+                (GuestAddress(0x1_0000_0000), 0x8_0000),
+            ]
+        );
+
+        // File mapping at the start of the high address space region.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[test_file_backed_mapping(0x1_0000_0000, 0x2000)]
+            ),
+            vec![
+                (GuestAddress(0), 0xD000_0000),
+                (GuestAddress(0x1_0000_2000), 0x8_0000 - 0x2000),
+            ]
+        );
+
+        // File mapping at the end of the high address space region.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[test_file_backed_mapping(0x1_0008_0000 - 0x2000, 0x2000)]
+            ),
+            vec![
+                (GuestAddress(0), 0xD000_0000),
+                (GuestAddress(0x1_0000_0000), 0x8_0000 - 0x2000),
+            ]
+        );
+
+        // File mapping fully contained within the middle of the high address space region.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[test_file_backed_mapping(0x1_0000_1000, 0x2000)]
+            ),
+            vec![
+                (GuestAddress(0), 0xD000_0000),
+                (GuestAddress(0x1_0000_0000), 0x1000),
+                (GuestAddress(0x1_0000_3000), 0x8_0000 - 0x3000),
+            ]
+        );
+
+        // File mapping overlapping two guest memory regions.
+        assert_eq!(
+            punch_holes_in_guest_mem_layout_for_mappings(
+                vec![
+                    (GuestAddress(0), 0xD000_0000),
+                    (GuestAddress(0x1_0000_0000), 0x8_0000),
+                ],
+                &[test_file_backed_mapping(0xA000_0000, 0x60002000)]
+            ),
+            vec![
+                (GuestAddress(0), 0xA000_0000),
+                (GuestAddress(0x1_0000_2000), 0x8_0000 - 0x2000),
+            ]
+        );
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 30a7bba..edab4b2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2583,7 +2583,7 @@
                               See --disk for valid options."),
           Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
           Argument::value("pmem-device", "PATH", "Path to a disk image."),
-          Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
+          Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file followed by size."),
           Argument::value("host_ip",
                           "IP",
                           "IP address to assign to host tap interface."),
@@ -2881,6 +2881,36 @@
     vms_request(&VmRequest::Resume, socket_path)
 }
 
+fn powerbtn_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
+    if args.len() == 0 {
+        print_help("crosvm powerbtn", "VM_SOCKET...", &[]);
+        println!("Triggers a power button event in the crosvm instance listening on each `VM_SOCKET` given.");
+        return Err(());
+    }
+    let socket_path = &args.next().unwrap();
+    let socket_path = Path::new(&socket_path);
+    vms_request(&VmRequest::Powerbtn, socket_path)
+}
+
+fn inject_gpe(mut args: std::env::Args) -> std::result::Result<(), ()> {
+    if args.len() < 2 {
+        print_help("crosvm gpe", "GPE# VM_SOCKET...", &[]);
+        println!("Injects a general-purpose event (GPE#) into the crosvm instance listening on each `VM_SOCKET` given.");
+        return Err(());
+    }
+    let gpe = match args.next().unwrap().parse::<u32>() {
+        Ok(n) => n,
+        Err(_) => {
+            error!("Failed to parse GPE#");
+            return Err(());
+        }
+    };
+
+    let socket_path = &args.next().unwrap();
+    let socket_path = Path::new(&socket_path);
+    vms_request(&VmRequest::Gpe(gpe), socket_path)
+}
+
 fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
     if args.len() < 2 {
         print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
@@ -3393,6 +3423,8 @@
     println!("    run - Start a new crosvm instance.");
     println!("    stop - Stops crosvm instances via their control sockets.");
     println!("    suspend - Suspends the crosvm instance.");
+    println!("    powerbtn - Triggers a power button event in the crosvm instance.");
+    println!("    gpe - Injects a general-purpose event into the crosvm instance.");
     println!("    usb - Manage attached virtual USB devices.");
     println!("    version - Show package version.");
     println!("    vfio - add/remove host vfio pci device into guest.");
@@ -3448,6 +3480,8 @@
             "resume" => resume_vms(args),
             "stop" => stop_vms(args),
             "suspend" => suspend_vms(args),
+            "powerbtn" => powerbtn_vms(args),
+            "gpe" => inject_gpe(args),
             "usb" => modify_usb(args),
             "version" => pkg_version(),
             "vfio" => modify_vfio(args),
diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs
index 9d135b9..ef5eea5 100644
--- a/src/plugin/vcpu.rs
+++ b/src/plugin/vcpu.rs
@@ -725,8 +725,10 @@
                 {
                     Err(SysError::new(EINVAL))
                 } else {
-                    let mut cap: kvm_enable_cap = Default::default();
-                    cap.cap = capability;
+                    let cap = kvm_enable_cap {
+                        cap: capability,
+                        ..Default::default()
+                    };
                     // Safe because the allowed capabilities don't take pointer arguments.
                     unsafe { vcpu.kvm_enable_cap(&cap) }
                 }
diff --git a/system_api_stub/Cargo.toml b/system_api_stub/Cargo.toml
index d5a8ec1..764a68c 100644
--- a/system_api_stub/Cargo.toml
+++ b/system_api_stub/Cargo.toml
@@ -2,7 +2,7 @@
 name = "system_api"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 path = "src/system_api.rs"
diff --git a/tests/plugins.rs b/tests/plugins.rs
index 8992045..1cbb2fd 100644
--- a/tests/plugins.rs
+++ b/tests/plugins.rs
@@ -62,11 +62,11 @@
         .arg(&out_bin)
         .arg("-L") // Path of shared object to link to.
         .arg(&libcrosvm_plugin_dir)
-        .arg("-lcrosvm_plugin")
         .arg("-Wl,-rpath") // Search for shared object in the same path when exec'd.
         .arg(&libcrosvm_plugin_dir)
         .args(&["-Wl,-rpath", "."]) // Also check current directory in case of sandboxing.
         .args(&["-xc", "-"]) // Read source code from piped stdin.
+        .arg("-lcrosvm_plugin")
         .stdin(Stdio::piped())
         .spawn()
         .expect("failed to spawn compiler");
@@ -262,6 +262,8 @@
     test_plugin(include_str!("plugin_supported_cpuid.c"));
 }
 
+// b:223675792
+#[ignore]
 #[test]
 fn test_enable_cap() {
     test_plugin(include_str!("plugin_enable_cap.c"));
diff --git a/third_party/vmm_vhost/Cargo.toml b/third_party/vmm_vhost/Cargo.toml
index 4bb822a..9f29a38 100644
--- a/third_party/vmm_vhost/Cargo.toml
+++ b/third_party/vmm_vhost/Cargo.toml
@@ -8,7 +8,7 @@
 documentation = "https://docs.rs/vhost"
 readme = "README.md"
 license = "Apache-2.0 or BSD-3-Clause"
-edition = "2018"
+edition = "2021"
 
 [features]
 default = []
diff --git a/tools/dev_container b/tools/dev_container
index 0128508..67a9cfe 100755
--- a/tools/dev_container
+++ b/tools/dev_container
@@ -55,6 +55,8 @@
     "--device /dev/vhost-vsock",
     # Use tmpfs in the container for faster performance.
     "--mount type=tmpfs,destination=/tmp",
+    # For plugin process jail
+    "--mount type=tmpfs,destination=/var/empty",
     f"gcr.io/crosvm-packages/crosvm_dev:{IMAGE_VERSION}",
 ]
 
diff --git a/tools/impl/test_config.py b/tools/impl/test_config.py
index b2d5229..acaba07 100755
--- a/tools/impl/test_config.py
+++ b/tools/impl/test_config.py
@@ -41,6 +41,7 @@
     CRATE_OPTIONS: dict[str, list[TestOption]] = {
         "cros_async": [TestOption.LARGE],
         "crosvm_plugin": [TestOption.DO_NOT_BUILD_AARCH64, TestOption.DO_NOT_BUILD_ARMHF],
+        "crosvm": [TestOption.SINGLE_THREADED],
         "devices": [
             TestOption.SINGLE_THREADED,
             TestOption.LARGE,
@@ -75,16 +76,15 @@
     }
 
     BUILD_FEATURES: dict[str, str] = {
-        "x86_64": "all-linux",
-        "aarch64": "all-linux",
-        "armhf": "all-linux-armhf",
+        "x86_64": "linux-x86_64",
+        "aarch64": "linux-aarch64",
+        "armhf": "linux-armhf",
     }
 elif os.name == "nt":
     CRATE_OPTIONS: dict[str, list[TestOption]] = {
         "aarch64": [TestOption.DO_NOT_BUILD],
         "acpi_tables": [TestOption.DO_NOT_BUILD],
         "arch": [TestOption.DO_NOT_BUILD],
-        "assertions": [TestOption.DO_NOT_BUILD],
         "audio_streams": [TestOption.DO_NOT_BUILD],
         "balloon_control": [],
         "base": [TestOption.DO_NOT_BUILD],
@@ -97,7 +97,6 @@
         "crosvm_plugin": [TestOption.DO_NOT_BUILD],
         "crosvm-fuzz": [TestOption.DO_NOT_BUILD],
         "crosvm": [TestOption.DO_NOT_BUILD],
-        "data_model": [TestOption.DO_NOT_BUILD],
         "devices": [TestOption.DO_NOT_BUILD],
         "disk": [TestOption.DO_NOT_BUILD],
         "ffi": [TestOption.DO_NOT_BUILD],
diff --git a/tpm2-sys/Cargo.toml b/tpm2-sys/Cargo.toml
index 89dd491..59cfe2c 100644
--- a/tpm2-sys/Cargo.toml
+++ b/tpm2-sys/Cargo.toml
@@ -2,7 +2,7 @@
 name = "tpm2-sys"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 links = "tpm2"
 
 [build-dependencies]
diff --git a/tpm2/Cargo.toml b/tpm2/Cargo.toml
index 56ee5fb..705be84 100644
--- a/tpm2/Cargo.toml
+++ b/tpm2/Cargo.toml
@@ -2,7 +2,7 @@
 name = "tpm2"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 tpm2-sys = { path = "../tpm2-sys" }
diff --git a/usb_sys/Cargo.toml b/usb_sys/Cargo.toml
index 74f7b92..e74193a 100644
--- a/usb_sys/Cargo.toml
+++ b/usb_sys/Cargo.toml
@@ -2,7 +2,7 @@
 name = "usb_sys"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 base = { path = "../common/base" }
diff --git a/usb_util/Cargo.toml b/usb_util/Cargo.toml
index a52b7dc..f161565 100644
--- a/usb_util/Cargo.toml
+++ b/usb_util/Cargo.toml
@@ -2,7 +2,7 @@
 name = "usb_util"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 assertions = { path = "../common/assertions" }
diff --git a/vfio_sys/Cargo.toml b/vfio_sys/Cargo.toml
index 856703e..782f1b2 100644
--- a/vfio_sys/Cargo.toml
+++ b/vfio_sys/Cargo.toml
@@ -2,7 +2,7 @@
 name = "vfio_sys"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 base = { path = "../common/base" }
diff --git a/vhost/Cargo.toml b/vhost/Cargo.toml
index 2f7adea..09403e4 100644
--- a/vhost/Cargo.toml
+++ b/vhost/Cargo.toml
@@ -2,7 +2,7 @@
 name = "vhost"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 assertions = { path = "../common/assertions" }
diff --git a/virtio_sys/Cargo.toml b/virtio_sys/Cargo.toml
index b90929b..61460a0 100644
--- a/virtio_sys/Cargo.toml
+++ b/virtio_sys/Cargo.toml
@@ -2,7 +2,7 @@
 name = "virtio_sys"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 base = { path = "../common/base" }
diff --git a/vm_control/Cargo.toml b/vm_control/Cargo.toml
index 12fdbbf..9f8b286 100644
--- a/vm_control/Cargo.toml
+++ b/vm_control/Cargo.toml
@@ -2,7 +2,7 @@
 name = "vm_control"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 gdb = ["gdbstub_arch"]
diff --git a/vm_control/src/client.rs b/vm_control/src/client.rs
index 51c8661..863b843 100644
--- a/vm_control/src/client.rs
+++ b/vm_control/src/client.rs
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 use crate::*;
-use base::{info, net::UnixSeqpacket, validate_raw_descriptor, RawDescriptor, Tube};
+use base::{info, validate_raw_descriptor, RawDescriptor, Tube, UnixSeqpacket};
 use remain::sorted;
 use thiserror::Error;
 
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
index 86b9493..4a21a0b 100644
--- a/vm_control/src/lib.rs
+++ b/vm_control/src/lib.rs
@@ -107,6 +107,11 @@
     }
 }
 
+pub trait PmResource {
+    fn pwrbtn_evt(&mut self) {}
+    fn gpe_evt(&mut self, _gpe: u32) {}
+}
+
 /// The maximum number of devices that can be listed in one `UsbControlCommand`.
 ///
 /// This value was set to be equal to `xhci_regs::MAX_PORTS` for convenience, but it is not
@@ -879,10 +884,14 @@
 pub enum VmRequest {
     /// Break the VM's run loop and exit.
     Exit,
+    /// Trigger a power button event in the guest.
+    Powerbtn,
     /// Suspend the VM's VCPUs until resume.
     Suspend,
     /// Resume the VM's VCPUs that were previously suspended.
     Resume,
+    /// Inject a general-purpose event.
+    Gpe(u32),
     /// Make the VM's RT VCPU real-time.
     MakeRT,
     /// Command for balloon driver.
@@ -963,6 +972,7 @@
         balloon_host_tube: Option<&Tube>,
         balloon_stats_id: &mut u64,
         disk_host_tubes: &[Tube],
+        pm: &mut Option<Arc<Mutex<dyn PmResource>>>,
         usb_control_tube: Option<&Tube>,
         bat_control: &mut Option<BatControl>,
         vcpu_handles: &[(JoinHandle<()>, mpsc::Sender<VcpuControl>)],
@@ -972,6 +982,15 @@
                 *run_mode = Some(VmRunMode::Exiting);
                 VmResponse::Ok
             }
+            VmRequest::Powerbtn => {
+                if pm.is_some() {
+                    pm.as_ref().unwrap().lock().pwrbtn_evt();
+                    VmResponse::Ok
+                } else {
+                    error!("{:#?} not supported", *self);
+                    VmResponse::Err(SysError::new(ENOTSUP))
+                }
+            }
             VmRequest::Suspend => {
                 *run_mode = Some(VmRunMode::Suspending);
                 VmResponse::Ok
@@ -980,6 +999,15 @@
                 *run_mode = Some(VmRunMode::Running);
                 VmResponse::Ok
             }
+            VmRequest::Gpe(gpe) => {
+                if pm.is_some() {
+                    pm.as_ref().unwrap().lock().gpe_evt(gpe);
+                    VmResponse::Ok
+                } else {
+                    error!("{:#?} not supported", *self);
+                    VmResponse::Err(SysError::new(ENOTSUP))
+                }
+            }
             VmRequest::MakeRT => {
                 for (handle, channel) in vcpu_handles {
                     if let Err(e) = channel.send(VcpuControl::MakeRT) {
diff --git a/vm_memory/Cargo.toml b/vm_memory/Cargo.toml
index d79df95..f9fc9d2 100644
--- a/vm_memory/Cargo.toml
+++ b/vm_memory/Cargo.toml
@@ -2,7 +2,7 @@
 name = "vm_memory"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 include = ["src/**/*", "Cargo.toml"]
 
 [dependencies]
diff --git a/x86_64/Cargo.toml b/x86_64/Cargo.toml
index 2e8ecd2..2d17645 100644
--- a/x86_64/Cargo.toml
+++ b/x86_64/Cargo.toml
@@ -2,7 +2,7 @@
 name = "x86_64"
 version = "0.1.0"
 authors = ["The Chromium OS Authors"]
-edition = "2018"
+edition = "2021"
 
 [features]
 gdb = ["gdbstub_arch", "arch/gdb"]
diff --git a/x86_64/src/acpi.rs b/x86_64/src/acpi.rs
index 613646c..2a52621 100644
--- a/x86_64/src/acpi.rs
+++ b/x86_64/src/acpi.rs
@@ -7,14 +7,17 @@
 
 use acpi_tables::{facs::FACS, rsdp::RSDP, sdt::SDT};
 use arch::VcpuAffinity;
-use base::error;
+use base::{error, warn};
 use data_model::DataInit;
-use devices::{PciAddress, PciInterruptPin};
+use devices::{ACPIPMResource, PciAddress, PciInterruptPin};
+use std::sync::Arc;
+use sync::Mutex;
 use vm_memory::{GuestAddress, GuestMemory};
 
 pub struct AcpiDevResource {
     pub amls: Vec<u8>,
     pub pm_iobase: u64,
+    pub pm: Arc<Mutex<ACPIPMResource>>,
     /// Additional system descriptor tables.
     pub sdts: Vec<SDT>,
 }
@@ -105,18 +108,34 @@
 // FADT fields offset
 const FADT_FIELD_FACS_ADDR32: usize = 36;
 const FADT_FIELD_DSDT_ADDR32: usize = 40;
-const FADT_FIELD_SCI_INTERRUPT: usize = 46;
+pub const FADT_FIELD_SCI_INTERRUPT: usize = 46;
 const FADT_FIELD_SMI_COMMAND: usize = 48;
 const FADT_FIELD_PM1A_EVENT_BLK_ADDR: usize = 56;
+const FADT_FIELD_PM1B_EVENT_BLK_ADDR: usize = 60;
 const FADT_FIELD_PM1A_CONTROL_BLK_ADDR: usize = 64;
+const FADT_FIELD_PM1B_CONTROL_BLK_ADDR: usize = 68;
+const FADT_FIELD_PM2_CONTROL_BLK_ADDR: usize = 72;
+const FADT_FIELD_GPE0_BLK_ADDR: usize = 80;
+const FADT_FIELD_GPE1_BLK_ADDR: usize = 84;
 const FADT_FIELD_PM1A_EVENT_BLK_LEN: usize = 88;
 const FADT_FIELD_PM1A_CONTROL_BLK_LEN: usize = 89;
+const FADT_FIELD_PM2_CONTROL_BLK_LEN: usize = 90;
+const FADT_FIELD_GPE0_BLK_LEN: usize = 92;
+const FADT_FIELD_GPE1_BLK_LEN: usize = 93;
+const FADT_FIELD_GPE1_BASE: usize = 94;
 const FADT_FIELD_FLAGS: usize = 112;
 const FADT_FIELD_RESET_REGISTER: usize = 116;
 const FADT_FIELD_RESET_VALUE: usize = 128;
 const FADT_FIELD_MINOR_REVISION: usize = 131;
 const FADT_FIELD_FACS_ADDR: usize = 132;
 const FADT_FIELD_DSDT_ADDR: usize = 140;
+const FADT_FIELD_X_PM1A_EVENT_BLK_ADDR: usize = 148;
+const FADT_FIELD_X_PM1B_EVENT_BLK_ADDR: usize = 160;
+const FADT_FIELD_X_PM1A_CONTROL_BLK_ADDR: usize = 172;
+const FADT_FIELD_X_PM1B_CONTROL_BLK_ADDR: usize = 184;
+const FADT_FIELD_X_PM2_CONTROL_BLK_ADDR: usize = 196;
+const FADT_FIELD_X_GPE0_BLK_ADDR: usize = 220;
+const FADT_FIELD_X_GPE1_BLK_ADDR: usize = 232;
 const FADT_FIELD_HYPERVISOR_ID: usize = 268;
 // MADT
 const MADT_LEN: u32 = 44;
@@ -152,7 +171,7 @@
 const MCFG_FIELD_START_BUS_NUMBER: usize = 54;
 const MCFG_FIELD_END_BUS_NUMBER: usize = 55;
 
-fn create_dsdt_table(amls: Vec<u8>) -> SDT {
+fn create_dsdt_table(amls: &[u8]) -> SDT {
     let mut dsdt = SDT::new(
         *b"DSDT",
         acpi_tables::HEADER_LEN,
@@ -163,19 +182,13 @@
     );
 
     if !amls.is_empty() {
-        dsdt.append_slice(amls.as_slice());
+        dsdt.append_slice(amls);
     }
 
     dsdt
 }
 
-fn create_facp_table(
-    sci_irq: u16,
-    pm_iobase: u32,
-    reset_port: u32,
-    reset_value: u8,
-    force_s2idle: bool,
-) -> SDT {
+fn create_facp_table(sci_irq: u16, force_s2idle: bool) -> SDT {
     let mut facp = SDT::new(
         *b"FACP",
         FADT_LEN,
@@ -185,8 +198,7 @@
         OEM_REVISION,
     );
 
-    let mut fadt_flags: u32 = FADT_POWER_BUTTON | FADT_SLEEP_BUTTON | // mask POWER and SLEEP BUTTON
-                          FADT_RESET_REGISTER; // indicate we support FADT RESET_REG
+    let mut fadt_flags: u32 = FADT_SLEEP_BUTTON; // mask SLEEP BUTTON
 
     if force_s2idle {
         fadt_flags |= FADT_LOW_POWER_S2IDLE;
@@ -197,28 +209,142 @@
     // SCI Interrupt
     facp.write(FADT_FIELD_SCI_INTERRUPT, sci_irq);
 
+    facp.write(FADT_FIELD_MINOR_REVISION, FADT_MINOR_REVISION); // FADT minor version
+    facp.write(FADT_FIELD_HYPERVISOR_ID, *b"CROSVM"); // Hypervisor Vendor Identity
+
+    facp
+}
+
+// Write virtualized FADT fields
+fn write_facp_overrides(
+    facp: &mut SDT,
+    facs_offset: GuestAddress,
+    dsdt_offset: GuestAddress,
+    pm_iobase: u32,
+    reset_port: u32,
+    reset_value: u8,
+) {
+    let fadt_flags: u32 = facp.read(FADT_FIELD_FLAGS);
+    // indicate we support FADT RESET_REG
+    facp.write(FADT_FIELD_FLAGS, fadt_flags | FADT_RESET_REGISTER);
+
+    facp.write(FADT_FIELD_SMI_COMMAND, 0u32);
+    facp.write(FADT_FIELD_FACS_ADDR32, 0u32);
+    facp.write(FADT_FIELD_DSDT_ADDR32, 0u32);
+    facp.write(FADT_FIELD_FACS_ADDR, facs_offset.0 as u64);
+    facp.write(FADT_FIELD_DSDT_ADDR, dsdt_offset.0 as u64);
+
     // PM1A Event Block Address
     facp.write(FADT_FIELD_PM1A_EVENT_BLK_ADDR, pm_iobase);
 
+    // PM1B Event Block Address (not supported)
+    facp.write(FADT_FIELD_PM1B_EVENT_BLK_ADDR, 0u32);
+
     // PM1A Control Block Address
     facp.write(
         FADT_FIELD_PM1A_CONTROL_BLK_ADDR,
         pm_iobase + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32,
     );
 
+    // PM1B Control Block Address (not supported)
+    facp.write(FADT_FIELD_PM1B_CONTROL_BLK_ADDR, 0u32);
+
+    // PM2 Control Block Address (not supported)
+    facp.write(FADT_FIELD_PM2_CONTROL_BLK_ADDR, 0u32);
+
+    // GPE0 Block Address
+    facp.write(
+        FADT_FIELD_GPE0_BLK_ADDR,
+        pm_iobase + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32 + 4,
+    );
+
+    // GPE1 Block Address (not supported)
+    facp.write(FADT_FIELD_GPE1_BLK_ADDR, 0u32);
+
     // PM1 Event Block Length
     facp.write(
         FADT_FIELD_PM1A_EVENT_BLK_LEN,
-        devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u8,
+        devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN,
     );
 
     // PM1 Control Block Length
     facp.write(
         FADT_FIELD_PM1A_CONTROL_BLK_LEN,
-        devices::acpi::ACPIPM_RESOURCE_CONTROLBLK_LEN as u8,
+        devices::acpi::ACPIPM_RESOURCE_CONTROLBLK_LEN,
     );
 
-    // Reset register.
+    // PM2 Control Block Length (not supported)
+    facp.write(FADT_FIELD_PM2_CONTROL_BLK_LEN, 0u8);
+
+    // GPE0 Block Length
+    facp.write(
+        FADT_FIELD_GPE0_BLK_LEN,
+        devices::acpi::ACPIPM_RESOURCE_GPE0_BLK_LEN,
+    );
+
+    // GPE1 Block Length (not supported)
+    facp.write(FADT_FIELD_GPE1_BLK_LEN, 0u8);
+
+    // GPE1 Base (not supported)
+    facp.write(FADT_FIELD_GPE1_BASE, 0u8);
+
+    // PM1A Extended Event Block Address (not supported)
+    facp.write(
+        FADT_FIELD_X_PM1A_EVENT_BLK_ADDR,
+        GenericAddress {
+            ..Default::default()
+        },
+    );
+
+    // PM1B Extended Event Block Address (not supported)
+    facp.write(
+        FADT_FIELD_X_PM1B_EVENT_BLK_ADDR,
+        GenericAddress {
+            ..Default::default()
+        },
+    );
+
+    // PM1A Extended Control Block Address (not supported)
+    facp.write(
+        FADT_FIELD_X_PM1A_CONTROL_BLK_ADDR,
+        GenericAddress {
+            ..Default::default()
+        },
+    );
+
+    // PM1B Extended Control Block Address (not supported)
+    facp.write(
+        FADT_FIELD_X_PM1B_CONTROL_BLK_ADDR,
+        GenericAddress {
+            ..Default::default()
+        },
+    );
+
+    // PM2 Extended Control Block Address (not supported)
+    facp.write(
+        FADT_FIELD_X_PM2_CONTROL_BLK_ADDR,
+        GenericAddress {
+            ..Default::default()
+        },
+    );
+
+    // GPE0 Extended Address (not supported)
+    facp.write(
+        FADT_FIELD_X_GPE0_BLK_ADDR,
+        GenericAddress {
+            ..Default::default()
+        },
+    );
+
+    // GPE1 Extended Address (not supported)
+    facp.write(
+        FADT_FIELD_X_GPE1_BLK_ADDR,
+        GenericAddress {
+            ..Default::default()
+        },
+    );
+
+    // Reset register
     facp.write(
         FADT_FIELD_RESET_REGISTER,
         GenericAddress {
@@ -230,11 +356,6 @@
         },
     );
     facp.write(FADT_FIELD_RESET_VALUE, reset_value);
-
-    facp.write(FADT_FIELD_MINOR_REVISION, FADT_MINOR_REVISION); // FADT minor version
-    facp.write(FADT_FIELD_HYPERVISOR_ID, *b"CROSVM"); // Hypervisor Vendor Identity
-
-    facp
 }
 
 fn next_offset(offset: GuestAddress, len: u64) -> Option<GuestAddress> {
@@ -363,7 +484,7 @@
     sci_irq: u32,
     reset_port: u32,
     reset_value: u8,
-    acpi_dev_resource: AcpiDevResource,
+    acpi_dev_resource: &AcpiDevResource,
     host_cpus: Option<VcpuAffinity>,
     apic_ids: &mut Vec<usize>,
     pci_irqs: &[(PciAddress, u32, PciInterruptPin)],
@@ -408,7 +529,7 @@
         Some(dsdt_offset) => dsdt_offset,
         None => {
             let dsdt_offset = offset;
-            let dsdt = create_dsdt_table(acpi_dev_resource.amls);
+            let dsdt = create_dsdt_table(&acpi_dev_resource.amls);
             guest_mem.write_at_addr(dsdt.as_slice(), offset).ok()?;
             offset = next_offset(offset, dsdt.len() as u64)?;
             dsdt_offset
@@ -416,23 +537,28 @@
     };
 
     // FACP aka FADT
-    let pm_iobase = acpi_dev_resource.pm_iobase as u32;
-    let mut facp = facp.unwrap_or_else(|| {
-        create_facp_table(
-            sci_irq as u16,
-            pm_iobase,
-            reset_port,
-            reset_value,
-            force_s2idle,
-        )
-    });
+    let mut facp = facp.map_or_else(
+        || create_facp_table(sci_irq as u16, force_s2idle),
+        |facp| {
+            let fadt_flags: u32 = facp.read(FADT_FIELD_FLAGS);
+            if fadt_flags & FADT_POWER_BUTTON != 0 {
+                warn!(
+                    "Control Method Power Button is not supported. FADT flags = 0x{:x}",
+                    fadt_flags
+                );
+            }
+            facp
+        },
+    );
 
-    // Crosvm FACP overrides.
-    facp.write(FADT_FIELD_SMI_COMMAND, 0u32);
-    facp.write(FADT_FIELD_FACS_ADDR32, 0u32);
-    facp.write(FADT_FIELD_DSDT_ADDR32, 0u32);
-    facp.write(FADT_FIELD_FACS_ADDR, facs_offset.0 as u64);
-    facp.write(FADT_FIELD_DSDT_ADDR, dsdt_offset.0 as u64);
+    write_facp_overrides(
+        &mut facp,
+        facs_offset,
+        dsdt_offset,
+        acpi_dev_resource.pm_iobase as u32,
+        reset_port,
+        reset_value,
+    );
 
     guest_mem.write_at_addr(facp.as_slice(), offset).ok()?;
     tables.push(offset.0);
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index 3aa7f0e..20a9916 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -56,7 +56,7 @@
 use acpi_tables::sdt::SDT;
 use acpi_tables::{aml, aml::Aml};
 use arch::{get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, VmComponents, VmImage};
-use base::Event;
+use base::{warn, Event};
 use devices::serial_device::{SerialHardware, SerialParameters};
 use devices::{
     BusDeviceObj, BusResumeDevice, IrqChip, IrqChipX86_64, PciAddress, PciConfigIo, PciConfigMmio,
@@ -453,6 +453,24 @@
         let tss_addr = GuestAddress(TSS_ADDR);
         vm.set_tss_addr(tss_addr).map_err(Error::SetTssAddr)?;
 
+        // Use IRQ info in ACPI if provided by the user.
+        let mut noirq = true;
+        let mut mptable = true;
+        let mut sci_irq = X86_64_SCI_IRQ;
+
+        for sdt in components.acpi_sdts.iter() {
+            if sdt.is_signature(b"DSDT") || sdt.is_signature(b"APIC") {
+                noirq = false;
+            } else if sdt.is_signature(b"FACP") {
+                mptable = false;
+                let sci_irq_fadt: u16 = sdt.read(acpi::FADT_FIELD_SCI_INTERRUPT);
+                sci_irq = sci_irq_fadt.into();
+                if !system_allocator.reserve_irq(sci_irq) {
+                    warn!("sci irq {} already reserved.", sci_irq);
+                }
+            }
+        }
+
         let mmio_bus = Arc::new(devices::Bus::new());
         let io_bus = Arc::new(devices::Bus::new());
 
@@ -532,18 +550,13 @@
             exit_evt.try_clone().map_err(Error::CloneEvent)?,
             components.acpi_sdts,
             irq_chip.as_irq_chip_mut(),
+            sci_irq,
             battery,
             &mmio_bus,
             max_bus,
             &mut resume_notify_devices,
         )?;
 
-        // Use IRQ info in ACPI if provided by the user.
-        let noirq = !acpi_dev_resource
-            .sdts
-            .iter()
-            .any(|sdt| sdt.is_signature(b"DSDT") || sdt.is_signature(b"APIC"));
-
         irq_chip
             .finalize_devices(system_allocator, &io_bus, &mmio_bus)
             .map_err(Error::RegisterIrqfd)?;
@@ -556,8 +569,11 @@
         // If another guest does need a way to pass these tables down to it's BIOS, this approach
         // should be rethought.
 
-        // Note that this puts the mptable at 0x9FC00 in guest physical memory.
-        mptable::setup_mptable(&mem, vcpu_count as u8, &pci_irqs).map_err(Error::SetupMptable)?;
+        if mptable {
+            // Note that this puts the mptable at 0x9FC00 in guest physical memory.
+            mptable::setup_mptable(&mem, vcpu_count as u8, &pci_irqs)
+                .map_err(Error::SetupMptable)?;
+        }
         smbios::setup_smbios(&mem, components.dmi_path).map_err(Error::SetupSmbios)?;
 
         let host_cpus = if components.host_cpu_topology {
@@ -570,10 +586,10 @@
         acpi::create_acpi_tables(
             &mem,
             vcpu_count as u8,
-            X86_64_SCI_IRQ,
+            sci_irq,
             0xcf9,
             6, // RST_CPU|SYS_RST
-            acpi_dev_resource,
+            &acpi_dev_resource,
             host_cpus,
             kvm_vcpu_ids,
             &pci_irqs,
@@ -646,6 +662,7 @@
             bat_control,
             #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
             gdb: components.gdb,
+            pm: Some(acpi_dev_resource.pm),
             root_config: pci,
             hotplug_bus: Vec::new(),
         })
@@ -1266,6 +1283,7 @@
         exit_evt: Event,
         sdts: Vec<SDT>,
         irq_chip: &mut dyn IrqChip,
+        sci_irq: u32,
         battery: (&Option<BatteryType>, Option<Minijail>),
         mmio_bus: &devices::Bus,
         max_bus: u8,
@@ -1281,17 +1299,21 @@
                     devices::acpi::ACPIPM_RESOURCE_LEN as u64,
                     pm_alloc,
                     "ACPIPM".to_string(),
-                    devices::acpi::ACPIPM_RESOURCE_LEN as u64,
+                    4, // must be 32-bit aligned
                 )
                 .map_err(Error::AllocateIOResouce)?,
             None => 0x600,
         };
 
         let pcie_vcfg = aml::Name::new("VCFG".into(), &Self::get_pcie_vcfg_mmio_base(mem));
-        Aml::to_aml_bytes(&pcie_vcfg, &mut amls);
+        pcie_vcfg.to_aml_bytes(&mut amls);
 
-        let pmresource = devices::ACPIPMResource::new(suspend_evt, exit_evt);
-        Aml::to_aml_bytes(&pmresource, &mut amls);
+        let pm_sci_evt = Event::new().map_err(Error::CreateEvent)?;
+        irq_chip
+            .register_irq_event(sci_irq, &pm_sci_evt, None)
+            .map_err(Error::RegisterIrqfd)?;
+        let pmresource = devices::ACPIPMResource::new(pm_sci_evt, suspend_evt, exit_evt);
+        pmresource.to_aml_bytes(&mut amls);
 
         let mut pci_dsdt_inner_data: Vec<&dyn aml::Aml> = Vec::new();
         let hid = aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A08"));
@@ -1340,18 +1362,13 @@
                 devices::acpi::ACPIPM_RESOURCE_LEN as u64,
             )
             .unwrap();
-        resume_notify_devices.push(pm);
+        resume_notify_devices.push(pm.clone());
 
         let bat_control = if let Some(battery_type) = battery.0 {
             match battery_type {
                 BatteryType::Goldfish => {
                     let control_tube = arch::add_goldfish_battery(
-                        &mut amls,
-                        battery.1,
-                        mmio_bus,
-                        irq_chip,
-                        X86_64_SCI_IRQ,
-                        resources,
+                        &mut amls, battery.1, mmio_bus, irq_chip, sci_irq, resources,
                     )
                     .map_err(Error::CreateBatDevices)?;
                     Some(BatControl {
@@ -1368,6 +1385,7 @@
             acpi::AcpiDevResource {
                 amls,
                 pm_iobase,
+                pm,
                 sdts,
             },
             bat_control,
diff --git a/x86_64/src/test_integration.rs b/x86_64/src/test_integration.rs
index b19f92d..90eec51 100644
--- a/x86_64/src/test_integration.rs
+++ b/x86_64/src/test_integration.rs
@@ -193,6 +193,7 @@
         exit_evt.try_clone().expect("unable to clone exit_evt"),
         Default::default(),
         &mut irq_chip,
+        X86_64_SCI_IRQ,
         (&None, None),
         &mmio_bus,
         max_bus,
@@ -221,7 +222,7 @@
         X86_64_SCI_IRQ,
         0xcf9,
         6,
-        acpi_dev_resource.0,
+        &acpi_dev_resource.0,
         None,
         &mut apic_ids,
         &pci_irqs,