Merge "Merge commit '47996808415af4e0b7bd11db103b52d5fdd5fda7' into HEAD"
diff --git a/Android.bp b/Android.bp
index 3339be3..cc5a759 100644
--- a/Android.bp
+++ b/Android.bp
@@ -109,6 +109,7 @@
         "libminijail_rust",
         "libmsg_socket",
         "libnet_util",
+        "libp9",
         "librand_ish",
         "libresources",
         "libsync_rust",
@@ -158,6 +159,7 @@
         "libminijail_rust",
         "libmsg_socket",
         "libnet_util",
+        "libp9",
         "librand_ish",
         "libresources",
         "libsync_rust",
@@ -232,6 +234,7 @@
         "libminijail_rust",
         "libmsg_socket",
         "libnet_util",
+        "libp9",
         "librand_ish",
         "libresources",
         "libsync_rust",
@@ -308,6 +311,7 @@
         "libminijail_rust",
         "libmsg_socket",
         "libnet_util",
+        "libp9",
         "librand_ish",
         "libresources",
         "libsync_rust",
@@ -328,9 +332,11 @@
 //   ../adhd/cras/client/libcras/src/libcras.rs
 //   ../minijail/rust/minijail-sys/lib.rs
 //   ../minijail/rust/minijail/src/lib.rs
+//   ../rust/crates/libchromeos-rs/src/lib.rs
 //   ../vm_tools/p9/src/lib.rs
 //   ../vm_tools/p9/wire_format_derive/wire_format_derive.rs
 //   async-trait-0.1.42
+//   autocfg-1.0.1
 //   bitflags-1.2.1 "default"
 //   cfg-if-0.1.10
 //   downcast-rs-1.2.0 "default,std"
@@ -343,9 +349,13 @@
 //   futures-sink-0.3.8 "alloc,std"
 //   futures-task-0.3.8 "alloc,once_cell,std"
 //   futures-util-0.3.8 "alloc,async-await,async-await-macro,channel,futures-channel,futures-io,futures-macro,futures-sink,io,memchr,proc-macro-hack,proc-macro-nested,sink,slab,std"
+//   getopts-0.2.21
 //   getrandom-0.1.15 "std"
+//   intrusive-collections-0.9.0 "alloc,default"
 //   libc-0.2.80 "default,std"
+//   log-0.4.11
 //   memchr-2.3.4 "default,std"
+//   memoffset-0.5.6 "default"
 //   once_cell-1.5.2 "alloc,std"
 //   paste-1.0.3
 //   pin-project-1.0.2
@@ -356,6 +366,7 @@
 //   proc-macro-hack-0.5.19
 //   proc-macro-nested-0.1.6
 //   proc-macro2-1.0.24 "default,proc-macro"
+//   protobuf-2.18.1
 //   quote-1.0.7 "default,proc-macro"
 //   rand-0.7.3 "alloc,default,getrandom,getrandom_package,libc,std"
 //   rand_chacha-0.2.2 "std"
@@ -363,8 +374,9 @@
 //   remain-0.2.2
 //   remove_dir_all-0.5.3
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   tempfile-3.1.0
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
+//   unicode-width-0.1.8 "default"
 //   unicode-xid-0.2.1 "default"
diff --git a/Cargo.lock b/Cargo.lock
index f410a02..a630236 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -41,10 +41,12 @@
  "acpi_tables 0.1.0",
  "base 0.1.0",
  "devices 0.1.0",
+ "gdbstub 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "hypervisor 0.1.0",
  "kernel_cmdline 0.1.0",
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
  "minijail 0.2.1",
+ "msg_socket 0.1.0",
  "resources 0.1.0",
  "sync 0.1.0",
  "vm_control 0.1.0",
@@ -74,10 +76,17 @@
 ]
 
 [[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "base"
 version = "0.1.0"
 dependencies = [
+ "cros_async 0.1.0",
  "data_model 0.1.0",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
  "sync 0.1.0",
  "sys_util 0.1.0",
 ]
@@ -110,7 +119,7 @@
 
 [[package]]
 name = "cfg-if"
-version = "0.1.5"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -126,13 +135,13 @@
 version = "0.1.0"
 dependencies = [
  "async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
- "base 0.1.0",
  "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "io_uring 0.1.0",
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
- "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "paste 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
  "syscall_defines 0.1.0",
  "thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -153,6 +162,7 @@
  "devices 0.1.0",
  "disk 0.1.0",
  "enumn 0.1.0",
+ "gdbstub 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gpu_buffer 0.1.0",
  "gpu_renderer 0.1.0",
  "hypervisor 0.1.0",
@@ -165,6 +175,7 @@
  "minijail 0.2.1",
  "msg_socket 0.1.0",
  "net_util 0.1.0",
+ "p9 0.1.0",
  "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "protos 0.1.0",
  "rand_ish 0.1.0",
@@ -172,6 +183,7 @@
  "resources 0.1.0",
  "sync 0.1.0",
  "tempfile 3.0.7",
+ "thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "vhost 0.1.0",
  "vm_control 0.1.0",
  "vm_memory 0.1.0",
@@ -206,11 +218,11 @@
  "audio_streams 0.1.0",
  "base 0.1.0",
  "bit_field 0.1.0",
- "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cros_async 0.1.0",
  "data_model 0.1.0",
  "disk 0.1.0",
  "enumn 0.1.0",
+ "fuse 0.1.0",
  "gpu_buffer 0.1.0",
  "gpu_display 0.1.0",
  "gpu_renderer 0.1.0",
@@ -274,6 +286,18 @@
 ]
 
 [[package]]
+name = "fuse"
+version = "0.1.0"
+dependencies = [
+ "base 0.1.0",
+ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "data_model 0.1.0",
+ "enumn 0.1.0",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "futures"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -356,6 +380,18 @@
 ]
 
 [[package]]
+name = "gdbstub"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "managed 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "paste 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "getopts"
 version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -410,11 +446,19 @@
 ]
 
 [[package]]
+name = "intrusive-collections"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memoffset 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "io_uring"
 version = "0.1.0"
 dependencies = [
- "base 0.1.0",
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
  "syscall_defines 0.1.0",
 ]
 
@@ -463,6 +507,19 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "libchromeos"
+version = "0.1.0"
+dependencies = [
+ "data_model 0.1.0",
+ "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "intrusive-collections 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "libcras"
 version = "0.1.0"
 dependencies = [
@@ -496,15 +553,28 @@
 version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "managed"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "memchr"
 version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "memoffset"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "minijail"
 version = "0.2.1"
 dependencies = [
@@ -524,6 +594,7 @@
 name = "msg_on_socket_derive"
 version = "0.1.0"
 dependencies = [
+ "base 0.1.0",
  "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -560,6 +631,14 @@
 ]
 
 [[package]]
+name = "num-traits"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "num_cpus"
 version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -572,28 +651,14 @@
 version = "0.1.0"
 dependencies = [
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libchromeos 0.1.0",
  "wire_format_derive 0.1.0",
 ]
 
 [[package]]
 name = "paste"
-version = "0.1.6"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "paste-impl"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
-]
 
 [[package]]
 name = "pin-utils"
@@ -857,6 +922,7 @@
 dependencies = [
  "base 0.1.0",
  "data_model 0.1.0",
+ "gdbstub 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "hypervisor 0.1.0",
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg_socket 0.1.0",
@@ -895,11 +961,13 @@
  "base 0.1.0",
  "data_model 0.1.0",
  "devices 0.1.0",
+ "gdbstub 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "hypervisor 0.1.0",
  "kernel_cmdline 0.1.0",
  "kernel_loader 0.1.0",
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
  "minijail 0.2.1",
+ "msg_socket 0.1.0",
  "remain 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "resources 0.1.0",
  "sync 0.1.0",
@@ -910,9 +978,10 @@
 [metadata]
 "checksum android_log-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e"
 "checksum async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92"
+"checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
 "checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
-"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
+"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 "checksum downcast-rs 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
 "checksum futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987"
 "checksum futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86"
@@ -923,13 +992,17 @@
 "checksum futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16"
 "checksum futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9"
 "checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76"
+"checksum gdbstub 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "347c27d24b8ac4a2bcad3ff3d0695271a0510c020bd8134b53d189e973ed58bf"
 "checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
+"checksum intrusive-collections 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4bca8c0bb831cd60d4dda79a58e3705ca6eb47efb65d665651a8d672213ec3db"
 "checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
 "checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
+"checksum managed 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
 "checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223"
+"checksum memoffset 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
+"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
 "checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238"
-"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49"
-"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5"
+"checksum paste 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba7ae1a2180ed02ddfdb5ab70c70d596a26dd642e097bb6fe78b1bde8588ed97"
 "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
 "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f"
 "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
diff --git a/Cargo.toml b/Cargo.toml
index 1b3e233..354e072 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -45,6 +45,7 @@
 virtio-gpu-next = ["gpu_renderer/virtio-gpu-next"]
 composite-disk = ["protos/composite-disk", "protobuf", "disk/composite-disk"]
 gfxstream = ["devices/gfxstream"]
+gdb = ["gdbstub", "thiserror",  "arch/gdb", "vm_control/gdb", "x86_64/gdb"]
 
 [dependencies]
 arch = { path = "arch" }
@@ -57,6 +58,7 @@
 devices = { path = "devices" }
 disk = { path = "disk" }
 enumn = { path = "enumn" }
+gdbstub = { version = "0.4.0", optional = true }
 gpu_buffer = { path = "gpu_buffer", optional = true }
 gpu_renderer = { path = "gpu_renderer", optional = true }
 hypervisor = { path = "hypervisor" }
@@ -69,6 +71,7 @@
 minijail = "*" # provided by ebuild
 msg_socket = { path = "msg_socket" }
 net_util = { path = "net_util" }
+p9 = { path = "../vm_tools/p9" }
 protobuf = { version = "2.3", optional = true }
 protos = { path = "protos", optional = true }
 rand_ish = { path = "rand_ish" }
@@ -76,6 +79,7 @@
 resources = { path = "resources" }
 sync = { path = "sync" }
 tempfile = "*"
+thiserror = { version = "1.0.20", optional = true }
 vhost = { path = "vhost" }
 vm_control = { path = "vm_control" }
 acpi_tables = { path = "acpi_tables" }
diff --git a/README.md b/README.md
index f6b8f5d..3999a5e 100644
--- a/README.md
+++ b/README.md
@@ -162,6 +162,34 @@
 the path `$XDG_RUNTIME_DIR/wayland-0` points to the socket of the Wayland
 compositor you would like the guest to use.
 
+### GDB Support
+
+crosvm supports [GDB Remote Serial Protocol] to allow developers to debug guest
+kernel via GDB.
+
+You can enable the feature by `--gdb` flag:
+
+```sh
+# Use uncompressed vmlinux
+$ crosvm run --gdb <port> ${USUAL_CROSVM_ARGS} vmlinux
+```
+
+Then, you can start GDB in another shell.
+
+```sh
+$ gdb vmlinux
+(gdb) target remote :<port>
+(gdb) hbreak start_kernel
+(gdb) c
+<start booting in the other shell>
+```
+
+For general techniques for debugging the Linux kernel via GDB, see this
+[kernel documentation].
+
+[GDB Remote Serial Protocol]: https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html
+[kernel documentation]: https://www.kernel.org/doc/html/latest/dev-tools/gdb-kernel-debugging.html
+
 ## Defaults
 
 The following are crosvm's default arguments and how to override them.
diff --git a/aarch64/Android.bp b/aarch64/Android.bp
index 90a99f9..790b958 100644
--- a/aarch64/Android.bp
+++ b/aarch64/Android.bp
@@ -95,6 +95,7 @@
 //   ../../adhd/cras/client/libcras/src/libcras.rs
 //   ../../minijail/rust/minijail-sys/lib.rs
 //   ../../minijail/rust/minijail/src/lib.rs
+//   ../../rust/crates/libchromeos-rs/src/lib.rs
 //   ../../vm_tools/p9/src/lib.rs
 //   ../../vm_tools/p9/wire_format_derive/wire_format_derive.rs
 //   ../acpi_tables/src/lib.rs
@@ -108,6 +109,7 @@
 //   ../devices/src/lib.rs
 //   ../disk/src/disk.rs
 //   ../enumn/src/lib.rs
+//   ../fuse/src/lib.rs
 //   ../hypervisor/src/lib.rs
 //   ../io_uring/src/lib.rs
 //   ../kernel_cmdline/src/kernel_cmdline.rs
@@ -133,6 +135,8 @@
 //   ../vm_control/src/lib.rs
 //   ../vm_memory/src/lib.rs
 //   async-trait-0.1.42
+//   autocfg-1.0.1
+//   base-0.1.0
 //   bitflags-1.2.1 "default"
 //   cfg-if-0.1.10
 //   downcast-rs-1.2.0 "default,std"
@@ -145,9 +149,13 @@
 //   futures-sink-0.3.8 "alloc,std"
 //   futures-task-0.3.8 "alloc,once_cell,std"
 //   futures-util-0.3.8 "alloc,async-await,async-await-macro,channel,futures-channel,futures-io,futures-macro,futures-sink,io,memchr,proc-macro-hack,proc-macro-nested,sink,slab,std"
+//   getopts-0.2.21
 //   getrandom-0.1.15 "std"
+//   intrusive-collections-0.9.0 "alloc,default"
 //   libc-0.2.80 "default,std"
+//   log-0.4.11
 //   memchr-2.3.4 "default,std"
+//   memoffset-0.5.6 "default"
 //   once_cell-1.5.2 "alloc,std"
 //   paste-1.0.3
 //   pin-project-1.0.2
@@ -158,6 +166,7 @@
 //   proc-macro-hack-0.5.19
 //   proc-macro-nested-0.1.6
 //   proc-macro2-1.0.24 "default,proc-macro"
+//   protobuf-2.18.1
 //   quote-1.0.7 "default,proc-macro"
 //   rand-0.7.3 "alloc,default,getrandom,getrandom_package,libc,std"
 //   rand_chacha-0.2.2 "std"
@@ -165,8 +174,9 @@
 //   remain-0.2.2
 //   remove_dir_all-0.5.3
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   tempfile-3.1.0
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
+//   unicode-width-0.1.8 "default"
 //   unicode-xid-0.2.1 "default"
diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs
index cda15fa..9606982 100644
--- a/aarch64/src/fdt.rs
+++ b/aarch64/src/fdt.rs
@@ -12,6 +12,7 @@
 };
 use arch::SERIAL_ADDR;
 use devices::{PciAddress, PciInterruptPin};
+use hypervisor::PsciVersion;
 use vm_memory::{GuestAddress, GuestMemory};
 
 // This is the start of DRAM in the physical address space.
@@ -182,18 +183,20 @@
     Ok(())
 }
 
-// TODO(sonnyrao) -- check to see if host kernel supports PSCI 0_2
-fn create_psci_node(fdt: &mut Vec<u8>) -> Result<()> {
-    let compatible = "arm,psci-0.2";
+fn create_psci_node(fdt: &mut Vec<u8>, version: &PsciVersion) -> Result<()> {
+    let compatible = if version.major == 1 {
+        // Put `psci-0.2` as well because PSCI 1.0 is compatible with PSCI 0.2.
+        format!(
+            "\"arm,psci-{}.{}\", \"arm,psci-0.2\"",
+            version.major, version.minor
+        )
+    } else {
+        format!("arm,psci-{}.{}", version.major, version.minor)
+    };
     begin_node(fdt, "psci")?;
-    property_string(fdt, "compatible", compatible)?;
+    property_string(fdt, "compatible", &compatible)?;
     // Only support aarch64 guest
     property_string(fdt, "method", "hvc")?;
-    // These constants are from PSCI
-    property_u32(fdt, "cpu_suspend", 0xc4000001)?;
-    property_u32(fdt, "cpu_off", 0x84000002)?;
-    property_u32(fdt, "cpu_on", 0xc4000003)?;
-    property_u32(fdt, "migrate", 0xc4000005)?;
     end_node(fdt)?;
 
     Ok(())
@@ -358,6 +361,7 @@
 /// * `initrd` - An optional tuple of initrd guest physical address and size
 /// * `android_fstab` - An optional file holding Android fstab entries
 /// * `is_gicv3` - True if gicv3, false if v2
+/// * `psci_version` - the current PSCI version
 pub fn create_fdt(
     fdt_max_size: usize,
     guest_mem: &GuestMemory,
@@ -371,6 +375,7 @@
     android_fstab: Option<File>,
     is_gicv3: bool,
     use_pmu: bool,
+    psci_version: PsciVersion,
 ) -> Result<()> {
     let mut fdt = vec![0; fdt_max_size];
     start_fdt(&mut fdt, fdt_max_size)?;
@@ -393,7 +398,7 @@
         create_pmu_node(&mut fdt, num_cpus)?;
     }
     create_serial_nodes(&mut fdt)?;
-    create_psci_node(&mut fdt)?;
+    create_psci_node(&mut fdt, &psci_version)?;
     create_pci_nodes(&mut fdt, pci_irqs, pci_device_base, pci_device_size)?;
     create_rtc_node(&mut fdt)?;
     // End giant node
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
index cb8fecf..9eb5a74 100644
--- a/aarch64/src/lib.rs
+++ b/aarch64/src/lib.rs
@@ -18,11 +18,14 @@
 use devices::{
     Bus, BusError, IrqChip, IrqChipAArch64, PciAddress, PciConfigMmio, PciDevice, PciInterruptPin,
 };
-use hypervisor::{DeviceKind, Hypervisor, HypervisorCap, VcpuAArch64, VcpuFeature, VmAArch64};
+use hypervisor::{
+    DeviceKind, Hypervisor, HypervisorCap, PsciVersion, VcpuAArch64, VcpuFeature, VmAArch64,
+};
 use minijail::Minijail;
 use remain::sorted;
 use resources::SystemAllocator;
 use sync::Mutex;
+use vm_control::BatteryType;
 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
 
 mod fdt;
@@ -123,6 +126,7 @@
     CreateVcpu(base::Error),
     CreateVm(Box<dyn StdError>),
     DowncastVcpu,
+    GetPsciVersion(base::Error),
     GetSerialCmdline(GetSerialCmdlineError),
     InitrdLoadFailure(arch::LoadImageError),
     KernelLoadFailure(arch::LoadImageError),
@@ -156,6 +160,7 @@
             CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
             CreateVm(e) => write!(f, "failed to create vm: {}", e),
             DowncastVcpu => write!(f, "vm created wrong kind of vcpu"),
+            GetPsciVersion(e) => write!(f, "failed to get PSCI version: {}", e),
             GetSerialCmdline(e) => write!(f, "failed to get serial cmdline: {}", e),
             InitrdLoadFailure(e) => write!(f, "initrd cound not be loaded: {}", e),
             KernelLoadFailure(e) => write!(f, "kernel cound not be loaded: {}", e),
@@ -197,6 +202,7 @@
         mut components: VmComponents,
         serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
+        _battery: (&Option<BatteryType>, Option<Minijail>),
         create_devices: FD,
         create_vm: FV,
         create_irq_chip: FI,
@@ -290,12 +296,7 @@
             .map_err(Error::RegisterIrqfd)?;
 
         mmio_bus
-            .insert(
-                pci_bus.clone(),
-                AARCH64_PCI_CFG_BASE,
-                AARCH64_PCI_CFG_SIZE,
-                false,
-            )
+            .insert(pci_bus.clone(), AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE)
             .map_err(Error::RegisterPci)?;
 
         let mut cmdline = Self::get_base_linux_cmdline();
@@ -316,6 +317,8 @@
         let kernel_size = arch::load_image(&mem, kernel_image, get_kernel_addr(), u64::max_value())
             .map_err(Error::KernelLoadFailure)?;
         let kernel_end = get_kernel_addr().offset() + kernel_size as u64;
+        let psci_version = vcpus[0].get_psci_version().map_err(Error::GetPsciVersion)?;
+
         Self::setup_system_memory(
             &mem,
             components.memory_size,
@@ -327,6 +330,7 @@
             kernel_end,
             irq_chip.get_vgic_version() == DeviceKind::ArmVgicV3,
             use_pmu,
+            psci_version,
         )?;
 
         Ok(RunnableLinuxVm {
@@ -344,6 +348,7 @@
             pid_debug_label_map,
             suspend_evt,
             rt_cpus: components.rt_cpus,
+            bat_control: None,
         })
     }
 
@@ -374,6 +379,7 @@
         kernel_end: u64,
         is_gicv3: bool,
         use_pmu: bool,
+        psci_version: PsciVersion,
     ) -> Result<()> {
         let initrd = match initrd_file {
             Some(initrd_file) => {
@@ -403,6 +409,7 @@
             android_fstab,
             is_gicv3,
             use_pmu,
+            psci_version,
         )
         .map_err(Error::CreateFdt)?;
         Ok(())
@@ -450,7 +457,7 @@
             .map_err(Error::RegisterIrqfd)?;
 
         let rtc = Arc::new(Mutex::new(devices::pl030::Pl030::new(rtc_evt)));
-        bus.insert(rtc, AARCH64_RTC_ADDR, AARCH64_RTC_SIZE, false)
+        bus.insert(rtc, AARCH64_RTC_ADDR, AARCH64_RTC_SIZE)
             .expect("failed to add rtc device");
 
         Ok(())
diff --git a/arch/Android.bp b/arch/Android.bp
index d67d261..e5f2ed4 100644
--- a/arch/Android.bp
+++ b/arch/Android.bp
@@ -16,6 +16,7 @@
         "libkernel_cmdline",
         "liblibc",
         "libminijail_rust",
+        "libmsg_socket",
         "libresources",
         "libsync_rust",
         "libvm_control",
@@ -51,6 +52,7 @@
         "libkernel_cmdline",
         "liblibc",
         "libminijail_rust",
+        "libmsg_socket",
         "libresources",
         "libsync_rust",
         "libvm_control",
@@ -65,6 +67,7 @@
 //   ../../adhd/audio_streams/src/audio_streams.rs
 //   ../../adhd/cras/client/cras-sys/src/lib.rs
 //   ../../adhd/cras/client/libcras/src/libcras.rs
+//   ../../libchromeos-rs/src/lib.rs
 //   ../../minijail/rust/minijail-sys/lib.rs
 //   ../../minijail/rust/minijail/src/lib.rs
 //   ../../vm_tools/p9/src/lib.rs
@@ -79,6 +82,7 @@
 //   ../devices/src/lib.rs
 //   ../disk/src/disk.rs
 //   ../enumn/src/lib.rs
+//   ../fuse/src/lib.rs
 //   ../hypervisor/src/lib.rs
 //   ../io_uring/src/lib.rs
 //   ../kernel_cmdline/src/kernel_cmdline.rs
@@ -104,6 +108,8 @@
 //   ../vm_control/src/lib.rs
 //   ../vm_memory/src/lib.rs
 //   async-trait-0.1.42
+//   autocfg-1.0.1
+//   base-0.1.0
 //   bitflags-1.2.1 "default"
 //   cfg-if-0.1.10
 //   downcast-rs-1.2.0 "default,std"
@@ -117,8 +123,11 @@
 //   futures-task-0.3.8 "alloc,once_cell,std"
 //   futures-util-0.3.8 "alloc,async-await,async-await-macro,channel,futures-channel,futures-io,futures-macro,futures-sink,io,memchr,proc-macro-hack,proc-macro-nested,sink,slab,std"
 //   getrandom-0.1.15 "std"
+//   intrusive-collections-0.9.0 "alloc,default"
 //   libc-0.2.80 "default,std"
+//   log-0.4.11
 //   memchr-2.3.4 "default,std"
+//   memoffset-0.5.6 "default"
 //   once_cell-1.5.2 "alloc,std"
 //   paste-1.0.3
 //   pin-project-1.0.2
@@ -129,6 +138,7 @@
 //   proc-macro-hack-0.5.19
 //   proc-macro-nested-0.1.6
 //   proc-macro2-1.0.24 "default,proc-macro"
+//   protobuf-2.18.1
 //   quote-1.0.7 "default,proc-macro"
 //   rand-0.7.3 "alloc,default,getrandom,getrandom_package,libc,std"
 //   rand_chacha-0.2.2 "std"
@@ -136,7 +146,7 @@
 //   remain-0.2.2
 //   remove_dir_all-0.5.3
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   tempfile-3.1.0
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
diff --git a/arch/Cargo.toml b/arch/Cargo.toml
index dfd3eb7..38b27f5 100644
--- a/arch/Cargo.toml
+++ b/arch/Cargo.toml
@@ -4,13 +4,18 @@
 authors = ["The Chromium OS Authors"]
 edition = "2018"
 
+[features]
+gdb = ["gdbstub"]
+
 [dependencies]
 acpi_tables = { path = "../acpi_tables" }
 devices = { path = "../devices" }
+gdbstub = { version = "0.4.0", optional = true }
 hypervisor = { path = "../hypervisor" }
 kernel_cmdline = { path = "../kernel_cmdline" }
 libc = "*"
 minijail = { path = "../../minijail/rust/minijail" } # ignored by ebuild
+msg_socket = { path = "../msg_socket" }
 resources = { path = "../resources" }
 sync = { path = "../sync" }
 base = { path = "../base" }
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index 354a8e9..61e71f6 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -12,10 +12,10 @@
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::io::{self, Read, Seek, SeekFrom};
-use std::os::unix::io::AsRawFd;
 use std::path::PathBuf;
 use std::sync::Arc;
 
+use acpi_tables::aml::Aml;
 use acpi_tables::sdt::SDT;
 use base::{syslog, AsRawDescriptor, Event};
 use devices::virtio::VirtioDevice;
@@ -25,10 +25,18 @@
 };
 use hypervisor::{IoEventAddress, Vm};
 use minijail::Minijail;
-use resources::SystemAllocator;
+use resources::{MmioType, SystemAllocator};
 use sync::Mutex;
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+use vm_control::VmControlRequestSocket;
+use vm_control::{
+    BatControl, BatControlCommand, BatControlRequestSocket, BatControlResult, BatteryType,
+};
 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
 
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+use gdbstub::arch::x86::reg::X86_64CoreRegs as GdbStubRegs;
+
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 use {
     devices::IrqChipAArch64 as IrqChipArch,
@@ -84,6 +92,8 @@
     pub acpi_sdts: Vec<SDT>,
     pub rt_cpus: Vec<usize>,
     pub protected_vm: bool,
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    pub gdb: Option<(u32, VmControlRequestSocket)>, // port and control socket.
 }
 
 /// Holds the elements needed to run a Linux VM. Created by `build_vm`.
@@ -104,6 +114,9 @@
     pub pid_debug_label_map: BTreeMap<u32, String>,
     pub suspend_evt: Event,
     pub rt_cpus: Vec<usize>,
+    pub bat_control: Option<BatControl>,
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    pub gdb: Option<(u32, VmControlRequestSocket)>,
 }
 
 /// The device and optional jail.
@@ -123,6 +136,7 @@
     ///
     /// * `components` - Parts to use to build the VM.
     /// * `serial_parameters` - definitions for how the serial devices should be configured.
+    /// * `battery` - defines what battery device will be created.
     /// * `create_devices` - Function to generate a list of devices.
     /// * `create_vm` - Function to generate a VM.
     /// * `create_irq_chip` - Function to generate an IRQ chip.
@@ -130,6 +144,7 @@
         components: VmComponents,
         serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
+        battery: (&Option<BatteryType>, Option<Minijail>),
         create_devices: FD,
         create_vm: FV,
         create_irq_chip: FI,
@@ -171,6 +186,43 @@
         has_bios: bool,
         no_smt: bool,
     ) -> Result<(), Self::Error>;
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    /// Reads vCPU's registers.
+    fn debug_read_registers<T: VcpuArch>(vcpu: &T) -> Result<GdbStubRegs, Self::Error>;
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    /// Writes vCPU's registers.
+    fn debug_write_registers<T: VcpuArch>(vcpu: &T, regs: &GdbStubRegs) -> Result<(), Self::Error>;
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    /// Reads bytes from the guest memory.
+    fn debug_read_memory<T: VcpuArch>(
+        vcpu: &T,
+        guest_mem: &GuestMemory,
+        vaddr: GuestAddress,
+        len: usize,
+    ) -> Result<Vec<u8>, Self::Error>;
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    /// Writes bytes to the specified guest memory.
+    fn debug_write_memory<T: VcpuArch>(
+        vcpu: &T,
+        guest_mem: &GuestMemory,
+        vaddr: GuestAddress,
+        buf: &[u8],
+    ) -> Result<(), Self::Error>;
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    /// Make the next vCPU's run single-step.
+    fn debug_enable_singlestep<T: VcpuArch>(vcpu: &T) -> Result<(), Self::Error>;
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    /// Set hardware breakpoints at the given addresses.
+    fn debug_set_hw_breakpoints<T: VcpuArch>(
+        vcpu: &T,
+        breakpoints: &[GuestAddress],
+    ) -> Result<(), Self::Error>;
 }
 
 /// Errors for device manager.
@@ -178,6 +230,8 @@
 pub enum DeviceRegistrationError {
     /// Could not allocate IO space for the device.
     AllocateIoAddrs(PciDeviceError),
+    /// Could not allocate MMIO or IO resource for the device.
+    AllocateIoResource(resources::Error),
     /// Could not allocate device address space for the device.
     AllocateDeviceAddrs(PciDeviceError),
     /// Could not allocate an IRQ number.
@@ -186,6 +240,8 @@
     CreatePipe(base::Error),
     // Unable to create serial device from serial parameters
     CreateSerialDevice(serial::Error),
+    // Unable to create socket
+    CreateSocket(io::Error),
     /// Could not clone an event.
     EventClone(base::Error),
     /// Could not create an event.
@@ -208,6 +264,8 @@
     AddrsExhausted,
     /// Could not register PCI device capabilities.
     RegisterDeviceCapabilities(PciDeviceError),
+    // Failed to register battery device.
+    RegisterBattery(devices::BatteryError),
 }
 
 impl Display for DeviceRegistrationError {
@@ -216,10 +274,12 @@
 
         match self {
             AllocateIoAddrs(e) => write!(f, "Allocating IO addresses: {}", e),
+            AllocateIoResource(e) => write!(f, "Allocating IO resource: {}", e),
             AllocateDeviceAddrs(e) => write!(f, "Allocating device addresses: {}", e),
             AllocateIrq => write!(f, "Allocating IRQ number"),
             CreatePipe(e) => write!(f, "failed to create pipe: {}", e),
             CreateSerialDevice(e) => write!(f, "failed to create serial device: {}", e),
+            CreateSocket(e) => write!(f, "failed to create socket: {}", e),
             Cmdline(e) => write!(f, "unable to add device to kernel command line: {}", e),
             EventClone(e) => write!(f, "failed to clone event: {}", e),
             EventCreate(e) => write!(f, "failed to create event: {}", e),
@@ -233,6 +293,7 @@
             RegisterDeviceCapabilities(e) => {
                 write!(f, "could not register PCI device capabilities: {}", e)
             }
+            RegisterBattery(e) => write!(f, "failed to register battery device to VM: {}", e),
         }
     }
 }
@@ -294,8 +355,8 @@
 
     for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() {
         let address = device_addrs[dev_idx];
-        let mut keep_fds = device.keep_fds();
-        syslog::push_fds(&mut keep_fds);
+        let mut keep_rds = device.keep_rds();
+        syslog::push_descriptors(&mut keep_rds);
 
         let irqfd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
         let irq_resample_fd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
@@ -320,8 +381,8 @@
             .register_irq_event(irq_num, &irqfd, Some(&irq_resample_fd))
             .map_err(DeviceRegistrationError::RegisterIrqfd)?;
 
-        keep_fds.push(irqfd.as_raw_fd());
-        keep_fds.push(irq_resample_fd.as_raw_fd());
+        keep_rds.push(irqfd.as_raw_descriptor());
+        keep_rds.push(irq_resample_fd.as_raw_descriptor());
         device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin);
         pci_irqs.push((address, irq_num, pci_irq_pin));
         let ranges = io_ranges.remove(&dev_idx).unwrap_or_default();
@@ -333,10 +394,10 @@
             let io_addr = IoEventAddress::Mmio(addr);
             vm.register_ioevent(&event, io_addr, datamatch)
                 .map_err(DeviceRegistrationError::RegisterIoevent)?;
-            keep_fds.push(event.as_raw_fd());
+            keep_rds.push(event.as_raw_descriptor());
         }
         let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
-            let proxy = ProxyDevice::new(device, &jail, keep_fds)
+            let proxy = ProxyDevice::new(device, &jail, keep_rds)
                 .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
             pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
             Arc::new(Mutex::new(proxy))
@@ -347,19 +408,99 @@
         root.add_device(address, arced_dev.clone());
         for range in &ranges {
             mmio_bus
-                .insert(arced_dev.clone(), range.0, range.1, true)
+                .insert(arced_dev.clone(), range.0, range.1)
                 .map_err(DeviceRegistrationError::MmioInsert)?;
         }
 
         for range in &device_ranges {
             mmio_bus
-                .insert(arced_dev.clone(), range.0, range.1, true)
+                .insert(arced_dev.clone(), range.0, range.1)
                 .map_err(DeviceRegistrationError::MmioInsert)?;
         }
     }
     Ok((root, pci_irqs, pid_labels))
 }
 
+/// Adds goldfish battery
+/// return the platform needed resouces include its AML data, irq number
+///
+/// # Arguments
+///
+/// * `amls` - the vector to put the goldfish battery AML
+/// * `battery_jail` - used when sandbox is enabled
+/// * `mmio_bus` - bus to add the devices to
+/// * `irq_chip` - the IrqChip object for registering irq events
+/// * `irq_num` - assigned interrupt to use
+/// * `resources` - the SystemAllocator to allocate IO and MMIO for acpi
+pub fn add_goldfish_battery(
+    amls: &mut Vec<u8>,
+    battery_jail: Option<Minijail>,
+    mmio_bus: &mut Bus,
+    irq_chip: &mut impl IrqChip,
+    irq_num: u32,
+    resources: &mut SystemAllocator,
+) -> Result<BatControlRequestSocket, DeviceRegistrationError> {
+    let alloc = resources.get_anon_alloc();
+    let mmio_base = resources
+        .mmio_allocator(MmioType::Low)
+        .allocate_with_align(
+            devices::bat::GOLDFISHBAT_MMIO_LEN,
+            alloc,
+            "GoldfishBattery".to_string(),
+            devices::bat::GOLDFISHBAT_MMIO_LEN,
+        )
+        .map_err(DeviceRegistrationError::AllocateIoResource)?;
+
+    let irq_evt = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
+    let irq_resample_evt = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
+
+    irq_chip
+        .register_irq_event(irq_num, &irq_evt, Some(&irq_resample_evt))
+        .map_err(DeviceRegistrationError::RegisterIrqfd)?;
+
+    let (control_socket, response_socket) =
+        msg_socket::pair::<BatControlCommand, BatControlResult>()
+            .map_err(DeviceRegistrationError::CreateSocket)?;
+
+    let goldfish_bat = devices::GoldfishBattery::new(
+        mmio_base,
+        irq_num,
+        irq_evt,
+        irq_resample_evt,
+        response_socket,
+    )
+    .map_err(DeviceRegistrationError::RegisterBattery)?;
+    Aml::to_aml_bytes(&goldfish_bat, amls);
+
+    match battery_jail.as_ref() {
+        Some(jail) => {
+            let mut keep_fds = goldfish_bat.keep_fds();
+            syslog::push_fds(&mut keep_fds);
+            mmio_bus
+                .insert(
+                    Arc::new(Mutex::new(
+                        ProxyDevice::new(goldfish_bat, &jail, keep_fds)
+                            .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
+                    )),
+                    mmio_base,
+                    devices::bat::GOLDFISHBAT_MMIO_LEN,
+                )
+                .map_err(DeviceRegistrationError::MmioInsert)?;
+        }
+        None => {
+            mmio_bus
+                .insert(
+                    Arc::new(Mutex::new(goldfish_bat)),
+                    mmio_base,
+                    devices::bat::GOLDFISHBAT_MMIO_LEN,
+                )
+                .map_err(DeviceRegistrationError::MmioInsert)?;
+        }
+    }
+
+    Ok(control_socket)
+}
+
 /// Errors for image loading.
 #[derive(Debug)]
 pub enum LoadImageError {
diff --git a/arch/src/serial.rs b/arch/src/serial.rs
index 3ebb84b..cf7d7b0 100644
--- a/arch/src/serial.rs
+++ b/arch/src/serial.rs
@@ -2,17 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use std::borrow::Cow;
 use std::collections::BTreeMap;
 use std::fmt::{self, Display};
 use std::fs::{File, OpenOptions};
 use std::io::{self, stdin, stdout, ErrorKind};
-use std::os::unix::io::{AsRawFd, RawFd};
+use std::os::unix::io::AsRawFd;
 use std::os::unix::net::UnixDatagram;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::str::FromStr;
 use std::sync::Arc;
+use std::thread;
+use std::time::Duration;
 
-use base::{error, info, read_raw_stdin, syslog, Event};
+use base::{error, info, read_raw_stdin, syslog, AsRawDescriptor, Event, RawDescriptor};
 use devices::{Bus, ProxyDevice, Serial, SerialDevice};
 use minijail::Minijail;
 use sync::Mutex;
@@ -25,6 +28,7 @@
     FileError(std::io::Error),
     InvalidSerialHardware(String),
     InvalidSerialType(String),
+    InvalidPath,
     PathRequired,
     SocketCreateFailed,
     Unimplemented(SerialType),
@@ -39,6 +43,7 @@
             FileError(e) => write!(f, "unable to open/create file: {}", e),
             InvalidSerialHardware(e) => write!(f, "invalid serial hardware: {}", e),
             InvalidSerialType(e) => write!(f, "invalid serial type: {}", e),
+            InvalidPath => write!(f, "serial device path is invalid"),
             PathRequired => write!(f, "serial device type file requires a path"),
             SocketCreateFailed => write!(f, "failed to create unbound socket"),
             Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()),
@@ -115,27 +120,20 @@
 
 struct WriteSocket {
     sock: UnixDatagram,
-    path: PathBuf,
     buf: String,
 }
 
 const BUF_CAPACITY: usize = 1024;
 
 impl WriteSocket {
-    pub fn new(s: UnixDatagram, p: PathBuf) -> WriteSocket {
+    pub fn new(s: UnixDatagram) -> WriteSocket {
         WriteSocket {
             sock: s,
-            path: p,
             buf: String::with_capacity(BUF_CAPACITY),
         }
     }
 
-    // |send_buf| uses an immutable |self|, even though its |sock| is possibly
-    // going be modified. This is needed to allow the mutating of |buf| between
-    // calls to |send_buf| in the io::Write impl.
     pub fn send_buf(&self, buf: &[u8]) -> io::Result<usize> {
-        // It's possible we aren't yet connected to the socket. Always try once
-        // to reconnect if sending fails.
         const SEND_RETRY: usize = 2;
         let mut sent = 0;
         for _ in 0..SEND_RETRY {
@@ -144,22 +142,7 @@
                     sent = bytes_sent;
                     break;
                 }
-                Err(e) => match e.kind() {
-                    ErrorKind::ConnectionRefused
-                    | ErrorKind::ConnectionReset
-                    | ErrorKind::ConnectionAborted
-                    | ErrorKind::NotConnected
-                    | ErrorKind::Other => match self.sock.connect(self.path.as_path()) {
-                        Ok(_) => {}
-                        Err(e) => {
-                            info!("Couldn't connect {:?}", e);
-                            break;
-                        }
-                    },
-                    _ => {
-                        info!("Send error: {:?}", e);
-                    }
-                },
+                Err(e) => info!("Send error: {:?}", e),
             }
         }
         Ok(sent)
@@ -215,27 +198,32 @@
     pub stdin: bool,
 }
 
+// The maximum length of a path that can be used as the address of a
+// unix socket. Note that this includes the null-terminator.
+const MAX_SOCKET_PATH_LENGTH: usize = 108;
+
 impl SerialParameters {
     /// Helper function to create a serial device from the defined parameters.
     ///
     /// # Arguments
     /// * `evt` - event used for interrupt events
-    /// * `keep_fds` - Vector of FDs required by this device if it were sandboxed in a child
-    ///                process. `evt` will always be added to this vector by this function.
+    /// * `keep_rds` - Vector of descriptors required by this device if it were sandboxed
+    ///                in a child process. `evt` will always be added to this vector by
+    ///                this function.
     pub fn create_serial_device<T: SerialDevice>(
         &self,
         protected_vm: bool,
         evt: &Event,
-        keep_fds: &mut Vec<RawFd>,
+        keep_rds: &mut Vec<RawDescriptor>,
     ) -> std::result::Result<T, Error> {
         let evt = evt.try_clone().map_err(Error::CloneEvent)?;
-        keep_fds.push(evt.as_raw_fd());
+        keep_rds.push(evt.as_raw_descriptor());
         let input: Option<Box<dyn io::Read + Send>> = if let Some(input_path) = &self.input {
             let input_file = File::open(input_path.as_path()).map_err(Error::FileError)?;
-            keep_fds.push(input_file.as_raw_fd());
+            keep_rds.push(input_file.as_raw_descriptor());
             Some(Box::new(input_file))
         } else if self.stdin {
-            keep_fds.push(stdin().as_raw_fd());
+            keep_rds.push(stdin().as_raw_descriptor());
             // This wrapper is used in place of the libstd native version because we don't want
             // buffering for stdin.
             struct StdinWrapper;
@@ -250,12 +238,12 @@
         };
         let output: Option<Box<dyn io::Write + Send>> = match self.type_ {
             SerialType::Stdout => {
-                keep_fds.push(stdout().as_raw_fd());
+                keep_rds.push(stdout().as_raw_descriptor());
                 Some(Box::new(stdout()))
             }
             SerialType::Sink => None,
             SerialType::Syslog => {
-                syslog::push_fds(keep_fds);
+                syslog::push_descriptors(keep_rds);
                 Some(Box::new(syslog::Syslogger::new(
                     syslog::Priority::Info,
                     syslog::Facility::Daemon,
@@ -268,36 +256,80 @@
                         .create(true)
                         .open(path.as_path())
                         .map_err(Error::FileError)?;
-                    keep_fds.push(file.as_raw_fd());
+                    keep_rds.push(file.as_raw_descriptor());
                     Some(Box::new(file))
                 }
                 None => return Err(Error::PathRequired),
             },
-            SerialType::UnixSocket => match &self.path {
-                Some(path) => {
-                    let sock = match UnixDatagram::bind(path) {
-                        Ok(sock) => sock,
-                        Err(e) => {
-                            if e.kind() == ErrorKind::AddrInUse {
-                                // In most cases vmlog_forwarder will
-                                // have already bound this address and
-                                // this error is expected. This
-                                // unbound socket will be connected on
-                                // first write.
-                                UnixDatagram::unbound().map_err(Error::FileError)?
-                            } else {
-                                error!("Couldn't bind: {:?}", e);
-                                return Err(Error::FileError(e));
-                            }
+            SerialType::UnixSocket => {
+                match &self.path {
+                    Some(path) => {
+                        // If the path is longer than 107 characters,
+                        // then we won't be able to connect directly
+                        // to it. Instead we can shorten the path by
+                        // opening the containing directory and using
+                        // /proc/self/fd/*/ to access it via a shorter
+                        // path.
+                        let mut path_cow = Cow::<Path>::Borrowed(path);
+                        let mut _dir_fd = None;
+                        if path.as_os_str().len() >= MAX_SOCKET_PATH_LENGTH {
+                            let mut short_path = PathBuf::with_capacity(MAX_SOCKET_PATH_LENGTH);
+                            short_path.push("/proc/self/fd/");
+
+                            // We don't actually want to open this
+                            // directory for reading, but the stdlib
+                            // requires all files be opened as at
+                            // least one of readable, writeable, or
+                            // appeandable.
+                            let dir = OpenOptions::new()
+                                .read(true)
+                                .open(path.parent().ok_or(Error::InvalidPath)?)
+                                .map_err(Error::FileError)?;
+
+                            short_path.push(dir.as_raw_fd().to_string());
+                            short_path.push(path.file_name().ok_or(Error::InvalidPath)?);
+                            path_cow = Cow::Owned(short_path);
+                            _dir_fd = Some(dir);
                         }
-                    };
-                    keep_fds.push(sock.as_raw_fd());
-                    Some(Box::new(WriteSocket::new(sock, path.to_path_buf())))
+
+                        // The shortened path may still be too long,
+                        // in which case we must give up here.
+                        if path_cow.as_os_str().len() >= MAX_SOCKET_PATH_LENGTH {
+                            return Err(Error::InvalidPath);
+                        }
+
+                        // There's a race condition between
+                        // vmlog_forwarder making the logging socket and
+                        // crosvm starting up, so we loop here until it's
+                        // available.
+                        let sock = UnixDatagram::unbound().map_err(Error::FileError)?;
+                        loop {
+                            match sock.connect(&path_cow) {
+                                Ok(_) => break,
+                                Err(e) => {
+                                    match e.kind() {
+                                        ErrorKind::NotFound | ErrorKind::ConnectionRefused => {
+                                            // logging socket doesn't
+                                            // exist yet, sleep for 10 ms
+                                            // and try again.
+                                            thread::sleep(Duration::from_millis(10))
+                                        }
+                                        _ => {
+                                            error!("Unexpected error connecting to logging socket: {:?}", e);
+                                            return Err(Error::FileError(e));
+                                        }
+                                    }
+                                }
+                            };
+                        }
+                        keep_rds.push(sock.as_raw_descriptor());
+                        Some(Box::new(WriteSocket::new(sock)))
+                    }
+                    None => return Err(Error::PathRequired),
                 }
-                None => return Err(Error::PathRequired),
-            },
+            }
         };
-        Ok(T::new(protected_vm, evt, input, output, keep_fds.to_vec()))
+        Ok(T::new(protected_vm, evt, input, output, keep_rds.to_vec()))
     }
 
     pub fn add_bind_mounts(&self, jail: &mut Minijail) -> Result<(), minijail::Error> {
@@ -413,13 +445,13 @@
                         .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
                 ));
                 io_bus
-                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
+                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8)
                     .unwrap();
             }
             None => {
                 let com = Arc::new(Mutex::new(com));
                 io_bus
-                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
+                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8)
                     .unwrap();
             }
         }
diff --git a/base/Android.bp b/base/Android.bp
index 4c6365c..abfd267 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -9,7 +9,9 @@
     auto_gen_config: true,
     edition: "2018",
     rustlibs: [
+        "libcros_async",
         "libdata_model",
+        "liblibc",
         "libsync_rust",
         "libsys_util",
     ],
@@ -34,7 +36,9 @@
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
+        "libcros_async",
         "libdata_model",
+        "liblibc",
         "libsync_rust",
         "libsys_util",
     ],
@@ -42,14 +46,31 @@
 
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/base/Cargo.toml b/base/Cargo.toml
index 1b9afc1..2520d42 100644
--- a/base/Cargo.toml
+++ b/base/Cargo.toml
@@ -8,6 +8,8 @@
 chromeos = ["sys_util/chromeos"]
 
 [dependencies]
+cros_async = { path = "../cros_async" }
 data_model = { path = "../data_model" }
+libc = "*"
 sync = { path = "../sync" }
 sys_util = { path = "../sys_util" }
diff --git a/base/src/async_types.rs b/base/src/async_types.rs
new file mode 100644
index 0000000..4fbe53e
--- /dev/null
+++ b/base/src/async_types.rs
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::AsRawDescriptor;
+use cros_async::IntoAsync;
+use std::os::unix::io::{AsRawFd, RawFd};
+
+/// Like `cros_async::IntoAsync`, except for use with crosvm's AsRawDescriptor
+/// trait object family.
+pub trait DescriptorIntoAsync: AsRawDescriptor {}
+
+/// To use an IO struct with cros_async, the type must be marked with
+/// DescriptorIntoAsync (to signify it is suitable for use with async
+/// operations), and then wrapped with this type.
+pub struct DescriptorAdapter<T: DescriptorIntoAsync>(pub T);
+impl<T> AsRawFd for DescriptorAdapter<T>
+where
+    T: DescriptorIntoAsync,
+{
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_descriptor()
+    }
+}
+impl<T> IntoAsync for DescriptorAdapter<T> where T: DescriptorIntoAsync {}
diff --git a/base/src/event.rs b/base/src/event.rs
index 2e8a2c2..abd9433 100644
--- a/base/src/event.rs
+++ b/base/src/event.rs
@@ -4,7 +4,7 @@
 
 use std::mem;
 use std::ops::Deref;
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
 use std::ptr;
 use std::time::Duration;
 
@@ -15,7 +15,7 @@
 /// See [EventFd](sys_util::EventFd) for struct- and method-level
 /// documentation.
 #[derive(Debug, PartialEq, Eq)]
-pub struct Event(pub(self) EventFd);
+pub struct Event(pub EventFd);
 impl Event {
     pub fn new() -> Result<Event> {
         EventFd::new().map(|eventfd| Event(eventfd))
@@ -56,26 +56,6 @@
     }
 }
 
-// TODO(mikehoyle): Remove Fd-related trait impls once the rest
-// of base is complete to accomodate it.
-impl AsRawFd for Event {
-    fn as_raw_fd(&self) -> RawFd {
-        self.0.as_raw_fd()
-    }
-}
-
-impl FromRawFd for Event {
-    unsafe fn from_raw_fd(descriptor: RawFd) -> Self {
-        Event(EventFd::from_raw_fd(descriptor))
-    }
-}
-
-impl IntoRawFd for Event {
-    fn into_raw_fd(self) -> RawFd {
-        self.0.into_raw_fd()
-    }
-}
-
 /// See [ScopedEvent](sys_util::ScopedEvent) for struct- and method-level
 /// documentation.
 pub struct ScopedEvent(Event);
diff --git a/base/src/ioctl.rs b/base/src/ioctl.rs
new file mode 100644
index 0000000..a50f689
--- /dev/null
+++ b/base/src/ioctl.rs
@@ -0,0 +1,60 @@
+// 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 crate::{AsRawDescriptor, IoctlNr};
+use std::os::raw::{c_int, c_ulong, c_void};
+
+/// Run an ioctl with no arguments.
+pub unsafe fn ioctl<F: AsRawDescriptor>(descriptor: &F, nr: IoctlNr) -> c_int {
+    libc::ioctl(descriptor.as_raw_descriptor(), nr, 0)
+}
+
+/// Run an ioctl with a single value argument.
+pub unsafe fn ioctl_with_val<F: AsRawDescriptor>(
+    descriptor: &F,
+    nr: IoctlNr,
+    arg: c_ulong,
+) -> c_int {
+    libc::ioctl(descriptor.as_raw_descriptor(), nr, arg)
+}
+
+/// Run an ioctl with an immutable reference.
+pub unsafe fn ioctl_with_ref<F: AsRawDescriptor, T>(descriptor: &F, nr: IoctlNr, arg: &T) -> c_int {
+    libc::ioctl(
+        descriptor.as_raw_descriptor(),
+        nr,
+        arg as *const T as *const c_void,
+    )
+}
+
+/// Run an ioctl with a mutable reference.
+pub unsafe fn ioctl_with_mut_ref<F: AsRawDescriptor, T>(
+    descriptor: &F,
+    nr: IoctlNr,
+    arg: &mut T,
+) -> c_int {
+    libc::ioctl(
+        descriptor.as_raw_descriptor(),
+        nr,
+        arg as *mut T as *mut c_void,
+    )
+}
+
+/// Run an ioctl with a raw pointer.
+pub unsafe fn ioctl_with_ptr<F: AsRawDescriptor, T>(
+    descriptor: &F,
+    nr: IoctlNr,
+    arg: *const T,
+) -> c_int {
+    libc::ioctl(descriptor.as_raw_descriptor(), nr, arg as *const c_void)
+}
+
+/// Run an ioctl with a mutable raw pointer.
+pub unsafe fn ioctl_with_mut_ptr<F: AsRawDescriptor, T>(
+    descriptor: &F,
+    nr: IoctlNr,
+    arg: *mut T,
+) -> c_int {
+    libc::ioctl(descriptor.as_raw_descriptor(), nr, arg as *mut c_void)
+}
diff --git a/base/src/lib.rs b/base/src/lib.rs
index bf40664..7f88632 100644
--- a/base/src/lib.rs
+++ b/base/src/lib.rs
@@ -4,12 +4,19 @@
 
 pub use sys_util::*;
 
+mod async_types;
 mod event;
+mod ioctl;
 mod mmap;
 mod shm;
 mod timer;
+mod wait_context;
 
+pub use async_types::*;
 pub use event::{Event, EventReadResult, ScopedEvent};
+pub use ioctl::{
+    ioctl, ioctl_with_mut_ptr, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val,
+};
 pub use mmap::{MemoryMapping, MemoryMappingBuilder};
 pub use shm::{SharedMemory, Unix as SharedMemoryUnix};
 pub use sys_util::ioctl::*;
@@ -20,13 +27,20 @@
 };
 pub use sys_util::{SeekHole, WriteZeroesAt};
 pub use timer::{FakeTimer, Timer};
+pub use wait_context::{EventToken, EventType, TriggeredEvent, WaitContext};
 
 /// Wraps an AsRawDescriptor in the simple Descriptor struct, which
 /// has AsRawFd methods for interfacing with sys_util
-fn wrap_descriptor(descriptor: &dyn AsRawDescriptor) -> Descriptor {
+pub fn wrap_descriptor(descriptor: &dyn AsRawDescriptor) -> Descriptor {
     Descriptor(descriptor.as_raw_descriptor())
 }
 
+/// Verifies that |raw_descriptor| is actually owned by this process and duplicates it
+/// to ensure that we have a unique handle to it.
+pub fn validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescriptor> {
+    validate_raw_fd(raw_descriptor)
+}
+
 /// A trait similar to `AsRawDescriptor` but supports an arbitrary number of descriptors.
 pub trait AsRawDescriptors {
     fn as_raw_descriptors(&self) -> Vec<RawDescriptor>;
diff --git a/base/src/shm.rs b/base/src/shm.rs
index 59594f6..f22e96f 100644
--- a/base/src/shm.rs
+++ b/base/src/shm.rs
@@ -8,7 +8,7 @@
 };
 use std::ffi::CStr;
 use std::fs::File;
-use std::os::unix::io::{AsRawFd, RawFd};
+use std::os::unix::io::AsRawFd;
 use sys_util::SharedMemory as SysUtilSharedMemory;
 
 /// See [SharedMemory](sys_util::SharedMemory) for struct- and method-level
@@ -71,14 +71,6 @@
     }
 }
 
-// TODO(mikehoyle): Remove this in favor of just AsRawDescriptor
-// when the rest of the codebase is ready.
-impl AsRawFd for SharedMemory {
-    fn as_raw_fd(&self) -> RawFd {
-        self.0.as_raw_fd()
-    }
-}
-
 impl AsRawDescriptor for SharedMemory {
     fn as_raw_descriptor(&self) -> RawDescriptor {
         self.0.as_raw_fd()
diff --git a/base/src/timer.rs b/base/src/timer.rs
index 7a95846..e3bfc0f 100644
--- a/base/src/timer.rs
+++ b/base/src/timer.rs
@@ -6,7 +6,7 @@
     AsRawDescriptor, FakeClock, FromRawDescriptor, IntoRawDescriptor, RawDescriptor, Result,
 };
 
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
 use std::sync::Arc;
 use std::time::Duration;
 use sync::Mutex;
@@ -54,14 +54,6 @@
             }
         }
 
-        // TODO(mikehoyle): Remove this in favor of AsRawDescriptor
-        // when PollContext is updated
-        impl AsRawFd for $timer {
-            fn as_raw_fd(&self) -> RawFd {
-                self.0.as_raw_fd()
-            }
-        }
-
         impl AsRawDescriptor for $timer {
             fn as_raw_descriptor(&self) -> RawDescriptor {
                 self.0.as_raw_fd()
diff --git a/base/src/wait_context.rs b/base/src/wait_context.rs
new file mode 100644
index 0000000..86a0488
--- /dev/null
+++ b/base/src/wait_context.rs
@@ -0,0 +1,160 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::unix::io::AsRawFd;
+use std::time::Duration;
+
+use crate::{wrap_descriptor, AsRawDescriptor, RawDescriptor, Result};
+use sys_util::{PollContext, PollToken, WatchingEvents};
+
+// Typedef PollToken as EventToken for better adherance to base naming.
+// As actual typdefing is experimental, define a new trait with the mirrored
+// attributes.
+pub trait EventToken: PollToken {}
+impl<T: PollToken> EventToken for T {}
+
+/// Represents an event that has been signaled and waited for via a wait function.
+#[derive(Copy, Clone, Debug)]
+pub struct TriggeredEvent<T: EventToken> {
+    pub token: T,
+    pub is_readable: bool,
+    pub is_writable: bool,
+    pub is_hungup: bool,
+}
+
+/// Represents types of events to watch for.
+pub enum EventType {
+    // Used to to temporarily stop waiting for events without
+    // removing the associated descriptor from the WaitContext.
+    // In most cases if a descriptor no longer needs to be
+    // waited on, prefer removing it entirely with
+    // WaitContext#delete
+    None,
+    Read,
+    Write,
+    ReadWrite,
+}
+
+/// Used to wait for multiple objects which are eligible for waiting.
+///
+/// # Example
+///
+/// ```
+/// # use base::{
+///     Result, Event, WaitContext,
+/// };
+/// # fn test() -> Result<()> {
+///     let evt1 = Event::new()?;
+///     let evt2 = Event::new()?;
+///     evt2.write(1)?;
+///
+///     let ctx: WaitContext<u32> = WaitContext::new()?;
+///     ctx.add(&evt1, 1)?;
+///     ctx.add(&evt2, 2)?;
+///
+///     let events = ctx.wait()?;
+///     let tokens: Vec<u32> = events.iter().filter(|e| e.is_readable)
+///         .map(|e| e.token).collect();
+///     assert_eq!(tokens, [2]);
+/// #   Ok(())
+/// # }
+/// ```
+pub struct WaitContext<T: EventToken>(PollContext<T>);
+
+impl<T: EventToken> WaitContext<T> {
+    /// Creates a new WaitContext.
+    pub fn new() -> Result<WaitContext<T>> {
+        PollContext::new().map(|poll_ctx| WaitContext(poll_ctx))
+    }
+
+    /// Creates a new WaitContext with the the associated triggers.
+    pub fn build_with(triggers: &[(&dyn AsRawDescriptor, T)]) -> Result<WaitContext<T>> {
+        let ctx = WaitContext::new()?;
+        ctx.add_many(triggers)?;
+        Ok(ctx)
+    }
+
+    /// Adds a trigger to the WaitContext.
+    pub fn add(&self, descriptor: &dyn AsRawDescriptor, token: T) -> Result<()> {
+        self.add_for_event(descriptor, EventType::Read, token)
+    }
+
+    /// Adds a trigger to the WaitContext watching for a specific type of event
+    pub fn add_for_event(
+        &self,
+        descriptor: &dyn AsRawDescriptor,
+        event_type: EventType,
+        token: T,
+    ) -> Result<()> {
+        self.0.add_fd_with_events(
+            &wrap_descriptor(descriptor),
+            convert_to_watching_events(event_type),
+            token,
+        )
+    }
+
+    /// Adds multiple triggers to the WaitContext.
+    pub fn add_many(&self, triggers: &[(&dyn AsRawDescriptor, T)]) -> Result<()> {
+        for trigger in triggers {
+            self.add(trigger.0, T::from_raw_token(trigger.1.as_raw_token()))?
+        }
+        Ok(())
+    }
+
+    /// Modifies a trigger already added to the WaitContext. If the descriptor is
+    /// already registered, its associated token will be updated.
+    pub fn modify(
+        &self,
+        descriptor: &dyn AsRawDescriptor,
+        event_type: EventType,
+        token: T,
+    ) -> Result<()> {
+        self.0.modify(
+            &wrap_descriptor(descriptor),
+            convert_to_watching_events(event_type),
+            token,
+        )
+    }
+
+    /// Removes the given handle from triggers registered in the WaitContext if
+    /// present.
+    pub fn delete(&self, descriptor: &dyn AsRawDescriptor) -> Result<()> {
+        self.0.delete(&wrap_descriptor(descriptor))
+    }
+
+    /// Waits for one or more of the registered triggers to become signaled.
+    pub fn wait(&self) -> Result<Vec<TriggeredEvent<T>>> {
+        self.wait_timeout(Duration::new(i64::MAX as u64, 0))
+    }
+    /// Waits for one or more of the registered triggers to become signaled, failing if no triggers
+    /// are signaled before the designated timeout has elapsed.
+    pub fn wait_timeout(&self, timeout: Duration) -> Result<Vec<TriggeredEvent<T>>> {
+        let events = self.0.wait_timeout(timeout)?;
+        let mut return_vec: Vec<TriggeredEvent<T>> = vec![];
+        for event in events.iter() {
+            return_vec.push(TriggeredEvent {
+                token: event.token(),
+                is_readable: event.readable(),
+                is_writable: event.writable(),
+                is_hungup: event.hungup(),
+            });
+        }
+        Ok(return_vec)
+    }
+}
+
+impl<T: PollToken> AsRawDescriptor for WaitContext<T> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.0.as_raw_fd()
+    }
+}
+
+fn convert_to_watching_events(event_type: EventType) -> WatchingEvents {
+    match event_type {
+        EventType::None => WatchingEvents::empty(),
+        EventType::Read => WatchingEvents::empty().set_read(),
+        EventType::Write => WatchingEvents::empty().set_write(),
+        EventType::ReadWrite => WatchingEvents::empty().set_read().set_write(),
+    }
+}
diff --git a/bit_field/Android.bp b/bit_field/Android.bp
index 0b48897..7bb0d73 100644
--- a/bit_field/Android.bp
+++ b/bit_field/Android.bp
@@ -71,5 +71,5 @@
 // dependent_library ["feature_list"]
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   syn-1.0.53 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
 //   unicode-xid-0.2.1 "default"
diff --git a/bit_field/bit_field_derive/Android.bp b/bit_field/bit_field_derive/Android.bp
index 92c63e8..056064c 100644
--- a/bit_field/bit_field_derive/Android.bp
+++ b/bit_field/bit_field_derive/Android.bp
@@ -31,5 +31,5 @@
 // dependent_library ["feature_list"]
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   syn-1.0.53 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
 //   unicode-xid-0.2.1 "default"
diff --git a/cros_async/Android.bp b/cros_async/Android.bp
index c4717d0..db62c8f 100644
--- a/cros_async/Android.bp
+++ b/cros_async/Android.bp
@@ -9,12 +9,12 @@
     auto_gen_config: true,
     edition: "2018",
     rustlibs: [
-        "libbase_rust",
         "libfutures",
         "libio_uring",
         "liblibc",
         "libpin_utils",
         "libslab",
+        "libsys_util",
         "libsyscall_defines",
         "libtempfile",
         "libthiserror",
@@ -44,12 +44,12 @@
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
-        "libbase_rust",
         "libfutures",
         "libio_uring",
         "liblibc",
         "libpin_utils",
         "libslab",
+        "libsys_util",
         "libsyscall_defines",
         "libthiserror",
     ],
@@ -86,7 +86,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/cros_async/Cargo.toml b/cros_async/Cargo.toml
index b502d24..08dc04f 100644
--- a/cros_async/Cargo.toml
+++ b/cros_async/Cargo.toml
@@ -8,9 +8,9 @@
 async-trait = "0.1.36"
 io_uring = { path = "../io_uring" }
 libc = "*"
-paste = "*"
+paste = "1.0"
 pin-utils = "0.1.0-alpha.4"
-base = { path = "../base" }
+sys_util = { path = "../sys_util" }
 syscall_defines = { path = "../syscall_defines" }
 slab = "0.4"
 thiserror = "1.0.20"
@@ -21,6 +21,5 @@
 features = ["alloc"]
 
 [dev-dependencies]
-base = { path = "../base" }
 tempfile = { path = "../tempfile" }
 vm_memory = { path = "../vm_memory" }
diff --git a/cros_async/src/event.rs b/cros_async/src/event.rs
index ad03d77..8105210 100644
--- a/cros_async/src/event.rs
+++ b/cros_async/src/event.rs
@@ -2,26 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use crate::{new, AsyncResult, IoSourceExt};
-use std::os::unix::io::AsRawFd;
+use crate::io_ext::async_from;
+use crate::{AsyncError, AsyncResult, IntoAsync, IoSourceExt};
+use std::convert::TryFrom;
+use sys_util::EventFd;
 
-/// An async version of sys_util::EventFd.
-pub struct EventAsync<F: AsRawFd + 'static> {
-    io_source: Box<dyn IoSourceExt<F> + 'static>,
+/// An async version of `sys_util::EventFd`.
+pub struct EventAsync {
+    io_source: Box<dyn IoSourceExt<EventFd>>,
 }
 
-impl<F: AsRawFd + 'static> EventAsync<F> {
-    /// Creates a new EventAsync wrapper around the provided eventfd.
-    #[allow(dead_code)]
-    pub fn new(f: F) -> AsyncResult<EventAsync<F>> {
-        Ok(EventAsync { io_source: new(f)? })
+impl EventAsync {
+    #[cfg(test)]
+    pub(crate) fn new_poll(event: EventFd) -> AsyncResult<EventAsync> {
+        Ok(EventAsync {
+            io_source: crate::io_ext::async_poll_from(event)?,
+        })
     }
 
-    /// Like new, but allows the source to be constructed directly. Used for
-    /// testing only.
     #[cfg(test)]
-    pub(crate) fn new_from_source(io_source: Box<dyn IoSourceExt<F> + 'static>) -> EventAsync<F> {
-        EventAsync { io_source }
+    pub(crate) fn new_uring(event: EventFd) -> AsyncResult<EventAsync> {
+        Ok(EventAsync {
+            io_source: crate::io_ext::async_uring_from(event)?,
+        })
     }
 
     /// Gets the next value from the eventfd.
@@ -31,21 +34,33 @@
     }
 }
 
+impl TryFrom<EventFd> for EventAsync {
+    type Error = AsyncError;
+
+    /// Creates a new EventAsync wrapper around the provided eventfd.
+    fn try_from(event: EventFd) -> AsyncResult<Self> {
+        Ok(EventAsync {
+            io_source: async_from(event)?,
+        })
+    }
+}
+
+impl IntoAsync for EventFd {}
+
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::io_ext::{new_poll, new_uring};
-    use base::Event;
     use futures::pin_mut;
+    use std::convert::TryInto;
 
     #[test]
     fn next_val_reads_value() {
-        async fn go(event: Event) -> u64 {
-            let event_async = EventAsync::new(event).unwrap();
+        async fn go(event: EventFd) -> u64 {
+            let event_async: EventAsync = event.try_into().unwrap();
             event_async.next_val().await.unwrap()
         }
 
-        let eventfd = Event::new().unwrap();
+        let eventfd = EventFd::new().unwrap();
         eventfd.write(0xaa).unwrap();
         let fut = go(eventfd);
         pin_mut!(fut);
@@ -55,23 +70,22 @@
 
     #[test]
     fn next_val_reads_value_poll_and_ring() {
-        async fn go(source: Box<dyn IoSourceExt<Event> + 'static>) -> u64 {
-            let event_async = EventAsync::new_from_source(source);
+        async fn go(event_async: EventAsync) -> u64 {
             event_async.next_val().await.unwrap()
         }
 
-        let eventfd = Event::new().unwrap();
+        let eventfd = EventFd::new().unwrap();
         eventfd.write(0xaa).unwrap();
-        let fut = go(new_poll(eventfd).unwrap());
+        let fut = go(EventAsync::new_uring(eventfd).unwrap());
         pin_mut!(fut);
-        let val = crate::run_executor(crate::RunOne::new(fut)).unwrap();
+        let val = crate::run_one_uring(fut).unwrap();
         assert_eq!(val, 0xaa);
 
-        let eventfd = Event::new().unwrap();
+        let eventfd = EventFd::new().unwrap();
         eventfd.write(0xaa).unwrap();
-        let fut = go(new_uring(eventfd).unwrap());
+        let fut = go(EventAsync::new_poll(eventfd).unwrap());
         pin_mut!(fut);
-        let val = crate::run_executor(crate::RunOne::new(fut)).unwrap();
+        let val = crate::run_one_poll(fut).unwrap();
         assert_eq!(val, 0xaa);
     }
 }
diff --git a/cros_async/src/fd_executor.rs b/cros_async/src/fd_executor.rs
index 4e20b2f..08ca2f4 100644
--- a/cros_async/src/fd_executor.rs
+++ b/cros_async/src/fd_executor.rs
@@ -21,7 +21,7 @@
 
 use slab::Slab;
 
-use base::{PollContext, WatchingEvents};
+use sys_util::{error, PollContext, WatchingEvents};
 
 use crate::executor::{ExecutableFuture, Executor, FutureList};
 use crate::WakerToken;
@@ -31,15 +31,15 @@
     /// Attempts to create two Executors on the same thread fail.
     AttemptedDuplicateExecutor,
     /// Failed to copy the FD for the polling context.
-    DuplicatingFd(base::Error),
+    DuplicatingFd(sys_util::Error),
     /// Failed accessing the thread local storage for wakers.
     InvalidContext,
     /// Creating a context to wait on FDs failed.
-    CreatingContext(base::Error),
+    CreatingContext(sys_util::Error),
     /// PollContext failure.
-    PollContextError(base::Error),
+    PollContextError(sys_util::Error),
     /// Failed to submit the waker to the polling context.
-    SubmittingWaker(base::Error),
+    SubmittingWaker(sys_util::Error),
     /// A Waker was canceled, but the operation isn't running.
     UnknownWaker,
 }
@@ -150,7 +150,7 @@
 // Tracks active wakers and associates wakers with the futures that registered them.
 struct FdWakerState {
     poll_ctx: PollContext<usize>,
-    tokens: Slab<(File, Waker)>,
+    tokens: Slab<(File, Option<Waker>)>,
     new_futures: VecDeque<ExecutableFuture<()>>,
 }
 
@@ -175,7 +175,7 @@
         self.poll_ctx
             .add_fd_with_events(&duped_fd, events, next_token)
             .map_err(Error::SubmittingWaker)?;
-        entry.insert((duped_fd, waker));
+        entry.insert((duped_fd, Some(waker)));
         Ok(WakerToken(next_token))
     }
 
@@ -184,9 +184,16 @@
         let events = self.poll_ctx.wait().map_err(Error::PollContextError)?;
         for e in events.iter() {
             let token = e.token();
-            let (fd, waker) = self.tokens.remove(token);
-            self.poll_ctx.delete(&fd).map_err(Error::PollContextError)?;
-            waker.wake();
+            if let Some((fd, waker)) = self.tokens.get_mut(token) {
+                self.poll_ctx.delete(fd).map_err(Error::PollContextError)?;
+                if let Some(waker) = waker.take() {
+                    waker.wake();
+                } else {
+                    error!("Woken twice");
+                }
+            } else {
+                error!("Unknown waker");
+            }
         }
         Ok(())
     }
@@ -274,7 +281,7 @@
 unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
     let ret = libc::dup(fd);
     if ret < 0 {
-        Err(Error::DuplicatingFd(base::Error::last()))
+        Err(Error::DuplicatingFd(sys_util::Error::last()))
     } else {
         Ok(ret)
     }
@@ -338,7 +345,7 @@
     #[test]
     fn test_it() {
         async fn do_test() {
-            let (r, _w) = base::pipe(true).unwrap();
+            let (r, _w) = sys_util::pipe(true).unwrap();
             let done = Box::pin(async { 5usize });
             let pending = Box::pin(TestFut::new(r));
             match futures::future::select(pending, done).await {
diff --git a/cros_async/src/io_ext.rs b/cros_async/src/io_ext.rs
index 0a946e3..5743d3d 100644
--- a/cros_async/src/io_ext.rs
+++ b/cros_async/src/io_ext.rs
@@ -18,11 +18,13 @@
 use crate::poll_source::PollSource;
 use crate::UringSource;
 use async_trait::async_trait;
+use std::fs::File;
 use std::os::unix::io::AsRawFd;
 use std::rc::Rc;
 use thiserror::Error as ThisError;
 
 use crate::uring_mem::{BackingMemory, MemRegion};
+use sys_util::net::UnixSeqpacket;
 
 #[derive(ThisError, Debug)]
 pub enum Error {
@@ -94,27 +96,41 @@
     fn as_source(&self) -> &F;
 }
 
+/// Marker trait signifying that the implementor is suitable for use with
+/// cros_async. Examples of this include File, and sys_util::net::UnixSeqpacket.
+///
+/// (Note: it'd be really nice to implement a TryFrom for any implementors, and
+/// remove our factory functions. Unfortunately
+/// https://github.com/rust-lang/rust/issues/50133 makes that too painful.)
+pub trait IntoAsync: AsRawFd {}
+
+impl IntoAsync for File {}
+impl IntoAsync for UnixSeqpacket {}
+impl IntoAsync for &UnixSeqpacket {}
+
 /// Creates a concrete `IoSourceExt` that uses uring if available or falls back to the fd_executor if not.
 /// Note that on older kernels (pre 5.6) FDs such as event or timer FDs are unreliable when
 /// having readvwritev performed through io_uring. To deal with EventFd or TimerFd, use
 /// `IoSourceExt::read_u64`.
-pub fn new<'a, F: AsRawFd + 'a>(f: F) -> Result<Box<dyn IoSourceExt<F> + 'a>> {
+pub fn async_from<'a, F: IntoAsync + 'a>(f: F) -> Result<Box<dyn IoSourceExt<F> + 'a>> {
     if crate::uring_executor::use_uring() {
-        new_uring(f)
+        async_uring_from(f)
     } else {
-        new_poll(f)
+        async_poll_from(f)
     }
 }
 
 /// Creates a concrete `IoSourceExt` using Uring.
-pub(crate) fn new_uring<'a, F: AsRawFd + 'a>(f: F) -> Result<Box<dyn IoSourceExt<F> + 'a>> {
+pub(crate) fn async_uring_from<'a, F: IntoAsync + 'a>(
+    f: F,
+) -> Result<Box<dyn IoSourceExt<F> + 'a>> {
     UringSource::new(f)
         .map(|u| Box::new(u) as Box<dyn IoSourceExt<F>>)
         .map_err(Error::Uring)
 }
 
 /// Creates a concrete `IoSourceExt` using the fd_executor.
-pub(crate) fn new_poll<'a, F: AsRawFd + 'a>(f: F) -> Result<Box<dyn IoSourceExt<F> + 'a>> {
+pub(crate) fn async_poll_from<'a, F: IntoAsync + 'a>(f: F) -> Result<Box<dyn IoSourceExt<F> + 'a>> {
     PollSource::new(f)
         .map(|u| Box::new(u) as Box<dyn IoSourceExt<F>>)
         .map_err(Error::Poll)
@@ -145,7 +161,7 @@
         }
 
         let f = File::open("/dev/zero").unwrap();
-        let uring_source = new_uring(f).unwrap();
+        let uring_source = async_uring_from(f).unwrap();
         let fut = go(uring_source);
         pin_mut!(fut);
         crate::uring_executor::URingExecutor::new(crate::RunOne::new(fut))
@@ -154,7 +170,7 @@
             .unwrap();
 
         let f = File::open("/dev/zero").unwrap();
-        let poll_source = new_poll(f).unwrap();
+        let poll_source = async_poll_from(f).unwrap();
         let fut = go(poll_source);
         pin_mut!(fut);
         crate::fd_executor::FdExecutor::new(crate::RunOne::new(fut))
@@ -175,7 +191,7 @@
         }
 
         let f = OpenOptions::new().write(true).open("/dev/null").unwrap();
-        let uring_source = new_uring(f).unwrap();
+        let uring_source = async_uring_from(f).unwrap();
         let fut = go(uring_source);
         pin_mut!(fut);
         crate::uring_executor::URingExecutor::new(crate::RunOne::new(fut))
@@ -184,7 +200,7 @@
             .unwrap();
 
         let f = OpenOptions::new().write(true).open("/dev/null").unwrap();
-        let poll_source = new_poll(f).unwrap();
+        let poll_source = async_poll_from(f).unwrap();
         let fut = go(poll_source);
         pin_mut!(fut);
         crate::fd_executor::FdExecutor::new(crate::RunOne::new(fut))
@@ -223,7 +239,7 @@
         }
 
         let f = File::open("/dev/zero").unwrap();
-        let uring_source = new_uring(f).unwrap();
+        let uring_source = async_uring_from(f).unwrap();
         let fut = go(uring_source);
         pin_mut!(fut);
         crate::uring_executor::URingExecutor::new(crate::RunOne::new(fut))
@@ -232,7 +248,7 @@
             .unwrap();
 
         let f = File::open("/dev/zero").unwrap();
-        let poll_source = new_poll(f).unwrap();
+        let poll_source = async_poll_from(f).unwrap();
         let fut = go(poll_source);
         pin_mut!(fut);
         crate::fd_executor::FdExecutor::new(crate::RunOne::new(fut))
@@ -257,7 +273,7 @@
         }
 
         let f = OpenOptions::new().write(true).open("/dev/null").unwrap();
-        let uring_source = new_uring(f).unwrap();
+        let uring_source = async_uring_from(f).unwrap();
         let fut = go(uring_source);
         pin_mut!(fut);
         crate::uring_executor::URingExecutor::new(crate::RunOne::new(fut))
@@ -266,7 +282,7 @@
             .unwrap();
 
         let f = OpenOptions::new().write(true).open("/dev/null").unwrap();
-        let poll_source = new_poll(f).unwrap();
+        let poll_source = async_poll_from(f).unwrap();
         let fut = go(poll_source);
         pin_mut!(fut);
         crate::fd_executor::FdExecutor::new(crate::RunOne::new(fut))
@@ -277,8 +293,8 @@
 
     #[test]
     fn read_u64s() {
-        async fn go<F: AsRawFd + Unpin>(async_source: F) -> u64 {
-            let source = new(async_source).unwrap();
+        async fn go(async_source: File) -> u64 {
+            let source = async_from(async_source).unwrap();
             source.read_u64().await.unwrap()
         }
 
@@ -294,7 +310,7 @@
 
     #[test]
     fn read_eventfds() {
-        use base::EventFd;
+        use sys_util::EventFd;
 
         async fn go<F: AsRawFd + Unpin>(source: Box<dyn IoSourceExt<F>>) -> u64 {
             source.read_u64().await.unwrap()
@@ -302,7 +318,7 @@
 
         let eventfd = EventFd::new().unwrap();
         eventfd.write(0x55).unwrap();
-        let fut = go(new(eventfd).unwrap());
+        let fut = go(async_uring_from(eventfd).unwrap());
         pin_mut!(fut);
         let val = crate::uring_executor::URingExecutor::new(crate::RunOne::new(fut))
             .unwrap()
@@ -312,7 +328,7 @@
 
         let eventfd = EventFd::new().unwrap();
         eventfd.write(0xaa).unwrap();
-        let fut = go(new_poll(eventfd).unwrap());
+        let fut = go(async_poll_from(eventfd).unwrap());
         pin_mut!(fut);
         let val = crate::fd_executor::FdExecutor::new(crate::RunOne::new(fut))
             .unwrap()
@@ -334,7 +350,7 @@
         }
 
         let f = tempfile::tempfile().unwrap();
-        let source = new(f).unwrap();
+        let source = async_from(f).unwrap();
 
         let fut = go(source);
         pin_mut!(fut);
diff --git a/cros_async/src/lib.rs b/cros_async/src/lib.rs
index 7438ec8..7bde511 100644
--- a/cros_async/src/lib.rs
+++ b/cros_async/src/lib.rs
@@ -74,7 +74,8 @@
 pub use event::EventAsync;
 pub use executor::Executor;
 pub use io_ext::{
-    new, Error as AsyncError, IoSourceExt, ReadAsync, Result as AsyncResult, WriteAsync,
+    async_from, Error as AsyncError, IntoAsync, IoSourceExt, ReadAsync, Result as AsyncResult,
+    WriteAsync,
 };
 pub use poll_source::PollSource;
 pub use select::SelectResult;
diff --git a/cros_async/src/poll_source.rs b/cros_async/src/poll_source.rs
index ed9404c..f100368 100644
--- a/cros_async/src/poll_source.rs
+++ b/cros_async/src/poll_source.rs
@@ -21,7 +21,7 @@
 use crate::AsyncError;
 use crate::AsyncResult;
 use crate::{IoSourceExt, ReadAsync, WriteAsync};
-use base::{self, add_fd_flags};
+use sys_util::{self, add_fd_flags};
 use thiserror::Error as ThisError;
 
 #[derive(ThisError, Debug)]
@@ -31,23 +31,23 @@
     AddingWaker(fd_executor::Error),
     /// An error occurred when executing fallocate synchronously.
     #[error("An error occurred when executing fallocate synchronously: {0}")]
-    Fallocate(base::Error),
+    Fallocate(sys_util::Error),
     /// An error occurred when executing fsync synchronously.
     #[error("An error occurred when executing fsync synchronously: {0}")]
-    Fsync(base::Error),
+    Fsync(sys_util::Error),
     /// An error occurred when reading the FD.
     ///
     #[error("An error occurred when reading the FD: {0}.")]
-    Read(base::Error),
+    Read(sys_util::Error),
     /// Can't seek file.
     #[error("An error occurred when seeking the FD: {0}.")]
-    Seeking(base::Error),
+    Seeking(sys_util::Error),
     /// An error occurred when setting the FD non-blocking.
     #[error("An error occurred setting the FD non-blocking: {0}.")]
-    SettingNonBlocking(base::Error),
+    SettingNonBlocking(sys_util::Error),
     /// An error occurred when writing the FD.
     #[error("An error occurred when writing the FD: {0}.")]
-    Write(base::Error),
+    Write(sys_util::Error),
 }
 pub type Result<T> = std::result::Result<T, Error>;
 
@@ -186,7 +186,7 @@
         if ret == 0 {
             Ok(())
         } else {
-            Err(AsyncError::Poll(Error::Fallocate(base::Error::last())))
+            Err(AsyncError::Poll(Error::Fallocate(sys_util::Error::last())))
         }
     }
 
@@ -196,7 +196,7 @@
         if ret == 0 {
             Ok(())
         } else {
-            Err(AsyncError::Poll(Error::Fsync(base::Error::last())))
+            Err(AsyncError::Poll(Error::Fsync(sys_util::Error::last())))
         }
     }
 }
@@ -236,7 +236,7 @@
     type Output = Result<(usize, Vec<u8>)>;
 
     fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
-        fn do_read(fd: RawFd, file_offset: u64, buf: &mut [u8]) -> base::Result<usize> {
+        fn do_read(fd: RawFd, file_offset: u64, buf: &mut [u8]) -> sys_util::Result<usize> {
             // Safe because we trust the kernel not to write past the length given and the length is
             // guaranteed to be valid from the pointer by the mut slice.
             let ret = unsafe {
@@ -249,7 +249,7 @@
             };
             match ret {
                 n if n >= 0 => Ok(n as usize),
-                _ => base::errno_result(),
+                _ => sys_util::errno_result(),
             }
         }
 
@@ -293,13 +293,13 @@
     type Output = Result<u64>;
 
     fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
-        fn do_read(fd: RawFd, buf: &mut [u8]) -> base::Result<usize> {
+        fn do_read(fd: RawFd, buf: &mut [u8]) -> sys_util::Result<usize> {
             // Safe because we trust the kernel not to write past the length given and the length is
             // guaranteed to be valid from the pointer by the mut slice.
             let ret = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
             match ret {
                 n if n >= 0 => Ok(n as usize),
-                _ => base::errno_result(),
+                _ => sys_util::errno_result(),
             }
         }
 
@@ -343,10 +343,10 @@
     type Output = Result<()>;
 
     fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
-        fn do_poll(fd: RawFd) -> base::Result<usize> {
+        fn do_poll(fd: RawFd) -> sys_util::Result<usize> {
             let poll_arg = libc::pollfd {
                 fd,
-                events: base::WatchingEvents::empty().set_read().get_raw() as i16,
+                events: sys_util::WatchingEvents::empty().set_read().get_raw() as i16,
                 revents: 0,
             };
 
@@ -354,7 +354,7 @@
             let ret = unsafe { libc::poll(&poll_arg as *const _ as *mut _, 1, 0) };
             match ret {
                 n if n >= 0 => Ok(n as usize),
-                _ => base::errno_result(),
+                _ => sys_util::errno_result(),
             }
         }
 
@@ -390,7 +390,7 @@
     type Output = Result<(usize, Vec<u8>)>;
 
     fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
-        fn do_write(fd: RawFd, file_offset: u64, buf: &[u8]) -> base::Result<usize> {
+        fn do_write(fd: RawFd, file_offset: u64, buf: &[u8]) -> sys_util::Result<usize> {
             // Safe because we trust the kernel not to write to the buffer, only read from it and
             // the length is guaranteed to be valid from the pointer by the mut slice.
             let ret = unsafe {
@@ -403,7 +403,7 @@
             };
             match ret {
                 n if n >= 0 => Ok(n as usize),
-                _ => base::errno_result(),
+                _ => sys_util::errno_result(),
             }
         }
 
@@ -455,7 +455,7 @@
             file_offset: u64,
             mem: &dyn BackingMemory,
             mem_offsets: &[MemRegion],
-        ) -> base::Result<usize> {
+        ) -> sys_util::Result<usize> {
             let mut iovecs = mem_offsets
                 .iter()
                 .filter_map(|&mem_vec| mem.get_iovec(mem_vec).ok())
@@ -472,7 +472,7 @@
             };
             match ret {
                 n if n >= 0 => Ok(n as usize),
-                _ => base::errno_result(),
+                _ => sys_util::errno_result(),
             }
         }
 
@@ -523,7 +523,7 @@
             file_offset: u64,
             mem: &dyn BackingMemory,
             mem_offsets: &[MemRegion],
-        ) -> base::Result<usize> {
+        ) -> sys_util::Result<usize> {
             let iovecs = mem_offsets
                 .iter()
                 .map(|&mem_vec| mem.get_iovec(mem_vec))
@@ -541,7 +541,7 @@
             };
             match ret {
                 n if n >= 0 => Ok(n as usize),
-                _ => base::errno_result(),
+                _ => sys_util::errno_result(),
             }
         }
 
diff --git a/cros_async/src/uring_executor.rs b/cros_async/src/uring_executor.rs
index 3d27eda..462582f 100644
--- a/cros_async/src/uring_executor.rs
+++ b/cros_async/src/uring_executor.rs
@@ -68,8 +68,8 @@
 use futures::pin_mut;
 use slab::Slab;
 
-use base::WatchingEvents;
 use io_uring::URingContext;
+use sys_util::WatchingEvents;
 
 use crate::executor::{ExecutableFuture, Executor, FutureList};
 use crate::uring_mem::{BackingMemory, MemRegion};
@@ -80,7 +80,7 @@
     /// Attempts to create two Executors on the same thread fail.
     AttemptedDuplicateExecutor,
     /// Failed to copy the FD for the polling context.
-    DuplicatingFd(base::Error),
+    DuplicatingFd(sys_util::Error),
     /// Failed accessing the thread local storage for wakers.
     InvalidContext,
     /// Invalid offset or length given for an iovec in backing memory.
@@ -346,7 +346,7 @@
     fn submit_poll(
         &mut self,
         source: &RegisteredSource,
-        events: &base::WatchingEvents,
+        events: &sys_util::WatchingEvents,
     ) -> Result<WakerToken> {
         let src = self
             .registered_sources
@@ -679,7 +679,7 @@
 unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
     let ret = libc::dup(fd);
     if ret < 0 {
-        Err(Error::DuplicatingFd(base::Error::last()))
+        Err(Error::DuplicatingFd(sys_util::Error::last()))
     } else {
         Ok(ret)
     }
@@ -733,7 +733,7 @@
         let bm = Rc::new(VecIoWrapper::from(vec![0u8; 4096])) as Rc<dyn BackingMemory>;
 
         // Use pipes to create a future that will block forever.
-        let (rx, mut tx) = base::pipe(true).unwrap();
+        let (rx, mut tx) = sys_util::pipe(true).unwrap();
 
         // Set up the TLS for the uring_executor by creating one.
         let _ex = URingExecutor::new(crate::executor::UnitFutures::new()).unwrap();
@@ -771,7 +771,7 @@
         let bm = Rc::new(VecIoWrapper::from(vec![0u8; 4096])) as Rc<dyn BackingMemory>;
 
         // Use pipes to create a future that will block forever.
-        let (mut rx, tx) = base::new_pipe_full().expect("Pipe failed");
+        let (mut rx, tx) = sys_util::new_pipe_full().expect("Pipe failed");
 
         // Set up the TLS for the uring_executor by creating one.
         let _ex = URingExecutor::new(crate::executor::UnitFutures::new()).unwrap();
@@ -797,7 +797,7 @@
         // Finishing the operation should put the Rc count back to 1.
         // write to the pipe to wake the read pipe and then wait for the uring result in the
         // executor.
-        let mut buf = vec![0u8; base::round_up_to_page_size(1)];
+        let mut buf = vec![0u8; sys_util::round_up_to_page_size(1)];
         rx.read(&mut buf).expect("read to empty failed");
         RingWakerState::wait_wake_event().expect("Failed to wait for read pipe ready");
         assert_eq!(Rc::strong_count(&bm), 1);
@@ -806,7 +806,7 @@
     #[test]
     fn registered_source_outlives_executor() {
         let bm = Rc::new(VecIoWrapper::from(vec![0u8; 4096])) as Rc<dyn BackingMemory>;
-        let (rx, tx) = base::pipe(true).unwrap();
+        let (rx, tx) = sys_util::pipe(true).unwrap();
 
         // Register a source before creating the executor.
         let rx_source = register_source(&rx).expect("register source failed");
@@ -854,7 +854,7 @@
 
         let bm = Rc::new(VecIoWrapper::from(vec![0u8; 16])) as Rc<dyn BackingMemory>;
 
-        let (rx, tx) = base::pipe(true).expect("Pipe failed");
+        let (rx, tx) = sys_util::pipe(true).expect("Pipe failed");
 
         let mut ex = URingExecutor::new(crate::executor::UnitFutures::new()).unwrap();
 
diff --git a/cros_async/src/uring_futures/read_vec.rs b/cros_async/src/uring_futures/read_vec.rs
index f80472b..09d851d 100644
--- a/cros_async/src/uring_futures/read_vec.rs
+++ b/cros_async/src/uring_futures/read_vec.rs
@@ -144,9 +144,9 @@
 
     #[test]
     fn event() {
-        use base::Event;
+        use sys_util::EventFd;
 
-        async fn write_event(ev: Event, wait: Event) {
+        async fn write_event(ev: EventFd, wait: EventFd) {
             let wait = UringSource::new(wait).unwrap();
             ev.write(55).unwrap();
             read_u64(&wait).await;
@@ -156,7 +156,7 @@
             read_u64(&wait).await;
         }
 
-        async fn read_events(ev: Event, signal: Event) {
+        async fn read_events(ev: EventFd, signal: EventFd) {
             let source = UringSource::new(ev).unwrap();
             assert_eq!(read_u64(&source).await, 55);
             signal.write(1).unwrap();
@@ -166,8 +166,8 @@
             signal.write(1).unwrap();
         }
 
-        let event = Event::new().unwrap();
-        let signal_wait = Event::new().unwrap();
+        let event = EventFd::new().unwrap();
+        let signal_wait = EventFd::new().unwrap();
         let write_task = write_event(event.try_clone().unwrap(), signal_wait.try_clone().unwrap());
         let read_task = read_events(event, signal_wait);
         let joined = futures::future::join(read_task, write_task);
@@ -180,7 +180,7 @@
         use futures::future::Either;
 
         async fn do_test() {
-            let (read_source, _w) = base::pipe(true).unwrap();
+            let (read_source, _w) = sys_util::pipe(true).unwrap();
             let source = UringSource::new(read_source).unwrap();
             let done = async { 5usize };
             let pending = read_u64(&source);
diff --git a/cros_async/src/uring_futures/uring_source.rs b/cros_async/src/uring_futures/uring_source.rs
index 15b88b8..1d6a1a2 100644
--- a/cros_async/src/uring_futures/uring_source.rs
+++ b/cros_async/src/uring_futures/uring_source.rs
@@ -299,17 +299,15 @@
 
     #[test]
     fn read_to_mem() {
-        use vm_memory::{GuestAddress, GuestMemory};
-
         use crate::uring_mem::VecIoWrapper;
+        use std::io::Write;
+        use tempfile::tempfile;
 
         let io_obj = Box::pin({
             // Use guest memory as a test file, it implements AsRawFd.
-            let source = GuestMemory::new(&[(GuestAddress(0), 8192)]).unwrap();
-            source
-                .get_slice_at_addr(GuestAddress(0), 8192)
-                .unwrap()
-                .write_bytes(0x55);
+            let mut source = tempfile().unwrap();
+            let data = vec![0x55; 8192];
+            source.write(&data).unwrap();
             UringSource::new(source).unwrap()
         });
 
diff --git a/data_model/Android.bp b/data_model/Android.bp
index 54cce1d..a35df0a 100644
--- a/data_model/Android.bp
+++ b/data_model/Android.bp
@@ -1,4 +1,4 @@
-// This file is generated by cargo2android.py --run --device --tests --dependencies --global_defaults=crosvm_defaults --add_workspace.
+// This file is generated by cargo2android.py --run --device --tests --dependencies --global_defaults=crosvm_defaults.
 
 rust_defaults {
     name: "data_model_defaults",
diff --git a/data_model/src/flexible_array.rs b/data_model/src/flexible_array.rs
index c47901e..c0689dc 100644
--- a/data_model/src/flexible_array.rs
+++ b/data_model/src/flexible_array.rs
@@ -43,11 +43,11 @@
 /// A complete definition of flexible array structs is found in the ISO 9899 specification
 /// (http://www.iso-9899.info/n1570.html). A flexible array struct is of the form:
 ///
-/// ```
+/// ```ignore
 /// #[repr(C)]
 /// struct T {
 ///    some_data: u32,
-///    nent: u32
+///    nent: u32,
 ///    entries: __IncompleteArrayField<S>,
 /// }
 /// ```
diff --git a/devices/.build_test_serial b/devices/.build_test_serial
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/devices/.build_test_serial
diff --git a/devices/Android.bp b/devices/Android.bp
index 7b0e9e5..dbb5c04 100644
--- a/devices/Android.bp
+++ b/devices/Android.bp
@@ -14,10 +14,10 @@
         "libaudio_streams",
         "libbase_rust",
         "libbit_field",
-        "libbitflags",
         "libcros_async",
         "libdata_model",
         "libdisk",
+        "libfuse_rust",
         "libhypervisor",
         "libkvm_sys",
         "liblibc",
@@ -109,10 +109,10 @@
         "libaudio_streams",
         "libbase_rust",
         "libbit_field",
-        "libbitflags",
         "libcros_async",
         "libdata_model",
         "libdisk",
+        "libfuse_rust",
         "libgpu_buffer",  // added manually
         "libgpu_display",  // added manually
         "libgpu_renderer",  // added manually
@@ -150,6 +150,7 @@
 //   ../../adhd/cras/client/libcras/src/libcras.rs
 //   ../../minijail/rust/minijail-sys/lib.rs
 //   ../../minijail/rust/minijail/src/lib.rs
+//   ../../rust/crates/libchromeos-rs/src/lib.rs
 //   ../../vm_tools/p9/src/lib.rs
 //   ../../vm_tools/p9/wire_format_derive/wire_format_derive.rs
 //   ../acpi_tables/src/lib.rs
@@ -161,6 +162,7 @@
 //   ../data_model/src/lib.rs
 //   ../disk/src/disk.rs
 //   ../enumn/src/lib.rs
+//   ../fuse/src/lib.rs
 //   ../hypervisor/src/lib.rs
 //   ../io_uring/src/lib.rs
 //   ../kvm/src/lib.rs
@@ -185,6 +187,8 @@
 //   ../vm_control/src/lib.rs
 //   ../vm_memory/src/lib.rs
 //   async-trait-0.1.42
+//   autocfg-1.0.1
+//   base-0.1.0
 //   bitflags-1.2.1 "default"
 //   cfg-if-0.1.10
 //   downcast-rs-1.2.0 "default,std"
@@ -197,9 +201,13 @@
 //   futures-sink-0.3.8 "alloc,std"
 //   futures-task-0.3.8 "alloc,once_cell,std"
 //   futures-util-0.3.8 "alloc,async-await,async-await-macro,channel,futures-channel,futures-io,futures-macro,futures-sink,io,memchr,proc-macro-hack,proc-macro-nested,sink,slab,std"
+//   getopts-0.2.21
 //   getrandom-0.1.15 "std"
+//   intrusive-collections-0.9.0 "alloc,default"
 //   libc-0.2.80 "default,std"
+//   log-0.4.11
 //   memchr-2.3.4 "default,std"
+//   memoffset-0.5.6 "default"
 //   once_cell-1.5.2 "alloc,std"
 //   paste-1.0.3
 //   pin-project-1.0.2
@@ -210,6 +218,7 @@
 //   proc-macro-hack-0.5.19
 //   proc-macro-nested-0.1.6
 //   proc-macro2-1.0.24 "default,proc-macro"
+//   protobuf-2.18.1
 //   quote-1.0.7 "default,proc-macro"
 //   rand-0.7.3 "alloc,default,getrandom,getrandom_package,libc,std"
 //   rand_chacha-0.2.2 "std"
@@ -217,8 +226,9 @@
 //   remain-0.2.2
 //   remove_dir_all-0.5.3
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   tempfile-3.1.0
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
+//   unicode-width-0.1.8 "default"
 //   unicode-xid-0.2.1 "default"
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index 2284a23..bacdc92 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -18,11 +18,11 @@
 acpi_tables = {path = "../acpi_tables" }
 audio_streams = { path = "../../adhd/audio_streams" } # ignored by ebuild
 bit_field = { path = "../bit_field" }
-bitflags = "1"
 cros_async = { path = "../cros_async" }
 data_model = { path = "../data_model" }
 disk = { path = "../disk" }
 enumn = { path = "../enumn" }
+fuse = {path = "../fuse" }
 gpu_buffer = { path = "../gpu_buffer", optional = true }
 gpu_display = { path = "../gpu_display", optional = true }
 gpu_renderer = { path = "../gpu_renderer", optional = true }
diff --git a/devices/src/acpi.rs b/devices/src/acpi.rs
index 780e985..80c0e95 100644
--- a/devices/src/acpi.rs
+++ b/devices/src/acpi.rs
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use crate::{BusDevice, BusResumeDevice};
+use crate::{BusAccessInfo, BusDevice, BusResumeDevice};
 use acpi_tables::{aml, aml::Aml};
 use base::{error, warn, Event};
 
 /// ACPI PM resource for handling OS suspend/resume request
 pub struct ACPIPMResource {
     suspend_evt: Event,
+    exit_evt: Event,
     pm1_status: u16,
     pm1_enable: u16,
     pm1_control: u16,
@@ -18,9 +19,10 @@
 
 impl ACPIPMResource {
     /// Constructs ACPI Power Management Resouce.
-    pub fn new(suspend_evt: Event) -> ACPIPMResource {
+    pub fn new(suspend_evt: Event, exit_evt: Event) -> ACPIPMResource {
         ACPIPMResource {
             suspend_evt,
+            exit_evt,
             pm1_status: 0,
             pm1_enable: 0,
             pm1_control: 0,
@@ -46,20 +48,23 @@
 const BITMASK_PM1CNT_WAKE_STATUS: u16 = 0x8000;
 const BITMASK_SLEEPCNT_WAKE_STATUS: u8 = 0x80;
 
+const BITMASK_PM1CNT_SLEEP_TYPE: u16 = 0x1C00;
+const SLEEP_TYPE_S5: u16 = 0;
+
 impl BusDevice for ACPIPMResource {
     fn debug_label(&self) -> String {
         "ACPIPMResource".to_owned()
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
-        let val = match offset as u16 {
+    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,
             _ => {
-                warn!("ACPIPM: Bad read from offset {}", offset);
+                warn!("ACPIPM: Bad read from {}", info);
                 return;
             }
         };
@@ -72,7 +77,7 @@
         }
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
         let max_bytes = std::mem::size_of::<u16>();
 
         // only allow maximum max_bytes to write
@@ -89,13 +94,19 @@
         }
         let val = u16::from_ne_bytes(val_arr);
 
-        match offset as u16 {
+        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 let Err(e) = self.suspend_evt.write(1) {
-                        error!("ACPIPM: failed to trigger suspend event: {}", e);
+                    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);
+                        }
+                    } else {
+                        if let Err(e) = self.suspend_evt.write(1) {
+                            error!("ACPIPM: failed to trigger suspend event: {}", e);
+                        }
                     }
                 }
                 self.pm1_control = val & !BITMASK_PM1CNT_SLEEP_ENABLE;
@@ -112,7 +123,7 @@
             }
             SLEEP_STATUS => self.sleep_status &= !val as u8,
             _ => {
-                warn!("ACPIPM: Bad write to offset {}", offset);
+                warn!("ACPIPM: Bad write to {}", info);
             }
         };
     }
@@ -136,5 +147,12 @@
             &aml::Package::new(vec![&aml::ONE, &aml::ONE, &aml::ZERO, &aml::ZERO]),
         )
         .to_aml_bytes(bytes);
+
+        // S5
+        aml::Name::new(
+            "_S5_".into(),
+            &aml::Package::new(vec![&aml::ZERO, &aml::ZERO, &aml::ZERO, &aml::ZERO]),
+        )
+        .to_aml_bytes(bytes);
     }
 }
diff --git a/devices/src/bat.rs b/devices/src/bat.rs
new file mode 100644
index 0000000..78345ee
--- /dev/null
+++ b/devices/src/bat.rs
@@ -0,0 +1,412 @@
+// 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 crate::{BusAccessInfo, BusDevice};
+use acpi_tables::{aml, aml::Aml};
+use base::{error, warn, AsRawDescriptor, Descriptor, Event, PollContext, PollToken};
+use msg_socket::{MsgReceiver, MsgSender};
+use std::fmt::{self, Display};
+use std::os::unix::io::RawFd;
+use std::sync::Arc;
+use std::thread;
+use sync::Mutex;
+use vm_control::{BatControlCommand, BatControlResponseSocket, BatControlResult};
+
+/// Errors for battery devices.
+#[derive(Debug)]
+pub enum BatteryError {
+    Non32BitMmioAddress,
+}
+
+impl Display for BatteryError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::BatteryError::*;
+
+        match self {
+            Non32BitMmioAddress => write!(f, "Non 32-bit mmio address space"),
+        }
+    }
+}
+
+type Result<T> = std::result::Result<T, BatteryError>;
+
+/// the GoldFish Battery MMIO length.
+pub const GOLDFISHBAT_MMIO_LEN: u64 = 0x1000;
+
+struct GoldfishBatteryState {
+    // interrupt state
+    int_status: u32,
+    int_enable: u32,
+    // AC state
+    ac_online: u32,
+    // Battery state
+    status: u32,
+    health: u32,
+    present: u32,
+    capacity: u32,
+}
+
+macro_rules! create_battery_func {
+    // $property: the battery property which is going to be modified.
+    // $int: the interrupt status which is going to be set to notify the guest.
+    ($fn:ident, $property:ident, $int:ident) => {
+        fn $fn(&mut self, value: u32) -> bool {
+            let old = std::mem::replace(&mut self.$property, value);
+            old != self.$property && self.set_int_status($int)
+        }
+    };
+}
+
+impl GoldfishBatteryState {
+    fn set_int_status(&mut self, mask: u32) -> bool {
+        if ((self.int_enable & mask) != 0) && ((self.int_status & mask) == 0) {
+            self.int_status |= mask;
+            return true;
+        }
+        false
+    }
+
+    fn int_status(&self) -> u32 {
+        self.int_status
+    }
+
+    create_battery_func!(set_ac_online, ac_online, AC_STATUS_CHANGED);
+
+    create_battery_func!(set_status, status, BATTERY_STATUS_CHANGED);
+
+    create_battery_func!(set_health, health, BATTERY_STATUS_CHANGED);
+
+    create_battery_func!(set_present, present, BATTERY_STATUS_CHANGED);
+
+    create_battery_func!(set_capacity, capacity, BATTERY_STATUS_CHANGED);
+}
+
+/// GoldFish Battery state
+pub struct GoldfishBattery {
+    state: Arc<Mutex<GoldfishBatteryState>>,
+    mmio_base: u32,
+    irq_num: u32,
+    irq_evt: Event,
+    irq_resample_evt: Event,
+    activated: bool,
+    monitor_thread: Option<thread::JoinHandle<()>>,
+    kill_evt: Option<Event>,
+    socket: Option<BatControlResponseSocket>,
+}
+
+/// Goldfish Battery MMIO offset
+const BATTERY_INT_STATUS: u32 = 0;
+const BATTERY_INT_ENABLE: u32 = 0x4;
+const BATTERY_AC_ONLINE: u32 = 0x8;
+const BATTERY_STATUS: u32 = 0xC;
+const BATTERY_HEALTH: u32 = 0x10;
+const BATTERY_PRESENT: u32 = 0x14;
+const BATTERY_CAPACITY: u32 = 0x18;
+const BATTERY_VOLTAGE: u32 = 0x1C;
+const BATTERY_TEMP: u32 = 0x20;
+const BATTERY_CHARGE_COUNTER: u32 = 0x24;
+const BATTERY_VOLTAGE_MAX: u32 = 0x28;
+const BATTERY_CURRENT_MAX: u32 = 0x2C;
+const BATTERY_CURRENT_NOW: u32 = 0x30;
+const BATTERY_CURRENT_AVG: u32 = 0x34;
+const BATTERY_CHARGE_FULL_UAH: u32 = 0x38;
+const BATTERY_CYCLE_COUNT: u32 = 0x40;
+
+/// Goldfish Battery interrupt bits
+const BATTERY_STATUS_CHANGED: u32 = 1 << 0;
+const AC_STATUS_CHANGED: u32 = 1 << 1;
+const BATTERY_INT_MASK: u32 = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED;
+
+fn command_monitor(
+    socket: BatControlResponseSocket,
+    irq_evt: Event,
+    irq_resample_evt: Event,
+    kill_evt: Event,
+    state: Arc<Mutex<GoldfishBatteryState>>,
+) {
+    #[derive(PollToken)]
+    enum Token {
+        Commands,
+        Resample,
+        Kill,
+    }
+
+    let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        (&Descriptor(socket.as_raw_descriptor()), Token::Commands),
+        (
+            &Descriptor(irq_resample_evt.as_raw_descriptor()),
+            Token::Resample,
+        ),
+        (&Descriptor(kill_evt.as_raw_descriptor()), Token::Kill),
+    ]) {
+        Ok(pc) => pc,
+        Err(e) => {
+            error!("failed to build PollContext: {}", e);
+            return;
+        }
+    };
+
+    'poll: loop {
+        let events = match poll_ctx.wait() {
+            Ok(v) => v,
+            Err(e) => {
+                error!("error while polling for events: {}", e);
+                break;
+            }
+        };
+
+        for event in events.iter_readable() {
+            match event.token() {
+                Token::Commands => {
+                    let req = match socket.recv() {
+                        Ok(req) => req,
+                        Err(e) => {
+                            error!("failed to receive request: {}", e);
+                            continue;
+                        }
+                    };
+
+                    let mut bat_state = state.lock();
+                    let inject_irq = match req {
+                        BatControlCommand::SetStatus(status) => bat_state.set_status(status.into()),
+                        BatControlCommand::SetHealth(health) => bat_state.set_health(health.into()),
+                        BatControlCommand::SetPresent(present) => {
+                            let v = if present != 0 { 1 } else { 0 };
+                            bat_state.set_present(v)
+                        }
+                        BatControlCommand::SetCapacity(capacity) => {
+                            let v = std::cmp::min(capacity, 100);
+                            bat_state.set_capacity(v)
+                        }
+                        BatControlCommand::SetACOnline(ac_online) => {
+                            let v = if ac_online != 0 { 1 } else { 0 };
+                            bat_state.set_ac_online(v)
+                        }
+                    };
+
+                    if inject_irq {
+                        let _ = irq_evt.write(1);
+                    }
+
+                    if let Err(e) = socket.send(&BatControlResult::Ok) {
+                        error!("failed to send response: {}", e);
+                    }
+                }
+                Token::Resample => {
+                    let _ = irq_resample_evt.read();
+                    if state.lock().int_status() != 0 {
+                        let _ = irq_evt.write(1);
+                    }
+                }
+                Token::Kill => break 'poll,
+            }
+        }
+    }
+}
+
+impl GoldfishBattery {
+    /// Create GoldfishBattery device model
+    ///
+    /// * `mmio_base` - The 32-bit mmio base address.
+    /// * `irq_num` - The corresponding interrupt number of the irq_evt
+    ///               which will be put into the ACPI DSDT.
+    /// * `irq_evt` - The interrupt event used to notify driver about
+    ///               the battery properties changing.
+    /// * `irq_resample_evt` - Resample interrupt event notified at EOI.
+    /// * `socket` - Battery control socket
+    pub fn new(
+        mmio_base: u64,
+        irq_num: u32,
+        irq_evt: Event,
+        irq_resample_evt: Event,
+        socket: BatControlResponseSocket,
+    ) -> Result<Self> {
+        if mmio_base + GOLDFISHBAT_MMIO_LEN - 1 > u32::MAX as u64 {
+            return Err(BatteryError::Non32BitMmioAddress);
+        }
+        let state = Arc::new(Mutex::new(GoldfishBatteryState {
+            capacity: 50,
+            health: 1,
+            present: 1,
+            status: 1,
+            ac_online: 1,
+            int_enable: 0,
+            int_status: 0,
+        }));
+
+        Ok(GoldfishBattery {
+            state,
+            mmio_base: mmio_base as u32,
+            irq_num,
+            irq_evt,
+            irq_resample_evt,
+            activated: false,
+            monitor_thread: None,
+            kill_evt: None,
+            socket: Some(socket),
+        })
+    }
+
+    /// return the fds used by this device
+    pub fn keep_fds(&self) -> Vec<RawFd> {
+        let mut fds = vec![
+            self.irq_evt.as_raw_descriptor(),
+            self.irq_resample_evt.as_raw_descriptor(),
+        ];
+
+        if let Some(socket) = &self.socket {
+            fds.push(socket.as_raw_descriptor());
+        }
+
+        fds
+    }
+
+    /// start a monitor thread to monitor the events from host
+    fn start_monitor(&mut self) {
+        if self.activated {
+            return;
+        }
+
+        let (self_kill_evt, kill_evt) = match Event::new().and_then(|e| Ok((e.try_clone()?, e))) {
+            Ok(v) => v,
+            Err(e) => {
+                error!(
+                    "{}: failed to create kill EventFd pair: {}",
+                    self.debug_label(),
+                    e
+                );
+                return;
+            }
+        };
+
+        if let Some(socket) = self.socket.take() {
+            let irq_evt = self.irq_evt.try_clone().unwrap();
+            let irq_resample_evt = self.irq_resample_evt.try_clone().unwrap();
+            let bat_state = self.state.clone();
+            let monitor_result = thread::Builder::new()
+                .name(self.debug_label())
+                .spawn(move || {
+                    command_monitor(socket, irq_evt, irq_resample_evt, kill_evt, bat_state);
+                });
+
+            self.monitor_thread = match monitor_result {
+                Err(e) => {
+                    error!(
+                        "{}: failed to spawn PowerIO monitor: {}",
+                        self.debug_label(),
+                        e
+                    );
+                    return;
+                }
+                Ok(join_handle) => Some(join_handle),
+            };
+            self.kill_evt = Some(self_kill_evt);
+            self.activated = true;
+        }
+    }
+}
+
+impl Drop for GoldfishBattery {
+    fn drop(&mut self) {
+        if let Some(kill_evt) = self.kill_evt.take() {
+            // Ignore the result because there is nothing we can do with a failure.
+            let _ = kill_evt.write(1);
+        }
+        if let Some(thread) = self.monitor_thread.take() {
+            let _ = thread.join();
+        }
+    }
+}
+
+impl BusDevice for GoldfishBattery {
+    fn debug_label(&self) -> String {
+        "GoldfishBattery".to_owned()
+    }
+
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
+        if data.len() != std::mem::size_of::<u32>() {
+            warn!(
+                "{}: unsupported read length {}, only support 4bytes read",
+                self.debug_label(),
+                data.len()
+            );
+            return;
+        }
+
+        let val = match info.offset as u32 {
+            BATTERY_INT_STATUS => {
+                // read to clear the interrupt status
+                std::mem::replace(&mut self.state.lock().int_status, 0)
+            }
+            BATTERY_INT_ENABLE => self.state.lock().int_enable,
+            BATTERY_AC_ONLINE => self.state.lock().ac_online,
+            BATTERY_STATUS => self.state.lock().status,
+            BATTERY_HEALTH => self.state.lock().health,
+            BATTERY_PRESENT => self.state.lock().present,
+            BATTERY_CAPACITY => self.state.lock().capacity,
+            BATTERY_VOLTAGE => 0,
+            BATTERY_TEMP => 0,
+            BATTERY_CHARGE_COUNTER => 0,
+            BATTERY_VOLTAGE_MAX => 0,
+            BATTERY_CURRENT_MAX => 0,
+            BATTERY_CURRENT_NOW => 0,
+            BATTERY_CURRENT_AVG => 0,
+            BATTERY_CHARGE_FULL_UAH => 0,
+            BATTERY_CYCLE_COUNT => 0,
+            _ => {
+                warn!("{}: unsupported read address {}", self.debug_label(), info);
+                return;
+            }
+        };
+
+        let val_arr = val.to_ne_bytes();
+        data.copy_from_slice(&val_arr);
+    }
+
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
+        if data.len() != std::mem::size_of::<u32>() {
+            warn!(
+                "{}: unsupported write length {}, only support 4bytes write",
+                self.debug_label(),
+                data.len()
+            );
+            return;
+        }
+
+        let mut val_arr = u32::to_ne_bytes(0 as u32);
+        val_arr.copy_from_slice(data);
+        let val = u32::from_ne_bytes(val_arr);
+
+        match info.offset as u32 {
+            BATTERY_INT_ENABLE => {
+                self.state.lock().int_enable = val;
+                if (val & BATTERY_INT_MASK) != 0 && !self.activated {
+                    self.start_monitor();
+                }
+            }
+            _ => {
+                warn!("{}: Bad write to address {}", self.debug_label(), info);
+            }
+        };
+    }
+}
+
+impl Aml for GoldfishBattery {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        aml::Device::new(
+            "GFBY".into(),
+            vec![
+                &aml::Name::new("_HID".into(), &"GFSH0001"),
+                &aml::Name::new(
+                    "_CRS".into(),
+                    &aml::ResourceTemplate::new(vec![
+                        &aml::Memory32Fixed::new(true, self.mmio_base, GOLDFISHBAT_MMIO_LEN as u32),
+                        &aml::Interrupt::new(true, false, false, true, self.irq_num),
+                    ]),
+                ),
+            ],
+        )
+        .to_aml_bytes(bytes);
+    }
+}
diff --git a/devices/src/bus.rs b/devices/src/bus.rs
index 3f93974..c6e7d78 100644
--- a/devices/src/bus.rs
+++ b/devices/src/bus.rs
@@ -10,8 +10,27 @@
 use std::result;
 use std::sync::Arc;
 
+use base::RawDescriptor;
+use msg_socket::MsgOnSocket;
 use sync::Mutex;
 
+/// Information about how a device was accessed.
+#[derive(Copy, Clone, Eq, PartialEq, Debug, MsgOnSocket)]
+pub struct BusAccessInfo {
+    /// Offset from base address that the device was accessed at.
+    pub offset: u64,
+    /// Absolute address of the device's access in its address space.
+    pub address: u64,
+    /// ID of the entity requesting a device access, usually the VCPU id.
+    pub id: usize,
+}
+
+// Implement `Display` for `MinMax`.
+impl std::fmt::Display for BusAccessInfo {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:?}", self)
+    }
+}
 /// Trait for devices that respond to reads or writes in an arbitrary address space.
 ///
 /// The device does not care where it exists in address space as each method is only given an offset
@@ -21,9 +40,9 @@
     /// Returns a label suitable for debug output.
     fn debug_label(&self) -> String;
     /// Reads at `offset` from this device
-    fn read(&mut self, offset: u64, data: &mut [u8]) {}
+    fn read(&mut self, offset: BusAccessInfo, data: &mut [u8]) {}
     /// Writes at `offset` into this device
-    fn write(&mut self, offset: u64, data: &[u8]) {}
+    fn write(&mut self, offset: BusAccessInfo, data: &[u8]) {}
     /// Sets a register in the configuration space. Only used by PCI.
     /// * `reg_idx` - The index of the config register to modify.
     /// * `offset` - Offset in to the register.
@@ -37,6 +56,11 @@
     fn on_sandboxed(&mut self) {}
 }
 
+pub trait BusDeviceSync: BusDevice + Sync {
+    fn read(&self, offset: BusAccessInfo, data: &mut [u8]);
+    fn write(&self, offset: BusAccessInfo, data: &[u8]);
+}
+
 pub trait BusResumeDevice: Send {
     /// notify the devices which are invoked
     /// before the VM resumes form suspend.
@@ -65,13 +89,10 @@
 ///
 /// * base - The address at which the range start.
 /// * len - The length of the range in bytes.
-/// * full_addr - If true, return the full address from `get_device`, otherwise return the offset
-///               from `base`
 #[derive(Debug, Copy, Clone)]
 pub struct BusRange {
     pub base: u64,
     pub len: u64,
-    pub full_addr: bool,
 }
 
 impl BusRange {
@@ -106,6 +127,12 @@
     }
 }
 
+#[derive(Clone)]
+enum BusDeviceEntry {
+    OuterSync(Arc<Mutex<dyn BusDevice>>),
+    InnerSync(Arc<dyn BusDeviceSync>),
+}
+
 /// A device container for routing reads and writes over some address space.
 ///
 /// This doesn't have any restrictions on what kind of device or address space this applies to. The
@@ -115,8 +142,9 @@
 /// resume back from S3 suspended state.
 #[derive(Clone)]
 pub struct Bus {
-    devices: BTreeMap<BusRange, Arc<Mutex<dyn BusDevice>>>,
+    devices: BTreeMap<BusRange, BusDeviceEntry>,
     resume_notify_devices: Vec<Arc<Mutex<dyn BusResumeDevice>>>,
+    access_id: usize,
 }
 
 impl Bus {
@@ -125,45 +153,68 @@
         Bus {
             devices: BTreeMap::new(),
             resume_notify_devices: Vec::new(),
+            access_id: 0,
         }
     }
 
-    fn first_before(&self, addr: u64) -> Option<(BusRange, &Mutex<dyn BusDevice>)> {
+    /// Sets the id that will be used for BusAccessInfo.
+    pub fn set_access_id(&mut self, id: usize) {
+        self.access_id = id;
+    }
+
+    fn first_before(&self, addr: u64) -> Option<(BusRange, &BusDeviceEntry)> {
         let (range, dev) = self
             .devices
-            .range(
-                ..=BusRange {
-                    base: addr,
-                    len: 1,
-                    full_addr: false,
-                },
-            )
+            .range(..=BusRange { base: addr, len: 1 })
             .rev()
             .next()?;
         Some((*range, dev))
     }
 
-    fn get_device(&self, addr: u64) -> Option<(u64, &Mutex<dyn BusDevice>)> {
+    fn get_device(&self, addr: u64) -> Option<(u64, u64, &BusDeviceEntry)> {
         if let Some((range, dev)) = self.first_before(addr) {
             let offset = addr - range.base;
             if offset < range.len {
-                if range.full_addr {
-                    return Some((addr, dev));
-                } else {
-                    return Some((offset, dev));
-                }
+                return Some((offset, addr, dev));
             }
         }
         None
     }
 
     /// Puts the given device at the given address space.
-    pub fn insert(
+    pub fn insert(&mut self, device: Arc<Mutex<dyn BusDevice>>, base: u64, len: u64) -> Result<()> {
+        if len == 0 {
+            return Err(Error::Overlap);
+        }
+
+        // Reject all cases where the new device's range overlaps with an existing device.
+        if self
+            .devices
+            .iter()
+            .any(|(range, _dev)| range.overlaps(base, len))
+        {
+            return Err(Error::Overlap);
+        }
+
+        if self
+            .devices
+            .insert(BusRange { base, len }, BusDeviceEntry::OuterSync(device))
+            .is_some()
+        {
+            return Err(Error::Overlap);
+        }
+
+        Ok(())
+    }
+
+    /// Puts the given device that implements BusDeviceSync at the given address space. Devices
+    /// that implement BusDeviceSync manage thread safety internally, and thus can be written to
+    /// by multiple threads simultaneously.
+    pub fn insert_sync(
         &mut self,
-        device: Arc<Mutex<dyn BusDevice>>,
+        device: Arc<dyn BusDeviceSync>,
         base: u64,
         len: u64,
-        full_addr: bool,
     ) -> Result<()> {
         if len == 0 {
             return Err(Error::Overlap);
@@ -180,14 +231,7 @@
 
         if self
             .devices
-            .insert(
-                BusRange {
-                    base,
-                    len,
-                    full_addr,
-                },
-                device,
-            )
+            .insert(BusRange { base, len }, BusDeviceEntry::InnerSync(device))
             .is_some()
         {
             return Err(Error::Overlap);
@@ -200,8 +244,16 @@
     ///
     /// Returns true on success, otherwise `data` is untouched.
     pub fn read(&self, addr: u64, data: &mut [u8]) -> bool {
-        if let Some((offset, dev)) = self.get_device(addr) {
-            dev.lock().read(offset, data);
+        if let Some((offset, address, dev)) = self.get_device(addr) {
+            let io = BusAccessInfo {
+                address,
+                offset,
+                id: self.access_id,
+            };
+            match dev {
+                BusDeviceEntry::OuterSync(dev) => dev.lock().read(io, data),
+                BusDeviceEntry::InnerSync(dev) => dev.read(io, data),
+            }
             true
         } else {
             false
@@ -212,8 +264,16 @@
     ///
     /// Returns true on success, otherwise `data` is untouched.
     pub fn write(&self, addr: u64, data: &[u8]) -> bool {
-        if let Some((offset, dev)) = self.get_device(addr) {
-            dev.lock().write(offset, data);
+        if let Some((offset, address, dev)) = self.get_device(addr) {
+            let io = BusAccessInfo {
+                address,
+                offset,
+                id: self.access_id,
+            };
+            match dev {
+                BusDeviceEntry::OuterSync(dev) => dev.lock().write(io, data),
+                BusDeviceEntry::InnerSync(dev) => dev.write(io, data),
+            }
             true
         } else {
             false
@@ -245,21 +305,34 @@
         }
     }
 
-    struct ConstantDevice;
+    struct ConstantDevice {
+        uses_full_addr: bool,
+    }
+
     impl BusDevice for ConstantDevice {
         fn debug_label(&self) -> String {
             "constant device".to_owned()
         }
 
-        fn read(&mut self, offset: u64, data: &mut [u8]) {
+        fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
+            let addr = if self.uses_full_addr {
+                info.address
+            } else {
+                info.offset
+            };
             for (i, v) in data.iter_mut().enumerate() {
-                *v = (offset as u8) + (i as u8);
+                *v = (addr as u8) + (i as u8);
             }
         }
 
-        fn write(&mut self, offset: u64, data: &[u8]) {
+        fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
+            let addr = if self.uses_full_addr {
+                info.address
+            } else {
+                info.offset
+            };
             for (i, v) in data.iter().enumerate() {
-                assert_eq!(*v, (offset as u8) + (i as u8))
+                assert_eq!(*v, (addr as u8) + (i as u8))
             }
         }
     }
@@ -268,41 +341,41 @@
     fn bus_insert() {
         let mut bus = Bus::new();
         let dummy = Arc::new(Mutex::new(DummyDevice));
-        assert!(bus.insert(dummy.clone(), 0x10, 0, false).is_err());
-        assert!(bus.insert(dummy.clone(), 0x10, 0x10, false).is_ok());
-        assert!(bus.insert(dummy.clone(), 0x0f, 0x10, false).is_err());
-        assert!(bus.insert(dummy.clone(), 0x10, 0x10, false).is_err());
-        assert!(bus.insert(dummy.clone(), 0x10, 0x15, false).is_err());
-        assert!(bus.insert(dummy.clone(), 0x12, 0x15, false).is_err());
-        assert!(bus.insert(dummy.clone(), 0x12, 0x01, false).is_err());
-        assert!(bus.insert(dummy.clone(), 0x0, 0x20, false).is_err());
-        assert!(bus.insert(dummy.clone(), 0x20, 0x05, false).is_ok());
-        assert!(bus.insert(dummy.clone(), 0x25, 0x05, false).is_ok());
-        assert!(bus.insert(dummy.clone(), 0x0, 0x10, false).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x10, 0).is_err());
+        assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x0f, 0x10).is_err());
+        assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_err());
+        assert!(bus.insert(dummy.clone(), 0x10, 0x15).is_err());
+        assert!(bus.insert(dummy.clone(), 0x12, 0x15).is_err());
+        assert!(bus.insert(dummy.clone(), 0x12, 0x01).is_err());
+        assert!(bus.insert(dummy.clone(), 0x0, 0x20).is_err());
+        assert!(bus.insert(dummy.clone(), 0x20, 0x05).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x25, 0x05).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x0, 0x10).is_ok());
     }
 
     #[test]
     fn bus_insert_full_addr() {
         let mut bus = Bus::new();
         let dummy = Arc::new(Mutex::new(DummyDevice));
-        assert!(bus.insert(dummy.clone(), 0x10, 0, true).is_err());
-        assert!(bus.insert(dummy.clone(), 0x10, 0x10, true).is_ok());
-        assert!(bus.insert(dummy.clone(), 0x0f, 0x10, true).is_err());
-        assert!(bus.insert(dummy.clone(), 0x10, 0x10, true).is_err());
-        assert!(bus.insert(dummy.clone(), 0x10, 0x15, true).is_err());
-        assert!(bus.insert(dummy.clone(), 0x12, 0x15, true).is_err());
-        assert!(bus.insert(dummy.clone(), 0x12, 0x01, true).is_err());
-        assert!(bus.insert(dummy.clone(), 0x0, 0x20, true).is_err());
-        assert!(bus.insert(dummy.clone(), 0x20, 0x05, true).is_ok());
-        assert!(bus.insert(dummy.clone(), 0x25, 0x05, true).is_ok());
-        assert!(bus.insert(dummy.clone(), 0x0, 0x10, true).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x10, 0).is_err());
+        assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x0f, 0x10).is_err());
+        assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_err());
+        assert!(bus.insert(dummy.clone(), 0x10, 0x15).is_err());
+        assert!(bus.insert(dummy.clone(), 0x12, 0x15).is_err());
+        assert!(bus.insert(dummy.clone(), 0x12, 0x01).is_err());
+        assert!(bus.insert(dummy.clone(), 0x0, 0x20).is_err());
+        assert!(bus.insert(dummy.clone(), 0x20, 0x05).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x25, 0x05).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x0, 0x10).is_ok());
     }
 
     #[test]
     fn bus_read_write() {
         let mut bus = Bus::new();
         let dummy = Arc::new(Mutex::new(DummyDevice));
-        assert!(bus.insert(dummy.clone(), 0x10, 0x10, false).is_ok());
+        assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
         assert!(bus.read(0x10, &mut [0, 0, 0, 0]));
         assert!(bus.write(0x10, &[0, 0, 0, 0]));
         assert!(bus.read(0x11, &mut [0, 0, 0, 0]));
@@ -318,8 +391,10 @@
     #[test]
     fn bus_read_write_values() {
         let mut bus = Bus::new();
-        let dummy = Arc::new(Mutex::new(ConstantDevice));
-        assert!(bus.insert(dummy.clone(), 0x10, 0x10, false).is_ok());
+        let dummy = Arc::new(Mutex::new(ConstantDevice {
+            uses_full_addr: false,
+        }));
+        assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
 
         let mut values = [0, 1, 2, 3];
         assert!(bus.read(0x10, &mut values));
@@ -333,8 +408,10 @@
     #[test]
     fn bus_read_write_full_addr_values() {
         let mut bus = Bus::new();
-        let dummy = Arc::new(Mutex::new(ConstantDevice));
-        assert!(bus.insert(dummy.clone(), 0x10, 0x10, true).is_ok());
+        let dummy = Arc::new(Mutex::new(ConstantDevice {
+            uses_full_addr: true,
+        }));
+        assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
 
         let mut values = [0u8; 4];
         assert!(bus.read(0x10, &mut values));
@@ -350,7 +427,6 @@
         let a = BusRange {
             base: 0x1000,
             len: 0x400,
-            full_addr: false,
         };
         assert!(a.contains(0x1000));
         assert!(a.contains(0x13ff));
@@ -364,7 +440,6 @@
         let a = BusRange {
             base: 0x1000,
             len: 0x400,
-            full_addr: false,
         };
         assert!(a.overlaps(0x1000, 0x400));
         assert!(a.overlaps(0xf00, 0x400));
diff --git a/devices/src/cmos.rs b/devices/src/cmos.rs
index 977b9ef..fcf0498 100644
--- a/devices/src/cmos.rs
+++ b/devices/src/cmos.rs
@@ -6,7 +6,7 @@
 use std::cmp::min;
 use std::mem;
 
-use crate::BusDevice;
+use crate::{BusAccessInfo, BusDevice};
 
 const INDEX_MASK: u8 = 0x7f;
 const INDEX_OFFSET: u64 = 0x0;
@@ -51,19 +51,19 @@
         "cmos".to_owned()
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
         if data.len() != 1 {
             return;
         }
 
-        match offset {
+        match info.offset {
             INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
             DATA_OFFSET => self.data[self.index as usize] = data[0],
             o => panic!("bad write offset on CMOS device: {}", o),
         }
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
         fn to_bcd(v: u8) -> u8 {
             assert!(v < 100);
             ((v / 10) << 4) | (v % 10)
@@ -73,7 +73,7 @@
             return;
         }
 
-        data[0] = match offset {
+        data[0] = match info.offset {
             INDEX_OFFSET => self.index,
             DATA_OFFSET => {
                 let seconds;
diff --git a/devices/src/i8042.rs b/devices/src/i8042.rs
index 08b54b0..6fdd978 100644
--- a/devices/src/i8042.rs
+++ b/devices/src/i8042.rs
@@ -4,7 +4,7 @@
 
 use base::{error, Event};
 
-use crate::BusDevice;
+use crate::{BusAccessInfo, BusDevice};
 
 /// A i8042 PS/2 controller that emulates just enough to shutdown the machine.
 pub struct I8042Device {
@@ -25,18 +25,18 @@
         "i8042".to_owned()
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
-        if data.len() == 1 && offset == 0x64 {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
+        if data.len() == 1 && info.offset == 0x64 {
             data[0] = 0x0;
-        } else if data.len() == 1 && offset == 0x61 {
+        } else if data.len() == 1 && info.offset == 0x61 {
             // Like kvmtool, we return bit 5 set in I8042_PORT_B_REG to
             // avoid hang in pit_calibrate_tsc() in Linux kernel.
             data[0] = 0x20;
         }
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
-        if data.len() == 1 && data[0] == 0xfe && offset == 0x64 {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
+        if data.len() == 1 && data[0] == 0xfe && info.offset == 0x64 {
             if let Err(e) = self.reset_evt.write(1) {
                 error!("failed to trigger i8042 reset event: {}", e);
             }
diff --git a/devices/src/irqchip/ioapic.rs b/devices/src/irqchip/ioapic.rs
index 11d78f3..bfab4ed 100644
--- a/devices/src/irqchip/ioapic.rs
+++ b/devices/src/irqchip/ioapic.rs
@@ -5,11 +5,15 @@
 // Implementation of an intel 82093AA Input/Output Advanced Programmable Interrupt Controller
 // See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
 
+use std::fmt::{self, Display};
+
+use super::IrqEvent;
+use crate::bus::BusAccessInfo;
 use crate::BusDevice;
-use base::{error, warn, Event, Result};
+use base::{error, warn, AsRawDescriptor, Error, Event, Result};
 use hypervisor::{IoapicState, MsiAddressMessage, MsiDataMessage, TriggerMode, NUM_IOAPIC_PINS};
-use msg_socket::{MsgReceiver, MsgSender};
-use vm_control::{VmIrqRequest, VmIrqRequestSocket, VmIrqResponse};
+use msg_socket::{MsgError, MsgReceiver, MsgSender};
+use vm_control::{MaybeOwnedDescriptor, VmIrqRequest, VmIrqRequestSocket, VmIrqResponse};
 
 const IOAPIC_VERSION_ID: u32 = 0x00170011;
 pub const IOAPIC_BASE_ADDRESS: u64 = 0xfec00000;
@@ -58,10 +62,11 @@
     /// Remote IRR for Edge Triggered Real Time Clock interrupts, which allows the CMOS to know when
     /// one of its interrupts is being coalesced.
     rtc_remote_irr: bool,
-    /// Irq events that are used to inject interrupts
-    irq_events: Vec<Event>,
-    /// Events that should be triggered on an EOI
-    resample_events: Vec<Option<Event>>,
+    /// Outgoing irq events that are used to inject MSI interrupts.
+    out_events: Vec<Option<IrqEvent>>,
+    /// Events that should be triggered on an EOI. The outer Vec is indexed by GSI, and the inner
+    /// Vec is an unordered list of registered resample events for the GSI.
+    resample_events: Vec<Vec<Event>>,
     /// Socket used to route MSI irqs
     irq_socket: VmIrqRequestSocket,
 }
@@ -71,20 +76,20 @@
         "userspace IOAPIC".to_string()
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
         if data.len() > 8 || data.len() == 0 {
             warn!("IOAPIC: Bad read size: {}", data.len());
             return;
         }
-        if offset >= IOAPIC_MEM_LENGTH_BYTES {
-            warn!("IOAPIC: Bad read from offset {}", offset);
+        if info.offset >= IOAPIC_MEM_LENGTH_BYTES {
+            warn!("IOAPIC: Bad read from {}", info);
         }
-        let out = match offset as u8 {
+        let out = match info.offset as u8 {
             IOREGSEL_OFF => self.state.ioregsel.into(),
             IOREGSEL_DUMMY_UPPER_32_BITS_OFF => 0,
             IOWIN_OFF => self.ioapic_read(),
             _ => {
-                warn!("IOAPIC: Bad read from offset {}", offset);
+                warn!("IOAPIC: Bad read from {}", info);
                 return;
             }
         };
@@ -96,15 +101,15 @@
         }
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
         if data.len() > 8 || data.len() == 0 {
             warn!("IOAPIC: Bad write size: {}", data.len());
             return;
         }
-        if offset >= IOAPIC_MEM_LENGTH_BYTES {
-            warn!("IOAPIC: Bad write to offset {}", offset);
+        if info.offset >= IOAPIC_MEM_LENGTH_BYTES {
+            warn!("IOAPIC: Bad write to {}", info);
         }
-        match offset as u8 {
+        match info.offset as u8 {
             IOREGSEL_OFF => self.state.ioregsel = data[0],
             IOREGSEL_DUMMY_UPPER_32_BITS_OFF => {} // Ignored.
             IOWIN_OFF => {
@@ -117,14 +122,14 @@
                 self.ioapic_write(val);
             }
             _ => {
-                warn!("IOAPIC: Bad write to offset {}", offset);
+                warn!("IOAPIC: Bad write to {}", info);
             }
         }
     }
 }
 
 impl Ioapic {
-    pub fn new(irq_events: Vec<Event>, irq_socket: VmIrqRequestSocket) -> Result<Ioapic> {
+    pub fn new(irq_socket: VmIrqRequestSocket) -> Result<Ioapic> {
         let mut state = IoapicState::default();
 
         for i in 0..NUM_IOAPIC_PINS {
@@ -134,7 +139,7 @@
         Ok(Ioapic {
             state,
             rtc_remote_irr: false,
-            irq_events,
+            out_events: (0..NUM_IOAPIC_PINS).map(|_| None).collect(),
             resample_events: Vec::new(),
             irq_socket,
         })
@@ -148,7 +153,7 @@
         self.state = *state
     }
 
-    pub fn register_resample_events(&mut self, resample_events: Vec<Option<Event>>) {
+    pub fn register_resample_events(&mut self, resample_events: Vec<Vec<Event>>) {
         self.resample_events = resample_events;
     }
 
@@ -164,12 +169,18 @@
             if self.state.redirect_table[i].get_vector() == vector
                 && self.state.redirect_table[i].get_trigger_mode() == TriggerMode::Level
             {
-                if self.resample_events.get(i).map_or(false, |e| e.is_some()) {
+                if self
+                    .resample_events
+                    .get(i)
+                    .map_or(false, |events| events.len() > 0)
+                {
                     self.service_irq(i, false);
                 }
 
-                if let Some(Some(resample_evt)) = &self.resample_events.get(i) {
-                    resample_evt.write(1).unwrap();
+                if let Some(resample_events) = self.resample_events.get(i) {
+                    for resample_evt in resample_events {
+                        resample_evt.write(1).unwrap();
+                    }
                 }
                 self.state.redirect_table[i].set_remote_irr(false);
             }
@@ -216,8 +227,8 @@
             return false;
         }
 
-        let injected = match self.irq_events.get(irq) {
-            Some(evt) => evt.write(1).is_ok(),
+        let injected = match self.out_events.get(irq) {
+            Some(Some(evt)) => evt.event.write(1).is_ok(),
             _ => false,
         };
 
@@ -284,30 +295,85 @@
                 data.set_delivery_mode(entry.get_delivery_mode());
                 data.set_trigger(entry.get_trigger_mode());
 
-                let request = VmIrqRequest::AddMsiRoute {
-                    gsi: index as u32,
-                    msi_address: address.get(0, 32),
-                    msi_data: data.get(0, 32) as u32,
-                };
-
-                if let Err(e) = self.irq_socket.send(&request) {
-                    error!("IOAPIC: failed to send AddMsiRoute request: {}", e);
-                    return;
-                }
-                match self.irq_socket.recv() {
-                    Ok(response) => {
-                        if let VmIrqResponse::Err(e) = response {
-                            error!("IOAPIC: failed to add msi route: {}", e);
-                        }
-                    }
-                    Err(e) => {
-                        error!("IOAPIC: failed to receive AddMsiRoute response: {}", e);
-                    }
+                let msi_address = address.get(0, 32);
+                let msi_data = data.get(0, 32);
+                if let Err(e) = self.setup_msi(index, msi_address, msi_data as u32) {
+                    error!("IOAPIC failed to set up MSI for index {}: {}", index, e);
                 }
             }
         }
     }
 
+    fn setup_msi(
+        &mut self,
+        index: usize,
+        msi_address: u64,
+        msi_data: u32,
+    ) -> std::result::Result<(), IoapicError> {
+        if msi_data == 0 {
+            // During boot, Linux first configures all ioapic pins with msi_data == 0; the routes
+            // aren't yet assigned to devices and aren't usable.  We skip MSI setup if msi_data is
+            // 0.
+            return Ok(());
+        }
+
+        // Allocate a GSI and event for the outgoing route, if we haven't already done it.
+        // The event will be used on the "outgoing" end of the ioapic to send an interrupt to the
+        // apics: when an incoming ioapic irq line gets signalled, the ioapic writes to the
+        // corresponding outgoing event. The GSI number is used to update the routing info (MSI
+        // data and addr) for the event. The GSI and event are allocated only once for each ioapic
+        // irq line, when the guest first sets up the ioapic with a valid route. If the guest
+        // later reconfigures an ioapic irq line, the same GSI and event are reused, and we change
+        // the GSI's route to the new MSI data+addr destination.
+        let gsi = if let Some(evt) = &self.out_events[index] {
+            evt.gsi
+        } else {
+            let event = Event::new().map_err(|e| IoapicError::CreateEvent(e))?;
+            let request = VmIrqRequest::AllocateOneMsi {
+                irqfd: MaybeOwnedDescriptor::Borrowed(event.as_raw_descriptor()),
+            };
+            self.irq_socket
+                .send(&request)
+                .map_err(IoapicError::AllocateOneMsiSend)?;
+            match self
+                .irq_socket
+                .recv()
+                .map_err(IoapicError::AllocateOneMsiRecv)?
+            {
+                VmIrqResponse::AllocateOneMsi { gsi, .. } => {
+                    self.out_events[index] = Some(IrqEvent {
+                        gsi,
+                        event,
+                        resample_event: None,
+                    });
+                    gsi
+                }
+                VmIrqResponse::Err(e) => return Err(IoapicError::AllocateOneMsi(e)),
+                _ => unreachable!(),
+            }
+        };
+
+        // Set the MSI route for the GSI.  This controls which apic(s) get the interrupt when the
+        // ioapic's outgoing event is written, and various attributes of how the interrupt is
+        // delivered.
+        let request = VmIrqRequest::AddMsiRoute {
+            gsi,
+            msi_address,
+            msi_data,
+        };
+        self.irq_socket
+            .send(&request)
+            .map_err(IoapicError::AddMsiRouteSend)?;
+        if let VmIrqResponse::Err(e) = self
+            .irq_socket
+            .recv()
+            .map_err(IoapicError::AddMsiRouteRecv)?
+        {
+            return Err(IoapicError::AddMsiRoute(e));
+        }
+        Ok(())
+    }
+
     fn ioapic_read(&mut self) -> u32 {
         match self.state.ioregsel {
             IOAPIC_REG_VERSION => IOAPIC_VERSION_ID,
@@ -330,6 +396,35 @@
     }
 }
 
+#[derive(Debug)]
+enum IoapicError {
+    AddMsiRoute(Error),
+    AddMsiRouteRecv(MsgError),
+    AddMsiRouteSend(MsgError),
+    AllocateOneMsi(Error),
+    AllocateOneMsiRecv(MsgError),
+    AllocateOneMsiSend(MsgError),
+    CreateEvent(Error),
+}
+
+impl Display for IoapicError {
+    #[remain::check]
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::IoapicError::*;
+
+        #[sorted]
+        match self {
+            AddMsiRoute(e) => write!(f, "AddMsiRoute failed: {}", e),
+            AddMsiRouteRecv(e) => write!(f, "failed to receive AddMsiRoute response: {}", e),
+            AddMsiRouteSend(e) => write!(f, "failed to send AddMsiRoute request: {}", e),
+            AllocateOneMsi(e) => write!(f, "AllocateOneMsi failed: {}", e),
+            AllocateOneMsiRecv(e) => write!(f, "failed to receive AllocateOneMsi response: {}", e),
+            AllocateOneMsiSend(e) => write!(f, "failed to send AllocateOneMsi request: {}", e),
+            CreateEvent(e) => write!(f, "failed to create event object: {}", e),
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -340,11 +435,20 @@
 
     fn new() -> Ioapic {
         let (_, device_socket) = msg_socket::pair::<VmIrqResponse, VmIrqRequest>().unwrap();
-        Ioapic::new(Vec::new(), device_socket).unwrap()
+        Ioapic::new(device_socket).unwrap()
+    }
+
+    fn ioapic_bus_address(offset: u8) -> BusAccessInfo {
+        let offset = offset as u64;
+        BusAccessInfo {
+            offset,
+            address: IOAPIC_BASE_ADDRESS + offset,
+            id: 0,
+        }
     }
 
     fn set_up(trigger: TriggerMode) -> (Ioapic, usize) {
-        let irq = hypervisor::NUM_IOAPIC_PINS - 1;
+        let irq = NUM_IOAPIC_PINS - 1;
         let ioapic = set_up_with_irq(irq, trigger);
         (ioapic, irq)
     }
@@ -352,19 +456,24 @@
     fn set_up_with_irq(irq: usize, trigger: TriggerMode) -> Ioapic {
         let mut ioapic = self::new();
         set_up_redirection_table_entry(&mut ioapic, irq, trigger);
+        ioapic.out_events[irq] = Some(IrqEvent {
+            gsi: NUM_IOAPIC_PINS as u32,
+            event: Event::new().unwrap(),
+            resample_event: None,
+        });
         ioapic
     }
 
     fn read_reg(ioapic: &mut Ioapic, selector: u8) -> u32 {
         let mut data = [0; 4];
-        ioapic.write(IOREGSEL_OFF.into(), &[selector]);
-        ioapic.read(IOWIN_OFF.into(), &mut data);
+        ioapic.write(ioapic_bus_address(IOREGSEL_OFF), &[selector]);
+        ioapic.read(ioapic_bus_address(IOWIN_OFF), &mut data);
         u32::from_ne_bytes(data)
     }
 
     fn write_reg(ioapic: &mut Ioapic, selector: u8, value: u32) {
-        ioapic.write(IOREGSEL_OFF.into(), &[selector]);
-        ioapic.write(IOWIN_OFF.into(), &value.to_ne_bytes());
+        ioapic.write(ioapic_bus_address(IOREGSEL_OFF), &[selector]);
+        ioapic.write(ioapic_bus_address(IOWIN_OFF), &value.to_ne_bytes());
     }
 
     fn read_entry(ioapic: &mut Ioapic, irq: usize) -> IoapicRedirectionTableEntry {
@@ -418,8 +527,8 @@
         let mut data_read = [0; 4];
 
         for i in 0..data_write.len() {
-            ioapic.write(IOREGSEL_OFF.into(), &data_write[i..i + 1]);
-            ioapic.read(IOREGSEL_OFF.into(), &mut data_read[i..i + 1]);
+            ioapic.write(ioapic_bus_address(IOREGSEL_OFF), &data_write[i..i + 1]);
+            ioapic.read(ioapic_bus_address(IOREGSEL_OFF), &mut data_read[i..i + 1]);
             assert_eq!(data_write[i], data_read[i]);
         }
     }
@@ -473,7 +582,7 @@
     #[should_panic(expected = "index out of bounds: the len is 24 but the index is 24")]
     fn service_invalid_irq() {
         let mut ioapic = self::new();
-        ioapic.service_irq(hypervisor::NUM_IOAPIC_PINS, false);
+        ioapic.service_irq(NUM_IOAPIC_PINS, false);
     }
 
     // Test a level triggered IRQ interrupt.
diff --git a/devices/src/irqchip/kvm/mod.rs b/devices/src/irqchip/kvm/mod.rs
index 3da840e..21b0a99 100644
--- a/devices/src/irqchip/kvm/mod.rs
+++ b/devices/src/irqchip/kvm/mod.rs
@@ -5,7 +5,11 @@
 use crate::Bus;
 use base::{error, Error, Event, Result};
 use hypervisor::kvm::KvmVcpu;
-use hypervisor::{IrqRoute, MPState, Vcpu};
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+use hypervisor::VmAArch64;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+use hypervisor::VmX86_64;
+use hypervisor::{HypervisorCap, IrqRoute, MPState, Vcpu};
 use kvm_sys::kvm_mp_state;
 use resources::SystemAllocator;
 
@@ -19,7 +23,7 @@
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 pub use aarch64::*;
 
-use crate::IrqChip;
+use crate::{IrqChip, IrqChipCap, IrqEventIndex, VcpuRunState};
 
 /// This IrqChip only works with Kvm so we only implement it for KvmVcpu.
 impl IrqChip for KvmKernelIrqChip {
@@ -38,8 +42,9 @@
         irq: u32,
         irq_event: &Event,
         resample_event: Option<&Event>,
-    ) -> Result<()> {
-        self.vm.register_irqfd(irq, irq_event, resample_event)
+    ) -> Result<Option<IrqEventIndex>> {
+        self.vm.register_irqfd(irq, irq_event, resample_event)?;
+        Ok(None)
     }
 
     /// Unregister an event for a particular GSI.
@@ -65,11 +70,11 @@
         self.vm.set_gsi_routing(&*current_routes)
     }
 
-    /// Return a vector of all registered irq numbers and their associated events.  To be used by
-    /// the main thread to wait for irq events to be triggered.
+    /// Return a vector of all registered irq numbers and their associated events and event
+    /// indices. These should be used by the main thread to wait for irq events.
     /// For the KvmKernelIrqChip, the kernel handles listening to irq events being triggered by
     /// devices, so this function always returns an empty Vec.
-    fn irq_event_tokens(&self) -> Result<Vec<(u32, Event)>> {
+    fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, u32, Event)>> {
         Ok(Vec::new())
     }
 
@@ -85,7 +90,7 @@
     /// Event, then the deassert will only happen after an EOI is broadcast for a vector
     /// associated with the irq line.
     /// This function should never be called on KvmKernelIrqChip.
-    fn service_irq_event(&mut self, _irq: u32) -> Result<()> {
+    fn service_irq_event(&mut self, _event_index: IrqEventIndex) -> Result<()> {
         error!("service_irq_event should never be called for KvmKernelIrqChip");
         Ok(())
     }
@@ -93,26 +98,35 @@
     /// Broadcast an end of interrupt.
     /// This should never be called on a KvmKernelIrqChip because a KVM vcpu should never exit
     /// with the KVM_EXIT_EOI_BROADCAST reason when an in-kernel irqchip exists.
-    fn broadcast_eoi(&mut self, _vector: u8) -> Result<()> {
+    fn broadcast_eoi(&self, _vector: u8) -> Result<()> {
         error!("broadcast_eoi should never be called for KvmKernelIrqChip");
         Ok(())
     }
 
-    /// Return true if there is a pending interrupt for the specified vcpu.
-    /// For the KvmKernelIrqChip this should always return false because KVM is responsible for
-    /// injecting all interrupts.
-    fn interrupt_requested(&self, _vcpu_id: usize) -> bool {
-        false
+    /// Injects any pending interrupts for `vcpu`.
+    /// For KvmKernelIrqChip this is a no-op because KVM is responsible for injecting all
+    /// interrupts.
+    fn inject_interrupts(&self, _vcpu: &dyn Vcpu) -> Result<()> {
+        Ok(())
     }
 
-    /// Check if the specified vcpu has any pending interrupts. Returns None for no interrupts,
-    /// otherwise Some(u32) should be the injected interrupt vector.
-    /// For the KvmKernelIrqChip this should always return None because KVM is responsible for
-    /// injecting all interrupts.
-    fn get_external_interrupt(&mut self, _vcpu_id: usize) -> Result<Option<u32>> {
-        Ok(None)
+    /// Notifies the irq chip that the specified VCPU has executed a halt instruction.
+    /// For KvmKernelIrqChip this is a no-op because KVM handles VCPU blocking.
+    fn halted(&self, _vcpu_id: usize) {}
+
+    /// Blocks until `vcpu` is in a runnable state or until interrupted by
+    /// `IrqChip::kick_halted_vcpus`.  Returns `VcpuRunState::Runnable if vcpu is runnable, or
+    /// `VcpuRunState::Interrupted` if the wait was interrupted.
+    /// For KvmKernelIrqChip this is a no-op and always returns Runnable because KVM handles VCPU
+    /// blocking.
+    fn wait_until_runnable(&self, _vcpu: &dyn Vcpu) -> Result<VcpuRunState> {
+        Ok(VcpuRunState::Runnable)
     }
 
+    /// Makes unrunnable VCPUs return immediately from `wait_until_runnable`.
+    /// For KvmKernelIrqChip this is a no-op because KVM handles VCPU blocking.
+    fn kick_halted_vcpus(&self) {}
+
     /// Get the current MP state of the specified VCPU.
     fn get_mp_state(&self, vcpu_id: usize) -> Result<MPState> {
         match self.vcpus.lock().get(vcpu_id) {
@@ -152,6 +166,16 @@
     fn process_delayed_irq_events(&mut self) -> Result<()> {
         Ok(())
     }
+
+    fn check_capability(&self, c: IrqChipCap) -> bool {
+        match c {
+            IrqChipCap::TscDeadlineTimer => self
+                .vm
+                .get_hypervisor()
+                .check_capability(&HypervisorCap::TscDeadlineTimer),
+            IrqChipCap::X2Apic => true,
+        }
+    }
 }
 
 #[cfg(test)]
diff --git a/devices/src/irqchip/kvm/x86_64.rs b/devices/src/irqchip/kvm/x86_64.rs
index c199302..1964f39 100644
--- a/devices/src/irqchip/kvm/x86_64.rs
+++ b/devices/src/irqchip/kvm/x86_64.rs
@@ -12,8 +12,8 @@
 use base::FakeClock as Clock;
 use hypervisor::kvm::{KvmVcpu, KvmVm};
 use hypervisor::{
-    IoapicState, IrqRoute, IrqSource, IrqSourceChip, LapicState, MPState, PicSelect, PicState,
-    PitState, Vcpu, Vm, NUM_IOAPIC_PINS,
+    HypervisorCap, IoapicState, IrqRoute, IrqSource, IrqSourceChip, LapicState, MPState, PicSelect,
+    PicState, PitState, Vcpu, VcpuX86_64, Vm, VmX86_64, NUM_IOAPIC_PINS,
 };
 use kvm_sys::*;
 use resources::SystemAllocator;
@@ -21,8 +21,11 @@
 use base::{error, Error, Event, Result};
 use vm_control::VmIrqRequestSocket;
 
-use crate::irqchip::{Ioapic, Pic, IOAPIC_BASE_ADDRESS, IOAPIC_MEM_LENGTH_BYTES};
-use crate::{Bus, IrqChip, IrqChipX86_64, Pit, PitError};
+use crate::irqchip::{
+    Ioapic, IrqEvent, IrqEventIndex, Pic, VcpuRunState, IOAPIC_BASE_ADDRESS,
+    IOAPIC_MEM_LENGTH_BYTES,
+};
+use crate::{Bus, IrqChip, IrqChipCap, IrqChipX86_64, Pit, PitError};
 
 /// PIT channel 0 timer is connected to IRQ 0
 const PIT_CHANNEL0_IRQ: u32 = 0;
@@ -148,14 +151,8 @@
     /// locked the ioapic and the ioapic sends a AddMsiRoute signal to the main thread (which
     /// itself may be busy trying to call service_irq).
     delayed_ioapic_irq_events: Arc<Mutex<Vec<usize>>>,
-    /// Vec of Events that the ioapic will use to trigger interrupts in KVM. This is not
-    /// wrapped in an Arc<Mutex<>> because the Events themselves can be cloned and they will
-    /// not change after the IrqChip is created.
-    irqfds: Vec<Event>,
     /// Array of Events that devices will use to assert ioapic pins.
-    irq_events: Arc<Mutex<[Option<Event>; NUM_IOAPIC_PINS]>>,
-    /// Array of Events that should be asserted when the ioapic receives an EOI.
-    resample_events: Arc<Mutex<[Option<Event>; NUM_IOAPIC_PINS]>>,
+    irq_events: Arc<Mutex<Vec<Option<IrqEvent>>>>,
 }
 
 fn kvm_dummy_msi_routes() -> Vec<IrqRoute> {
@@ -174,6 +171,7 @@
     }
     routes
 }
+
 impl KvmSplitIrqChip {
     /// Construct a new KvmSplitIrqChip.
     pub fn new(vm: KvmVm, num_vcpus: usize, irq_socket: VmIrqRequestSocket) -> Result<Self> {
@@ -185,22 +183,13 @@
                 |e| match e {
                     PitError::CloneEvent(err) => err,
                     PitError::CreateEvent(err) => err,
-                    PitError::CreatePollContext(err) => err,
-                    PitError::PollError(err) => err,
+                    PitError::CreateWaitContext(err) => err,
+                    PitError::WaitError(err) => err,
                     PitError::TimerCreateError(err) => err,
                     PitError::SpawnThread(_) => Error::new(libc::EIO),
                 },
             )?,
         ));
-        let mut irqfds: Vec<Event> = Vec::with_capacity(NUM_IOAPIC_PINS);
-        let mut irqfds_for_ioapic: Vec<Event> = Vec::with_capacity(NUM_IOAPIC_PINS);
-
-        for i in 0..NUM_IOAPIC_PINS {
-            let evt = Event::new()?;
-            vm.register_irqfd(i as u32, &evt, None)?;
-            irqfds_for_ioapic.push(evt.try_clone()?);
-            irqfds.push(evt);
-        }
 
         let mut chip = KvmSplitIrqChip {
             vm,
@@ -208,11 +197,9 @@
             routes: Arc::new(Mutex::new(Vec::new())),
             pit,
             pic: Arc::new(Mutex::new(Pic::new())),
-            ioapic: Arc::new(Mutex::new(Ioapic::new(irqfds_for_ioapic, irq_socket)?)),
+            ioapic: Arc::new(Mutex::new(Ioapic::new(irq_socket)?)),
             delayed_ioapic_irq_events: Arc::new(Mutex::new(Vec::new())),
-            irqfds,
             irq_events: Arc::new(Mutex::new(Default::default())),
-            resample_events: Arc::new(Mutex::new(Default::default())),
         };
 
         // Setup standard x86 irq routes
@@ -251,6 +238,31 @@
         }
         chips
     }
+
+    /// Return true if there is a pending interrupt for the specified vcpu. For KvmSplitIrqChip
+    /// this calls interrupt_requested on the pic.
+    fn interrupt_requested(&self, vcpu_id: usize) -> bool {
+        // Pic interrupts for the split irqchip only go to vcpu 0
+        if vcpu_id != 0 {
+            return false;
+        }
+        self.pic.lock().interrupt_requested()
+    }
+
+    /// Check if the specified vcpu has any pending interrupts. Returns None for no interrupts,
+    /// otherwise Some(u32) should be the injected interrupt vector. For KvmSplitIrqChip
+    /// this calls get_external_interrupt on the pic.
+    fn get_external_interrupt(&self, vcpu_id: usize) -> Result<Option<u32>> {
+        // Pic interrupts for the split irqchip only go to vcpu 0
+        if vcpu_id != 0 {
+            return Ok(None);
+        }
+        if let Some(vector) = self.pic.lock().get_external_interrupt() {
+            Ok(Some(vector as u32))
+        } else {
+            Ok(None)
+        }
+    }
 }
 
 /// Convenience function for determining whether or not two irq routes conflict.
@@ -300,33 +312,41 @@
         irq: u32,
         irq_event: &Event,
         resample_event: Option<&Event>,
-    ) -> Result<()> {
+    ) -> Result<Option<IrqEventIndex>> {
         if irq < NUM_IOAPIC_PINS as u32 {
-            // safe to index here because irq_events is NUM_IOAPIC_PINS long
-            self.irq_events.lock()[irq as usize] = Some(irq_event.try_clone()?);
-            if let Some(evt) = resample_event {
-                self.resample_events.lock()[irq as usize] = Some(evt.try_clone()?);
+            let mut evt = IrqEvent {
+                gsi: irq,
+                event: irq_event.try_clone()?,
+                resample_event: None,
+            };
+
+            if let Some(resample_event) = resample_event {
+                evt.resample_event = Some(resample_event.try_clone()?);
             }
 
-            Ok(())
+            let mut irq_events = self.irq_events.lock();
+            let index = irq_events.len();
+            irq_events.push(Some(evt));
+            Ok(Some(index))
         } else {
-            self.vm.register_irqfd(irq, irq_event, resample_event)
+            self.vm.register_irqfd(irq, irq_event, resample_event)?;
+            Ok(None)
         }
     }
 
     /// Unregister an event for a particular GSI.
     fn unregister_irq_event(&mut self, irq: u32, irq_event: &Event) -> Result<()> {
         if irq < NUM_IOAPIC_PINS as u32 {
-            match &self.irq_events.lock()[irq as usize] {
-                // We only do something if irq_event is the same as our existing event
-                Some(evt) if evt == irq_event => {
-                    // safe to index here because irq_events is NUM_IOAPIC_PINS long
-                    self.irq_events.lock()[irq as usize] = None;
-                    self.resample_events.lock()[irq as usize] = None;
-                    Ok(())
+            let mut irq_events = self.irq_events.lock();
+            for (index, evt) in irq_events.iter().enumerate() {
+                if let Some(evt) = evt {
+                    if evt.gsi == irq && irq_event.eq(&evt.event) {
+                        irq_events[index] = None;
+                        break;
+                    }
                 }
-                _ => Ok(()),
             }
+            Ok(())
         } else {
             self.vm.unregister_irqfd(irq, irq_event)
         }
@@ -341,10 +361,7 @@
 
         // We only call set_gsi_routing with the msi routes
         let mut msi_routes = routes.clone();
-        msi_routes.retain(|r| match r.source {
-            IrqSource::Msi { .. } => true,
-            _ => false,
-        });
+        msi_routes.retain(|r| matches!(r.source, IrqSource::Msi { .. }));
 
         self.vm.set_gsi_routing(&*msi_routes)
     }
@@ -356,21 +373,18 @@
 
         // We only call set_gsi_routing with the msi routes
         let mut msi_routes = routes.to_vec().clone();
-        msi_routes.retain(|r| match r.source {
-            IrqSource::Msi { .. } => true,
-            _ => false,
-        });
+        msi_routes.retain(|r| matches!(r.source, IrqSource::Msi { .. }));
 
         self.vm.set_gsi_routing(&*msi_routes)
     }
 
-    /// Return a vector of all registered irq numbers and their associated events. To be used by
-    /// the main thread to wait for irq events to be triggered.
-    fn irq_event_tokens(&self) -> Result<Vec<(u32, Event)>> {
-        let mut tokens: Vec<(u32, Event)> = Vec::new();
-        for i in 0..NUM_IOAPIC_PINS {
-            if let Some(evt) = &self.irq_events.lock()[i] {
-                tokens.push((i as u32, evt.try_clone()?));
+    /// Return a vector of all registered irq numbers and their associated events and event
+    /// indices. These should be used by the main thread to wait for irq events.
+    fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, u32, Event)>> {
+        let mut tokens: Vec<(IrqEventIndex, u32, Event)> = Vec::new();
+        for (index, evt) in self.irq_events.lock().iter().enumerate() {
+            if let Some(evt) = evt {
+                tokens.push((index, evt.gsi, evt.event.try_clone()?));
             }
         }
         Ok(tokens)
@@ -402,36 +416,36 @@
     /// attempts to call service_irq on those chips. If the ioapic is unable to be immediately
     /// locked, we add the irq to the delayed_ioapic_irq_events Vec (though we still read
     /// from the Event that triggered the irq event).
-    fn service_irq_event(&mut self, irq: u32) -> Result<()> {
-        if let Some(evt) = &self.irq_events.lock()[irq as usize] {
-            evt.read()?;
-        }
-        let chips = self.routes_to_chips(irq);
+    fn service_irq_event(&mut self, event_index: IrqEventIndex) -> Result<()> {
+        if let Some(evt) = &self.irq_events.lock()[event_index] {
+            evt.event.read()?;
+            let chips = self.routes_to_chips(evt.gsi);
 
-        for (chip, pin) in chips {
-            match chip {
-                IrqSourceChip::PicPrimary | IrqSourceChip::PicSecondary => {
-                    let mut pic = self.pic.lock();
-                    if self.resample_events.lock()[pin as usize].is_some() {
-                        pic.service_irq(pin as u8, true);
-                    } else {
-                        pic.service_irq(pin as u8, true);
-                        pic.service_irq(pin as u8, false);
-                    }
-                }
-                IrqSourceChip::Ioapic => {
-                    if let Ok(mut ioapic) = self.ioapic.try_lock() {
-                        if self.resample_events.lock()[pin as usize].is_some() {
-                            ioapic.service_irq(pin as usize, true);
+            for (chip, pin) in chips {
+                match chip {
+                    IrqSourceChip::PicPrimary | IrqSourceChip::PicSecondary => {
+                        let mut pic = self.pic.lock();
+                        if evt.resample_event.is_some() {
+                            pic.service_irq(pin as u8, true);
                         } else {
-                            ioapic.service_irq(pin as usize, true);
-                            ioapic.service_irq(pin as usize, false);
+                            pic.service_irq(pin as u8, true);
+                            pic.service_irq(pin as u8, false);
                         }
-                    } else {
-                        self.delayed_ioapic_irq_events.lock().push(pin as usize);
                     }
+                    IrqSourceChip::Ioapic => {
+                        if let Ok(mut ioapic) = self.ioapic.try_lock() {
+                            if evt.resample_event.is_some() {
+                                ioapic.service_irq(pin as usize, true);
+                            } else {
+                                ioapic.service_irq(pin as usize, true);
+                                ioapic.service_irq(pin as usize, false);
+                            }
+                        } else {
+                            self.delayed_ioapic_irq_events.lock().push(event_index);
+                        }
+                    }
+                    _ => {}
                 }
-                _ => {}
             }
         }
 
@@ -439,36 +453,52 @@
     }
 
     /// Broadcast an end of interrupt. For KvmSplitIrqChip this sends the EOI to the ioapic
-    fn broadcast_eoi(&mut self, vector: u8) -> Result<()> {
+    fn broadcast_eoi(&self, vector: u8) -> Result<()> {
         self.ioapic.lock().end_of_interrupt(vector);
         Ok(())
     }
 
-    /// Return true if there is a pending interrupt for the specified vcpu. For KvmSplitIrqChip
-    /// this calls interrupt_requested on the pic.
-    fn interrupt_requested(&self, vcpu_id: usize) -> bool {
-        // Pic interrupts for the split irqchip only go to vcpu 0
-        if vcpu_id != 0 {
-            return false;
+    /// Injects any pending interrupts for `vcpu`.
+    /// For KvmSplitIrqChip this injects any PIC interrupts on vcpu_id 0.
+    fn inject_interrupts(&self, vcpu: &dyn Vcpu) -> Result<()> {
+        let vcpu: &KvmVcpu = vcpu
+            .downcast_ref()
+            .expect("KvmSplitIrqChip::add_vcpu called with non-KvmVcpu");
+
+        let vcpu_id = vcpu.id();
+        if !self.interrupt_requested(vcpu_id) || !vcpu.ready_for_interrupt() {
+            return Ok(());
         }
-        self.pic.lock().interrupt_requested()
+
+        if let Some(vector) = self.get_external_interrupt(vcpu_id)? {
+            vcpu.interrupt(vector as u32)?;
+        }
+
+        // The second interrupt request should be handled immediately, so ask vCPU to exit as soon as
+        // possible.
+        if self.interrupt_requested(vcpu_id) {
+            vcpu.set_interrupt_window_requested(true);
+        }
+        Ok(())
     }
 
-    /// Check if the specified vcpu has any pending interrupts. Returns None for no interrupts,
-    /// otherwise Some(u32) should be the injected interrupt vector. For KvmSplitIrqChip
-    /// this calls get_external_interrupt on the pic.
-    fn get_external_interrupt(&mut self, vcpu_id: usize) -> Result<Option<u32>> {
-        // Pic interrupts for the split irqchip only go to vcpu 0
-        if vcpu_id != 0 {
-            return Ok(None);
-        }
-        if let Some(vector) = self.pic.lock().get_external_interrupt() {
-            Ok(Some(vector as u32))
-        } else {
-            Ok(None)
-        }
+    /// Notifies the irq chip that the specified VCPU has executed a halt instruction.
+    /// For KvmSplitIrqChip this is a no-op because KVM handles VCPU blocking.
+    fn halted(&self, _vcpu_id: usize) {}
+
+    /// Blocks until `vcpu` is in a runnable state or until interrupted by
+    /// `IrqChip::kick_halted_vcpus`.  Returns `VcpuRunState::Runnable if vcpu is runnable, or
+    /// `VcpuRunState::Interrupted` if the wait was interrupted.
+    /// For KvmSplitIrqChip this is a no-op and always returns Runnable because KVM handles VCPU
+    /// blocking.
+    fn wait_until_runnable(&self, _vcpu: &dyn Vcpu) -> Result<VcpuRunState> {
+        Ok(VcpuRunState::Runnable)
     }
 
+    /// Makes unrunnable VCPUs return immediately from `wait_until_runnable`.
+    /// For KvmSplitIrqChip this is a no-op because KVM handles VCPU blocking.
+    fn kick_halted_vcpus(&self) {}
+
     /// Get the current MP state of the specified VCPU.
     fn get_mp_state(&self, vcpu_id: usize) -> Result<MPState> {
         match self.vcpus.lock().get(vcpu_id) {
@@ -487,10 +517,6 @@
 
     /// Attempt to clone this IrqChip instance.
     fn try_clone(&self) -> Result<Self> {
-        let mut new_irqfds: Vec<Event> = Vec::with_capacity(NUM_IOAPIC_PINS);
-        for i in 0..NUM_IOAPIC_PINS {
-            new_irqfds.push(self.irqfds[i].try_clone()?);
-        }
         Ok(KvmSplitIrqChip {
             vm: self.vm.try_clone()?,
             vcpus: self.vcpus.clone(),
@@ -499,9 +525,7 @@
             pic: self.pic.clone(),
             ioapic: self.ioapic.clone(),
             delayed_ioapic_irq_events: self.delayed_ioapic_irq_events.clone(),
-            irqfds: new_irqfds,
             irq_events: self.irq_events.clone(),
-            resample_events: self.resample_events.clone(),
         })
     }
 
@@ -514,13 +538,13 @@
         mmio_bus: &mut Bus,
     ) -> Result<()> {
         // Insert pit into io_bus
-        io_bus.insert(self.pit.clone(), 0x040, 0x8, true).unwrap();
-        io_bus.insert(self.pit.clone(), 0x061, 0x1, true).unwrap();
+        io_bus.insert(self.pit.clone(), 0x040, 0x8).unwrap();
+        io_bus.insert(self.pit.clone(), 0x061, 0x1).unwrap();
 
         // Insert pic into io_bus
-        io_bus.insert(self.pic.clone(), 0x20, 0x2, true).unwrap();
-        io_bus.insert(self.pic.clone(), 0xa0, 0x2, true).unwrap();
-        io_bus.insert(self.pic.clone(), 0x4d0, 0x2, true).unwrap();
+        io_bus.insert(self.pic.clone(), 0x20, 0x2).unwrap();
+        io_bus.insert(self.pic.clone(), 0xa0, 0x2).unwrap();
+        io_bus.insert(self.pic.clone(), 0x4d0, 0x2).unwrap();
 
         // Insert ioapic into mmio_bus
         mmio_bus
@@ -528,26 +552,26 @@
                 self.ioapic.clone(),
                 IOAPIC_BASE_ADDRESS,
                 IOAPIC_MEM_LENGTH_BYTES,
-                false,
             )
             .unwrap();
 
         // At this point, all of our devices have been created and they have registered their
         // irq events, so we can clone our resample events
-        let mut ioapic_resample_events: Vec<Option<Event>> = Vec::with_capacity(NUM_IOAPIC_PINS);
-        let mut pic_resample_events: Vec<Option<Event>> = Vec::with_capacity(NUM_IOAPIC_PINS);
+        let mut ioapic_resample_events: Vec<Vec<Event>> =
+            (0..NUM_IOAPIC_PINS).map(|_| Vec::new()).collect();
+        let mut pic_resample_events: Vec<Vec<Event>> =
+            (0..NUM_IOAPIC_PINS).map(|_| Vec::new()).collect();
 
-        for i in 0..NUM_IOAPIC_PINS {
-            match &self.resample_events.lock()[i] {
-                Some(e) => {
-                    ioapic_resample_events.push(Some(e.try_clone()?));
-                    pic_resample_events.push(Some(e.try_clone()?));
+        for evt in self.irq_events.lock().iter() {
+            if let Some(evt) = evt {
+                if (evt.gsi as usize) >= NUM_IOAPIC_PINS {
+                    continue;
                 }
-                None => {
-                    ioapic_resample_events.push(None);
-                    pic_resample_events.push(None);
+                if let Some(resample_evt) = &evt.resample_event {
+                    ioapic_resample_events[evt.gsi as usize].push(resample_evt.try_clone()?);
+                    pic_resample_events[evt.gsi as usize].push(resample_evt.try_clone()?);
                 }
-            };
+            }
         }
 
         // Register resample events with the ioapic
@@ -576,22 +600,39 @@
     /// processes each delayed event in the vec each time it's called. If the ioapic is still
     /// locked, we keep the queued irqs for the next time this function is called.
     fn process_delayed_irq_events(&mut self) -> Result<()> {
-        self.delayed_ioapic_irq_events.lock().retain(|&irq| {
-            if let Ok(mut ioapic) = self.ioapic.try_lock() {
-                if self.resample_events.lock()[irq].is_some() {
-                    ioapic.service_irq(irq, true);
+        self.delayed_ioapic_irq_events
+            .lock()
+            .retain(|&event_index| {
+                if let Some(evt) = &self.irq_events.lock()[event_index] {
+                    if let Ok(mut ioapic) = self.ioapic.try_lock() {
+                        if evt.resample_event.is_some() {
+                            ioapic.service_irq(evt.gsi as usize, true);
+                        } else {
+                            ioapic.service_irq(evt.gsi as usize, true);
+                            ioapic.service_irq(evt.gsi as usize, false);
+                        }
+
+                        false
+                    } else {
+                        true
+                    }
                 } else {
-                    ioapic.service_irq(irq, true);
-                    ioapic.service_irq(irq, false);
+                    true
                 }
-                false
-            } else {
-                true
-            }
-        });
+            });
 
         Ok(())
     }
+
+    fn check_capability(&self, c: IrqChipCap) -> bool {
+        match c {
+            IrqChipCap::TscDeadlineTimer => self
+                .vm
+                .get_hypervisor()
+                .check_capability(&HypervisorCap::TscDeadlineTimer),
+            IrqChipCap::X2Apic => true,
+        }
+    }
 }
 
 impl IrqChipX86_64 for KvmSplitIrqChip {
@@ -814,7 +855,7 @@
 
         // there should be one token on a fresh split irqchip, for the pit
         assert_eq!(tokens.len(), 1);
-        assert_eq!(tokens[0].0, 0);
+        assert_eq!(tokens[0].1, 0);
 
         // register another irq event
         let evt = Event::new().expect("failed to create event");
@@ -827,9 +868,9 @@
 
         // now there should be two tokens
         assert_eq!(tokens.len(), 2);
-        assert_eq!(tokens[0].0, 0);
-        assert_eq!(tokens[1].0, 6);
-        assert_eq!(tokens[1].1, evt);
+        assert_eq!(tokens[0].1, 0);
+        assert_eq!(tokens[1].1, 6);
+        assert_eq!(tokens[1].2, evt);
     }
 
     #[test]
@@ -849,8 +890,10 @@
         let evt = Event::new().expect("failed to create event");
         let mut resample_evt = Event::new().expect("failed to create event");
 
-        chip.register_irq_event(1, &evt, Some(&resample_evt))
-            .expect("failed to register_irq_event");
+        let evt_index = chip
+            .register_irq_event(1, &evt, Some(&resample_evt))
+            .expect("failed to register_irq_event")
+            .expect("register_irq_event should not return None");
 
         // Once we finalize devices, the pic/pit/ioapic should be attached to io and mmio busses
         chip.finalize_devices(&mut resources, &mut io_bus, &mut mmio_bus)
@@ -888,7 +931,8 @@
 
         // if we assert irq line one, and then get the resulting interrupt, an auto-eoi should
         // occur and cause the resample_event to be written to
-        chip.service_irq_event(1).expect("failed to service irq");
+        chip.service_irq_event(evt_index)
+            .expect("failed to service irq");
 
         assert!(chip.interrupt_requested(0));
         assert_eq!(
diff --git a/devices/src/irqchip/mod.rs b/devices/src/irqchip/mod.rs
index 636e26a..d431360 100644
--- a/devices/src/irqchip/mod.rs
+++ b/devices/src/irqchip/mod.rs
@@ -40,6 +40,14 @@
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
 pub use ioapic::*;
 
+pub type IrqEventIndex = usize;
+
+struct IrqEvent {
+    event: Event,
+    gsi: u32,
+    resample_event: Option<Event>,
+}
+
 /// Trait that abstracts interactions with interrupt controllers.
 ///
 /// Each VM will have one IrqChip instance which is responsible for routing IRQ lines and
@@ -58,7 +66,7 @@
         irq: u32,
         irq_event: &Event,
         resample_event: Option<&Event>,
-    ) -> Result<()>;
+    ) -> Result<Option<IrqEventIndex>>;
 
     /// Unregister an event for a particular GSI.
     fn unregister_irq_event(&mut self, irq: u32, irq_event: &Event) -> Result<()>;
@@ -69,9 +77,9 @@
     /// Replace all irq routes with the supplied routes
     fn set_irq_routes(&mut self, routes: &[IrqRoute]) -> Result<()>;
 
-    /// Return a vector of all registered irq numbers and their associated events.  To be used by
-    /// the main thread to wait for irq events to be triggered.
-    fn irq_event_tokens(&self) -> Result<Vec<(u32, Event)>>;
+    /// Return a vector of all registered irq numbers and their associated events and event
+    /// indices. These should be used by the main thread to wait for irq events.
+    fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, u32, Event)>>;
 
     /// Either assert or deassert an IRQ line.  Sends to either an interrupt controller, or does
     /// a send_msi if the irq is associated with an MSI.
@@ -81,17 +89,27 @@
     /// that triggered the irq event will be read from. If the irq is associated with a resample
     /// Event, then the deassert will only happen after an EOI is broadcast for a vector
     /// associated with the irq line.
-    fn service_irq_event(&mut self, irq: u32) -> Result<()>;
+    fn service_irq_event(&mut self, event_index: IrqEventIndex) -> Result<()>;
 
     /// Broadcast an end of interrupt.
-    fn broadcast_eoi(&mut self, vector: u8) -> Result<()>;
+    fn broadcast_eoi(&self, vector: u8) -> Result<()>;
 
-    /// Return true if there is a pending interrupt for the specified vcpu.
-    fn interrupt_requested(&self, vcpu_id: usize) -> bool;
+    /// Injects any pending interrupts for `vcpu`.
+    fn inject_interrupts(&self, vcpu: &dyn Vcpu) -> Result<()>;
 
-    /// Check if the specified vcpu has any pending interrupts. Returns None for no interrupts,
-    /// otherwise Some(u32) should be the injected interrupt vector.
-    fn get_external_interrupt(&mut self, vcpu_id: usize) -> Result<Option<u32>>;
+    /// Notifies the irq chip that the specified VCPU has executed a halt instruction.
+    fn halted(&self, vcpu_id: usize);
+
+    /// Blocks until `vcpu` is in a runnable state or until interrupted by
+    /// `IrqChip::kick_halted_vcpus`.  Returns `VcpuRunState::Runnable if vcpu is runnable, or
+    /// `VcpuRunState::Interrupted` if the wait was interrupted.
+    fn wait_until_runnable(&self, vcpu: &dyn Vcpu) -> Result<VcpuRunState>;
+
+    /// Makes unrunnable VCPUs return immediately from `wait_until_runnable`.
+    /// For UserspaceIrqChip, every vcpu gets kicked so its current or next call to
+    /// `wait_until_runnable` will immediately return false.  After that one kick, subsequent
+    /// `wait_until_runnable` calls go back to waiting for runnability normally.
+    fn kick_halted_vcpus(&self);
 
     /// Get the current MP state of the specified VCPU.
     fn get_mp_state(&self, vcpu_id: usize) -> Result<MPState>;
@@ -115,4 +133,23 @@
 
     /// Process any irqs events that were delayed because of any locking issues.
     fn process_delayed_irq_events(&mut self) -> Result<()>;
+
+    /// Checks if a particular `IrqChipCap` is available.
+    fn check_capability(&self, c: IrqChipCap) -> bool;
+}
+
+/// A capability the `IrqChip` can possibly expose.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum IrqChipCap {
+    /// APIC TSC-deadline timer mode.
+    TscDeadlineTimer,
+    /// Extended xAPIC (x2APIC) standard.
+    X2Apic,
+}
+
+/// A capability the `IrqChip` can possibly expose.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum VcpuRunState {
+    Runnable,
+    Interrupted,
 }
diff --git a/devices/src/irqchip/pic.rs b/devices/src/irqchip/pic.rs
index 679f58c..4cca6a2 100644
--- a/devices/src/irqchip/pic.rs
+++ b/devices/src/irqchip/pic.rs
@@ -12,6 +12,7 @@
 // For the purposes of both using more descriptive terms and avoiding terms with lots of charged
 // emotional context, this file refers to them instead as "primary" and "secondary" PICs.
 
+use crate::bus::BusAccessInfo;
 use crate::BusDevice;
 use base::{debug, warn, Event};
 use hypervisor::{PicInitState, PicSelect, PicState};
@@ -19,8 +20,9 @@
 pub struct Pic {
     // Indicates a pending INTR signal to LINT0 of vCPU, checked by vCPU thread.
     interrupt_request: bool,
-    // Events that need to be triggered when an ISR is cleared
-    resample_events: Vec<Option<Event>>,
+    // Events that need to be triggered when an ISR is cleared. The outer Vec is indexed by GSI,
+    // and the inner Vec is an unordered list of registered resample events for the GSI.
+    resample_events: Vec<Vec<Event>>,
     // Index 0 (aka PicSelect::Primary) is the primary pic, the rest are secondary.
     pics: [PicState; 2],
 }
@@ -86,28 +88,28 @@
         "userspace PIC".to_string()
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
         if data.len() != 1 {
             warn!("PIC: Bad write size: {}", data.len());
             return;
         }
-        match offset {
+        match info.address {
             PIC_PRIMARY_COMMAND => self.pic_write_command(PicSelect::Primary, data[0]),
             PIC_PRIMARY_DATA => self.pic_write_data(PicSelect::Primary, data[0]),
             PIC_PRIMARY_ELCR => self.pic_write_elcr(PicSelect::Primary, data[0]),
             PIC_SECONDARY_COMMAND => self.pic_write_command(PicSelect::Secondary, data[0]),
             PIC_SECONDARY_DATA => self.pic_write_data(PicSelect::Secondary, data[0]),
             PIC_SECONDARY_ELCR => self.pic_write_elcr(PicSelect::Secondary, data[0]),
-            _ => warn!("PIC: Invalid write to offset {}", offset),
+            _ => warn!("PIC: Invalid write to {}", info),
         }
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
         if data.len() != 1 {
             warn!("PIC: Bad read size: {}", data.len());
             return;
         }
-        data[0] = match offset {
+        data[0] = match info.address {
             PIC_PRIMARY_COMMAND => self.pic_read_command(PicSelect::Primary),
             PIC_PRIMARY_DATA => self.pic_read_data(PicSelect::Primary),
             PIC_PRIMARY_ELCR => self.pic_read_elcr(PicSelect::Primary),
@@ -115,7 +117,7 @@
             PIC_SECONDARY_DATA => self.pic_read_data(PicSelect::Secondary),
             PIC_SECONDARY_ELCR => self.pic_read_elcr(PicSelect::Secondary),
             _ => {
-                warn!("PIC: Invalid read from offset {}", offset);
+                warn!("PIC: Invalid read from {}", info);
                 return;
             }
         };
@@ -154,7 +156,7 @@
         self.pics[select as usize] = *state;
     }
 
-    pub fn register_resample_events(&mut self, resample_events: Vec<Option<Event>>) {
+    pub fn register_resample_events(&mut self, resample_events: Vec<Vec<Event>>) {
         self.resample_events = resample_events;
     }
 
@@ -370,8 +372,10 @@
         } else {
             irq + 8
         };
-        if let Some(Some(resample_evt)) = self.resample_events.get(irq as usize) {
-            resample_evt.write(1).unwrap();
+        if let Some(resample_events) = self.resample_events.get(irq as usize) {
+            for resample_evt in resample_events {
+                resample_evt.write(1).unwrap();
+            }
         }
     }
 
@@ -540,11 +544,29 @@
         pic: Pic,
     }
 
+    fn pic_bus_address(address: u64) -> BusAccessInfo {
+        // The PIC is added to the io_bus in three locations, so the offset depends on which
+        // address range the address is in. The PIC implementation currently does not use the
+        // offset, but we're setting it accurately here in case it does in the future.
+        let offset = match address {
+            x if x >= PIC_PRIMARY && x < PIC_PRIMARY + 0x2 => address - PIC_PRIMARY,
+            x if x >= PIC_SECONDARY && x < PIC_SECONDARY + 0x2 => address - PIC_SECONDARY,
+            x if x >= PIC_PRIMARY_ELCR && x < PIC_PRIMARY_ELCR + 0x2 => address - PIC_PRIMARY_ELCR,
+            _ => panic!("invalid PIC address: {:#x}", address),
+        };
+
+        BusAccessInfo {
+            offset,
+            address,
+            id: 0,
+        }
+    }
+
     fn set_up() -> TestData {
         let mut pic = Pic::new();
         // Use edge-triggered mode.
-        pic.write(PIC_PRIMARY_ELCR, &[0]);
-        pic.write(PIC_SECONDARY_ELCR, &[0]);
+        pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0]);
+        pic.write(pic_bus_address(PIC_SECONDARY_ELCR), &[0]);
         TestData { pic }
     }
 
@@ -559,10 +581,10 @@
             PicSelect::Secondary => PIC_SECONDARY_DATA,
         };
 
-        pic.write(command_offset, &[icw1]);
-        pic.write(data_offset, &[icw2]);
-        pic.write(data_offset, &[icw3]);
-        pic.write(data_offset, &[icw4]);
+        pic.write(pic_bus_address(command_offset), &[icw1]);
+        pic.write(pic_bus_address(data_offset), &[icw2]);
+        pic.write(pic_bus_address(data_offset), &[icw3]);
+        pic.write(pic_bus_address(data_offset), &[icw4]);
     }
 
     /// Convenience function for primary ICW init.
@@ -607,12 +629,16 @@
         let data_write = [0x5f];
         let mut data_read = [0];
 
-        data.pic.write(PIC_PRIMARY_ELCR, &data_write);
-        data.pic.read(PIC_PRIMARY_ELCR, &mut data_read);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_ELCR), &data_write);
+        data.pic
+            .read(pic_bus_address(PIC_PRIMARY_ELCR), &mut data_read);
         assert_eq!(data_read, data_write);
 
-        data.pic.write(PIC_SECONDARY_ELCR, &data_write);
-        data.pic.read(PIC_SECONDARY_ELCR, &mut data_read);
+        data.pic
+            .write(pic_bus_address(PIC_SECONDARY_ELCR), &data_write);
+        data.pic
+            .read(pic_bus_address(PIC_SECONDARY_ELCR), &mut data_read);
         assert_eq!(data_read, data_write);
     }
 
@@ -623,13 +649,16 @@
 
         // ICW1
         let mut data_write = [0x10];
-        data.pic.write(PIC_PRIMARY_COMMAND, &data_write);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &data_write);
 
         data_write[0] = 0x08;
-        data.pic.write(PIC_PRIMARY_DATA, &data_write);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
 
         data_write[0] = 0xff;
-        data.pic.write(PIC_PRIMARY_DATA, &data_write);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
 
         assert_eq!(
             data.pic.pics[PicSelect::Primary as usize].init_state,
@@ -675,19 +704,23 @@
         icw_init_secondary(&mut data.pic);
 
         // OCW1: Write to IMR.
-        data.pic.write(PIC_SECONDARY_DATA, &[0x5f]);
+        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x5f]);
 
         // OCW2: Set rotate on auto EOI.
-        data.pic.write(PIC_SECONDARY_COMMAND, &[0x80]);
+        data.pic
+            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
 
         // OCW2: Set priority.
-        data.pic.write(PIC_SECONDARY_COMMAND, &[0xc0]);
+        data.pic
+            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xc0]);
 
         // OCW3: Change flags.
-        data.pic.write(PIC_SECONDARY_COMMAND, &[0x6b]);
+        data.pic
+            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x6b]);
 
         let mut data_read = [0];
-        data.pic.read(PIC_SECONDARY_DATA, &mut data_read);
+        data.pic
+            .read(pic_bus_address(PIC_SECONDARY_DATA), &mut data_read);
         assert_eq!(data_read, [0x5f]);
 
         let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
@@ -713,13 +746,15 @@
         icw_init_secondary(&mut data.pic);
 
         // OCW2: Set rotate on auto EOI.
-        data.pic.write(PIC_SECONDARY_COMMAND, &[0x80]);
+        data.pic
+            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
 
         let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
         assert!(secondary_pic.rotate_on_auto_eoi);
 
         // OCW2: Clear rotate on auto EOI.
-        data.pic.write(PIC_SECONDARY_COMMAND, &[0x00]);
+        data.pic
+            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x00]);
 
         let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
         assert!(!secondary_pic.rotate_on_auto_eoi);
@@ -807,8 +842,10 @@
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
 
         // OCW2: Non-specific EOI, one for primary and one for secondary.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
-        data.pic.write(PIC_SECONDARY_COMMAND, &[0x20]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
+        data.pic
+            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x20]);
 
         // Now that the first IRQ is no longer in service, the second IRQ can be ack'd.
         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
@@ -827,7 +864,7 @@
         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
 
         // OCW2: Mask IRQ line 6 on secondary (IRQ 14).
-        data.pic.write(PIC_SECONDARY_DATA, &[0x40]);
+        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x40]);
 
         data.pic.service_irq(/*irq=*/ 14, /*level=*/ true);
         assert_eq!(data.pic.get_external_interrupt(), None);
@@ -839,7 +876,7 @@
 
         // OCW2: Unmask IRQ line 6 on secondary (IRQ 14)
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
-        data.pic.write(PIC_SECONDARY_DATA, &[0x00]);
+        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
 
         // Previously-masked interrupt can now be served.
         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
@@ -859,8 +896,8 @@
         icw_init_both(&mut data.pic);
 
         // OCW2: Mask *all* IRQ lines on primary and secondary.
-        data.pic.write(PIC_PRIMARY_DATA, &[0xff]);
-        data.pic.write(PIC_SECONDARY_DATA, &[0xff]);
+        data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xff]);
+        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0xff]);
 
         data.pic.service_irq(/*irq=*/ 14, /*level=*/ true);
         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
@@ -870,14 +907,14 @@
         assert_eq!(data.pic.get_external_interrupt(), None);
 
         // OCW2: Unmask IRQ lines on secondary.
-        data.pic.write(PIC_SECONDARY_DATA, &[0x00]);
+        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
 
         // Cascade line is masked, so the primary *still* cannot get any IRQs.
         assert_eq!(data.pic.get_external_interrupt(), None);
 
         // Unmask cascade line on primary.
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
-        data.pic.write(PIC_PRIMARY_DATA, &[0xfb]);
+        data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xfb]);
 
         // Previously-masked IRQs should now be served in order of priority.
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
@@ -886,7 +923,7 @@
 
         // Unmask all other IRQ lines on primary.
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
-        data.pic.write(PIC_PRIMARY_DATA, &[0x00]);
+        data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0x00]);
         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
     }
 
@@ -903,25 +940,32 @@
         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
 
         // Read primary IRR.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x0a]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0a]);
         let mut data_read = [0];
-        data.pic.read(PIC_PRIMARY_COMMAND, &mut data_read);
+        data.pic
+            .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
         assert_eq!(data_read[0], 1 << 5);
 
         // Read primary ISR.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x0b]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0b]);
         data_read = [0];
-        data.pic.read(PIC_PRIMARY_COMMAND, &mut data_read);
+        data.pic
+            .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
         assert_eq!(data_read[0], 1 << 4);
 
         // Non-sepcific EOI to end IRQ4.  Then, PIC should signal CPU about IRQ5.
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
 
         // Poll command on primary.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x0c]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0c]);
         data_read = [0];
-        data.pic.read(PIC_PRIMARY_COMMAND, &mut data_read);
+        data.pic
+            .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
         assert_eq!(data_read[0], 5);
     }
 
@@ -953,7 +997,8 @@
 
         // In edge triggered mode, there should be no IRQ after this EOI.
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
     }
 
     /// Raising the same IRQ line twice in level-triggered mode should send two IRQ requests out.
@@ -963,7 +1008,7 @@
         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
 
         // Turn IRQ4 to level-triggered mode.
-        data.pic.write(PIC_PRIMARY_ELCR, &[0x10]);
+        data.pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0x10]);
 
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
@@ -974,7 +1019,8 @@
 
         // In level-triggered mode, there should be another IRQ request after this EOI.
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
     }
 
     /// Specific EOI command in OCW2.
@@ -989,11 +1035,13 @@
 
         // Specific EOI command on IRQ3. Primary PIC's ISR should be unaffected since it's targeted
         // at the wrong IRQ number.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x63]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x63]);
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 4);
 
         // Specific EOI command on IRQ4. Primary PIC's ISR should now be cleared.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x64]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x64]);
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
     }
 
@@ -1004,7 +1052,8 @@
         icw_init_both(&mut data.pic);
 
         // OCW3: Clear rotate on auto EOI mode.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x00]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x00]);
 
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
         data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
@@ -1018,7 +1067,8 @@
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 0);
 
         // OCW2: Set rotate on auto EOI mode.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x80]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x80]);
 
         // TODO(mutexlox): Verify APIC interaction when it is implemented.
         data.pic.service_irq(/*irq=*/ 5, /*level*/ true);
@@ -1043,12 +1093,14 @@
 
         // Rotate on specific EOI IRQ4. Since this is a different IRQ number, Should not have an
         // effect on isr.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0xe4]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe4]);
 
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 5);
 
         // Rotate on specific EOI IRQ5. This should clear the isr.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0xe5]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe5]);
 
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
@@ -1066,7 +1118,8 @@
         data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
 
         // Rotate on non-specific EOI.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0xa0]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xa0]);
 
         // The EOI should have cleared isr.
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
@@ -1096,9 +1149,11 @@
         //   - The first resets bit 2 in the primary isr (the highest-priority bit that was set
         //     before the EOI)
         //   - The second resets the secondary PIC's highest-priority isr bit.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
+        data.pic
+            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
         // Rotate non-specific EOI.
-        data.pic.write(PIC_SECONDARY_COMMAND, &[0xa0]);
+        data.pic
+            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xa0]);
 
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
diff --git a/devices/src/lib.rs b/devices/src/lib.rs
index 83f1f1f..64de009 100644
--- a/devices/src/lib.rs
+++ b/devices/src/lib.rs
@@ -16,6 +16,7 @@
 #[macro_use]
 mod register_space;
 pub mod acpi;
+pub mod bat;
 mod serial;
 mod serial_device;
 pub mod usb;
@@ -24,8 +25,9 @@
 pub mod virtio;
 
 pub use self::acpi::ACPIPMResource;
+pub use self::bat::{BatteryError, GoldfishBattery};
 pub use self::bus::Error as BusError;
-pub use self::bus::{Bus, BusDevice, BusRange, BusResumeDevice};
+pub use self::bus::{Bus, BusAccessInfo, BusDevice, BusRange, BusResumeDevice};
 pub use self::cmos::Cmos;
 pub use self::i8042::I8042Device;
 pub use self::irqchip::*;
diff --git a/devices/src/pci/ac97.rs b/devices/src/pci/ac97.rs
index 35ba396..245c700 100644
--- a/devices/src/pci/ac97.rs
+++ b/devices/src/pci/ac97.rs
@@ -5,11 +5,10 @@
 use std::default::Default;
 use std::error;
 use std::fmt::{self, Display};
-use std::os::unix::io::RawFd;
 use std::str::FromStr;
 
 use audio_streams::shm_streams::{NullShmStreamSource, ShmStreamSource};
-use base::{error, Event};
+use base::{error, Event, RawDescriptor};
 use libcras::{CrasClient, CrasClientType, CrasSocketType};
 use resources::{Alloc, MmioType, SystemAllocator};
 use vm_memory::GuestMemory;
@@ -306,8 +305,8 @@
         (&mut self.config_regs).write_reg(reg_idx, offset, data)
     }
 
-    fn keep_fds(&self) -> Vec<RawFd> {
-        if let Some(server_fds) = self.bus_master.keep_fds() {
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        if let Some(server_fds) = self.bus_master.keep_rds() {
             server_fds
         } else {
             Vec::new()
diff --git a/devices/src/pci/ac97_bus_master.rs b/devices/src/pci/ac97_bus_master.rs
index f172fd8..dc0729a 100644
--- a/devices/src/pci/ac97_bus_master.rs
+++ b/devices/src/pci/ac97_bus_master.rs
@@ -6,7 +6,6 @@
 use std::convert::AsRef;
 use std::convert::TryInto;
 use std::fmt::{self, Display};
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use std::thread;
@@ -16,7 +15,9 @@
     shm_streams::{ShmStream, ShmStreamSource},
     BoxError, DummyStreamControl, SampleFormat, StreamControl, StreamDirection, StreamEffect,
 };
-use base::{self, error, set_rt_prio_limit, set_rt_round_robin, warn, Event};
+use base::{
+    self, error, set_rt_prio_limit, set_rt_round_robin, warn, AsRawDescriptor, Event, RawDescriptor,
+};
 use sync::{Condvar, Mutex};
 use vm_memory::{GuestAddress, GuestMemory};
 
@@ -218,10 +219,10 @@
     }
 
     /// Returns any file descriptors that need to be kept open when entering a jail.
-    pub fn keep_fds(&self) -> Option<Vec<RawFd>> {
-        let mut fds = self.audio_server.keep_fds();
-        fds.push(self.mem.as_raw_fd());
-        Some(fds)
+    pub fn keep_rds(&self) -> Option<Vec<RawDescriptor>> {
+        let mut rds = self.audio_server.keep_fds();
+        rds.push(self.mem.as_raw_descriptor());
+        Some(rds)
     }
 
     /// Provides the events needed to raise interrupts in the guest.
diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs
index 5572916..65ed080 100644
--- a/devices/src/pci/mod.rs
+++ b/devices/src/pci/mod.rs
@@ -23,8 +23,8 @@
 pub use self::msix::{MsixCap, MsixConfig, MsixStatus};
 pub use self::pci_configuration::{
     PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityID,
-    PciClassCode, PciConfiguration, PciHeaderType, PciProgrammingInterface, PciSerialBusSubClass,
-    PciSubclass,
+    PciClassCode, PciConfiguration, PciDisplaySubclass, PciHeaderType, PciProgrammingInterface,
+    PciSerialBusSubClass, PciSubclass,
 };
 pub use self::pci_device::Error as PciDeviceError;
 pub use self::pci_device::PciDevice;
diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs
index af06646..7240793 100644
--- a/devices/src/pci/msix.rs
+++ b/devices/src/pci/msix.rs
@@ -3,12 +3,11 @@
 // found in the LICENSE file.
 
 use crate::pci::{PciCapability, PciCapabilityID};
-use base::{error, Error as SysError, Event};
+use base::{error, AsRawDescriptor, Error as SysError, Event, RawDescriptor};
 use msg_socket::{MsgError, MsgReceiver, MsgSender};
 use std::convert::TryInto;
 use std::fmt::{self, Display};
-use std::os::unix::io::{AsRawFd, RawFd};
-use vm_control::{MaybeOwnedFd, VmIrqRequest, VmIrqRequestSocket, VmIrqResponse};
+use vm_control::{MaybeOwnedDescriptor, VmIrqRequest, VmIrqRequestSocket, VmIrqResponse};
 
 use data_model::DataInit;
 
@@ -238,7 +237,7 @@
             let irqfd = Event::new().unwrap();
             self.msi_device_socket
                 .send(&VmIrqRequest::AllocateOneMsi {
-                    irqfd: MaybeOwnedFd::Borrowed(irqfd.as_raw_fd()),
+                    irqfd: MaybeOwnedDescriptor::Borrowed(irqfd.as_raw_descriptor()),
                 })
                 .map_err(MsixError::AllocateOneMsiSend)?;
             let irq_num: u32;
@@ -498,8 +497,8 @@
     }
 
     /// Return the raw fd of the MSI device socket
-    pub fn get_msi_socket(&self) -> RawFd {
-        self.msi_device_socket.as_ref().as_raw_fd()
+    pub fn get_msi_socket(&self) -> RawDescriptor {
+        self.msi_device_socket.as_ref().as_raw_descriptor()
     }
 
     /// Return irqfd of MSI-X Table entry
@@ -514,9 +513,9 @@
     }
 }
 
-impl AsRawFd for MsixConfig {
-    fn as_raw_fd(&self) -> RawFd {
-        self.msi_device_socket.as_raw_fd()
+impl AsRawDescriptor for MsixConfig {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.msi_device_socket.as_raw_descriptor()
     }
 }
 
diff --git a/devices/src/pci/pci_configuration.rs b/devices/src/pci/pci_configuration.rs
index fac75ab..a1d86d4 100644
--- a/devices/src/pci/pci_configuration.rs
+++ b/devices/src/pci/pci_configuration.rs
@@ -70,6 +70,22 @@
     fn get_register_value(&self) -> u8;
 }
 
+/// Subclasses of the DisplayController class.
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+pub enum PciDisplaySubclass {
+    VgaCompatibleController = 0x00,
+    XgaCompatibleController = 0x01,
+    ThreeDController = 0x02,
+    Other = 0x80,
+}
+
+impl PciSubclass for PciDisplaySubclass {
+    fn get_register_value(&self) -> u8 {
+        *self as u8
+    }
+}
+
 /// Subclasses of the MultimediaController class.
 #[allow(dead_code)]
 #[derive(Copy, Clone)]
diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs
index c92f0b2..be336e7 100644
--- a/devices/src/pci/pci_device.rs
+++ b/devices/src/pci/pci_device.rs
@@ -3,15 +3,14 @@
 // found in the LICENSE file.
 
 use std::fmt::{self, Display};
-use std::os::unix::io::RawFd;
 
-use base::Event;
+use base::{Event, RawDescriptor};
 use hypervisor::Datamatch;
 use resources::{Error as SystemAllocatorFaliure, SystemAllocator};
 
 use crate::pci::pci_configuration;
 use crate::pci::{PciAddress, PciInterruptPin};
-use crate::BusDevice;
+use crate::{BusAccessInfo, BusDevice};
 
 #[derive(Debug)]
 pub enum Error {
@@ -54,7 +53,7 @@
     fn assign_address(&mut self, _address: PciAddress) {}
     /// A vector of device-specific file descriptors that must be kept open
     /// after jailing. Must be called before the process is jailed.
-    fn keep_fds(&self) -> Vec<RawFd>;
+    fn keep_rds(&self) -> Vec<RawDescriptor>;
     /// Assign a legacy PCI IRQ to this device.
     /// The device may write to `irq_evt` to trigger an interrupt.
     /// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary.
@@ -120,12 +119,12 @@
         PciDevice::debug_label(self)
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
-        self.read_bar(offset, data)
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
+        self.read_bar(info.address, data)
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
-        self.write_bar(offset, data)
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
+        self.write_bar(info.address, data)
     }
 
     fn config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
@@ -153,8 +152,8 @@
     fn assign_address(&mut self, address: PciAddress) {
         (**self).assign_address(address)
     }
-    fn keep_fds(&self) -> Vec<RawFd> {
-        (**self).keep_fds()
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        (**self).keep_rds()
     }
     fn assign_irq(
         &mut self,
diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs
index 91d6094..c1891be 100644
--- a/devices/src/pci/pci_root.rs
+++ b/devices/src/pci/pci_root.rs
@@ -5,16 +5,16 @@
 use std::collections::BTreeMap;
 use std::convert::TryInto;
 use std::fmt::{self, Display};
-use std::os::unix::io::RawFd;
 use std::sync::Arc;
 
+use base::RawDescriptor;
 use sync::Mutex;
 
 use crate::pci::pci_configuration::{
     PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
 };
 use crate::pci::pci_device::PciDevice;
-use crate::BusDevice;
+use crate::{BusAccessInfo, BusDevice};
 
 // A PciDevice that holds the root hub's configuration.
 struct PciRootConfiguration {
@@ -25,7 +25,7 @@
     fn debug_label(&self) -> String {
         "pci root device".to_owned()
     }
-    fn keep_fds(&self) -> Vec<RawFd> {
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
         Vec::new()
     }
     fn read_config_register(&self, reg_idx: usize) -> u32 {
@@ -226,16 +226,16 @@
         format!("pci config io-port 0x{:03x}", self.config_address)
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
         // `offset` is relative to 0xcf8
-        let value = match offset {
+        let value = match info.offset {
             0..=3 => self.config_address,
             4..=7 => self.config_space_read(),
             _ => 0xffff_ffff,
         };
 
         // Only allow reads to the register boundary.
-        let start = offset as usize % 4;
+        let start = info.offset as usize % 4;
         let end = start + data.len();
         if end <= 4 {
             for i in start..end {
@@ -248,9 +248,9 @@
         }
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
         // `offset` is relative to 0xcf8
-        match offset {
+        match info.offset {
             o @ 0..=3 => self.set_config_address(o, data),
             o @ 4..=7 => self.config_space_write(o - 4, data),
             _ => (),
@@ -286,27 +286,27 @@
         "pci config mmio".to_owned()
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
         // Only allow reads to the register boundary.
-        let start = offset as usize % 4;
+        let start = info.offset as usize % 4;
         let end = start + data.len();
-        if end > 4 || offset > u32::max_value() as u64 {
+        if end > 4 || info.offset > u32::max_value() as u64 {
             for d in data {
                 *d = 0xff;
             }
             return;
         }
 
-        let value = self.config_space_read(offset as u32);
+        let value = self.config_space_read(info.offset as u32);
         for i in start..end {
             data[i - start] = (value >> (i * 8)) as u8;
         }
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
-        if offset > u32::max_value() as u64 {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
+        if info.offset > u32::max_value() as u64 {
             return;
         }
-        self.config_space_write(offset as u32, offset % 4, data)
+        self.config_space_write(info.offset as u32, info.offset % 4, data)
     }
 }
diff --git a/devices/src/pci/vfio_pci.rs b/devices/src/pci/vfio_pci.rs
index 88a5216..7cea8e3 100644
--- a/devices/src/pci/vfio_pci.rs
+++ b/devices/src/pci/vfio_pci.rs
@@ -2,19 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::sync::Arc;
 use std::u32;
 
-use base::{error, Event, MappedRegion, MemoryMapping, MemoryMappingBuilder};
+use base::{
+    error, AsRawDescriptor, Event, MappedRegion, MemoryMapping, MemoryMappingBuilder, RawDescriptor,
+};
 use hypervisor::Datamatch;
 use msg_socket::{MsgReceiver, MsgSender};
 use resources::{Alloc, MmioType, SystemAllocator};
 
 use vfio_sys::*;
 use vm_control::{
-    MaybeOwnedFd, VmIrqRequest, VmIrqRequestSocket, VmIrqResponse, VmMemoryControlRequestSocket,
-    VmMemoryRequest, VmMemoryResponse,
+    MaybeOwnedDescriptor, VmIrqRequest, VmIrqRequestSocket, VmIrqResponse,
+    VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse,
 };
 
 use crate::pci::msix::{
@@ -270,7 +271,7 @@
         }
 
         if let Err(e) = self.vm_socket_irq.send(&VmIrqRequest::AllocateOneMsi {
-            irqfd: MaybeOwnedFd::Borrowed(self.irqfd.as_ref().unwrap().as_raw_fd()),
+            irqfd: MaybeOwnedDescriptor::Borrowed(self.irqfd.as_ref().unwrap().as_raw_descriptor()),
         }) {
             error!("failed to send AllocateOneMsi request: {:?}", e);
             return;
@@ -661,8 +662,8 @@
             None => return,
         };
 
-        if let Some(fds) = irqfds {
-            if let Err(e) = self.device.irq_enable(fds, VfioIrqType::Msix) {
+        if let Some(descriptors) = irqfds {
+            if let Err(e) = self.device.irq_enable(descriptors, VfioIrqType::Msix) {
                 error!("failed to enable msix: {}", e);
                 self.enable_intx();
                 return;
@@ -709,7 +710,7 @@
                 if self
                     .vm_socket_mem
                     .send(&VmMemoryRequest::RegisterMmapMemory {
-                        fd: MaybeOwnedFd::Borrowed(self.device.as_raw_fd()),
+                        descriptor: MaybeOwnedDescriptor::Borrowed(self.device.as_raw_descriptor()),
                         size: mmap_size as usize,
                         offset,
                         gpa: guest_map_start,
@@ -776,22 +777,22 @@
         self.pci_address = Some(address);
     }
 
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut fds = self.device.keep_fds();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut rds = self.device.keep_rds();
         if let Some(ref interrupt_evt) = self.interrupt_evt {
-            fds.push(interrupt_evt.as_raw_fd());
+            rds.push(interrupt_evt.as_raw_descriptor());
         }
         if let Some(ref interrupt_resample_evt) = self.interrupt_resample_evt {
-            fds.push(interrupt_resample_evt.as_raw_fd());
+            rds.push(interrupt_resample_evt.as_raw_descriptor());
         }
-        fds.push(self.vm_socket_mem.as_raw_fd());
+        rds.push(self.vm_socket_mem.as_raw_descriptor());
         if let Some(msi_cap) = &self.msi_cap {
-            fds.push(msi_cap.vm_socket_irq.as_raw_fd());
+            rds.push(msi_cap.vm_socket_irq.as_raw_descriptor());
         }
         if let Some(msix_cap) = &self.msix_cap {
-            fds.push(msix_cap.config.as_raw_fd());
+            rds.push(msix_cap.config.as_raw_descriptor());
         }
-        fds
+        rds
     }
 
     fn assign_irq(
@@ -833,10 +834,7 @@
             low = self.config.read_config_dword(offset);
 
             let low_flag = low & 0xf;
-            let is_64bit = match low_flag & 0x4 {
-                0x4 => true,
-                _ => false,
-            };
+            let is_64bit = low_flag & 0x4 == 0x4;
             if (low_flag & 0x1 == 0 || i == VFIO_PCI_ROM_REGION_INDEX) && low != 0 {
                 let mut upper: u32 = 0xffffffff;
                 if is_64bit {
diff --git a/devices/src/pit.rs b/devices/src/pit.rs
index 491cf32..63fa5be 100644
--- a/devices/src/pit.rs
+++ b/devices/src/pit.rs
@@ -5,12 +5,13 @@
 
 use std::fmt::{self, Display};
 use std::io::Error as IoError;
-use std::os::unix::io::AsRawFd;
 use std::sync::Arc;
 use std::thread;
 use std::time::Duration;
 
-use base::{error, warn, Error as SysError, Event, Fd, PollContext, PollToken};
+use base::{
+    error, warn, AsRawDescriptor, Descriptor, Error as SysError, Event, PollToken, WaitContext,
+};
 use bit_field::BitField1;
 use bit_field::*;
 use hypervisor::{PitChannelState, PitRWMode, PitRWState, PitState};
@@ -26,6 +27,7 @@
 #[cfg(not(test))]
 use base::Timer;
 
+use crate::bus::BusAccessInfo;
 use crate::BusDevice;
 
 // Bitmask for areas of standard (non-ReadBack) Control Word Format. Constant
@@ -148,10 +150,10 @@
 #[derive(Debug)]
 pub enum PitError {
     TimerCreateError(SysError),
-    /// Creating PollContext failed.
-    CreatePollContext(SysError),
-    /// Error while polling for events.
-    PollError(SysError),
+    /// Creating WaitContext failed.
+    CreateWaitContext(SysError),
+    /// Error while waiting for events.
+    WaitError(SysError),
     /// Error while trying to create worker thread.
     SpawnThread(IoError),
     /// Error while creating event.
@@ -166,8 +168,8 @@
 
         match self {
             TimerCreateError(e) => write!(f, "failed to create pit counter due to timer fd: {}", e),
-            CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
-            PollError(err) => write!(f, "failed to poll events: {}", err),
+            CreateWaitContext(e) => write!(f, "failed to create poll context: {}", e),
+            WaitError(err) => write!(f, "failed to wait for events: {}", err),
             SpawnThread(err) => write!(f, "failed to spawn thread: {}", err),
             CreateEvent(err) => write!(f, "failed to create event: {}", err),
             CloneEvent(err) => write!(f, "failed to clone event: {}", err),
@@ -213,31 +215,31 @@
         "userspace PIT".to_string()
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
         self.ensure_started();
 
         if data.len() != 1 {
             warn!("Bad write size for Pit: {}", data.len());
             return;
         }
-        match PortIOSpace::n(offset as i64) {
+        match PortIOSpace::n(info.address as i64) {
             Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().write_counter(data[0]),
             Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().write_counter(data[0]),
             Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().write_counter(data[0]),
             Some(PortIOSpace::PortCommand) => self.command_write(data[0]),
             Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().write_speaker(data[0]),
-            None => warn!("PIT: bad write to offset {}", offset),
+            None => warn!("PIT: bad write to {}", info),
         }
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
         self.ensure_started();
 
         if data.len() != 1 {
             warn!("Bad read size for Pit: {}", data.len());
             return;
         }
-        data[0] = match PortIOSpace::n(offset as i64) {
+        data[0] = match PortIOSpace::n(info.address as i64) {
             Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().read_counter(),
             Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().read_counter(),
             Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().read_counter(),
@@ -250,7 +252,7 @@
             }
             Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().read_speaker(),
             None => {
-                warn!("PIT: bad read from offset {}", offset);
+                warn!("PIT: bad read from {}", info);
                 return;
             }
         };
@@ -295,7 +297,7 @@
     fn start(&mut self) -> PitResult<()> {
         let mut worker = Worker {
             pit_counter: self.counters[0].clone(),
-            fd: Fd(self.counters[0].lock().timer.as_raw_fd()),
+            fd: Descriptor(self.counters[0].lock().timer.as_raw_descriptor()),
         };
         let evt = self.kill_evt.try_clone().map_err(PitError::CloneEvent)?;
 
@@ -550,28 +552,9 @@
         //  - 2 counter select bits, which aren't used by the counter/channel itself
         self.command = (state.mode << 1) | ((state.rw_mode as u8) << 4);
         self.gate = state.gate;
-        self.latched = match state.count_latched {
-            PitRWState::None => false,
-            _ => true,
-        };
-
-        match state.read_state {
-            PitRWState::Word1 => {
-                self.read_low_byte = true;
-            }
-            _ => {
-                self.read_low_byte = false;
-            }
-        }
-
-        match state.write_state {
-            PitRWState::Word1 => {
-                self.wrote_low_byte = true;
-            }
-            _ => {
-                self.wrote_low_byte = false;
-            }
-        }
+        self.latched = state.count_latched != PitRWState::None;
+        self.read_low_byte = state.read_state == PitRWState::Word1;
+        self.wrote_low_byte = state.write_state == PitRWState::Word1;
 
         // To convert the count_load_time to an instant we have to convert it to a
         // duration by comparing it to get_monotonic_time.  Then subtract that duration from
@@ -903,7 +886,7 @@
 
 struct Worker {
     pit_counter: Arc<Mutex<PitCounter>>,
-    fd: Fd,
+    fd: Descriptor,
 }
 
 impl Worker {
@@ -916,14 +899,14 @@
             Kill,
         }
 
-        let poll_ctx: PollContext<Token> =
-            PollContext::build_with(&[(&self.fd, Token::TimerExpire), (&kill_evt, Token::Kill)])
-                .map_err(PitError::CreatePollContext)?;
+        let wait_ctx: WaitContext<Token> =
+            WaitContext::build_with(&[(&self.fd, Token::TimerExpire), (&kill_evt, Token::Kill)])
+                .map_err(PitError::CreateWaitContext)?;
 
         loop {
-            let events = poll_ctx.wait().map_err(PitError::PollError)?;
-            for event in events.iter_readable() {
-                match event.token() {
+            let events = wait_ctx.wait().map_err(PitError::WaitError)?;
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::TimerExpire => {
                         let mut pit = self.pit_counter.lock();
                         pit.timer_handler();
@@ -944,14 +927,35 @@
         clock: Arc<Mutex<Clock>>,
     }
 
+    fn pit_bus_address(address: PortIOSpace) -> BusAccessInfo {
+        // The PIT is added to the io_bus in two locations, so the offset depends on which
+        // address range the address is in. The PIT implementation currently does not use the
+        // offset, but we're setting it accurately here in case it does in the future.
+        let offset = match address as u64 {
+            x if x >= PortIOSpace::PortCounter0Data as u64
+                && x < PortIOSpace::PortCounter0Data as u64 + 0x8 =>
+            {
+                address as u64 - PortIOSpace::PortCounter0Data as u64
+            }
+            x if x == PortIOSpace::PortSpeaker as u64 => 0,
+            _ => panic!("invalid PIT address: {:#x}", address as u64),
+        };
+
+        BusAccessInfo {
+            offset,
+            address: address as u64,
+            id: 0,
+        }
+    }
+
     /// Utility method for writing a command word to a command register.
     fn write_command(pit: &mut Pit, command: u8) {
-        pit.write(PortIOSpace::PortCommand as u64, &[command])
+        pit.write(pit_bus_address(PortIOSpace::PortCommand), &[command])
     }
 
     /// Utility method for writing a command word to the speaker register.
     fn write_speaker(pit: &mut Pit, command: u8) {
-        pit.write(PortIOSpace::PortSpeaker as u64, &[command])
+        pit.write(pit_bus_address(PortIOSpace::PortSpeaker), &[command])
     }
 
     /// Utility method for writing to a counter.
@@ -961,17 +965,17 @@
             1 => PortIOSpace::PortCounter1Data,
             2 => PortIOSpace::PortCounter2Data,
             _ => panic!("Invalid counter_idx: {}", counter_idx),
-        } as u64;
+        };
         // Write the least, then the most, significant byte.
         if access_mode == CommandAccess::CommandRWLeast
             || access_mode == CommandAccess::CommandRWBoth
         {
-            pit.write(port, &[(data & 0xff) as u8]);
+            pit.write(pit_bus_address(port), &[(data & 0xff) as u8]);
         }
         if access_mode == CommandAccess::CommandRWMost
             || access_mode == CommandAccess::CommandRWBoth
         {
-            pit.write(port, &[(data >> 8) as u8]);
+            pit.write(pit_bus_address(port), &[(data >> 8) as u8]);
         }
     }
 
@@ -982,20 +986,20 @@
             1 => PortIOSpace::PortCounter1Data,
             2 => PortIOSpace::PortCounter2Data,
             _ => panic!("Invalid counter_idx: {}", counter_idx),
-        } as u64;
+        };
         let mut result: u16 = 0;
         if access_mode == CommandAccess::CommandRWLeast
             || access_mode == CommandAccess::CommandRWBoth
         {
             let mut buffer = [0];
-            pit.read(port, &mut buffer);
+            pit.read(pit_bus_address(port), &mut buffer);
             result = buffer[0].into();
         }
         if access_mode == CommandAccess::CommandRWMost
             || access_mode == CommandAccess::CommandRWBoth
         {
             let mut buffer = [0];
-            pit.read(port, &mut buffer);
+            pit.read(pit_bus_address(port), &mut buffer);
             result |= u16::from(buffer[0]) << 8;
         }
         assert_eq!(result, expected);
@@ -1123,7 +1127,8 @@
     fn read_command() {
         let mut data = set_up();
         let mut buf = [0];
-        data.pit.read(PortIOSpace::PortCommand as u64, &mut buf);
+        data.pit
+            .read(pit_bus_address(PortIOSpace::PortCommand), &mut buf);
         assert_eq!(buf, [0]);
     }
 
@@ -1444,7 +1449,21 @@
     #[test]
     fn invalid_write_and_read() {
         let mut data = set_up();
-        data.pit.write(0x44, &[0]);
-        data.pit.read(0x55, &mut [0]);
+        data.pit.write(
+            BusAccessInfo {
+                address: 0x44,
+                offset: 0x4,
+                id: 0,
+            },
+            &[0],
+        );
+        data.pit.read(
+            BusAccessInfo {
+                address: 0x55,
+                offset: 0x15,
+                id: 0,
+            },
+            &mut [0],
+        );
     }
 }
diff --git a/devices/src/pl030.rs b/devices/src/pl030.rs
index 9db36eb..f295b96 100644
--- a/devices/src/pl030.rs
+++ b/devices/src/pl030.rs
@@ -3,9 +3,10 @@
 // found in the LICENSE file.
 
 use base::{warn, Event};
+use std::convert::TryFrom;
 use std::time::{SystemTime, UNIX_EPOCH};
 
-use crate::BusDevice;
+use crate::{BusAccessInfo, BusDevice};
 
 // Register offsets
 // Data register
@@ -74,17 +75,17 @@
         "Pl030".to_owned()
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
-        if data.len() != 4 {
-            warn!("bad write size: {} for pl030", data.len());
-            return;
-        }
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
+        let data_array = match <&[u8; 4]>::try_from(data) {
+            Ok(array) => array,
+            _ => {
+                warn!("bad write size: {} for pl030", data.len());
+                return;
+            }
+        };
 
-        let reg_val: u32 = (data[0] as u32) << 24
-            | (data[1] as u32) << 16
-            | (data[2] as u32) << 8
-            | (data[3] as u32);
-        match offset {
+        let reg_val = u32::from_ne_bytes(*data_array);
+        match info.offset {
             RTCDR => {
                 warn!("invalid write to read-only RTCDR register");
             }
@@ -113,17 +114,20 @@
             RTCCR => {
                 self.counter_delta_time = get_epoch_time();
             }
-            o => panic!("pl030: bad write offset {}", o),
+            o => panic!("pl030: bad write {}", o),
         }
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
-        if data.len() != 4 {
-            warn!("bad read size: {} for pl030", data.len());
-            return;
-        }
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
+        let data_array = match <&mut [u8; 4]>::try_from(data) {
+            Ok(array) => array,
+            _ => {
+                warn!("bad write size for pl030");
+                return;
+            }
+        };
 
-        let reg_content: u32 = match offset {
+        let reg_content: u32 = match info.offset {
             RTCDR => get_epoch_time(),
             RTCMR => self.match_value,
             RTCSTAT => self.interrupt_active as u32,
@@ -135,11 +139,51 @@
             AMBA_ID_OFFSET => PL030_AMBA_ID,
             AMBA_MASK_OFFSET => PL030_AMBA_MASK,
 
-            o => panic!("pl030: bad read offset {}", o),
+            o => panic!("pl030: bad read {}", o),
         };
-        data[0] = reg_content as u8;
-        data[1] = (reg_content >> 8) as u8;
-        data[2] = (reg_content >> 16) as u8;
-        data[3] = (reg_content >> 24) as u8;
+        *data_array = reg_content.to_ne_bytes();
+    }
+}
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    // The RTC device is placed at page 2 in the mmio bus
+    const AARCH64_RTC_ADDR: u64 = 0x2000;
+
+    fn pl030_bus_address(offset: u64) -> BusAccessInfo {
+        BusAccessInfo {
+            address: AARCH64_RTC_ADDR + offset,
+            offset,
+            id: 0,
+        }
+    }
+
+    #[test]
+    fn test_interrupt_status_register() {
+        let event = Event::new().unwrap();
+        let mut device = Pl030::new(event.try_clone().unwrap());
+        let mut register = [0, 0, 0, 0];
+
+        // set interrupt
+        device.write(pl030_bus_address(RTCEOI), &[1, 0, 0, 0]);
+        device.read(pl030_bus_address(RTCSTAT), &mut register);
+        assert_eq!(register, [1, 0, 0, 0]);
+        assert_eq!(event.read().unwrap(), 1);
+
+        // clear interrupt
+        device.write(pl030_bus_address(RTCEOI), &[0, 0, 0, 0]);
+        device.read(pl030_bus_address(RTCSTAT), &mut register);
+        assert_eq!(register, [0, 0, 0, 0]);
+    }
+
+    #[test]
+    fn test_match_register() {
+        let mut device = Pl030::new(Event::new().unwrap());
+        let mut register = [0, 0, 0, 0];
+
+        device.write(pl030_bus_address(RTCMR), &[1, 2, 3, 4]);
+        device.read(pl030_bus_address(RTCMR), &mut register);
+        assert_eq!(register, [1, 2, 3, 4]);
     }
 }
diff --git a/devices/src/proxy.rs b/devices/src/proxy.rs
index e07b380..383eee4 100644
--- a/devices/src/proxy.rs
+++ b/devices/src/proxy.rs
@@ -5,16 +5,15 @@
 //! Runs hardware devices in child processes.
 
 use std::fmt::{self, Display};
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::time::Duration;
 use std::{self, io};
 
-use base::{error, net::UnixSeqpacket};
+use base::{error, net::UnixSeqpacket, AsRawDescriptor, RawDescriptor};
 use libc::{self, pid_t};
 use minijail::{self, Minijail};
 use msg_socket::{MsgOnSocket, MsgReceiver, MsgSender, MsgSocket};
 
-use crate::BusDevice;
+use crate::{BusAccessInfo, BusDevice};
 
 /// Errors for proxy devices.
 #[derive(Debug)]
@@ -41,11 +40,11 @@
 enum Command {
     Read {
         len: u32,
-        offset: u64,
+        info: BusAccessInfo,
     },
     Write {
         len: u32,
-        offset: u64,
+        info: BusAccessInfo,
         data: [u8; 8],
     },
     ReadConfig(u32),
@@ -79,14 +78,14 @@
         };
 
         let res = match cmd {
-            Command::Read { len, offset } => {
+            Command::Read { len, info } => {
                 let mut buffer = [0u8; 8];
-                device.read(offset, &mut buffer[0..len as usize]);
+                device.read(info, &mut buffer[0..len as usize]);
                 sock.send(&CommandResult::ReadResult(buffer))
             }
-            Command::Write { len, offset, data } => {
+            Command::Write { len, info, data } => {
                 let len = len as usize;
-                device.write(offset, &data[0..len]);
+                device.write(info, &data[0..len]);
                 // Command::Write does not have a result.
                 Ok(())
             }
@@ -135,19 +134,19 @@
     /// # Arguments
     /// * `device` - The device to isolate to another process.
     /// * `jail` - The jail to use for isolating the given device.
-    /// * `keep_fds` - File descriptors that will be kept open in the child.
+    /// * `keep_rds` - File descriptors that will be kept open in the child.
     pub fn new<D: BusDevice>(
         mut device: D,
         jail: &Minijail,
-        mut keep_fds: Vec<RawFd>,
+        mut keep_rds: Vec<RawDescriptor>,
     ) -> Result<ProxyDevice> {
         let debug_label = device.debug_label();
         let (child_sock, parent_sock) = UnixSeqpacket::pair().map_err(Error::Io)?;
 
-        keep_fds.push(child_sock.as_raw_fd());
+        keep_rds.push(child_sock.as_raw_descriptor());
         // Forking here is safe as long as the program is still single threaded.
         let pid = unsafe {
-            match jail.fork(Some(&keep_fds)).map_err(Error::ForkingJail)? {
+            match jail.fork(Some(&keep_rds)).map_err(Error::ForkingJail)? {
                 0 => {
                     device.on_sandboxed();
                     child_proc(child_sock, &mut device);
@@ -238,23 +237,23 @@
         }
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
         let len = data.len() as u32;
         if let Some(CommandResult::ReadResult(buffer)) =
-            self.sync_send(&Command::Read { len, offset })
+            self.sync_send(&Command::Read { len, info })
         {
             let len = data.len();
             data.clone_from_slice(&buffer[0..len]);
         }
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
         let mut buffer = [0u8; 8];
         let len = data.len() as u32;
         buffer[0..data.len()].clone_from_slice(data);
         self.send_no_result(&Command::Write {
             len,
-            offset,
+            info,
             data: buffer,
         });
     }
@@ -265,3 +264,83 @@
         self.sync_send(&Command::Shutdown);
     }
 }
+
+/// Note: These tests must be run with --test-threads=1 to allow minijail to fork
+/// the process.
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    /// A simple test echo device that outputs the same u8 that was written to it.
+    struct EchoDevice {
+        data: u8,
+        config: u8,
+    }
+    impl EchoDevice {
+        fn new() -> EchoDevice {
+            EchoDevice { data: 0, config: 0 }
+        }
+    }
+    impl BusDevice for EchoDevice {
+        fn debug_label(&self) -> String {
+            "EchoDevice".to_owned()
+        }
+
+        fn write(&mut self, _info: BusAccessInfo, data: &[u8]) {
+            assert!(data.len() == 1);
+            self.data = data[0];
+        }
+
+        fn read(&mut self, _info: BusAccessInfo, data: &mut [u8]) {
+            assert!(data.len() == 1);
+            data[0] = self.data;
+        }
+
+        fn config_register_write(&mut self, _reg_idx: usize, _offset: u64, data: &[u8]) {
+            assert!(data.len() == 1);
+            self.config = data[0];
+        }
+
+        fn config_register_read(&self, _reg_idx: usize) -> u32 {
+            self.config as u32
+        }
+    }
+
+    fn new_proxied_echo_device() -> ProxyDevice {
+        let device = EchoDevice::new();
+        let keep_fds: Vec<RawDescriptor> = Vec::new();
+        let minijail = Minijail::new().unwrap();
+        ProxyDevice::new(device, &minijail, keep_fds).unwrap()
+    }
+
+    // TODO(b/173833661): Find a way to ensure these tests are run single-threaded.
+    #[test]
+    #[ignore]
+    fn test_debug_label() {
+        let proxy_device = new_proxied_echo_device();
+        assert_eq!(proxy_device.debug_label(), "EchoDevice");
+    }
+
+    #[test]
+    #[ignore]
+    fn test_proxied_read_write() {
+        let mut proxy_device = new_proxied_echo_device();
+        let address = BusAccessInfo {
+            offset: 0,
+            address: 0,
+            id: 0,
+        };
+        proxy_device.write(address, &[42]);
+        let mut read_buffer = [0];
+        proxy_device.read(address, &mut read_buffer);
+        assert_eq!(read_buffer, [42]);
+    }
+
+    #[test]
+    #[ignore]
+    fn test_proxied_config() {
+        let mut proxy_device = new_proxied_echo_device();
+        proxy_device.config_register_write(0, 0, &[42]);
+        assert_eq!(proxy_device.config_register_read(0), 42);
+    }
+}
diff --git a/devices/src/serial.rs b/devices/src/serial.rs
index ca0dee9..d4e6d45 100644
--- a/devices/src/serial.rs
+++ b/devices/src/serial.rs
@@ -4,14 +4,14 @@
 
 use std::collections::VecDeque;
 use std::io::{self, Write};
-use std::os::unix::io::RawFd;
 use std::sync::atomic::{AtomicU8, Ordering};
 use std::sync::mpsc::{channel, Receiver, TryRecvError};
 use std::sync::Arc;
 use std::thread::{self};
 
-use base::{error, Event, Result};
+use base::{error, Event, RawDescriptor, Result};
 
+use crate::bus::BusAccessInfo;
 use crate::{BusDevice, SerialDevice};
 
 const LOOP_SIZE: usize = 0x40;
@@ -88,7 +88,7 @@
         interrupt_evt: Event,
         input: Option<Box<dyn io::Read + Send>>,
         out: Option<Box<dyn io::Write + Send>>,
-        _keep_fds: Vec<RawFd>,
+        _keep_rds: Vec<RawDescriptor>,
     ) -> Serial {
         Serial {
             interrupt_enable: Default::default(),
@@ -311,24 +311,24 @@
         "serial".to_owned()
     }
 
-    fn write(&mut self, offset: u64, data: &[u8]) {
+    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
         if data.len() != 1 {
             return;
         }
 
-        if let Err(e) = self.handle_write(offset as u8, data[0]) {
+        if let Err(e) = self.handle_write(info.offset as u8, data[0]) {
             error!("serial failed write: {}", e);
         }
     }
 
-    fn read(&mut self, offset: u64, data: &mut [u8]) {
+    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
         if data.len() != 1 {
             return;
         }
 
         self.handle_input_thread();
 
-        data[0] = match offset as u8 {
+        data[0] = match info.offset as u8 {
             DLAB_LOW if self.is_dlab_set() => self.baud_divisor as u8,
             DLAB_HIGH if self.is_dlab_set() => (self.baud_divisor >> 8) as u8,
             DATA => {
@@ -404,6 +404,15 @@
         }
     }
 
+    fn serial_bus_address(offset: u8) -> BusAccessInfo {
+        // Serial devices only use the offset of the BusAccessInfo
+        BusAccessInfo {
+            offset: offset as u64,
+            address: 0,
+            id: 0,
+        }
+    }
+
     #[test]
     fn serial_output() {
         let intr_evt = Event::new().unwrap();
@@ -417,9 +426,9 @@
             Vec::new(),
         );
 
-        serial.write(DATA as u64, &['a' as u8]);
-        serial.write(DATA as u64, &['b' as u8]);
-        serial.write(DATA as u64, &['c' as u8]);
+        serial.write(serial_bus_address(DATA), &['a' as u8]);
+        serial.write(serial_bus_address(DATA), &['b' as u8]);
+        serial.write(serial_bus_address(DATA), &['c' as u8]);
         assert_eq!(
             serial_out.buf.lock().as_slice(),
             &['a' as u8, 'b' as u8, 'c' as u8]
@@ -439,18 +448,18 @@
             Vec::new(),
         );
 
-        serial.write(IER as u64, &[IER_RECV_BIT]);
+        serial.write(serial_bus_address(IER), &[IER_RECV_BIT]);
         serial
             .queue_input_bytes(&['a' as u8, 'b' as u8, 'c' as u8])
             .unwrap();
 
         assert_eq!(intr_evt.read(), Ok(1));
         let mut data = [0u8; 1];
-        serial.read(DATA as u64, &mut data[..]);
+        serial.read(serial_bus_address(DATA), &mut data[..]);
         assert_eq!(data[0], 'a' as u8);
-        serial.read(DATA as u64, &mut data[..]);
+        serial.read(serial_bus_address(DATA), &mut data[..]);
         assert_eq!(data[0], 'b' as u8);
-        serial.read(DATA as u64, &mut data[..]);
+        serial.read(serial_bus_address(DATA), &mut data[..]);
         assert_eq!(data[0], 'c' as u8);
     }
 }
diff --git a/devices/src/serial_device.rs b/devices/src/serial_device.rs
index effa5fe..280730e 100644
--- a/devices/src/serial_device.rs
+++ b/devices/src/serial_device.rs
@@ -3,9 +3,8 @@
 // found in the LICENSE file.
 
 use std::io;
-use std::os::unix::io::RawFd;
 
-use base::Event;
+use base::{Event, RawDescriptor};
 
 /// Abstraction over serial-like devices that can be created given an event and optional input and
 /// output streams.
@@ -15,6 +14,6 @@
         interrupt_evt: Event,
         input: Option<Box<dyn io::Read + Send>>,
         output: Option<Box<dyn io::Write + Send>>,
-        keep_fds: Vec<RawFd>,
+        keep_rds: Vec<RawDescriptor>,
     ) -> Self;
 }
diff --git a/devices/src/usb/host_backend/host_backend_device_provider.rs b/devices/src/usb/host_backend/host_backend_device_provider.rs
index 0f5f2a7..0af8bbf 100644
--- a/devices/src/usb/host_backend/host_backend_device_provider.rs
+++ b/devices/src/usb/host_backend/host_backend_device_provider.rs
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use std::fs::File;
 use std::sync::Arc;
 
 use super::error::*;
@@ -11,17 +12,16 @@
 use crate::utils::AsyncJobQueue;
 use crate::utils::{EventHandler, EventLoop, FailHandle};
 use base::net::UnixSeqpacket;
-use base::{error, WatchingEvents};
+use base::{error, AsRawDescriptor, FromRawDescriptor, RawDescriptor, WatchingEvents};
 use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
 use std::collections::HashMap;
 use std::mem;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::time::Duration;
 use sync::Mutex;
 use usb_util::Device;
 use vm_control::{
-    MaybeOwnedFd, UsbControlAttachedDevice, UsbControlCommand, UsbControlResult, UsbControlSocket,
-    USB_CONTROL_MAX_PORTS,
+    MaybeOwnedDescriptor, UsbControlAttachedDevice, UsbControlCommand, UsbControlResult,
+    UsbControlSocket, USB_CONTROL_MAX_PORTS,
 };
 
 const SOCKET_TIMEOUT_MS: u64 = 2000;
@@ -110,9 +110,9 @@
             })
     }
 
-    fn keep_fds(&self) -> Vec<RawFd> {
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
         match self {
-            HostBackendDeviceProvider::Created { sock } => vec![sock.lock().as_raw_fd()],
+            HostBackendDeviceProvider::Created { sock } => vec![sock.lock().as_raw_descriptor()],
             _ => {
                 error!(
                     "Trying to get keepfds when HostBackendDeviceProvider is not in created state"
@@ -160,17 +160,18 @@
 
     /// Open a usbdevfs file to create a host USB device object.
     /// `fd` should be an open file descriptor for a file in `/dev/bus/usb`.
-    fn handle_attach_device(&self, fd: Option<MaybeOwnedFd>) -> UsbControlResult {
+    fn handle_attach_device(&self, fd: Option<MaybeOwnedDescriptor>) -> UsbControlResult {
         let usb_file = match fd {
-            Some(MaybeOwnedFd::Owned(file)) => file,
+            Some(MaybeOwnedDescriptor::Owned(file)) => file,
             _ => {
                 error!("missing fd in UsbControlCommand::AttachDevice message");
                 return UsbControlResult::FailedToOpenDevice;
             }
         };
 
-        let raw_fd = usb_file.as_raw_fd();
-        let device = match Device::new(usb_file) {
+        let raw_descriptor = usb_file.as_raw_descriptor();
+        // Safe as it is valid to have multiple variables accessing the same fd.
+        let device = match Device::new(unsafe { File::from_raw_descriptor(raw_descriptor) }) {
             Ok(d) => d,
             Err(e) => {
                 error!("could not construct USB device from fd: {}", e);
@@ -185,7 +186,7 @@
         });
 
         if let Err(e) = self.event_loop.add_event(
-            &MaybeOwnedFd::Borrowed(raw_fd),
+            &MaybeOwnedDescriptor::Borrowed(raw_descriptor),
             WatchingEvents::empty().set_read().set_write(),
             Arc::downgrade(&event_handler),
         ) {
@@ -230,9 +231,11 @@
                     let device = device_ctx.device.lock();
                     let fd = device.fd();
 
-                    if let Err(e) = self
-                        .event_loop
-                        .remove_event_for_fd(&MaybeOwnedFd::Borrowed(fd.as_raw_fd()))
+                    if let Err(e) =
+                        self.event_loop
+                            .remove_event_for_fd(&MaybeOwnedDescriptor::Borrowed(
+                                fd.as_raw_descriptor(),
+                            ))
                     {
                         error!(
                             "failed to remove poll change handler from event loop: {}",
@@ -274,7 +277,9 @@
         let sock = self.sock.lock();
         let cmd = sock.recv().map_err(Error::ReadControlSock)?;
         let result = match cmd {
-            UsbControlCommand::AttachDevice { fd, .. } => self.handle_attach_device(fd),
+            UsbControlCommand::AttachDevice { descriptor, .. } => {
+                self.handle_attach_device(descriptor)
+            }
             UsbControlCommand::DetachDevice { port } => self.handle_detach_device(port),
             UsbControlCommand::ListDevice { ports } => self.handle_list_devices(ports),
         };
diff --git a/devices/src/usb/xhci/xhci_backend_device_provider.rs b/devices/src/usb/xhci/xhci_backend_device_provider.rs
index c014d77..aefcf53 100644
--- a/devices/src/usb/xhci/xhci_backend_device_provider.rs
+++ b/devices/src/usb/xhci/xhci_backend_device_provider.rs
@@ -4,7 +4,7 @@
 
 use super::usb_hub::UsbHub;
 use crate::utils::{EventLoop, FailHandle};
-use std::os::unix::io::RawFd;
+use base::RawDescriptor;
 use std::sync::Arc;
 
 /// Xhci backend provider will run on an EventLoop and connect new devices to usb ports.
@@ -17,6 +17,6 @@
         hub: Arc<UsbHub>,
     ) -> std::result::Result<(), ()>;
 
-    /// Keep fds that should be kept open.
-    fn keep_fds(&self) -> Vec<RawFd>;
+    /// Keep raw descriptors that should be kept open.
+    fn keep_rds(&self) -> Vec<RawDescriptor>;
 }
diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs
index 67393ca..0c97c7b 100644
--- a/devices/src/usb/xhci/xhci_controller.rs
+++ b/devices/src/usb/xhci/xhci_controller.rs
@@ -12,10 +12,9 @@
 use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider;
 use crate::usb::xhci::xhci_regs::{init_xhci_mmio_space_and_regs, XhciRegs};
 use crate::utils::FailHandle;
-use base::{error, Event};
+use base::{error, Event, RawDescriptor};
 use resources::{Alloc, MmioType, SystemAllocator};
 use std::mem;
-use std::os::unix::io::RawFd;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use vm_memory::GuestMemory;
@@ -171,9 +170,9 @@
         self.pci_address = Some(address);
     }
 
-    fn keep_fds(&self) -> Vec<RawFd> {
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
         match &self.state {
-            XhciControllerState::Created { device_provider } => device_provider.keep_fds(),
+            XhciControllerState::Created { device_provider } => device_provider.keep_rds(),
             _ => {
                 error!("xhci controller is in a wrong state");
                 vec![]
diff --git a/devices/src/utils/error.rs b/devices/src/utils/error.rs
index 1d0227a..453b052 100644
--- a/devices/src/utils/error.rs
+++ b/devices/src/utils/error.rs
@@ -11,9 +11,9 @@
     CreateEvent(SysError),
     ReadEvent(SysError),
     WriteEvent(SysError),
-    CreatePollContext(SysError),
-    PollContextAddFd(SysError),
-    PollContextDeleteFd(SysError),
+    CreateWaitContext(SysError),
+    WaitContextAddDescriptor(SysError),
+    WaitContextDeleteDescriptor(SysError),
     StartThread(std::io::Error),
 }
 
@@ -26,9 +26,11 @@
             CreateEvent(e) => write!(f, "failed to create event: {}", e),
             ReadEvent(e) => write!(f, "failed to read event: {}", e),
             WriteEvent(e) => write!(f, "failed to write event: {}", e),
-            CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
-            PollContextAddFd(e) => write!(f, "failed to add fd to poll context: {}", e),
-            PollContextDeleteFd(e) => write!(f, "failed to delete fd from poll context: {}", e),
+            CreateWaitContext(e) => write!(f, "failed to create poll context: {}", e),
+            WaitContextAddDescriptor(e) => write!(f, "failed to add fd to poll context: {}", e),
+            WaitContextDeleteDescriptor(e) => {
+                write!(f, "failed to delete fd from poll context: {}", e)
+            }
             StartThread(e) => write!(f, "failed to start thread: {}", e),
         }
     }
diff --git a/devices/src/utils/event_loop.rs b/devices/src/utils/event_loop.rs
index c710eb2..f99d18a 100644
--- a/devices/src/utils/event_loop.rs
+++ b/devices/src/utils/event_loop.rs
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 
 use super::error::{Error, Result};
-use base::{error, warn, EpollContext, EpollEvents, Event, PollToken, WatchingEvents};
+use base::{
+    error, warn, wrap_descriptor, AsRawDescriptor, Descriptor, EpollContext, EpollEvents, Event,
+    RawDescriptor, WatchingEvents,
+};
 use std::collections::BTreeMap;
 use std::mem::drop;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::sync::{Arc, Weak};
 use std::thread;
 use sync::Mutex;
@@ -35,31 +37,12 @@
     }
 }
 
-/// Fd is a wrapper of RawFd. It implements AsRawFd trait and PollToken trait for RawFd.
-/// It does not own the fd, thus won't close the fd when dropped.
-struct Fd(pub RawFd);
-impl AsRawFd for Fd {
-    fn as_raw_fd(&self) -> RawFd {
-        self.0
-    }
-}
-
-impl PollToken for Fd {
-    fn as_raw_token(&self) -> u64 {
-        self.0 as u64
-    }
-
-    fn from_raw_token(data: u64) -> Self {
-        Fd(data as RawFd)
-    }
-}
-
 /// EpollEventLoop is an event loop blocked on a set of fds. When a monitered events is triggered,
 /// event loop will invoke the mapped handler.
 pub struct EventLoop {
     fail_handle: Option<Arc<dyn FailHandle>>,
-    poll_ctx: Arc<EpollContext<Fd>>,
-    handlers: Arc<Mutex<BTreeMap<RawFd, Weak<dyn EventHandler>>>>,
+    poll_ctx: Arc<EpollContext<Descriptor>>,
+    handlers: Arc<Mutex<BTreeMap<RawDescriptor, Weak<dyn EventHandler>>>>,
     stop_evt: Event,
 }
 
@@ -78,11 +61,17 @@
             .and_then(|e| Ok((e.try_clone()?, e)))
             .map_err(Error::CreateEvent)?;
 
-        let fd_callbacks: Arc<Mutex<BTreeMap<RawFd, Weak<dyn EventHandler>>>> =
+        let fd_callbacks: Arc<Mutex<BTreeMap<RawDescriptor, Weak<dyn EventHandler>>>> =
             Arc::new(Mutex::new(BTreeMap::new()));
-        let poll_ctx: EpollContext<Fd> = EpollContext::new()
-            .and_then(|pc| pc.add(&stop_evt, Fd(stop_evt.as_raw_fd())).and(Ok(pc)))
-            .map_err(Error::CreatePollContext)?;
+        let poll_ctx: EpollContext<Descriptor> = EpollContext::new()
+            .and_then(|pc| {
+                pc.add(
+                    &wrap_descriptor(&stop_evt),
+                    Descriptor(stop_evt.as_raw_descriptor()),
+                )
+                .and(Ok(pc))
+            })
+            .map_err(Error::CreateWaitContext)?;
 
         let poll_ctx = Arc::new(poll_ctx);
         let event_loop = EventLoop {
@@ -110,10 +99,10 @@
                         }
                     };
                     for event in &events {
-                        if event.token().as_raw_fd() == stop_evt.as_raw_fd() {
+                        if event.token().as_raw_descriptor() == stop_evt.as_raw_descriptor() {
                             return;
                         } else {
-                            let fd = event.token().as_raw_fd();
+                            let fd = event.token().as_raw_descriptor();
                             let mut locked = fd_callbacks.lock();
                             let weak_handler = match locked.get(&fd) {
                                 Some(cb) => cb.clone(),
@@ -137,7 +126,7 @@
                                 }
                                 // If the handler is already gone, we remove the fd.
                                 None => {
-                                    let _ = poll_ctx.delete(&Fd(fd));
+                                    let _ = poll_ctx.delete(&Descriptor(fd));
                                     if locked.remove(&fd).is_none() {
                                         error!("fail to remove handler for file descriptor {}", fd);
                                     }
@@ -153,39 +142,45 @@
     }
 
     /// Add a new event to event loop. The event handler will be invoked when `event` happens on
-    /// `fd`.
+    /// `descriptor`.
     ///
-    /// If the same `fd` is added multiple times, the old handler will be replaced.
+    /// If the same `descriptor` is added multiple times, the old handler will be replaced.
     /// EventLoop will not keep `handler` alive, if handler is dropped when `event` is triggered,
     /// the event will be removed.
     pub fn add_event(
         &self,
-        fd: &dyn AsRawFd,
+        descriptor: &dyn AsRawDescriptor,
         events: WatchingEvents,
         handler: Weak<dyn EventHandler>,
     ) -> Result<()> {
         if self.fail_handle.failed() {
             return Err(Error::EventLoopAlreadyFailed);
         }
-        self.handlers.lock().insert(fd.as_raw_fd(), handler);
+        self.handlers
+            .lock()
+            .insert(descriptor.as_raw_descriptor(), handler);
         // This might fail due to epoll syscall. Check epoll_ctl(2).
         self.poll_ctx
-            .add_fd_with_events(fd, events, Fd(fd.as_raw_fd()))
-            .map_err(Error::PollContextAddFd)
+            .add_fd_with_events(
+                &wrap_descriptor(descriptor),
+                events,
+                Descriptor(descriptor.as_raw_descriptor()),
+            )
+            .map_err(Error::WaitContextAddDescriptor)
     }
 
-    /// Removes event for this `fd`. This function returns false if it fails.
+    /// Removes event for this `descriptor`. This function returns false if it fails.
     ///
-    /// EventLoop does not guarantee all events for `fd` is handled.
-    pub fn remove_event_for_fd(&self, fd: &dyn AsRawFd) -> Result<()> {
+    /// EventLoop does not guarantee all events for `descriptor` is handled.
+    pub fn remove_event_for_fd(&self, descriptor: &dyn AsRawDescriptor) -> Result<()> {
         if self.fail_handle.failed() {
             return Err(Error::EventLoopAlreadyFailed);
         }
         // This might fail due to epoll syscall. Check epoll_ctl(2).
         self.poll_ctx
-            .delete(fd)
-            .map_err(Error::PollContextDeleteFd)?;
-        self.handlers.lock().remove(&fd.as_raw_fd());
+            .delete(&wrap_descriptor(descriptor))
+            .map_err(Error::WaitContextDeleteDescriptor)?;
+        self.handlers.lock().remove(&descriptor.as_raw_descriptor());
         Ok(())
     }
 
diff --git a/devices/src/vfio.rs b/devices/src/vfio.rs
index d3a1f26..24db879 100644
--- a/devices/src/vfio.rs
+++ b/devices/src/vfio.rs
@@ -9,7 +9,6 @@
 use std::fs::{File, OpenOptions};
 use std::io;
 use std::mem;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::os::unix::prelude::FileExt;
 use std::path::{Path, PathBuf};
 use std::sync::Arc;
@@ -18,7 +17,7 @@
 
 use base::{
     ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, warn,
-    AsRawDescriptor, Error, Event, RawDescriptor, SafeDescriptor,
+    AsRawDescriptor, Error, Event, FromRawDescriptor, RawDescriptor, SafeDescriptor,
 };
 use hypervisor::{DeviceKind, Vm};
 use vm_memory::GuestMemory;
@@ -97,7 +96,7 @@
             .open("/dev/vfio/vfio")
             .map_err(VfioError::OpenContainer)?;
 
-        // Safe as file is vfio container fd and ioctl is defined by kernel.
+        // Safe as file is vfio container descriptor and ioctl is defined by kernel.
         let version = unsafe { ioctl(&container, VFIO_GET_API_VERSION()) };
         if version as u8 != VFIO_API_VERSION {
             return Err(VfioError::VfioApiVersion);
@@ -219,9 +218,9 @@
     }
 }
 
-impl AsRawFd for VfioContainer {
-    fn as_raw_fd(&self) -> RawFd {
-        self.container.as_raw_fd()
+impl AsRawDescriptor for VfioContainer {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.container.as_raw_descriptor()
     }
 }
 
@@ -256,10 +255,16 @@
             return Err(VfioError::GroupViable);
         }
 
-        // Safe as we are the owner of group_file and container_raw_fd which are valid value,
+        // Safe as we are the owner of group_file and container_raw_descriptor which are valid value,
         // and we verify the ret value
-        let container_raw_fd = container.as_raw_fd();
-        ret = unsafe { ioctl_with_ref(&group_file, VFIO_GROUP_SET_CONTAINER(), &container_raw_fd) };
+        let container_raw_descriptor = container.as_raw_descriptor();
+        ret = unsafe {
+            ioctl_with_ref(
+                &group_file,
+                VFIO_GROUP_SET_CONTAINER(),
+                &container_raw_descriptor,
+            )
+        };
         if ret < 0 {
             return Err(VfioError::GroupSetContainer(get_error()));
         }
@@ -268,13 +273,13 @@
     }
 
     fn kvm_device_add_group(&self, kvm_vfio_file: &SafeDescriptor) -> Result<(), VfioError> {
-        let group_fd = self.as_raw_fd();
-        let group_fd_ptr = &group_fd as *const i32;
+        let group_descriptor = self.as_raw_descriptor();
+        let group_descriptor_ptr = &group_descriptor as *const i32;
         let vfio_dev_attr = kvm_sys::kvm_device_attr {
             flags: 0,
             group: kvm_sys::KVM_DEV_VFIO_GROUP,
             attr: kvm_sys::KVM_DEV_VFIO_GROUP_ADD as u64,
-            addr: group_fd_ptr as u64,
+            addr: group_descriptor_ptr as u64,
         };
 
         // Safe as we are the owner of vfio_dev_fd and vfio_dev_attr which are valid value,
@@ -305,13 +310,13 @@
         }
 
         // Safe as ret is valid FD
-        Ok(unsafe { File::from_raw_fd(ret) })
+        Ok(unsafe { File::from_raw_descriptor(ret) })
     }
 }
 
-impl AsRawFd for VfioGroup {
-    fn as_raw_fd(&self) -> RawFd {
-        self.group.as_raw_fd()
+impl AsRawDescriptor for VfioGroup {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.group.as_raw_descriptor()
     }
 }
 
@@ -326,7 +331,7 @@
     // flags for this region: read/write/mmap
     flags: u32,
     size: u64,
-    // region offset used to read/write with vfio device fd
+    // region offset used to read/write with vfio device descriptor
     offset: u64,
     // vectors for mmap offset and size
     mmaps: Vec<vfio_region_sparse_mmap_area>,
@@ -338,7 +343,7 @@
 pub struct VfioDevice {
     dev: File,
     container: Arc<Mutex<VfioContainer>>,
-    group_fd: RawFd,
+    group_descriptor: RawDescriptor,
     // vec for vfio device's regions
     regions: Vec<VfioRegion>,
 }
@@ -370,16 +375,20 @@
         Ok(VfioDevice {
             dev: new_dev,
             container,
-            group_fd: group.as_raw_fd(),
+            group_descriptor: group.as_raw_descriptor(),
             regions: dev_regions,
         })
     }
 
     /// Enable vfio device's irq and associate Irqfd Event with device.
-    /// When MSIx is enabled, multi vectors will be supported, so fds is vector and the vector
+    /// When MSIx is enabled, multi vectors will be supported, so descriptors is vector and the vector
     /// length is the num of MSIx vectors
-    pub fn irq_enable(&self, fds: Vec<&Event>, irq_type: VfioIrqType) -> Result<(), VfioError> {
-        let count = fds.len();
+    pub fn irq_enable(
+        &self,
+        descriptors: Vec<&Event>,
+        irq_type: VfioIrqType,
+    ) -> Result<(), VfioError> {
+        let count = descriptors.len();
         let u32_size = mem::size_of::<u32>();
         let mut irq_set = vec_with_array_field::<vfio_irq_set, u32>(count);
         irq_set[0].argsz = (mem::size_of::<vfio_irq_set>() + count * u32_size) as u32;
@@ -392,14 +401,14 @@
         irq_set[0].start = 0;
         irq_set[0].count = count as u32;
 
-        // irq_set.data could be none, bool or fd according to flags, so irq_set.data
-        // is u8 default, here irq_set.data is fd as u32, so 4 default u8 are combined
+        // irq_set.data could be none, bool or descriptor according to flags, so irq_set.data
+        // is u8 default, here irq_set.data is descriptor as u32, so 4 default u8 are combined
         // together as u32. It is safe as enough space is reserved through
         // vec_with_array_field(u32)<count>.
         let mut data = unsafe { irq_set[0].data.as_mut_slice(count * u32_size) };
-        for fd in fds.iter().take(count) {
+        for descriptor in descriptors.iter().take(count) {
             let (left, right) = data.split_at_mut(u32_size);
-            left.copy_from_slice(&fd.as_raw_fd().to_ne_bytes()[..]);
+            left.copy_from_slice(&descriptor.as_raw_descriptor().to_ne_bytes()[..]);
             data = right;
         }
 
@@ -420,8 +429,8 @@
     /// generate another interrupts.
     /// This function enable resample irqfd and let vfio kernel could get EOI notification.
     ///
-    /// fd: should be resample IrqFd.
-    pub fn resample_virq_enable(&self, fd: &Event) -> Result<(), VfioError> {
+    /// descriptor: should be resample IrqFd.
+    pub fn resample_virq_enable(&self, descriptor: &Event) -> Result<(), VfioError> {
         let mut irq_set = vec_with_array_field::<vfio_irq_set, u32>(1);
         irq_set[0].argsz = (mem::size_of::<vfio_irq_set>() + mem::size_of::<u32>()) as u32;
         irq_set[0].flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK;
@@ -430,12 +439,12 @@
         irq_set[0].count = 1;
 
         {
-            // irq_set.data could be none, bool or fd according to flags, so irq_set.data
-            // is u8 default, here irq_set.data is fd as u32, so 4 default u8 are combined
+            // irq_set.data could be none, bool or descriptor according to flags, so irq_set.data
+            // is u8 default, here irq_set.data is descriptor as u32, so 4 default u8 are combined
             // together as u32. It is safe as enough space is reserved through
             // vec_with_array_field(u32)<1>.
-            let fds = unsafe { irq_set[0].data.as_mut_slice(4) };
-            fds.copy_from_slice(&fd.as_raw_fd().to_le_bytes()[..]);
+            let descriptors = unsafe { irq_set[0].data.as_mut_slice(4) };
+            descriptors.copy_from_slice(&descriptor.as_raw_descriptor().to_le_bytes()[..]);
         }
 
         // Safe as we are the owner of self and irq_set which are valid value
@@ -675,7 +684,7 @@
     }
 
     /// get a region's offset
-    /// return: Region offset from the start of vfio device fd
+    /// return: Region offset from the start of vfio device descriptor
     pub fn get_region_offset(&self, index: u32) -> u64 {
         match self.regions.get(index as usize) {
             Some(v) => v.offset,
@@ -782,13 +791,13 @@
         }
     }
 
-    /// get vfio device's fds which are passed into minijail process
-    pub fn keep_fds(&self) -> Vec<RawFd> {
-        let mut fds = Vec::new();
-        fds.push(self.as_raw_fd());
-        fds.push(self.group_fd);
-        fds.push(self.container.lock().as_raw_fd());
-        fds
+    /// get vfio device's descriptors which are passed into minijail process
+    pub fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut rds = Vec::new();
+        rds.push(self.as_raw_descriptor());
+        rds.push(self.group_descriptor);
+        rds.push(self.container.lock().as_raw_descriptor());
+        rds
     }
 
     /// Add (iova, user_addr) map into vfio container iommu table
@@ -807,13 +816,6 @@
     }
 }
 
-// TODO(mikehoyle): Remove this in favor of AsRawDescriptor
-impl AsRawFd for VfioDevice {
-    fn as_raw_fd(&self) -> RawFd {
-        self.dev.as_raw_fd()
-    }
-}
-
 impl AsRawDescriptor for VfioDevice {
     fn as_raw_descriptor(&self) -> RawDescriptor {
         self.dev.as_raw_descriptor()
diff --git a/devices/src/virtio/balloon.rs b/devices/src/virtio/balloon.rs
index 9820dbf..83184b9 100644
--- a/devices/src/virtio/balloon.rs
+++ b/devices/src/virtio/balloon.rs
@@ -3,12 +3,13 @@
 // found in the LICENSE file.
 
 use std::fmt::{self, Display};
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Arc;
 use std::thread;
 
-use base::{self, error, info, warn, Event, PollContext, PollToken};
+use base::{
+    self, error, info, warn, AsRawDescriptor, Event, PollToken, RawDescriptor, WaitContext,
+};
 use data_model::{DataInit, Le16, Le32, Le64};
 use msg_socket::{MsgReceiver, MsgSender};
 use vm_control::{
@@ -228,7 +229,7 @@
         let deflate_queue_evt = queue_evts.remove(0);
         let stats_queue_evt = queue_evts.remove(0);
 
-        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
             (&inflate_queue_evt, Token::Inflate),
             (&deflate_queue_evt, Token::Deflate),
             (&stats_queue_evt, Token::Stats),
@@ -238,13 +239,13 @@
         ]) {
             Ok(pc) => pc,
             Err(e) => {
-                error!("failed creating PollContext: {}", e);
+                error!("failed creating WaitContext: {}", e);
                 return;
             }
         };
 
-        'poll: loop {
-            let events = match poll_ctx.wait() {
+        'wait: loop {
+            let events = match wait_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed polling for events: {}", e);
@@ -254,26 +255,26 @@
 
             let mut needs_interrupt_inflate = false;
             let mut needs_interrupt_deflate = false;
-            for event in events.iter_readable() {
-                match event.token() {
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::Inflate => {
                         if let Err(e) = inflate_queue_evt.read() {
                             error!("failed reading inflate queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         needs_interrupt_inflate |= self.process_inflate_deflate(true);
                     }
                     Token::Deflate => {
                         if let Err(e) = deflate_queue_evt.read() {
                             error!("failed reading deflate queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         needs_interrupt_deflate |= self.process_inflate_deflate(false);
                     }
                     Token::Stats => {
                         if let Err(e) = stats_queue_evt.read() {
                             error!("failed reading stats queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         self.process_stats();
                     }
@@ -297,14 +298,14 @@
                     Token::InterruptResample => {
                         self.interrupt.interrupt_resample();
                     }
-                    Token::Kill => break 'poll,
+                    Token::Kill => break 'wait,
                 }
             }
-            for event in events.iter_hungup() {
-                if event.token() == Token::CommandSocket && !event.readable() {
+            for event in events.iter().filter(|e| e.is_hungup) {
+                if event.token == Token::CommandSocket && !event.is_readable {
                     // If this call fails, the command socket was already removed from the
-                    // PollContext.
-                    let _ = poll_ctx.delete(&self.command_socket);
+                    // WaitContext.
+                    let _ = wait_ctx.delete(&self.command_socket);
                 }
             }
 
@@ -373,8 +374,8 @@
 }
 
 impl VirtioDevice for Balloon {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        vec![self.command_socket.as_ref().unwrap().as_raw_fd()]
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        vec![self.command_socket.as_ref().unwrap().as_raw_descriptor()]
     }
 
     fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/block.rs b/devices/src/virtio/block.rs
index 271d5f7..79e8458 100644
--- a/devices/src/virtio/block.rs
+++ b/devices/src/virtio/block.rs
@@ -6,7 +6,6 @@
 use std::fmt::{self, Display};
 use std::io::{self, Write};
 use std::mem::size_of;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::result;
 use std::sync::Arc;
 use std::thread;
@@ -15,7 +14,10 @@
 
 use base::Error as SysError;
 use base::Result as SysResult;
-use base::{error, info, iov_max, warn, Event, PollContext, PollToken, Timer};
+use base::{
+    error, info, iov_max, warn, AsRawDescriptor, Event, PollToken, RawDescriptor, Timer,
+    WaitContext,
+};
 use data_model::{DataInit, Le16, Le32, Le64};
 use disk::DiskFile;
 use msg_socket::{MsgReceiver, MsgSender};
@@ -377,7 +379,7 @@
         };
         let mut flush_timer_armed = false;
 
-        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
             (&flush_timer, Token::FlushTimer),
             (&queue_evt, Token::QueueAvailable),
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
@@ -391,13 +393,13 @@
         }) {
             Ok(pc) => pc,
             Err(e) => {
-                error!("failed creating PollContext: {}", e);
+                error!("failed creating WaitContext: {}", e);
                 return;
             }
         };
 
-        'poll: loop {
-            let events = match poll_ctx.wait() {
+        'wait: loop {
+            let events = match wait_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed polling for events: {}", e);
@@ -406,22 +408,22 @@
             };
 
             let mut needs_config_interrupt = false;
-            for event in events.iter_readable() {
-                match event.token() {
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::FlushTimer => {
                         if let Err(e) = self.disk_image.fsync() {
                             error!("Failed to flush the disk: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         if let Err(e) = flush_timer.wait() {
                             error!("Failed to clear flush timer: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                     }
                     Token::QueueAvailable => {
                         if let Err(e) = queue_evt.read() {
                             error!("failed reading queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         self.process_queue(0, &mut flush_timer, &mut flush_timer_armed);
                     }
@@ -430,14 +432,14 @@
                             Some(cs) => cs,
                             None => {
                                 error!("received control socket request with no control socket");
-                                break 'poll;
+                                break 'wait;
                             }
                         };
                         let req = match control_socket.recv() {
                             Ok(req) => req,
                             Err(e) => {
                                 error!("control socket failed recv: {}", e);
-                                break 'poll;
+                                break 'wait;
                             }
                         };
 
@@ -454,13 +456,13 @@
                         // We already know there is Some control_socket used to recv a request.
                         if let Err(e) = self.control_socket.as_ref().unwrap().send(&resp) {
                             error!("control socket failed send: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                     }
                     Token::InterruptResample => {
                         self.interrupt.interrupt_resample();
                     }
-                    Token::Kill => break 'poll,
+                    Token::Kill => break 'wait,
                 }
             }
             if needs_config_interrupt {
@@ -716,18 +718,18 @@
 }
 
 impl VirtioDevice for Block {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
 
         if let Some(disk_image) = &self.disk_image {
-            keep_fds.extend(disk_image.as_raw_descriptors());
+            keep_rds.extend(disk_image.as_raw_descriptors());
         }
 
         if let Some(control_socket) = &self.control_socket {
-            keep_fds.push(control_socket.as_raw_fd());
+            keep_rds.push(control_socket.as_raw_descriptor());
         }
 
-        keep_fds
+        keep_rds
     }
 
     fn features(&self) -> u64 {
diff --git a/devices/src/virtio/console.rs b/devices/src/virtio/console.rs
index bd6da3d..93945da 100644
--- a/devices/src/virtio/console.rs
+++ b/devices/src/virtio/console.rs
@@ -3,11 +3,10 @@
 // found in the LICENSE file.
 
 use std::io::{self, Read, Write};
-use std::os::unix::io::RawFd;
 use std::sync::mpsc::{channel, Receiver, TryRecvError};
 use std::thread;
 
-use base::{error, Event, PollContext, PollToken};
+use base::{error, Event, PollToken, RawDescriptor, WaitContext};
 use data_model::{DataInit, Le16, Le32};
 use vm_memory::GuestMemory;
 
@@ -236,7 +235,7 @@
         // the main worker thread with an event for notification bridges this gap.
         let mut in_channel = self.spawn_input_thread(&in_avail_evt);
 
-        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
             (&transmit_evt, Token::TransmitQueueAvailable),
             (&receive_evt, Token::ReceiveQueueAvailable),
             (&in_avail_evt, Token::InputAvailable),
@@ -245,7 +244,7 @@
         ]) {
             Ok(pc) => pc,
             Err(e) => {
-                error!("failed creating PollContext: {}", e);
+                error!("failed creating WaitContext: {}", e);
                 return;
             }
         };
@@ -255,8 +254,8 @@
             None => Box::new(io::sink()),
         };
 
-        'poll: loop {
-            let events = match poll_ctx.wait() {
+        'wait: loop {
+            let events = match wait_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed polling for events: {}", e);
@@ -264,33 +263,33 @@
                 }
             };
 
-            for event in events.iter_readable() {
-                match event.token() {
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::TransmitQueueAvailable => {
                         if let Err(e) = transmit_evt.read() {
                             error!("failed reading transmit queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         self.process_transmit_queue(&mut transmit_queue, &mut output);
                     }
                     Token::ReceiveQueueAvailable => {
                         if let Err(e) = receive_evt.read() {
                             error!("failed reading receive queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         self.handle_input(&mut in_channel, &mut receive_queue);
                     }
                     Token::InputAvailable => {
                         if let Err(e) = in_avail_evt.read() {
                             error!("failed reading in_avail_evt: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         self.handle_input(&mut in_channel, &mut receive_queue);
                     }
                     Token::InterruptResample => {
                         self.interrupt.interrupt_resample();
                     }
-                    Token::Kill => break 'poll,
+                    Token::Kill => break 'wait,
                 }
             }
         }
@@ -304,7 +303,7 @@
     worker_thread: Option<thread::JoinHandle<Worker>>,
     input: Option<Box<dyn io::Read + Send>>,
     output: Option<Box<dyn io::Write + Send>>,
-    keep_fds: Vec<RawFd>,
+    keep_rds: Vec<RawDescriptor>,
 }
 
 impl SerialDevice for Console {
@@ -313,7 +312,7 @@
         _evt: Event,
         input: Option<Box<dyn io::Read + Send>>,
         output: Option<Box<dyn io::Write + Send>>,
-        keep_fds: Vec<RawFd>,
+        keep_rds: Vec<RawDescriptor>,
     ) -> Console {
         Console {
             base_features: base_features(protected_vm),
@@ -321,7 +320,7 @@
             worker_thread: None,
             input,
             output,
-            keep_fds,
+            keep_rds,
         }
     }
 }
@@ -340,8 +339,8 @@
 }
 
 impl VirtioDevice for Console {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        self.keep_fds.clone()
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        self.keep_rds.clone()
     }
 
     fn features(&self) -> u64 {
diff --git a/devices/src/virtio/fs/mod.rs b/devices/src/virtio/fs/mod.rs
index a877270..6c4a3b4 100644
--- a/devices/src/virtio/fs/mod.rs
+++ b/devices/src/virtio/fs/mod.rs
@@ -2,33 +2,25 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std::ffi::FromBytesWithNulError;
 use std::fmt;
 use std::io;
 use std::mem;
-use std::os::unix::io::RawFd;
 use std::sync::Arc;
 use std::thread;
 
-use base::{error, warn, Error as SysError, Event};
+use base::{error, warn, Error as SysError, Event, RawDescriptor};
 use data_model::{DataInit, Le32};
 use vm_memory::GuestMemory;
 
 use crate::virtio::{copy_config, DescriptorError, Interrupt, Queue, VirtioDevice, TYPE_FS};
 
-mod filesystem;
-#[allow(dead_code)]
-mod fuse;
-#[cfg(fuzzing)]
-pub mod fuzzing;
 mod multikey;
 pub mod passthrough;
 mod read_dir;
-mod server;
 mod worker;
 
+use fuse::Server;
 use passthrough::PassthroughFs;
-use server::Server;
 use worker::Worker;
 
 // The fs device does not have a fixed number of queues.
@@ -57,10 +49,10 @@
     TagTooLong(usize),
     /// Failed to create the file system.
     CreateFs(io::Error),
-    /// Creating PollContext failed.
-    CreatePollContext(SysError),
+    /// Creating WaitContext failed.
+    CreateWaitContext(SysError),
     /// Error while polling for events.
-    PollError(SysError),
+    WaitError(SysError),
     /// Error while reading from the virtio queue's Event.
     ReadQueueEvent(SysError),
     /// A request is missing readable descriptors.
@@ -69,27 +61,20 @@
     NoWritableDescriptors,
     /// Failed to signal the virio used queue.
     SignalUsedQueue(SysError),
-    /// Failed to decode protocol messages.
-    DecodeMessage(io::Error),
-    /// Failed to encode protocol messages.
-    EncodeMessage(io::Error),
-    /// One or more parameters are missing.
-    MissingParameter,
-    /// A C string parameter is invalid.
-    InvalidCString(FromBytesWithNulError),
     /// The `len` field of the header is too small.
-    InvalidHeaderLength,
-    /// A `DescriptorChain` contains invalid data.
     InvalidDescriptorChain(DescriptorError),
-    /// The `size` field of the `SetxattrIn` message does not match the length
-    /// of the decoded value.
-    InvalidXattrSize((u32, usize)),
-    /// Requested too many `iovec`s for an `ioctl` retry.
-    TooManyIovecs((usize, usize)),
+    /// Error happened in FUSE.
+    FuseError(fuse::Error),
 }
 
 impl ::std::error::Error for Error {}
 
+impl From<fuse::Error> for Error {
+    fn from(err: fuse::Error) -> Error {
+        Error::FuseError(err)
+    }
+}
+
 impl fmt::Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use Error::*;
@@ -100,29 +85,14 @@
                 len, FS_MAX_TAG_LEN
             ),
             CreateFs(err) => write!(f, "failed to create file system: {}", err),
-            CreatePollContext(err) => write!(f, "failed to create PollContext: {}", err),
-            PollError(err) => write!(f, "failed to poll events: {}", err),
+            CreateWaitContext(err) => write!(f, "failed to create WaitContext: {}", err),
+            WaitError(err) => write!(f, "failed to wait for events: {}", err),
             ReadQueueEvent(err) => write!(f, "failed to read from virtio queue Event: {}", err),
             NoReadableDescriptors => write!(f, "request does not have any readable descriptors"),
             NoWritableDescriptors => write!(f, "request does not have any writable descriptors"),
             SignalUsedQueue(err) => write!(f, "failed to signal used queue: {}", err),
-            DecodeMessage(err) => write!(f, "failed to decode fuse message: {}", err),
-            EncodeMessage(err) => write!(f, "failed to encode fuse message: {}", err),
-            MissingParameter => write!(f, "one or more parameters are missing"),
-            InvalidHeaderLength => write!(f, "the `len` field of the header is too small"),
-            InvalidCString(err) => write!(f, "a c string parameter is invalid: {}", err),
             InvalidDescriptorChain(err) => write!(f, "DescriptorChain is invalid: {}", err),
-            InvalidXattrSize((size, len)) => write!(
-                f,
-                "The `size` field of the `SetxattrIn` message does not match the length of the\
-                 decoded value: size = {}, value.len() = {}",
-                size, len
-            ),
-            TooManyIovecs((count, max)) => write!(
-                f,
-                "requested too many `iovec`s for an `ioctl` retry reply: requested {}, max: {}",
-                count, max
-            ),
+            FuseError(err) => write!(f, "fuse error: {}", err),
         }
     }
 }
@@ -193,10 +163,10 @@
 }
 
 impl VirtioDevice for Fs {
-    fn keep_fds(&self) -> Vec<RawFd> {
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
         self.fs
             .as_ref()
-            .map(PassthroughFs::keep_fds)
+            .map(PassthroughFs::keep_rds)
             .unwrap_or_else(Vec::new)
     }
 
diff --git a/devices/src/virtio/fs/passthrough.rs b/devices/src/virtio/fs/passthrough.rs
index b6700b1..f4591bb 100644
--- a/devices/src/virtio/fs/passthrough.rs
+++ b/devices/src/virtio/fs/passthrough.rs
@@ -11,24 +11,25 @@
 use std::io;
 use std::mem::{self, size_of, MaybeUninit};
 use std::os::raw::{c_int, c_long};
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::ptr;
 use std::str::FromStr;
 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
 use std::sync::Arc;
 use std::time::Duration;
 
-use base::{error, ioctl_ior_nr, ioctl_iow_nr, ioctl_with_mut_ptr, ioctl_with_ptr, warn};
+use base::{
+    error, ioctl_ior_nr, ioctl_iow_nr, ioctl_with_mut_ptr, ioctl_with_ptr, warn, AsRawDescriptor,
+    FromRawDescriptor, RawDescriptor,
+};
 use data_model::DataInit;
+use fuse::filesystem::{
+    Context, DirectoryIterator, Entry, FileSystem, FsOptions, GetxattrReply, IoctlFlags,
+    IoctlIovec, IoctlReply, ListxattrReply, OpenOptions, SetattrValid, ZeroCopyReader,
+    ZeroCopyWriter, ROOT_ID,
+};
 use rand_ish::SimpleRng;
 use sync::Mutex;
 
-use crate::virtio::fs::filesystem::{
-    Context, DirectoryIterator, Entry, FileSystem, FsOptions, GetxattrReply, IoctlFlags,
-    IoctlIovec, IoctlReply, ListxattrReply, OpenOptions, SetattrValid, ZeroCopyReader,
-    ZeroCopyWriter,
-};
-use crate::virtio::fs::fuse;
 use crate::virtio::fs::multikey::MultikeyBTreeMap;
 use crate::virtio::fs::read_dir::ReadDir;
 
@@ -221,9 +222,9 @@
     let fscreate = unsafe { CStr::from_bytes_with_nul_unchecked(b"thread-self/attr/fscreate\0") };
 
     // Safe because this doesn't modify any memory and we check the return value.
-    let fd = unsafe {
+    let raw_descriptor = unsafe {
         libc::openat(
-            proc.as_raw_fd(),
+            proc.as_raw_descriptor(),
             fscreate.as_ptr(),
             libc::O_CLOEXEC | libc::O_WRONLY,
         )
@@ -231,15 +232,15 @@
 
     // We don't expect this to fail and we're not in a position to return an error here so just
     // panic.
-    if fd < 0 {
+    if raw_descriptor < 0 {
         panic!(
             "Failed to open /proc/thread-self/attr/fscreate: {}",
             io::Error::last_os_error()
         );
     }
 
-    // Safe because we just opened this fd.
-    unsafe { File::from_raw_fd(fd) }
+    // Safe because we just opened this descriptor.
+    unsafe { File::from_raw_descriptor(raw_descriptor) }
 }
 
 struct ScopedSecurityContext;
@@ -252,7 +253,7 @@
             // Safe because this doesn't modify any memory and we check the return value.
             let ret = unsafe {
                 libc::write(
-                    file.as_raw_fd(),
+                    file.as_raw_descriptor(),
                     ctx.as_ptr() as *const libc::c_void,
                     ctx.to_bytes_with_nul().len(),
                 )
@@ -277,7 +278,7 @@
                 .expect("Uninitialized thread-local when dropping ScopedSecurityContext");
 
             // Safe because this doesn't modify any memory and we check the return value.
-            let ret = unsafe { libc::write(file.as_raw_fd(), ptr::null(), 0) };
+            let ret = unsafe { libc::write(file.as_raw_descriptor(), ptr::null(), 0) };
 
             if ret < 0 {
                 warn!(
@@ -303,7 +304,7 @@
     // value.
     let res = unsafe {
         libc::fstatat64(
-            f.as_raw_fd(),
+            f.as_raw_descriptor(),
             pathname.as_ptr(),
             st.as_mut_ptr(),
             libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
@@ -462,23 +463,23 @@
         let proc_cstr = unsafe { CStr::from_bytes_with_nul_unchecked(PROC_CSTR) };
 
         // Safe because this doesn't modify any memory and we check the return value.
-        let fd = unsafe {
+        let raw_descriptor = unsafe {
             libc::openat(
                 libc::AT_FDCWD,
                 proc_cstr.as_ptr(),
                 libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
             )
         };
-        if fd < 0 {
+        if raw_descriptor < 0 {
             return Err(io::Error::last_os_error());
         }
 
-        // Safe because we just opened this fd.
-        let proc = unsafe { File::from_raw_fd(fd) };
+        // Safe because we just opened this descriptor.
+        let proc = unsafe { File::from_raw_descriptor(raw_descriptor) };
 
         Ok(PassthroughFs {
             inodes: Mutex::new(MultikeyBTreeMap::new()),
-            next_inode: AtomicU64::new(fuse::ROOT_ID + 1),
+            next_inode: AtomicU64::new(ROOT_ID + 1),
 
             handles: Mutex::new(BTreeMap::new()),
             next_handle: AtomicU64::new(0),
@@ -491,8 +492,8 @@
         })
     }
 
-    pub fn keep_fds(&self) -> Vec<RawFd> {
-        vec![self.proc.as_raw_fd()]
+    pub fn keep_rds(&self) -> Vec<RawDescriptor> {
+        vec![self.proc.as_raw_descriptor()]
     }
 
     fn rewrite_xattr_name<'xattr>(&self, name: &'xattr CStr) -> Cow<'xattr, CStr> {
@@ -532,7 +533,7 @@
     }
 
     fn open_inode(&self, inode: &InodeData, mut flags: i32) -> io::Result<File> {
-        let pathname = CString::new(format!("self/fd/{}", inode.file.as_raw_fd()))
+        let pathname = CString::new(format!("self/fd/{}", inode.file.as_raw_descriptor()))
             .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
 
         // When writeback caching is enabled, the kernel may send read requests even if the
@@ -558,23 +559,23 @@
         // really check `flags` because if the kernel can't handle poorly specified flags then we
         // have much bigger problems. Also, clear the `O_NOFOLLOW` flag if it is set since we need
         // to follow the `/proc/self/fd` symlink to get the file.
-        let fd = unsafe {
+        let raw_descriptor = unsafe {
             libc::openat(
-                self.proc.as_raw_fd(),
+                self.proc.as_raw_descriptor(),
                 pathname.as_ptr(),
                 (flags | libc::O_CLOEXEC) & !(libc::O_NOFOLLOW | libc::O_DIRECT),
             )
         };
-        if fd < 0 {
+        if raw_descriptor < 0 {
             return Err(io::Error::last_os_error());
         }
 
-        // Safe because we just opened this fd.
-        Ok(unsafe { File::from_raw_fd(fd) })
+        // Safe because we just opened this descriptor.
+        Ok(unsafe { File::from_raw_descriptor(raw_descriptor) })
     }
 
     // Performs an ascii case insensitive lookup and returns an O_PATH fd for the entry, if found.
-    fn ascii_casefold_lookup(&self, dir: &InodeData, name: &[u8]) -> io::Result<RawFd> {
+    fn ascii_casefold_lookup(&self, dir: &InodeData, name: &[u8]) -> io::Result<RawDescriptor> {
         let parent = self.open_inode(dir, libc::O_RDONLY | libc::O_DIRECTORY)?;
         let mut buf = [0u8; 1024];
         let mut offset = 0;
@@ -589,7 +590,7 @@
                 if name.eq_ignore_ascii_case(entry.name.to_bytes()) {
                     return Ok(unsafe {
                         libc::openat(
-                            parent.as_raw_fd(),
+                            parent.as_raw_descriptor(),
                             entry.name.as_ptr(),
                             libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
                         )
@@ -601,30 +602,30 @@
     }
 
     fn do_lookup(&self, parent: &InodeData, name: &CStr) -> io::Result<Entry> {
-        let fd = {
+        let raw_descriptor = {
             // Safe because this doesn't modify any memory and we check the return value.
-            let fd = unsafe {
+            let raw_descriptor = unsafe {
                 libc::openat(
-                    parent.file.as_raw_fd(),
+                    parent.file.as_raw_descriptor(),
                     name.as_ptr(),
                     libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
                 )
             };
 
-            if fd < 0 && self.cfg.ascii_casefold {
+            if raw_descriptor < 0 && self.cfg.ascii_casefold {
                 // Ignore any errors during casefold lookup.
                 self.ascii_casefold_lookup(parent, name.to_bytes())
-                    .unwrap_or(fd)
+                    .unwrap_or(raw_descriptor)
             } else {
-                fd
+                raw_descriptor
             }
         };
-        if fd < 0 {
+        if raw_descriptor < 0 {
             return Err(io::Error::last_os_error());
         }
 
-        // Safe because we just opened this fd.
-        let f = unsafe { File::from_raw_fd(fd) };
+        // Safe because we just opened this descriptor.
+        let f = unsafe { File::from_raw_descriptor(raw_descriptor) };
 
         let st = stat(&f)?;
 
@@ -722,7 +723,7 @@
 
     fn do_unlink(&self, parent: &InodeData, name: &CStr, flags: libc::c_int) -> io::Result<()> {
         // Safe because this doesn't modify any memory and we check the return value.
-        let res = unsafe { libc::unlinkat(parent.file.as_raw_fd(), name.as_ptr(), flags) };
+        let res = unsafe { libc::unlinkat(parent.file.as_raw_descriptor(), name.as_ptr(), flags) };
         if res == 0 {
             Ok(())
         } else {
@@ -738,14 +739,12 @@
     where
         F: FnOnce() -> T,
     {
-        let root = self
-            .find_inode(fuse::ROOT_ID)
-            .expect("failed to find root inode");
+        let root = self.find_inode(ROOT_ID).expect("failed to find root inode");
         let chdir_lock = self.chdir_mutex.lock();
 
         // Safe because this doesn't modify any memory and we check the return value. Since the
         // fchdir should never fail we just use debug_asserts.
-        let proc_cwd = unsafe { libc::fchdir(self.proc.as_raw_fd()) };
+        let proc_cwd = unsafe { libc::fchdir(self.proc.as_raw_descriptor()) };
         debug_assert_eq!(
             proc_cwd,
             0,
@@ -757,7 +756,7 @@
 
         // Safe because this doesn't modify any memory and we check the return value. Since the
         // fchdir should never fail we just use debug_asserts.
-        let root_cwd = unsafe { libc::fchdir(root.file.as_raw_fd()) };
+        let root_cwd = unsafe { libc::fchdir(root.file.as_raw_descriptor()) };
         debug_assert_eq!(
             root_cwd,
             0,
@@ -774,7 +773,7 @@
             // For non-regular files and directories, we cannot open the fd normally. Instead we
             // emulate an _at syscall by changing the CWD to /proc, running the path based syscall,
             // and then setting the CWD back to the root directory.
-            let path = CString::new(format!("self/fd/{}", inode.file.as_raw_fd()))
+            let path = CString::new(format!("self/fd/{}", inode.file.as_raw_descriptor()))
                 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
 
             // Safe because this will only modify `value` and we check the return value.
@@ -798,7 +797,7 @@
             // Safe because this will only write to `value` and we check the return value.
             unsafe {
                 libc::fgetxattr(
-                    f.as_raw_fd(),
+                    f.as_raw_descriptor(),
                     name.as_ptr(),
                     value.as_mut_ptr() as *mut libc::c_void,
                     value.len() as libc::size_t,
@@ -1054,7 +1053,7 @@
         let name = CString::new(name).expect("SimpleRng produced string with nul-bytes");
 
         // Safe because this doesn't modify any memory and we check the return value.
-        let ret = unsafe { libc::mkdirat(parent.as_raw_fd(), name.as_ptr(), mode) };
+        let ret = unsafe { libc::mkdirat(parent.as_raw_descriptor(), name.as_ptr(), mode) };
         if ret == 0 {
             return Ok(name);
         }
@@ -1089,22 +1088,22 @@
         let name = create_temp_dir(parent, mode)?;
 
         // Safe because this doesn't modify any memory and we check the return value.
-        let fd = unsafe {
+        let raw_descriptor = unsafe {
             libc::openat(
-                parent.as_raw_fd(),
+                parent.as_raw_descriptor(),
                 name.as_ptr(),
                 libc::O_DIRECTORY | libc::O_CLOEXEC,
             )
         };
-        if fd < 0 {
+        if raw_descriptor < 0 {
             return Err(io::Error::last_os_error());
         }
 
         Ok(TempDir {
             parent,
             name,
-            // Safe because we just opened this fd.
-            file: unsafe { File::from_raw_fd(fd) },
+            // Safe because we just opened this descriptor.
+            file: unsafe { File::from_raw_descriptor(raw_descriptor) },
         })
     }
 
@@ -1126,9 +1125,9 @@
     }
 }
 
-impl<'a> AsRawFd for TempDir<'a> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.file.as_raw_fd()
+impl<'a> AsRawDescriptor for TempDir<'a> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.file.as_raw_descriptor()
     }
 }
 
@@ -1137,7 +1136,7 @@
         // Safe because this doesn't modify any memory and we check the return value.
         let ret = unsafe {
             libc::unlinkat(
-                self.parent.as_raw_fd(),
+                self.parent.as_raw_descriptor(),
                 self.name.as_ptr(),
                 libc::AT_REMOVEDIR,
             )
@@ -1161,19 +1160,19 @@
         // Safe because this doesn't modify any memory and we check the return value.
         // We use `O_PATH` because we just want this for traversing the directory tree
         // and not for actually reading the contents.
-        let fd = unsafe {
+        let raw_descriptor = unsafe {
             libc::openat(
                 libc::AT_FDCWD,
                 root.as_ptr(),
                 libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
             )
         };
-        if fd < 0 {
+        if raw_descriptor < 0 {
             return Err(io::Error::last_os_error());
         }
 
-        // Safe because we just opened this fd above.
-        let f = unsafe { File::from_raw_fd(fd) };
+        // Safe because we just opened this descriptor above.
+        let f = unsafe { File::from_raw_descriptor(raw_descriptor) };
 
         let st = stat(&f)?;
 
@@ -1186,13 +1185,13 @@
 
         // Not sure why the root inode gets a refcount of 2 but that's what libfuse does.
         inodes.insert(
-            fuse::ROOT_ID,
+            ROOT_ID,
             InodeAltKey {
                 ino: st.st_ino,
                 dev: st.st_dev,
             },
             Arc::new(InodeData {
-                inode: fuse::ROOT_ID,
+                inode: ROOT_ID,
                 file: f,
                 refcount: AtomicU64::new(2),
                 filetype: st.st_mode.into(),
@@ -1223,7 +1222,7 @@
         let mut out = MaybeUninit::<libc::statvfs64>::zeroed();
 
         // Safe because this will only modify `out` and we check the return value.
-        let res = unsafe { libc::fstatvfs64(data.file.as_raw_fd(), out.as_mut_ptr()) };
+        let res = unsafe { libc::fstatvfs64(data.file.as_raw_descriptor(), out.as_mut_ptr()) };
         if res == 0 {
             // Safe because the kernel guarantees that `out` has been initialized.
             Ok(unsafe { out.assume_init() })
@@ -1312,7 +1311,7 @@
 
         // Set the uid and gid for the directory. Safe because this doesn't modify any memory and we
         // check the return value.
-        let ret = unsafe { libc::fchown(tmpdir.as_raw_fd(), ctx.uid, gid) };
+        let ret = unsafe { libc::fchown(tmpdir.as_raw_descriptor(), ctx.uid, gid) };
         if ret < 0 {
             return Err(io::Error::last_os_error());
         }
@@ -1323,9 +1322,9 @@
         let ret = unsafe {
             libc::syscall(
                 libc::SYS_renameat2,
-                data.file.as_raw_fd(),
+                data.file.as_raw_descriptor(),
                 tmpdir.basename().as_ptr(),
-                data.file.as_raw_fd(),
+                data.file.as_raw_descriptor(),
                 name.as_ptr(),
                 libc::RENAME_NOREPLACE,
             )
@@ -1434,14 +1433,20 @@
         let current_dir = unsafe { CStr::from_bytes_with_nul_unchecked(b".\0") };
 
         // Safe because this doesn't modify any memory and we check the return value.
-        let fd =
-            unsafe { libc::openat(data.file.as_raw_fd(), current_dir.as_ptr(), tmpflags, mode) };
-        if fd < 0 {
+        let raw_descriptor = unsafe {
+            libc::openat(
+                data.file.as_raw_descriptor(),
+                current_dir.as_ptr(),
+                tmpflags,
+                mode,
+            )
+        };
+        if raw_descriptor < 0 {
             return Err(io::Error::last_os_error());
         }
 
-        // Safe because we just opened this fd.
-        let tmpfile = unsafe { File::from_raw_fd(fd) };
+        // Safe because we just opened this descriptor.
+        let tmpfile = unsafe { File::from_raw_descriptor(raw_descriptor) };
 
         // We need to respect the setgid bit in the parent directory if it is set.
         let st = stat(&data.file)?;
@@ -1453,21 +1458,21 @@
 
         // Now set the uid and gid for the file. Safe because this doesn't modify any memory and we
         // check the return value.
-        let ret = unsafe { libc::fchown(tmpfile.as_raw_fd(), ctx.uid, gid) };
+        let ret = unsafe { libc::fchown(tmpfile.as_raw_descriptor(), ctx.uid, gid) };
         if ret < 0 {
             return Err(io::Error::last_os_error());
         }
 
-        let proc_path = CString::new(format!("self/fd/{}", tmpfile.as_raw_fd()))
+        let proc_path = CString::new(format!("self/fd/{}", tmpfile.as_raw_descriptor()))
             .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
 
         // Finally link it into the file system tree so that it's visible to other processes. Safe
         // because this doesn't modify any memory and we check the return value.
         let ret = unsafe {
             libc::linkat(
-                self.proc.as_raw_fd(),
+                self.proc.as_raw_descriptor(),
                 proc_path.as_ptr(),
-                data.file.as_raw_fd(),
+                data.file.as_raw_descriptor(),
                 name.as_ptr(),
                 libc::AT_SYMLINK_FOLLOW,
             )
@@ -1558,7 +1563,7 @@
         let inode_data = self.find_inode(inode)?;
 
         enum Data {
-            Handle(Arc<HandleData>, RawFd),
+            Handle(Arc<HandleData>, RawDescriptor),
             ProcPath(CString),
         }
 
@@ -1566,10 +1571,10 @@
         let data = if let Some(handle) = handle {
             let hd = self.find_handle(handle, inode)?;
 
-            let fd = hd.file.lock().as_raw_fd();
+            let fd = hd.file.lock().as_raw_descriptor();
             Data::Handle(hd, fd)
         } else {
-            let pathname = CString::new(format!("self/fd/{}", inode_data.file.as_raw_fd()))
+            let pathname = CString::new(format!("self/fd/{}", inode_data.file.as_raw_descriptor()))
                 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
             Data::ProcPath(pathname)
         };
@@ -1580,7 +1585,7 @@
                 match data {
                     Data::Handle(_, fd) => libc::fchmod(fd, attr.st_mode),
                     Data::ProcPath(ref p) => {
-                        libc::fchmodat(self.proc.as_raw_fd(), p.as_ptr(), attr.st_mode, 0)
+                        libc::fchmodat(self.proc.as_raw_descriptor(), p.as_ptr(), attr.st_mode, 0)
                     }
                 }
             };
@@ -1609,7 +1614,7 @@
             // Safe because this doesn't modify any memory and we check the return value.
             let res = unsafe {
                 libc::fchownat(
-                    inode_data.file.as_raw_fd(),
+                    inode_data.file.as_raw_descriptor(),
                     empty.as_ptr(),
                     uid,
                     gid,
@@ -1628,7 +1633,7 @@
                 _ => {
                     // There is no `ftruncateat` so we need to get a new fd and truncate it.
                     let f = self.open_inode(&inode_data, libc::O_NONBLOCK | libc::O_RDWR)?;
-                    unsafe { libc::ftruncate64(f.as_raw_fd(), attr.st_size) }
+                    unsafe { libc::ftruncate64(f.as_raw_descriptor(), attr.st_size) }
                 }
             };
             if res < 0 {
@@ -1666,7 +1671,7 @@
             let res = match data {
                 Data::Handle(_, fd) => unsafe { libc::futimens(fd, tvs.as_ptr()) },
                 Data::ProcPath(ref p) => unsafe {
-                    libc::utimensat(self.proc.as_raw_fd(), p.as_ptr(), tvs.as_ptr(), 0)
+                    libc::utimensat(self.proc.as_raw_descriptor(), p.as_ptr(), tvs.as_ptr(), 0)
                 },
             };
             if res < 0 {
@@ -1695,9 +1700,9 @@
         let res = unsafe {
             libc::syscall(
                 libc::SYS_renameat2,
-                old_inode.file.as_raw_fd(),
+                old_inode.file.as_raw_descriptor(),
                 oldname.as_ptr(),
-                new_inode.file.as_raw_fd(),
+                new_inode.file.as_raw_descriptor(),
                 newname.as_ptr(),
                 flags,
             )
@@ -1737,7 +1742,7 @@
         // Safe because this doesn't modify any memory and we check the return value.
         let res = unsafe {
             libc::mknodat(
-                data.file.as_raw_fd(),
+                data.file.as_raw_descriptor(),
                 name.as_ptr(),
                 mode as libc::mode_t,
                 rdev as libc::dev_t,
@@ -1761,15 +1766,15 @@
         let data = self.find_inode(inode)?;
         let new_inode = self.find_inode(newparent)?;
 
-        let path = CString::new(format!("self/fd/{}", data.file.as_raw_fd()))
+        let path = CString::new(format!("self/fd/{}", data.file.as_raw_descriptor()))
             .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
 
         // Safe because this doesn't modify any memory and we check the return value.
         let res = unsafe {
             libc::linkat(
-                self.proc.as_raw_fd(),
+                self.proc.as_raw_descriptor(),
                 path.as_ptr(),
-                new_inode.file.as_raw_fd(),
+                new_inode.file.as_raw_descriptor(),
                 newname.as_ptr(),
                 libc::AT_SYMLINK_FOLLOW,
             )
@@ -1799,8 +1804,13 @@
         let (_uid, _gid) = set_creds(ctx.uid, ctx.gid)?;
 
         // Safe because this doesn't modify any memory and we check the return value.
-        let res =
-            unsafe { libc::symlinkat(linkname.as_ptr(), data.file.as_raw_fd(), name.as_ptr()) };
+        let res = unsafe {
+            libc::symlinkat(
+                linkname.as_ptr(),
+                data.file.as_raw_descriptor(),
+                name.as_ptr(),
+            )
+        };
         if res == 0 {
             self.do_lookup(&data, name)
         } else {
@@ -1820,7 +1830,7 @@
         // Safe because this will only modify the contents of `buf` and we check the return value.
         let res = unsafe {
             libc::readlinkat(
-                data.file.as_raw_fd(),
+                data.file.as_raw_descriptor(),
                 empty.as_ptr(),
                 buf.as_mut_ptr() as *mut libc::c_char,
                 buf.len(),
@@ -1847,7 +1857,7 @@
         // behavior by doing the same thing (dup-ing the fd and then immediately closing it). Safe
         // because this doesn't modify any memory and we check the return values.
         unsafe {
-            let newfd = libc::dup(data.file.lock().as_raw_fd());
+            let newfd = libc::dup(data.file.lock().as_raw_descriptor());
             if newfd < 0 {
                 return Err(io::Error::last_os_error());
             }
@@ -1863,7 +1873,7 @@
     fn fsync(&self, _ctx: Context, inode: Inode, datasync: bool, handle: Handle) -> io::Result<()> {
         let data = self.find_handle(handle, inode)?;
 
-        let fd = data.file.lock().as_raw_fd();
+        let fd = data.file.lock().as_raw_descriptor();
 
         // Safe because this doesn't modify any memory and we check the return value.
         let res = unsafe {
@@ -1958,7 +1968,7 @@
             // For non-regular files and directories, we cannot open the fd normally. Instead we
             // emulate an _at syscall by changing the CWD to /proc, running the path based syscall,
             // and then setting the CWD back to the root directory.
-            let path = CString::new(format!("self/fd/{}", data.file.as_raw_fd()))
+            let path = CString::new(format!("self/fd/{}", data.file.as_raw_descriptor()))
                 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
 
             // Safe because this doesn't modify any memory and we check the return value.
@@ -1983,7 +1993,7 @@
             // Safe because this doesn't modify any memory and we check the return value.
             unsafe {
                 libc::fsetxattr(
-                    f.as_raw_fd(),
+                    f.as_raw_descriptor(),
                     name.as_ptr(),
                     value.as_ptr() as *const libc::c_void,
                     value.len() as libc::size_t,
@@ -2035,7 +2045,7 @@
             // For non-regular files and directories, we cannot open the fd normally. Instead we
             // emulate an _at syscall by changing the CWD to /proc, running the path based syscall,
             // and then setting the CWD back to the root directory.
-            let path = CString::new(format!("self/fd/{}", data.file.as_raw_fd()))
+            let path = CString::new(format!("self/fd/{}", data.file.as_raw_descriptor()))
                 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
 
             // Safe because this will only modify `buf` and we check the return value.
@@ -2058,7 +2068,7 @@
             // Safe because this will only write to `buf` and we check the return value.
             unsafe {
                 libc::flistxattr(
-                    f.as_raw_fd(),
+                    f.as_raw_descriptor(),
                     buf.as_mut_ptr() as *mut libc::c_char,
                     buf.len() as libc::size_t,
                 )
@@ -2095,7 +2105,7 @@
             // For non-regular files and directories, we cannot open the fd normally. Instead we
             // emulate an _at syscall by changing the CWD to /proc, running the path based syscall,
             // and then setting the CWD back to the root directory.
-            let path = CString::new(format!("self/fd/{}", data.file.as_raw_fd()))
+            let path = CString::new(format!("self/fd/{}", data.file.as_raw_descriptor()))
                 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
 
             // Safe because this doesn't modify any memory and we check the return value.
@@ -2110,7 +2120,7 @@
             let f = self.open_inode(&data, libc::O_RDONLY | dir)?;
 
             // Safe because this doesn't modify any memory and we check the return value.
-            unsafe { libc::fremovexattr(f.as_raw_fd(), name.as_ptr()) }
+            unsafe { libc::fremovexattr(f.as_raw_descriptor(), name.as_ptr()) }
         };
 
         if res == 0 {
@@ -2131,7 +2141,7 @@
     ) -> io::Result<()> {
         let data = self.find_handle(handle, inode)?;
 
-        let fd = data.file.lock().as_raw_fd();
+        let fd = data.file.lock().as_raw_descriptor();
         // Safe because this doesn't modify any memory and we check the return value.
         let res = unsafe {
             libc::fallocate64(
@@ -2246,8 +2256,8 @@
         let src_data = self.find_handle(handle_src, inode_src)?;
         let dst_data = self.find_handle(handle_dst, inode_dst)?;
 
-        let src = src_data.file.lock().as_raw_fd();
-        let dst = dst_data.file.lock().as_raw_fd();
+        let src = src_data.file.lock().as_raw_descriptor();
+        let dst = dst_data.file.lock().as_raw_descriptor();
 
         let res = unsafe {
             libc::syscall(
@@ -2289,7 +2299,7 @@
             )
         };
         assert!(fd >= 0, "Failed to open env::temp_dir()");
-        let parent = unsafe { File::from_raw_fd(fd) };
+        let parent = unsafe { File::from_raw_descriptor(fd) };
         let t = TempDir::new(&parent, 0o755).expect("Failed to create temporary directory");
 
         let basename = t.basename().to_string_lossy();
@@ -2310,7 +2320,7 @@
             )
         };
         assert!(fd >= 0, "Failed to open env::temp_dir()");
-        let parent = unsafe { File::from_raw_fd(fd) };
+        let parent = unsafe { File::from_raw_descriptor(fd) };
         let t = TempDir::new(&parent, 0o755).expect("Failed to create temporary directory");
 
         let basename = t.basename().to_string_lossy();
@@ -2331,7 +2341,7 @@
             )
         };
         assert!(fd >= 0, "Failed to open env::temp_dir()");
-        let parent = unsafe { File::from_raw_fd(fd) };
+        let parent = unsafe { File::from_raw_descriptor(fd) };
         let t = TempDir::new(&parent, 0o755).expect("Failed to create temporary directory");
 
         let (basename_cstr, _) = t.into_inner();
diff --git a/devices/src/virtio/fs/read_dir.rs b/devices/src/virtio/fs/read_dir.rs
index d02960d..710943c 100644
--- a/devices/src/virtio/fs/read_dir.rs
+++ b/devices/src/virtio/fs/read_dir.rs
@@ -6,11 +6,10 @@
 use std::io;
 use std::mem::size_of;
 use std::ops::{Deref, DerefMut};
-use std::os::unix::io::AsRawFd;
 
+use base::AsRawDescriptor;
 use data_model::DataInit;
-
-use crate::virtio::fs::filesystem::{DirEntry, DirectoryIterator};
+use fuse::filesystem::{DirEntry, DirectoryIterator};
 
 #[repr(C, packed)]
 #[derive(Clone, Copy)]
@@ -29,9 +28,9 @@
 }
 
 impl<P: DerefMut<Target = [u8]>> ReadDir<P> {
-    pub fn new<D: AsRawFd>(dir: &D, offset: libc::off64_t, mut buf: P) -> io::Result<Self> {
+    pub fn new<D: AsRawDescriptor>(dir: &D, offset: libc::off64_t, mut buf: P) -> io::Result<Self> {
         // Safe because this doesn't modify any memory and we check the return value.
-        let res = unsafe { libc::lseek64(dir.as_raw_fd(), offset, libc::SEEK_SET) };
+        let res = unsafe { libc::lseek64(dir.as_raw_descriptor(), offset, libc::SEEK_SET) };
         if res < 0 {
             return Err(io::Error::last_os_error());
         }
@@ -41,7 +40,7 @@
         let res = unsafe {
             libc::syscall(
                 libc::SYS_getdents64,
-                dir.as_raw_fd(),
+                dir.as_raw_descriptor(),
                 buf.as_mut_ptr() as *mut LinuxDirent64,
                 buf.len() as libc::c_int,
             )
diff --git a/devices/src/virtio/fs/worker.rs b/devices/src/virtio/fs/worker.rs
index 7875ed0..9a4da6c 100644
--- a/devices/src/virtio/fs/worker.rs
+++ b/devices/src/virtio/fs/worker.rs
@@ -2,20 +2,48 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use std::fs::File;
+use std::io;
 use std::sync::Arc;
 
-use base::{error, Event, PollContext, PollToken};
+use base::{error, Event, PollToken, WaitContext};
+use fuse::filesystem::{FileSystem, ZeroCopyReader, ZeroCopyWriter};
 use vm_memory::GuestMemory;
 
-use crate::virtio::fs::filesystem::FileSystem;
-use crate::virtio::fs::server::Server;
 use crate::virtio::fs::{Error, Result};
 use crate::virtio::{Interrupt, Queue, Reader, Writer};
 
+impl fuse::Reader for Reader {}
+
+impl fuse::Writer for Writer {
+    fn write_at<F>(&mut self, offset: usize, f: F) -> io::Result<usize>
+    where
+        F: Fn(&mut Self) -> io::Result<usize>,
+    {
+        let mut writer = Writer::split_at(self, offset);
+        f(&mut writer)
+    }
+
+    fn has_sufficient_buffer(&self, size: u32) -> bool {
+        self.available_bytes() >= size as usize
+    }
+}
+
+impl ZeroCopyReader for Reader {
+    fn read_to(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize> {
+        self.read_to_at(f, count, off)
+    }
+}
+
+impl ZeroCopyWriter for Writer {
+    fn write_from(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize> {
+        self.write_from_at(f, count, off)
+    }
+}
 pub struct Worker<F: FileSystem + Sync> {
     mem: GuestMemory,
     queue: Queue,
-    server: Arc<Server<F>>,
+    server: Arc<fuse::Server<F>>,
     irq: Arc<Interrupt>,
 }
 
@@ -23,7 +51,7 @@
     pub fn new(
         mem: GuestMemory,
         queue: Queue,
-        server: Arc<Server<F>>,
+        server: Arc<fuse::Server<F>>,
         irq: Arc<Interrupt>,
     ) -> Worker<F> {
         Worker {
@@ -73,20 +101,20 @@
             Kill,
         }
 
-        let poll_ctx =
-            PollContext::build_with(&[(&queue_evt, Token::QueueReady), (&kill_evt, Token::Kill)])
-                .map_err(Error::CreatePollContext)?;
+        let wait_ctx =
+            WaitContext::build_with(&[(&queue_evt, Token::QueueReady), (&kill_evt, Token::Kill)])
+                .map_err(Error::CreateWaitContext)?;
 
         if watch_resample_event {
-            poll_ctx
+            wait_ctx
                 .add(self.irq.get_resample_evt(), Token::InterruptResample)
-                .map_err(Error::CreatePollContext)?;
+                .map_err(Error::CreateWaitContext)?;
         }
 
         loop {
-            let events = poll_ctx.wait().map_err(Error::PollError)?;
-            for event in events.iter_readable() {
-                match event.token() {
+            let events = wait_ctx.wait().map_err(Error::WaitError)?;
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::QueueReady => {
                         queue_evt.read().map_err(Error::ReadQueueEvent)?;
                         if let Err(e) = self.process_queue() {
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index 330e5a1..6fd7095 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -14,7 +14,6 @@
 use std::io::Read;
 use std::mem::{self, size_of};
 use std::num::NonZeroU8;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::Arc;
@@ -23,7 +22,10 @@
 
 use data_model::*;
 
-use base::{debug, error, warn, Event, ExternalMapping, PollContext, PollToken};
+use base::{
+    debug, error, warn, AsRawDescriptor, Event, ExternalMapping, PollToken, RawDescriptor,
+    WaitContext,
+};
 use sync::Mutex;
 use vm_memory::{GuestAddress, GuestMemory};
 
@@ -72,6 +74,8 @@
     pub renderer_use_glx: bool,
     pub renderer_use_surfaceless: bool,
     #[cfg(feature = "gfxstream")]
+    pub gfxstream_use_guest_angle: bool,
+    #[cfg(feature = "gfxstream")]
     pub gfxstream_use_syncfd: bool,
     #[cfg(feature = "gfxstream")]
     pub gfxstream_support_vulkan: bool,
@@ -99,6 +103,8 @@
             renderer_use_glx: false,
             renderer_use_surfaceless: true,
             #[cfg(feature = "gfxstream")]
+            gfxstream_use_guest_angle: false,
+            #[cfg(feature = "gfxstream")]
             gfxstream_use_syncfd: true,
             #[cfg(feature = "gfxstream")]
             gfxstream_support_vulkan: true,
@@ -941,7 +947,7 @@
             ResourceBridge { index: usize },
         }
 
-        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
             (&self.ctrl_evt, Token::CtrlQueue),
             (&self.cursor_evt, Token::CursorQueue),
             (&*self.state.display().borrow(), Token::Display),
@@ -950,14 +956,14 @@
         ]) {
             Ok(pc) => pc,
             Err(e) => {
-                error!("failed creating PollContext: {}", e);
+                error!("failed creating WaitContext: {}", e);
                 return;
             }
         };
 
         for (index, bridge) in self.resource_bridges.iter().enumerate() {
-            if let Err(e) = poll_ctx.add(bridge, Token::ResourceBridge { index }) {
-                error!("failed to add resource bridge to PollContext: {}", e);
+            if let Err(e) = wait_ctx.add(bridge, Token::ResourceBridge { index }) {
+                error!("failed to add resource bridge to WaitContext: {}", e);
             }
         }
 
@@ -971,7 +977,7 @@
 
         // Declare this outside the loop so we don't keep allocating and freeing the vector.
         let mut process_resource_bridge = Vec::with_capacity(self.resource_bridges.len());
-        'poll: loop {
+        'wait: loop {
             // If there are outstanding fences, wake up early to poll them.
             let duration = if !self.state.fence_descriptors.is_empty() {
                 Duration::from_millis(FENCE_POLL_MS)
@@ -979,7 +985,7 @@
                 Duration::new(i64::MAX as u64, 0)
             };
 
-            let events = match poll_ctx.wait_timeout(duration) {
+            let events = match wait_ctx.wait_timeout(duration) {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed polling for events: {}", e);
@@ -996,15 +1002,15 @@
 
             // This display isn't typically used when the virt-wl device is available and it can
             // lead to hung fds (crbug.com/1027379). Disable if it's hung.
-            for event in events.iter_hungup() {
-                if let Token::Display = event.token() {
+            for event in events.iter().filter(|e| e.is_hungup) {
+                if let Token::Display = event.token {
                     error!("default display hang-up detected");
-                    let _ = poll_ctx.delete(&*self.state.display().borrow());
+                    let _ = wait_ctx.delete(&*self.state.display().borrow());
                 }
             }
 
-            for event in events.iter_readable() {
-                match event.token() {
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::CtrlQueue => {
                         let _ = self.ctrl_evt.read();
                         // Set flag that control queue is available to be read, but defer reading
@@ -1030,7 +1036,7 @@
                         self.interrupt.interrupt_resample();
                     }
                     Token::Kill => {
-                        break 'poll;
+                        break 'wait;
                     }
                 }
             }
@@ -1141,6 +1147,7 @@
             .use_surfaceless(gpu_parameters.renderer_use_surfaceless);
         #[cfg(feature = "gfxstream")]
         let renderer_flags = renderer_flags
+            .use_guest_angle(gpu_parameters.gfxstream_use_guest_angle)
             .use_syncfd(gpu_parameters.gfxstream_use_syncfd)
             .support_vulkan(gpu_parameters.gfxstream_support_vulkan);
 
@@ -1200,24 +1207,24 @@
 }
 
 impl VirtioDevice for Gpu {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
         // TODO(davidriley): Remove once virgl has another path to include
         // debugging logs.
         if cfg!(debug_assertions) {
-            keep_fds.push(libc::STDOUT_FILENO);
-            keep_fds.push(libc::STDERR_FILENO);
+            keep_rds.push(libc::STDOUT_FILENO);
+            keep_rds.push(libc::STDERR_FILENO);
         }
 
         if let Some(ref gpu_device_socket) = self.gpu_device_socket {
-            keep_fds.push(gpu_device_socket.as_raw_fd());
+            keep_rds.push(gpu_device_socket.as_raw_descriptor());
         }
 
-        keep_fds.push(self.exit_evt.as_raw_fd());
+        keep_rds.push(self.exit_evt.as_raw_descriptor());
         for bridge in &self.resource_bridges {
-            keep_fds.push(bridge.as_raw_fd());
+            keep_rds.push(bridge.as_raw_descriptor());
         }
-        keep_fds
+        keep_rds
     }
 
     fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/gpu/virtio_3d_backend.rs b/devices/src/virtio/gpu/virtio_3d_backend.rs
index dc38992..383b361 100644
--- a/devices/src/virtio/gpu/virtio_3d_backend.rs
+++ b/devices/src/virtio/gpu/virtio_3d_backend.rs
@@ -8,12 +8,11 @@
 use std::cell::RefCell;
 use std::collections::btree_map::Entry;
 use std::collections::BTreeMap as Map;
-use std::os::unix::io::AsRawFd;
 use std::rc::Rc;
 use std::sync::Arc;
 use std::usize;
 
-use base::{error, warn, Error, ExternalMapping};
+use base::{error, warn, AsRawDescriptor, Error, ExternalMapping};
 use data_model::*;
 use msg_socket::{MsgReceiver, MsgSender};
 use resources::Alloc;
@@ -38,7 +37,7 @@
 use crate::virtio::resource_bridge::{PlaneInfo, ResourceInfo, ResourceResponse};
 
 use vm_control::{
-    MaybeOwnedFd, MemSlot, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse,
+    MaybeOwnedDescriptor, MemSlot, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse,
 };
 
 struct Virtio3DResource {
@@ -152,7 +151,7 @@
         };
 
         match display.borrow_mut().import_dmabuf(
-            dmabuf.as_raw_fd(),
+            dmabuf.as_raw_descriptor(),
             offset,
             stride,
             query.out_modifier,
@@ -829,7 +828,7 @@
         let request = match export {
             Ok(ref export) => VmMemoryRequest::RegisterFdAtPciBarOffset(
                 self.pci_bar,
-                MaybeOwnedFd::Borrowed(export.as_raw_fd()),
+                MaybeOwnedDescriptor::Borrowed(export.as_raw_descriptor()),
                 resource.size as usize,
                 offset,
             ),
diff --git a/devices/src/virtio/input/evdev.rs b/devices/src/virtio/input/evdev.rs
index 128692a..c8f8f57 100644
--- a/devices/src/virtio/input/evdev.rs
+++ b/devices/src/virtio/input/evdev.rs
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std::os::unix::io::AsRawFd;
-
 use base::{ioctl_ior_nr, ioctl_iow_nr, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};
 use data_model::Le32;
 
@@ -18,6 +16,8 @@
 use std::os::raw::c_uint;
 use std::ptr::null;
 
+use base::{AsRawDescriptor, Descriptor};
+
 const EVDEV: c_uint = 69;
 
 #[repr(C)]
@@ -112,12 +112,16 @@
 }
 
 /// Gets id information from an event device (see EVIOCGID ioctl for details).
-pub fn device_ids<T: AsRawFd>(fd: &T) -> Result<virtio_input_device_ids> {
+pub fn device_ids<T: AsRawDescriptor>(descriptor: &T) -> Result<virtio_input_device_ids> {
     let mut dev_id = evdev_id::new();
     let len = unsafe {
         // Safe because the kernel won't write more than size of evdev_id and we check the return
         // value
-        ioctl_with_mut_ref(fd, EVIOCGID(), &mut dev_id)
+        ioctl_with_mut_ref(
+            &Descriptor(descriptor.as_raw_descriptor()),
+            EVIOCGID(),
+            &mut dev_id,
+        )
     };
     if len < 0 {
         return Err(InputError::EvdevIdError(errno()));
@@ -131,12 +135,16 @@
 }
 
 /// Gets the name of an event device (see EVIOCGNAME ioctl for details).
-pub fn name<T: AsRawFd>(fd: &T) -> Result<Vec<u8>> {
+pub fn name<T: AsRawDescriptor>(descriptor: &T) -> Result<Vec<u8>> {
     let mut name = evdev_buffer::new();
     let len = unsafe {
         // Safe because the kernel won't write more than size of evdev_buffer and we check the
         // return value
-        ioctl_with_mut_ref(fd, EVIOCGNAME(), &mut name)
+        ioctl_with_mut_ref(
+            &Descriptor(descriptor.as_raw_descriptor()),
+            EVIOCGNAME(),
+            &mut name,
+        )
     };
     if len < 0 {
         return Err(InputError::EvdevNameError(errno()));
@@ -145,12 +153,16 @@
 }
 
 /// Gets the unique (serial) name of an event device (see EVIOCGUNIQ ioctl for details).
-pub fn serial_name<T: AsRawFd>(fd: &T) -> Result<Vec<u8>> {
+pub fn serial_name<T: AsRawDescriptor>(descriptor: &T) -> Result<Vec<u8>> {
     let mut uniq = evdev_buffer::new();
     let len = unsafe {
         // Safe because the kernel won't write more than size of evdev_buffer and we check the
         // return value
-        ioctl_with_mut_ref(fd, EVIOCGUNIQ(), &mut uniq)
+        ioctl_with_mut_ref(
+            &Descriptor(descriptor.as_raw_descriptor()),
+            EVIOCGUNIQ(),
+            &mut uniq,
+        )
     };
     if len < 0 {
         return Err(InputError::EvdevSerialError(errno()));
@@ -159,12 +171,16 @@
 }
 
 /// Gets the properties of an event device (see EVIOCGPROP ioctl for details).
-pub fn properties<T: AsRawFd>(fd: &T) -> Result<virtio_input_bitmap> {
+pub fn properties<T: AsRawDescriptor>(descriptor: &T) -> Result<virtio_input_bitmap> {
     let mut props = evdev_buffer::new();
     let len = unsafe {
         // Safe because the kernel won't write more than size of evdev_buffer and we check the
         // return value
-        ioctl_with_mut_ref(fd, EVIOCGPROP(), &mut props)
+        ioctl_with_mut_ref(
+            &Descriptor(descriptor.as_raw_descriptor()),
+            EVIOCGPROP(),
+            &mut props,
+        )
     };
     if len < 0 {
         return Err(InputError::EvdevPropertiesError(errno()));
@@ -174,14 +190,20 @@
 
 /// Gets the event types supported by an event device as well as the event codes supported for each
 /// type (see EVIOCGBIT ioctl for details).
-pub fn supported_events<T: AsRawFd>(fd: &T) -> Result<BTreeMap<u16, virtio_input_bitmap>> {
+pub fn supported_events<T: AsRawDescriptor>(
+    descriptor: &T,
+) -> Result<BTreeMap<u16, virtio_input_bitmap>> {
     let mut evts: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
 
     let mut evt_types = evdev_buffer::new();
     let len = unsafe {
         // Safe because the kernel won't write more than size of evdev_buffer and we check the
         // return value
-        ioctl_with_mut_ref(fd, EVIOCGBIT(0), &mut evt_types)
+        ioctl_with_mut_ref(
+            &Descriptor(descriptor.as_raw_descriptor()),
+            EVIOCGBIT(0),
+            &mut evt_types,
+        )
     };
     if len < 0 {
         return Err(InputError::EvdevEventTypesError(errno()));
@@ -198,7 +220,11 @@
         let len = unsafe {
             // Safe because the kernel won't write more than size of evdev_buffer and we check the
             // return value
-            ioctl_with_mut_ref(fd, EVIOCGBIT(ev as c_uint), &mut evt_codes)
+            ioctl_with_mut_ref(
+                &Descriptor(descriptor.as_raw_descriptor()),
+                EVIOCGBIT(ev as c_uint),
+                &mut evt_codes,
+            )
         };
         if len < 0 {
             return Err(InputError::EvdevEventTypesError(errno()));
@@ -209,7 +235,7 @@
 }
 
 /// Gets the absolute axes of an event device (see EVIOCGABS ioctl for details).
-pub fn abs_info<T: AsRawFd>(fd: &T) -> BTreeMap<u16, virtio_input_absinfo> {
+pub fn abs_info<T: AsRawDescriptor>(descriptor: &T) -> BTreeMap<u16, virtio_input_absinfo> {
     let mut ret: BTreeMap<u16, virtio_input_absinfo> = BTreeMap::new();
 
     for abs in 0..ABS_MAX {
@@ -218,7 +244,11 @@
         let len = unsafe {
             // Safe because the kernel won't write more than size of evdev_buffer and we check the
             // return value
-            ioctl_with_mut_ref(fd, EVIOCGABS(abs as c_uint), &mut abs_info)
+            ioctl_with_mut_ref(
+                &Descriptor(descriptor.as_raw_descriptor()),
+                EVIOCGABS(abs as c_uint),
+                &mut abs_info,
+            )
         };
         if len > 0 {
             ret.insert(abs, virtio_input_absinfo::from(abs_info));
@@ -228,13 +258,17 @@
 }
 
 /// Grabs an event device (see EVIOCGGRAB ioctl for details). After this function succeeds the given
-/// fd has exclusive access to the device, effectively making it unusable for any other process in
+/// descriptor has exclusive access to the device, effectively making it unusable for any other process in
 /// the host.
-pub fn grab_evdev<T: AsRawFd>(fd: &mut T) -> Result<()> {
+pub fn grab_evdev<T: AsRawDescriptor>(descriptor: &mut T) -> Result<()> {
     let val: u32 = 1;
     let ret = unsafe {
         // Safe because the kernel only read the value of the ptr and we check the return value
-        ioctl_with_ref(fd, EVIOCGRAB(), &val)
+        ioctl_with_ref(
+            &Descriptor(descriptor.as_raw_descriptor()),
+            EVIOCGRAB(),
+            &val,
+        )
     };
     if ret == 0 {
         Ok(())
@@ -243,11 +277,15 @@
     }
 }
 
-pub fn ungrab_evdev<T: AsRawFd>(fd: &mut T) -> Result<()> {
+pub fn ungrab_evdev<T: AsRawDescriptor>(descriptor: &mut T) -> Result<()> {
     let ret = unsafe {
         // Safe because the kernel only reads the value of the ptr (doesn't dereference) and
         // we check the return value
-        ioctl_with_ptr(fd, EVIOCGRAB(), null::<u32>())
+        ioctl_with_ptr(
+            &Descriptor(descriptor.as_raw_descriptor()),
+            EVIOCGRAB(),
+            null::<u32>(),
+        )
     };
     if ret == 0 {
         Ok(())
diff --git a/devices/src/virtio/input/event_source.rs b/devices/src/virtio/input/event_source.rs
index cf95f4a..650b193 100644
--- a/devices/src/virtio/input/event_source.rs
+++ b/devices/src/virtio/input/event_source.rs
@@ -6,20 +6,19 @@
 use super::evdev::{grab_evdev, ungrab_evdev};
 use super::InputError;
 use super::Result;
-use base::warn;
+use base::{warn, AsRawDescriptor, RawDescriptor};
 use data_model::DataInit;
 use linux_input_sys::{input_event, virtio_input_event, InputEventDecoder};
 use std::collections::VecDeque;
 use std::io::Read;
 use std::io::Write;
-use std::os::unix::io::{AsRawFd, RawFd};
 
 /// Encapsulates a socket or device node into an abstract event source, providing a common
 /// interface.
 /// It supports read and write operations to provide and accept events just like an event device
 /// node would, except that it handles virtio_input_event instead of input_event structures.
 /// It's necessary to call receive_events() before events are available for read.
-pub trait EventSource: AsRawFd {
+pub trait EventSource: AsRawDescriptor {
     /// Perform any necessary initialization before receiving and sending events from/to the source.
     fn init(&mut self) -> Result<()> {
         Ok(())
@@ -49,9 +48,9 @@
     read_idx: usize,
 }
 
-impl<T: AsRawFd> EventSourceImpl<T> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.source.as_raw_fd()
+impl<T: AsRawDescriptor> EventSourceImpl<T> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.source.as_raw_descriptor()
     }
 }
 
@@ -141,7 +140,7 @@
 
 impl<T> SocketEventSource<T>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     pub fn new(source: T) -> SocketEventSource<T> {
         SocketEventSource {
@@ -150,15 +149,15 @@
     }
 }
 
-impl<T: AsRawFd> AsRawFd for SocketEventSource<T> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.evt_source_impl.as_raw_fd()
+impl<T: AsRawDescriptor> AsRawDescriptor for SocketEventSource<T> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.evt_source_impl.as_raw_descriptor()
     }
 }
 
 impl<T> EventSource for SocketEventSource<T>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     fn init(&mut self) -> Result<()> {
         Ok(())
@@ -193,7 +192,7 @@
 
 impl<T> EvdevEventSource<T>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     pub fn new(source: T) -> EvdevEventSource<T> {
         EvdevEventSource {
@@ -202,15 +201,15 @@
     }
 }
 
-impl<T: AsRawFd> AsRawFd for EvdevEventSource<T> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.evt_source_impl.as_raw_fd()
+impl<T: AsRawDescriptor> AsRawDescriptor for EvdevEventSource<T> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.evt_source_impl.as_raw_descriptor()
     }
 }
 
 impl<T> EventSource for EvdevEventSource<T>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     fn init(&mut self) -> Result<()> {
         grab_evdev(self)
diff --git a/devices/src/virtio/input/mod.rs b/devices/src/virtio/input/mod.rs
index 08ffa22..91400d7 100644
--- a/devices/src/virtio/input/mod.rs
+++ b/devices/src/virtio/input/mod.rs
@@ -10,9 +10,7 @@
 
 use self::constants::*;
 
-use std::os::unix::io::{AsRawFd, RawFd};
-
-use base::{error, warn, Event, PollContext, PollToken};
+use base::{error, warn, AsRawDescriptor, Event, PollToken, RawDescriptor, WaitContext};
 use data_model::{DataInit, Le16, Le32};
 use vm_memory::GuestMemory;
 
@@ -265,7 +263,7 @@
         }
     }
 
-    fn from_evdev<T: AsRawFd>(source: &T) -> Result<VirtioInputConfig> {
+    fn from_evdev<T: AsRawDescriptor>(source: &T) -> Result<VirtioInputConfig> {
         Ok(VirtioInputConfig::new(
             evdev::device_ids(source)?,
             evdev::name(source)?,
@@ -461,23 +459,23 @@
             InterruptResample,
             Kill,
         }
-        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
             (&event_queue_evt, Token::EventQAvailable),
             (&status_queue_evt, Token::StatusQAvailable),
             (&self.event_source, Token::InputEventsAvailable),
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
             (&kill_evt, Token::Kill),
         ]) {
-            Ok(poll_ctx) => poll_ctx,
+            Ok(wait_ctx) => wait_ctx,
             Err(e) => {
-                error!("failed creating PollContext: {}", e);
+                error!("failed creating WaitContext: {}", e);
                 return;
             }
         };
 
-        'poll: loop {
-            let poll_events = match poll_ctx.wait() {
-                Ok(poll_events) => poll_events,
+        'wait: loop {
+            let wait_events = match wait_ctx.wait() {
+                Ok(wait_events) => wait_events,
                 Err(e) => {
                     error!("failed polling for events: {}", e);
                     break;
@@ -485,19 +483,19 @@
             };
 
             let mut needs_interrupt = false;
-            for poll_event in poll_events.iter_readable() {
-                match poll_event.token() {
+            for wait_event in wait_events.iter().filter(|e| e.is_readable) {
+                match wait_event.token {
                     Token::EventQAvailable => {
                         if let Err(e) = event_queue_evt.read() {
                             error!("failed reading event queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         needs_interrupt |= self.send_events();
                     }
                     Token::StatusQAvailable => {
                         if let Err(e) = status_queue_evt.read() {
                             error!("failed reading status queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         match self.process_status_queue() {
                             Ok(b) => needs_interrupt |= b,
@@ -513,7 +511,7 @@
                     }
                     Token::Kill => {
                         let _ = kill_evt.read();
-                        break 'poll;
+                        break 'wait;
                     }
                 }
             }
@@ -556,9 +554,9 @@
 where
     T: 'static + EventSource + Send,
 {
-    fn keep_fds(&self) -> Vec<RawFd> {
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
         if let Some(source) = &self.source {
-            return vec![source.as_raw_fd()];
+            return vec![source.as_raw_descriptor()];
         }
         Vec::new()
     }
@@ -665,7 +663,7 @@
 /// Creates a new virtio input device from an event device node
 pub fn new_evdev<T>(source: T, virtio_features: u64) -> Result<Input<EvdevEventSource<T>>>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     Ok(Input {
         kill_evt: None,
@@ -684,7 +682,7 @@
     virtio_features: u64,
 ) -> Result<Input<SocketEventSource<T>>>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     Ok(Input {
         kill_evt: None,
@@ -704,7 +702,7 @@
     virtio_features: u64,
 ) -> Result<Input<SocketEventSource<T>>>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     Ok(Input {
         kill_evt: None,
@@ -718,7 +716,7 @@
 /// Creates a new virtio mouse which supports primary, secondary, wheel and REL events.
 pub fn new_mouse<T>(source: T, virtio_features: u64) -> Result<Input<SocketEventSource<T>>>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     Ok(Input {
         kill_evt: None,
@@ -732,7 +730,7 @@
 /// Creates a new virtio keyboard, which supports the same events as an en-us physical keyboard.
 pub fn new_keyboard<T>(source: T, virtio_features: u64) -> Result<Input<SocketEventSource<T>>>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write + AsRawDescriptor,
 {
     Ok(Input {
         kill_evt: None,
diff --git a/devices/src/virtio/net.rs b/devices/src/virtio/net.rs
index 417a05c..92368c4 100644
--- a/devices/src/virtio/net.rs
+++ b/devices/src/virtio/net.rs
@@ -7,13 +7,12 @@
 use std::mem;
 use std::net::Ipv4Addr;
 use std::os::raw::c_uint;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::result;
 use std::sync::Arc;
 use std::thread;
 
 use base::Error as SysError;
-use base::{error, warn, Event, PollContext, PollToken, WatchingEvents};
+use base::{error, warn, AsRawDescriptor, Event, EventType, PollToken, RawDescriptor, WaitContext};
 use data_model::{DataInit, Le16, Le64};
 use net_util::{Error as TapError, MacAddress, TapT};
 use virtio_sys::virtio_net;
@@ -33,18 +32,18 @@
 pub enum NetError {
     /// Creating kill event failed.
     CreateKillEvent(SysError),
-    /// Creating PollContext failed.
-    CreatePollContext(SysError),
+    /// Creating WaitContext failed.
+    CreateWaitContext(SysError),
     /// Cloning kill event failed.
     CloneKillEvent(SysError),
     /// Descriptor chain was invalid.
     DescriptorChain(DescriptorError),
-    /// Removing EPOLLIN from the tap fd events failed.
-    PollDisableTap(SysError),
-    /// Adding EPOLLIN to the tap fd events failed.
-    PollEnableTap(SysError),
-    /// Error while polling for events.
-    PollError(SysError),
+    /// Removing read event from the tap fd events failed.
+    WaitContextDisableTap(SysError),
+    /// Adding read event to the tap fd events failed.
+    WaitContextEnableTap(SysError),
+    /// Error while waiting for events.
+    WaitError(SysError),
     /// Error reading data from control queue.
     ReadCtrlData(io::Error),
     /// Error reading header from control queue.
@@ -79,12 +78,12 @@
 
         match self {
             CreateKillEvent(e) => write!(f, "failed to create kill event: {}", e),
-            CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
+            CreateWaitContext(e) => write!(f, "failed to create wait context: {}", e),
             CloneKillEvent(e) => write!(f, "failed to clone kill event: {}", e),
             DescriptorChain(e) => write!(f, "failed to valildate descriptor chain: {}", e),
-            PollDisableTap(e) => write!(f, "failed to disable EPOLLIN on tap fd: {}", e),
-            PollEnableTap(e) => write!(f, "failed to enable EPOLLIN on tap fd: {}", e),
-            PollError(e) => write!(f, "error while polling for events: {}", e),
+            WaitContextDisableTap(e) => write!(f, "failed to disable EPOLLIN on tap fd: {}", e),
+            WaitContextEnableTap(e) => write!(f, "failed to enable EPOLLIN on tap fd: {}", e),
+            WaitError(e) => write!(f, "error while waiting for events: {}", e),
             ReadCtrlData(e) => write!(f, "failed to read control message data: {}", e),
             ReadCtrlHeader(e) => write!(f, "failed to read control message header: {}", e),
             RxDescriptorsExhausted => write!(f, "no rx descriptors available"),
@@ -339,35 +338,35 @@
             Kill,
         }
 
-        let poll_ctx: PollContext<Token> = PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
             (&self.tap, Token::RxTap),
             (&rx_queue_evt, Token::RxQueue),
             (&tx_queue_evt, Token::TxQueue),
             (&self.kill_evt, Token::Kill),
         ])
-        .map_err(NetError::CreatePollContext)?;
+        .map_err(NetError::CreateWaitContext)?;
 
         if let Some(ctrl_evt) = &ctrl_queue_evt {
-            poll_ctx
+            wait_ctx
                 .add(ctrl_evt, Token::CtrlQueue)
-                .map_err(NetError::CreatePollContext)?;
+                .map_err(NetError::CreateWaitContext)?;
             // Let CtrlQueue's thread handle InterruptResample also.
-            poll_ctx
+            wait_ctx
                 .add(self.interrupt.get_resample_evt(), Token::InterruptResample)
-                .map_err(NetError::CreatePollContext)?;
+                .map_err(NetError::CreateWaitContext)?;
         }
 
         let mut tap_polling_enabled = true;
-        'poll: loop {
-            let events = poll_ctx.wait().map_err(NetError::PollError)?;
-            for event in events.iter_readable() {
-                match event.token() {
+        'wait: loop {
+            let events = wait_ctx.wait().map_err(NetError::WaitError)?;
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::RxTap => match self.process_rx() {
                         Ok(()) => {}
                         Err(NetError::RxDescriptorsExhausted) => {
-                            poll_ctx
-                                .modify(&self.tap, WatchingEvents::empty(), Token::RxTap)
-                                .map_err(NetError::PollDisableTap)?;
+                            wait_ctx
+                                .modify(&self.tap, EventType::None, Token::RxTap)
+                                .map_err(NetError::WaitContextDisableTap)?;
                             tap_polling_enabled = false;
                         }
                         Err(e) => return Err(e),
@@ -375,19 +374,19 @@
                     Token::RxQueue => {
                         if let Err(e) = rx_queue_evt.read() {
                             error!("net: error reading rx queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         if !tap_polling_enabled {
-                            poll_ctx
-                                .modify(&self.tap, WatchingEvents::empty().set_read(), Token::RxTap)
-                                .map_err(NetError::PollEnableTap)?;
+                            wait_ctx
+                                .modify(&self.tap, EventType::Read, Token::RxTap)
+                                .map_err(NetError::WaitContextEnableTap)?;
                             tap_polling_enabled = true;
                         }
                     }
                     Token::TxQueue => {
                         if let Err(e) = tx_queue_evt.read() {
                             error!("net: error reading tx queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         self.process_tx();
                     }
@@ -395,14 +394,14 @@
                         if let Some(ctrl_evt) = &ctrl_queue_evt {
                             if let Err(e) = ctrl_evt.read() {
                                 error!("net: error reading ctrl queue Event: {}", e);
-                                break 'poll;
+                                break 'wait;
                             }
                         } else {
-                            break 'poll;
+                            break 'wait;
                         }
                         if let Err(e) = self.process_ctrl() {
                             error!("net: failed to process control message: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                     }
                     Token::InterruptResample => {
@@ -410,7 +409,7 @@
                     }
                     Token::Kill => {
                         let _ = self.kill_evt.read();
-                        break 'poll;
+                        break 'wait;
                     }
                 }
             }
@@ -579,21 +578,21 @@
 where
     T: 'static + TapT,
 {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
 
         for tap in &self.taps {
-            keep_fds.push(tap.as_raw_fd());
+            keep_rds.push(tap.as_raw_descriptor());
         }
 
         for worker_kill_evt in &self.workers_kill_evt {
-            keep_fds.push(worker_kill_evt.as_raw_fd());
+            keep_rds.push(worker_kill_evt.as_raw_descriptor());
         }
         for kill_evt in &self.kill_evts {
-            keep_fds.push(kill_evt.as_raw_fd());
+            keep_rds.push(kill_evt.as_raw_descriptor());
         }
 
-        keep_fds
+        keep_rds
     }
 
     fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/p9.rs b/devices/src/virtio/p9.rs
index ff8aad4..a082afa 100644
--- a/devices/src/virtio/p9.rs
+++ b/devices/src/virtio/p9.rs
@@ -5,12 +5,10 @@
 use std::fmt::{self, Display};
 use std::io::{self, Write};
 use std::mem;
-use std::os::unix::io::RawFd;
-use std::path::{Path, PathBuf};
 use std::result;
 use std::thread;
 
-use base::{error, warn, Error as SysError, Event, PollContext, PollToken};
+use base::{error, warn, Error as SysError, Event, PollToken, RawDescriptor, WaitContext};
 use vm_memory::GuestMemory;
 
 use super::{
@@ -28,12 +26,12 @@
 pub enum P9Error {
     /// The tag for the 9P device was too large to fit in the config space.
     TagTooLong(usize),
-    /// The root directory for the 9P server is not absolute.
-    RootNotAbsolute(PathBuf),
-    /// Creating PollContext failed.
-    CreatePollContext(SysError),
+    /// Creating WaitContext failed.
+    CreateWaitContext(SysError),
+    /// Failed to create a 9p server.
+    CreateServer(io::Error),
     /// Error while polling for events.
-    PollError(SysError),
+    WaitError(SysError),
     /// Error while reading from the virtio queue's Event.
     ReadQueueEvent(SysError),
     /// A request is missing readable descriptors.
@@ -61,13 +59,9 @@
                 len,
                 ::std::u16::MAX
             ),
-            RootNotAbsolute(buf) => write!(
-                f,
-                "P9 root directory is not absolute: root = {}",
-                buf.display()
-            ),
-            CreatePollContext(err) => write!(f, "failed to create PollContext: {}", err),
-            PollError(err) => write!(f, "failed to poll events: {}", err),
+            CreateWaitContext(err) => write!(f, "failed to create WaitContext: {}", err),
+            WaitError(err) => write!(f, "failed to wait for events: {}", err),
+            CreateServer(err) => write!(f, "failed to create 9p server: {}", err),
             ReadQueueEvent(err) => write!(f, "failed to read from virtio queue Event: {}", err),
             NoReadableDescriptors => write!(f, "request does not have any readable descriptors"),
             NoWritableDescriptors => write!(f, "request does not have any writable descriptors"),
@@ -121,17 +115,17 @@
             Kill,
         }
 
-        let poll_ctx: PollContext<Token> = PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
             (&queue_evt, Token::QueueReady),
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
             (&kill_evt, Token::Kill),
         ])
-        .map_err(P9Error::CreatePollContext)?;
+        .map_err(P9Error::CreateWaitContext)?;
 
         loop {
-            let events = poll_ctx.wait().map_err(P9Error::PollError)?;
-            for event in events.iter_readable() {
-                match event.token() {
+            let events = wait_ctx.wait().map_err(P9Error::WaitError)?;
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::QueueReady => {
                         queue_evt.read().map_err(P9Error::ReadQueueEvent)?;
                         self.process_queue()?;
@@ -157,19 +151,11 @@
 }
 
 impl P9 {
-    pub fn new<P: AsRef<Path> + Into<Box<Path>>>(
-        base_features: u64,
-        root: P,
-        tag: &str,
-    ) -> P9Result<P9> {
+    pub fn new(base_features: u64, tag: &str, p9_cfg: p9::Config) -> P9Result<P9> {
         if tag.len() > ::std::u16::MAX as usize {
             return Err(P9Error::TagTooLong(tag.len()));
         }
 
-        if !root.as_ref().is_absolute() {
-            return Err(P9Error::RootNotAbsolute(root.as_ref().into()));
-        }
-
         let len = tag.len() as u16;
         let mut cfg = Vec::with_capacity(tag.len() + mem::size_of::<u16>());
         cfg.push(len as u8);
@@ -177,13 +163,10 @@
 
         cfg.write_all(tag.as_bytes()).map_err(P9Error::Internal)?;
 
+        let server = p9::Server::with_config(p9_cfg).map_err(P9Error::CreateServer)?;
         Ok(P9 {
             config: cfg,
-            server: Some(p9::Server::new(
-                root,
-                Default::default(),
-                Default::default(),
-            )),
+            server: Some(server),
             kill_evt: None,
             avail_features: base_features | 1 << VIRTIO_9P_MOUNT_TAG,
             acked_features: 0,
@@ -193,8 +176,11 @@
 }
 
 impl VirtioDevice for P9 {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        Vec::new()
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        self.server
+            .as_ref()
+            .map(p9::Server::keep_fds)
+            .unwrap_or_else(Vec::new)
     }
 
     fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/pmem.rs b/devices/src/virtio/pmem.rs
index b241ea6..6412268 100644
--- a/devices/src/virtio/pmem.rs
+++ b/devices/src/virtio/pmem.rs
@@ -5,10 +5,9 @@
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::io;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::thread;
 
-use base::{error, Event, PollContext, PollToken};
+use base::{error, AsRawDescriptor, Event, PollToken, RawDescriptor, WaitContext};
 use base::{Error as SysError, Result as SysResult};
 use vm_memory::{GuestAddress, GuestMemory};
 
@@ -176,20 +175,20 @@
             Kill,
         }
 
-        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
             (&queue_evt, Token::QueueAvailable),
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
             (&kill_evt, Token::Kill),
         ]) {
             Ok(pc) => pc,
             Err(e) => {
-                error!("failed creating PollContext: {}", e);
+                error!("failed creating WaitContext: {}", e);
                 return;
             }
         };
 
-        'poll: loop {
-            let events = match poll_ctx.wait() {
+        'wait: loop {
+            let events = match wait_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed polling for events: {}", e);
@@ -198,19 +197,19 @@
             };
 
             let mut needs_interrupt = false;
-            for event in events.iter_readable() {
-                match event.token() {
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::QueueAvailable => {
                         if let Err(e) = queue_evt.read() {
                             error!("failed reading queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         needs_interrupt |= self.process_queue();
                     }
                     Token::InterruptResample => {
                         self.interrupt.interrupt_resample();
                     }
-                    Token::Kill => break 'poll,
+                    Token::Kill => break 'wait,
                 }
             }
             if needs_interrupt {
@@ -271,16 +270,16 @@
 }
 
 impl VirtioDevice for Pmem {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
         if let Some(disk_image) = &self.disk_image {
-            keep_fds.push(disk_image.as_raw_fd());
+            keep_rds.push(disk_image.as_raw_descriptor());
         }
 
         if let Some(ref pmem_device_socket) = self.pmem_device_socket {
-            keep_fds.push(pmem_device_socket.as_raw_fd());
+            keep_rds.push(pmem_device_socket.as_raw_descriptor());
         }
-        keep_fds
+        keep_rds
     }
 
     fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/queue.rs b/devices/src/virtio/queue.rs
index 60a7fc6..ce3f51f 100644
--- a/devices/src/virtio/queue.rs
+++ b/devices/src/virtio/queue.rs
@@ -5,7 +5,6 @@
 use std::cell::RefCell;
 use std::cmp::min;
 use std::num::Wrapping;
-use std::os::unix::io::AsRawFd;
 use std::rc::Rc;
 use std::sync::atomic::{fence, Ordering};
 
@@ -330,6 +329,84 @@
         }
     }
 
+    // Get the index of the first available descriptor chain in the available ring
+    // (the next one that the driver will fill).
+    //
+    // All available ring entries between `self.next_avail` and `get_avail_index()` are available
+    // to be processed by the device.
+    fn get_avail_index(&self, mem: &GuestMemory) -> Wrapping<u16> {
+        let avail_index_addr = self.avail_ring.unchecked_add(2);
+        let avail_index: u16 = mem.read_obj_from_addr(avail_index_addr).unwrap();
+
+        // Make sure following reads (e.g. desc_idx) don't pass the avail_index read.
+        fence(Ordering::Acquire);
+
+        Wrapping(avail_index)
+    }
+
+    // Set the `avail_event` field in the used ring.
+    //
+    // This allows the device to inform the driver that driver-to-device notification
+    // (kicking the ring) is not necessary until the driver reaches the `avail_index` descriptor.
+    //
+    // This value is only used if the `VIRTIO_F_EVENT_IDX` feature has been negotiated.
+    fn set_avail_event(&mut self, mem: &GuestMemory, avail_index: Wrapping<u16>) {
+        let avail_event_addr = self
+            .used_ring
+            .unchecked_add(4 + 8 * u64::from(self.actual_size()));
+        mem.write_obj_at_addr(avail_index.0, avail_event_addr)
+            .unwrap();
+    }
+
+    // Query the value of a single-bit flag in the available ring.
+    //
+    // Returns `true` if `flag` is currently set (by the driver) in the available ring flags.
+    fn get_avail_flag(&self, mem: &GuestMemory, flag: u16) -> bool {
+        let avail_flags: u16 = mem.read_obj_from_addr(self.avail_ring).unwrap();
+        avail_flags & flag == flag
+    }
+
+    // Get the `used_event` field in the available ring.
+    //
+    // The returned value is the index of the next descriptor chain entry for which the driver
+    // needs to be notified upon use.  Entries before this index may be used without notifying
+    // the driver.
+    //
+    // This value is only valid if the `VIRTIO_F_EVENT_IDX` feature has been negotiated.
+    fn get_used_event(&self, mem: &GuestMemory) -> Wrapping<u16> {
+        let used_event_addr = self
+            .avail_ring
+            .unchecked_add(4 + 2 * u64::from(self.actual_size()));
+        let used_event: u16 = mem.read_obj_from_addr(used_event_addr).unwrap();
+        Wrapping(used_event)
+    }
+
+    // Set the `idx` field in the used ring.
+    //
+    // This indicates to the driver that all entries up to (but not including) `used_index` have
+    // been used by the device and may be processed by the driver.
+    fn set_used_index(&mut self, mem: &GuestMemory, used_index: Wrapping<u16>) {
+        // This fence ensures all descriptor writes are visible before the index update.
+        fence(Ordering::Release);
+
+        let used_index_addr = self.used_ring.unchecked_add(2);
+        mem.write_obj_at_addr(used_index.0, used_index_addr)
+            .unwrap();
+    }
+
+    // Set a single-bit flag in the used ring.
+    //
+    // Changes the bit specified by the mask in `flag` to `value`.
+    fn set_used_flag(&mut self, mem: &GuestMemory, flag: u16, value: bool) {
+        let mut used_flags: u16 = mem.read_obj_from_addr(self.used_ring).unwrap();
+        if value {
+            used_flags |= flag;
+        } else {
+            used_flags &= !flag;
+        }
+        mem.write_obj_at_addr(used_flags, self.used_ring).unwrap();
+    }
+
     /// Get the first available descriptor chain without removing it from the queue.
     /// Call `pop_peeked` to remove the returned descriptor chain from the queue.
     pub fn peek(&mut self, mem: &GuestMemory) -> Option<DescriptorChain> {
@@ -338,13 +415,10 @@
         }
 
         let queue_size = self.actual_size();
-        let avail_index_addr = mem.checked_offset(self.avail_ring, 2).unwrap();
-        let avail_index: u16 = mem.read_obj_from_addr(avail_index_addr).unwrap();
-        // make sure desc_index read doesn't bypass avail_index read
-        fence(Ordering::Acquire);
-        let avail_len = Wrapping(avail_index) - self.next_avail;
+        let avail_index = self.get_avail_index(mem);
+        let avail_len = avail_index - self.next_avail;
 
-        if avail_len.0 > queue_size || self.next_avail == Wrapping(avail_index) {
+        if avail_len.0 > queue_size || self.next_avail == avail_index {
             return None;
         }
 
@@ -362,11 +436,7 @@
     pub fn pop_peeked(&mut self, mem: &GuestMemory) {
         self.next_avail += Wrapping(1);
         if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
-            let avail_event_off = self
-                .used_ring
-                .unchecked_add((4 + 8 * self.actual_size()).into());
-            mem.write_obj_at_addr(self.next_avail.0 as u16, avail_event_off)
-                .unwrap();
+            self.set_avail_event(mem, self.next_avail);
         }
     }
 
@@ -386,10 +456,10 @@
 
     /// Asynchronously read the next descriptor chain from the queue.
     /// Returns a `DescriptorChain` when it is `await`ed.
-    pub async fn next_async<F: AsRawFd + Unpin>(
+    pub async fn next_async(
         &mut self,
         mem: &GuestMemory,
-        eventfd: &mut EventAsync<F>,
+        eventfd: &mut EventAsync,
     ) -> std::result::Result<DescriptorChain, AsyncError> {
         loop {
             // Check if there are more descriptors available.
@@ -420,12 +490,7 @@
             .unwrap();
 
         self.next_used += Wrapping(1);
-
-        // This fence ensures all descriptor writes are visible before the index update is.
-        fence(Ordering::Release);
-
-        mem.write_obj_at_addr(self.next_used.0 as u16, used_ring.unchecked_add(2))
-            .unwrap();
+        self.set_used_index(mem, self.next_used);
     }
 
     /// Enable / Disable guest notify device that requests are available on
@@ -438,47 +503,27 @@
         }
 
         if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
-            let avail_index_addr = mem.checked_offset(self.avail_ring, 2).unwrap();
-            let avail_index: u16 = mem.read_obj_from_addr(avail_index_addr).unwrap();
-            let avail_event_off = self
-                .used_ring
-                .unchecked_add((4 + 8 * self.actual_size()).into());
-            mem.write_obj_at_addr(avail_index, avail_event_off).unwrap();
+            self.set_avail_event(mem, self.get_avail_index(mem));
         } else {
-            let mut used_flags: u16 = mem.read_obj_from_addr(self.used_ring).unwrap();
-            if self.notification_disable_count == 0 {
-                used_flags &= !VIRTQ_USED_F_NO_NOTIFY;
-            } else {
-                used_flags |= VIRTQ_USED_F_NO_NOTIFY;
-            }
-            mem.write_obj_at_addr(used_flags, self.used_ring).unwrap();
+            self.set_used_flag(
+                mem,
+                VIRTQ_USED_F_NO_NOTIFY,
+                self.notification_disable_count > 0,
+            );
         }
     }
 
     // Check Whether guest enable interrupt injection or not.
     fn available_interrupt_enabled(&self, mem: &GuestMemory) -> bool {
         if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
-            let used_event_off = self
-                .avail_ring
-                .unchecked_add((4 + 2 * self.actual_size()).into());
-            let used_event: u16 = mem.read_obj_from_addr(used_event_off).unwrap();
+            let used_event = self.get_used_event(mem);
             // if used_event >= self.last_used, driver handle interrupt quickly enough, new
             // interrupt could be injected.
             // if used_event < self.last_used, driver hasn't finished the last interrupt,
             // so no need to inject new interrupt.
-            if self.next_used - Wrapping(used_event) - Wrapping(1) < self.next_used - self.last_used
-            {
-                true
-            } else {
-                false
-            }
+            self.next_used - used_event - Wrapping(1) < self.next_used - self.last_used
         } else {
-            let avail_flags: u16 = mem.read_obj_from_addr(self.avail_ring).unwrap();
-            if avail_flags & VIRTQ_AVAIL_F_NO_INTERRUPT == VIRTQ_AVAIL_F_NO_INTERRUPT {
-                false
-            } else {
-                true
-            }
+            !self.get_avail_flag(mem, VIRTQ_AVAIL_F_NO_INTERRUPT)
         }
     }
 
diff --git a/devices/src/virtio/resource_bridge.rs b/devices/src/virtio/resource_bridge.rs
index 2a2343f..067e832 100644
--- a/devices/src/virtio/resource_bridge.rs
+++ b/devices/src/virtio/resource_bridge.rs
@@ -8,6 +8,7 @@
 use std::fmt;
 use std::fs::File;
 
+use base::RawDescriptor;
 use msg_on_socket_derive::MsgOnSocket;
 use msg_socket::{MsgError, MsgReceiver, MsgSender, MsgSocket};
 
diff --git a/devices/src/virtio/rng.rs b/devices/src/virtio/rng.rs
index 332a616..466ffd2 100644
--- a/devices/src/virtio/rng.rs
+++ b/devices/src/virtio/rng.rs
@@ -5,10 +5,9 @@
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::io;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::thread;
 
-use base::{error, warn, Event, PollContext, PollToken};
+use base::{error, warn, AsRawDescriptor, Event, PollToken, RawDescriptor, WaitContext};
 use vm_memory::GuestMemory;
 
 use super::{Interrupt, Queue, VirtioDevice, Writer, TYPE_RNG};
@@ -74,20 +73,20 @@
             Kill,
         }
 
-        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
             (&queue_evt, Token::QueueAvailable),
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
             (&kill_evt, Token::Kill),
         ]) {
             Ok(pc) => pc,
             Err(e) => {
-                error!("failed creating PollContext: {}", e);
+                error!("failed creating WaitContext: {}", e);
                 return;
             }
         };
 
-        'poll: loop {
-            let events = match poll_ctx.wait() {
+        'wait: loop {
+            let events = match wait_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed polling for events: {}", e);
@@ -96,19 +95,19 @@
             };
 
             let mut needs_interrupt = false;
-            for event in events.iter_readable() {
-                match event.token() {
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::QueueAvailable => {
                         if let Err(e) = queue_evt.read() {
                             error!("failed reading queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         needs_interrupt |= self.process_queue();
                     }
                     Token::InterruptResample => {
                         self.interrupt.interrupt_resample();
                     }
-                    Token::Kill => break 'poll,
+                    Token::Kill => break 'wait,
                 }
             }
             if needs_interrupt {
@@ -153,14 +152,14 @@
 }
 
 impl VirtioDevice for Rng {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
 
         if let Some(random_file) = &self.random_file {
-            keep_fds.push(random_file.as_raw_fd());
+            keep_rds.push(random_file.as_raw_descriptor());
         }
 
-        keep_fds
+        keep_rds
     }
 
     fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/tpm.rs b/devices/src/virtio/tpm.rs
index 9afdc69..ee68ea1 100644
--- a/devices/src/virtio/tpm.rs
+++ b/devices/src/virtio/tpm.rs
@@ -7,11 +7,10 @@
 use std::fs;
 use std::io::{self, Read, Write};
 use std::ops::BitOrAssign;
-use std::os::unix::io::RawFd;
 use std::path::PathBuf;
 use std::thread;
 
-use base::{error, Event, PollContext, PollToken};
+use base::{error, Event, PollToken, RawDescriptor, WaitContext};
 use vm_memory::GuestMemory;
 
 use super::{
@@ -111,20 +110,20 @@
             Kill,
         }
 
-        let poll_ctx = match PollContext::build_with(&[
+        let wait_ctx = match WaitContext::build_with(&[
             (&self.queue_evt, Token::QueueAvailable),
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
             (&self.kill_evt, Token::Kill),
         ]) {
             Ok(pc) => pc,
             Err(e) => {
-                error!("vtpm failed creating PollContext: {}", e);
+                error!("vtpm failed creating WaitContext: {}", e);
                 return;
             }
         };
 
-        'poll: loop {
-            let events = match poll_ctx.wait() {
+        'wait: loop {
+            let events = match wait_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("vtpm failed polling for events: {}", e);
@@ -133,19 +132,19 @@
             };
 
             let mut needs_interrupt = NeedsInterrupt::No;
-            for event in events.iter_readable() {
-                match event.token() {
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::QueueAvailable => {
                         if let Err(e) = self.queue_evt.read() {
                             error!("vtpm failed reading queue Event: {}", e);
-                            break 'poll;
+                            break 'wait;
                         }
                         needs_interrupt |= self.process_queue();
                     }
                     Token::InterruptResample => {
                         self.interrupt.interrupt_resample();
                     }
-                    Token::Kill => break 'poll,
+                    Token::Kill => break 'wait,
                 }
             }
             if needs_interrupt == NeedsInterrupt::Yes {
@@ -185,7 +184,7 @@
 }
 
 impl VirtioDevice for Tpm {
-    fn keep_fds(&self) -> Vec<RawFd> {
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
         Vec::new()
     }
 
diff --git a/devices/src/virtio/vhost/control_socket.rs b/devices/src/virtio/vhost/control_socket.rs
index 9edc9a9..fc1d0f3 100644
--- a/devices/src/virtio/vhost/control_socket.rs
+++ b/devices/src/virtio/vhost/control_socket.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 base::Error as SysError;
+use base::{Error as SysError, RawDescriptor};
 use msg_socket::{MsgOnSocket, MsgSocket};
 
 #[derive(MsgOnSocket, Debug)]
diff --git a/devices/src/virtio/vhost/mod.rs b/devices/src/virtio/vhost/mod.rs
index 2646d54..e1cd067 100644
--- a/devices/src/virtio/vhost/mod.rs
+++ b/devices/src/virtio/vhost/mod.rs
@@ -27,10 +27,8 @@
     CloneKillEvent(SysError),
     /// Creating kill event failed.
     CreateKillEvent(SysError),
-    /// Creating poll context failed.
-    CreatePollContext(SysError),
-    /// Error while polling for events.
-    PollError(SysError),
+    /// Creating wait context failed.
+    CreateWaitContext(SysError),
     /// Enabling tap interface failed.
     TapEnable(TapError),
     /// Open tap device failed.
@@ -75,6 +73,8 @@
     VhostVsockSetCid(VhostError),
     /// Failed to start vhost-vsock driver.
     VhostVsockStart(VhostError),
+    /// Error while waiting for events.
+    WaitError(SysError),
 }
 
 pub type Result<T> = std::result::Result<T, Error>;
@@ -88,8 +88,7 @@
         match self {
             CloneKillEvent(e) => write!(f, "failed to clone kill event: {}", e),
             CreateKillEvent(e) => write!(f, "failed to create kill event: {}", e),
-            CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
-            PollError(e) => write!(f, "failed polling for events: {}", e),
+            CreateWaitContext(e) => write!(f, "failed to create poll context: {}", e),
             TapEnable(e) => write!(f, "failed to enable tap interface: {}", e),
             TapOpen(e) => write!(f, "failed to open tap device: {}", e),
             TapSetIp(e) => write!(f, "failed to set tap IP: {}", e),
@@ -112,6 +111,7 @@
             VhostSetVringNum(e) => write!(f, "failed to set vring num: {}", e),
             VhostVsockSetCid(e) => write!(f, "failed to set CID for guest: {}", e),
             VhostVsockStart(e) => write!(f, "failed to start vhost-vsock driver: {}", e),
+            WaitError(e) => write!(f, "failed waiting for events: {}", e),
         }
     }
 }
diff --git a/devices/src/virtio/vhost/net.rs b/devices/src/virtio/vhost/net.rs
index 14dc852..9aede36 100644
--- a/devices/src/virtio/vhost/net.rs
+++ b/devices/src/virtio/vhost/net.rs
@@ -4,12 +4,11 @@
 
 use std::mem;
 use std::net::Ipv4Addr;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::thread;
 
 use net_util::{MacAddress, TapT};
 
-use base::{error, warn, Event};
+use base::{error, warn, AsRawDescriptor, Event, RawDescriptor};
 use vhost::NetT as VhostNetT;
 use virtio_sys::virtio_net;
 use vm_memory::GuestMemory;
@@ -131,37 +130,37 @@
     T: TapT + 'static,
     U: VhostNetT<T> + 'static,
 {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
 
         if let Some(tap) = &self.tap {
-            keep_fds.push(tap.as_raw_fd());
+            keep_rds.push(tap.as_raw_descriptor());
         }
 
         if let Some(vhost_net_handle) = &self.vhost_net_handle {
-            keep_fds.push(vhost_net_handle.as_raw_fd());
+            keep_rds.push(vhost_net_handle.as_raw_descriptor());
         }
 
         if let Some(vhost_interrupt) = &self.vhost_interrupt {
             for vhost_int in vhost_interrupt.iter() {
-                keep_fds.push(vhost_int.as_raw_fd());
+                keep_rds.push(vhost_int.as_raw_descriptor());
             }
         }
 
         if let Some(workers_kill_evt) = &self.workers_kill_evt {
-            keep_fds.push(workers_kill_evt.as_raw_fd());
+            keep_rds.push(workers_kill_evt.as_raw_descriptor());
         }
-        keep_fds.push(self.kill_evt.as_raw_fd());
+        keep_rds.push(self.kill_evt.as_raw_descriptor());
 
         if let Some(request_socket) = &self.request_socket {
-            keep_fds.push(request_socket.as_raw_fd());
+            keep_rds.push(request_socket.as_raw_descriptor());
         }
 
         if let Some(response_socket) = &self.response_socket {
-            keep_fds.push(response_socket.as_raw_fd());
+            keep_rds.push(response_socket.as_raw_descriptor());
         }
 
-        keep_fds
+        keep_rds
     }
 
     fn device_type(&self) -> u32 {
@@ -381,9 +380,9 @@
     }
 
     #[test]
-    fn keep_fds() {
+    fn keep_rds() {
         let net = create_net_common();
-        let fds = net.keep_fds();
+        let fds = net.keep_rds();
         assert!(fds.len() >= 1, "We should have gotten at least one fd");
     }
 
diff --git a/devices/src/virtio/vhost/vsock.rs b/devices/src/virtio/vhost/vsock.rs
index 9702cc9..0061181 100644
--- a/devices/src/virtio/vhost/vsock.rs
+++ b/devices/src/virtio/vhost/vsock.rs
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::thread;
 
 use data_model::{DataInit, Le64};
 
-use base::{error, warn, Event};
+use base::{error, warn, AsRawDescriptor, Event, RawDescriptor};
 use vhost::Vhost;
 use vhost::Vsock as VhostVsockHandle;
 use vm_memory::GuestMemory;
@@ -89,24 +88,24 @@
 }
 
 impl VirtioDevice for Vsock {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
 
         if let Some(handle) = &self.vhost_handle {
-            keep_fds.push(handle.as_raw_fd());
+            keep_rds.push(handle.as_raw_descriptor());
         }
 
         if let Some(interrupt) = &self.interrupts {
             for vhost_int in interrupt.iter() {
-                keep_fds.push(vhost_int.as_raw_fd());
+                keep_rds.push(vhost_int.as_raw_descriptor());
             }
         }
 
         if let Some(worker_kill_evt) = &self.worker_kill_evt {
-            keep_fds.push(worker_kill_evt.as_raw_fd());
+            keep_rds.push(worker_kill_evt.as_raw_descriptor());
         }
 
-        keep_fds
+        keep_rds
     }
 
     fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/vhost/worker.rs b/devices/src/virtio/vhost/worker.rs
index 1c0f10b..04c933a 100644
--- a/devices/src/virtio/vhost/worker.rs
+++ b/devices/src/virtio/vhost/worker.rs
@@ -4,7 +4,7 @@
 
 use std::os::raw::c_ulonglong;
 
-use base::{error, Error as SysError, Event, PollContext, PollToken};
+use base::{error, Error as SysError, Event, PollToken, WaitContext};
 use vhost::Vhost;
 
 use super::control_socket::{VhostDevRequest, VhostDevResponse, VhostDevResponseSocket};
@@ -106,28 +106,28 @@
             ControlNotify,
         }
 
-        let poll_ctx: PollContext<Token> = PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
             (&self.kill_evt, Token::Kill),
         ])
-        .map_err(Error::CreatePollContext)?;
+        .map_err(Error::CreateWaitContext)?;
 
         for (index, vhost_int) in self.vhost_interrupt.iter().enumerate() {
-            poll_ctx
+            wait_ctx
                 .add(vhost_int, Token::VhostIrqi { index })
-                .map_err(Error::CreatePollContext)?;
+                .map_err(Error::CreateWaitContext)?;
         }
         if let Some(socket) = &self.response_socket {
-            poll_ctx
+            wait_ctx
                 .add(socket, Token::ControlNotify)
-                .map_err(Error::CreatePollContext)?;
+                .map_err(Error::CreateWaitContext)?;
         }
 
-        'poll: loop {
-            let events = poll_ctx.wait().map_err(Error::PollError)?;
+        'wait: loop {
+            let events = wait_ctx.wait().map_err(Error::WaitError)?;
 
-            for event in events.iter_readable() {
-                match event.token() {
+            for event in events.iter().filter(|e| e.is_readable) {
+                match event.token {
                     Token::VhostIrqi { index } => {
                         self.vhost_interrupt[index]
                             .read()
@@ -139,7 +139,7 @@
                     }
                     Token::Kill => {
                         let _ = self.kill_evt.read();
-                        break 'poll;
+                        break 'wait;
                     }
                     Token::ControlNotify => {
                         if let Some(socket) = &self.response_socket {
diff --git a/devices/src/virtio/video/decoder/backend/mod.rs b/devices/src/virtio/video/decoder/backend/mod.rs
new file mode 100644
index 0000000..a9c1411
--- /dev/null
+++ b/devices/src/virtio/video/decoder/backend/mod.rs
@@ -0,0 +1,138 @@
+// 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.
+
+//! This module implements the interface that actual decoder devices need to
+//! implement in order to provide video decoding capability to the guest.
+
+use std::fs::File;
+
+use crate::virtio::video::{
+    error::{VideoError, VideoResult},
+    format::{Format, Rect},
+};
+use base::RawDescriptor;
+
+pub mod vda;
+
+pub struct FramePlane {
+    pub offset: i32,
+    pub stride: i32,
+}
+
+/// Contains the device's state for one playback session, i.e. one stream.
+pub trait DecoderSession {
+    /// Tell how many output buffers will be used for this session. This method
+    /// Must be called after a `ProvidePictureBuffers` event is emitted, and
+    /// before the first call to `use_output_buffers()`.
+    fn set_output_buffer_count(&self, count: usize) -> VideoResult<()>;
+
+    /// Decode the compressed stream contained in [`offset`..`offset`+`bytes_used`]
+    /// of the shared memory in `descriptor`. `bitstream_id` is the identifier for that
+    /// part of the stream (most likely, a timestamp).
+    ///
+    /// The device takes ownership of `descriptor` and is responsible for closing it
+    /// once it is not used anymore.
+    ///
+    /// The device will emit a `NotifyEndOfBitstreamBuffer` event after the input
+    /// buffer has been entirely processed.
+    ///
+    /// The device will emit a `PictureReady` event with the `bitstream_id` field
+    /// set to the same value as the argument of the same name for each picture
+    /// produced from that input buffer.
+    fn decode(
+        &self,
+        bitstream_id: i32,
+        descriptor: RawDescriptor,
+        offset: u32,
+        bytes_used: u32,
+    ) -> VideoResult<()>;
+
+    /// Flush the decoder device, i.e. finish processing of all queued decode
+    /// requests.
+    ///
+    /// The device will emit a `FlushCompleted` event once the flush is done.
+    fn flush(&self) -> VideoResult<()>;
+
+    /// Reset the decoder device, i.e. cancel all pending decoding requests.
+    ///
+    /// The device will emit a `ResetCompleted` event once the reset is done.
+    fn reset(&self) -> VideoResult<()>;
+
+    /// Returns the event pipe on which the availability of an event will be
+    /// signaled.
+    fn event_pipe(&self) -> &File;
+
+    /// Ask the device to use the memory buffer in `output_buffer` to store
+    /// decoded frames in pixel format `format`. `planes` describes how the
+    /// frame's planes should be laid out in the buffer, and `picture_buffer_id`
+    /// is the ID of the picture, that will be reproduced in `PictureReady` events
+    /// using this buffer.
+    ///
+    /// The device takes ownership of `output_buffer` and is responsible for
+    /// closing it once the buffer is not used anymore (either when the session
+    /// is closed, or a new set of buffers is provided for the session).
+    ///
+    /// The device will emit a `PictureReady` event with the `picture_buffer_id`
+    /// field set to the same value as the argument of the same name when a
+    /// frame has been decoded into that buffer.
+    fn use_output_buffer(
+        &self,
+        picture_buffer_id: i32,
+        format: Format,
+        output_buffer: RawDescriptor,
+        planes: &[FramePlane],
+    ) -> VideoResult<()>;
+
+    /// Ask the device to reuse an output buffer previously passed to
+    /// `use_output_buffer` and that has previously been returned to the decoder
+    /// in a `PictureReady` event.
+    ///
+    /// The device will emit a `PictureReady` event with the `picture_buffer_id`
+    /// field set to the same value as the argument of the same name when a
+    /// frame has been decoded into that buffer.
+    fn reuse_output_buffer(&self, picture_buffer_id: i32) -> VideoResult<()>;
+
+    /// Blocking call to read a single event from the event pipe.
+    fn read_event(&mut self) -> VideoResult<DecoderEvent>;
+}
+
+pub trait DecoderBackend {
+    type Session: DecoderSession;
+
+    /// Create a new decoding session for the passed `profile`.
+    fn new_session(&self, format: Format) -> VideoResult<Self::Session>;
+}
+
+#[derive(Debug)]
+pub enum DecoderEvent {
+    /// Emitted when the device knows the buffer format it will need to decode
+    /// frames, and how many buffers it will need. The decoder is supposed to
+    /// provide buffers of the requested dimensions using `use_output_buffer`.
+    ProvidePictureBuffers {
+        min_num_buffers: u32,
+        width: i32,
+        height: i32,
+        visible_rect: Rect,
+    },
+    /// Emitted when the decoder is done decoding a picture. `picture_buffer_id`
+    /// corresponds to the argument of the same name passed to `use_output_buffer()`
+    /// or `reuse_output_buffer()`. `bitstream_id` corresponds to the argument of
+    /// the same name passed to `decode()` and can be used to match decoded frames
+    /// to the input buffer they were produced from.
+    PictureReady {
+        picture_buffer_id: i32,
+        bitstream_id: i32,
+        visible_rect: Rect,
+    },
+    /// Emitted when an input buffer passed to `decode()` is not used by the
+    /// device anymore and can be reused by the decoder. The parameter corresponds
+    /// to the `bitstream_id` argument passed to `decode()`.
+    NotifyEndOfBitstreamBuffer(i32),
+    /// Emitted when a decoding error has occured.
+    NotifyError(VideoError),
+    /// Emitted after `flush()` has been called to signal that the flush is completed.
+    FlushCompleted(VideoResult<()>),
+    /// Emitted after `reset()` has been called to signal that the reset is completed.
+    ResetCompleted(VideoResult<()>),
+}
diff --git a/devices/src/virtio/video/decoder/backend/vda.rs b/devices/src/virtio/video/decoder/backend/vda.rs
new file mode 100644
index 0000000..716347c
--- /dev/null
+++ b/devices/src/virtio/video/decoder/backend/vda.rs
@@ -0,0 +1,192 @@
+// 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 base::{error, RawDescriptor};
+use std::convert::TryFrom;
+
+use libvda::decode::Event as LibvdaEvent;
+
+use crate::virtio::video::{
+    decoder::backend::*,
+    error::{VideoError, VideoResult},
+    format::{Format, Rect},
+};
+
+impl TryFrom<Format> for libvda::Profile {
+    type Error = VideoError;
+
+    fn try_from(format: Format) -> Result<Self, Self::Error> {
+        Ok(match format {
+            Format::VP8 => libvda::Profile::VP8,
+            Format::VP9 => libvda::Profile::VP9Profile0,
+            Format::H264 => libvda::Profile::H264ProfileBaseline,
+            _ => {
+                error!("specified format {} is not supported by VDA", format);
+                return Err(VideoError::InvalidParameter);
+            }
+        })
+    }
+}
+
+impl TryFrom<Format> for libvda::PixelFormat {
+    type Error = VideoError;
+
+    fn try_from(format: Format) -> Result<Self, Self::Error> {
+        Ok(match format {
+            Format::NV12 => libvda::PixelFormat::NV12,
+            _ => {
+                error!("specified format {} is not supported by VDA", format);
+                return Err(VideoError::InvalidParameter);
+            }
+        })
+    }
+}
+
+impl From<&FramePlane> for libvda::FramePlane {
+    fn from(plane: &FramePlane) -> Self {
+        libvda::FramePlane {
+            offset: plane.offset,
+            stride: plane.stride,
+        }
+    }
+}
+
+impl From<libvda::decode::Event> for DecoderEvent {
+    fn from(event: libvda::decode::Event) -> Self {
+        // We cannot use the From trait here since neither libvda::decode::Response
+        // no std::result::Result are defined in the current crate.
+        fn vda_response_to_result(resp: libvda::decode::Response) -> VideoResult<()> {
+            match resp {
+                libvda::decode::Response::Success => Ok(()),
+                resp => Err(VideoError::VdaFailure(resp)),
+            }
+        }
+
+        match event {
+            LibvdaEvent::ProvidePictureBuffers {
+                min_num_buffers,
+                width,
+                height,
+                visible_rect_left,
+                visible_rect_top,
+                visible_rect_right,
+                visible_rect_bottom,
+            } => DecoderEvent::ProvidePictureBuffers {
+                min_num_buffers,
+                width,
+                height,
+                visible_rect: Rect {
+                    left: visible_rect_left,
+                    top: visible_rect_top,
+                    right: visible_rect_right,
+                    bottom: visible_rect_bottom,
+                },
+            },
+            LibvdaEvent::PictureReady {
+                buffer_id,
+                bitstream_id,
+                left,
+                top,
+                right,
+                bottom,
+            } => DecoderEvent::PictureReady {
+                picture_buffer_id: buffer_id,
+                bitstream_id,
+                visible_rect: Rect {
+                    left,
+                    top,
+                    right,
+                    bottom,
+                },
+            },
+            LibvdaEvent::NotifyEndOfBitstreamBuffer { bitstream_id } => {
+                DecoderEvent::NotifyEndOfBitstreamBuffer(bitstream_id)
+            }
+            LibvdaEvent::NotifyError(resp) => {
+                DecoderEvent::NotifyError(VideoError::VdaFailure(resp))
+            }
+            LibvdaEvent::ResetResponse(resp) => {
+                DecoderEvent::ResetCompleted(vda_response_to_result(resp))
+            }
+            LibvdaEvent::FlushResponse(resp) => {
+                DecoderEvent::FlushCompleted(vda_response_to_result(resp))
+            }
+        }
+    }
+}
+
+pub struct LibvdaSession<'a> {
+    session: libvda::decode::Session<'a>,
+}
+
+impl<'a> DecoderSession for LibvdaSession<'a> {
+    fn set_output_buffer_count(&self, count: usize) -> VideoResult<()> {
+        Ok(self.session.set_output_buffer_count(count)?)
+    }
+
+    fn decode(
+        &self,
+        bitstream_id: i32,
+        descriptor: RawDescriptor,
+        offset: u32,
+        bytes_used: u32,
+    ) -> VideoResult<()> {
+        Ok(self
+            .session
+            .decode(bitstream_id, descriptor, offset, bytes_used)?)
+    }
+
+    fn flush(&self) -> VideoResult<()> {
+        Ok(self.session.flush()?)
+    }
+
+    fn reset(&self) -> VideoResult<()> {
+        Ok(self.session.reset()?)
+    }
+
+    fn event_pipe(&self) -> &std::fs::File {
+        self.session.pipe()
+    }
+
+    fn use_output_buffer(
+        &self,
+        picture_buffer_id: i32,
+        format: Format,
+        output_buffer: RawDescriptor,
+        planes: &[FramePlane],
+    ) -> VideoResult<()> {
+        let vda_planes: Vec<libvda::FramePlane> = planes.into_iter().map(Into::into).collect();
+        Ok(self.session.use_output_buffer(
+            picture_buffer_id,
+            libvda::PixelFormat::try_from(format)?,
+            output_buffer,
+            &vda_planes,
+        )?)
+    }
+
+    fn reuse_output_buffer(&self, picture_buffer_id: i32) -> VideoResult<()> {
+        Ok(self.session.reuse_output_buffer(picture_buffer_id)?)
+    }
+
+    fn read_event(&mut self) -> VideoResult<DecoderEvent> {
+        self.session
+            .read_event()
+            .map(Into::into)
+            .map_err(Into::into)
+    }
+}
+
+impl<'a> DecoderBackend for &'a libvda::decode::VdaInstance {
+    type Session = LibvdaSession<'a>;
+
+    fn new_session(&self, format: Format) -> VideoResult<Self::Session> {
+        let profile = libvda::Profile::try_from(format)?;
+        let session = self.open_session(profile).map_err(|e| {
+            error!("failed to open a session for {:?}: {}", format, e);
+            VideoError::InvalidOperation
+        })?;
+
+        Ok(LibvdaSession { session })
+    }
+}
diff --git a/devices/src/virtio/video/decoder/mod.rs b/devices/src/virtio/video/decoder/mod.rs
index 52c8594..c1cb84e 100644
--- a/devices/src/virtio/video/decoder/mod.rs
+++ b/devices/src/virtio/video/decoder/mod.rs
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-//! Implementation of a virtio video decoder device backed by LibVDA.
+//! Implementation of a virtio video decoder backed by a device.
 
 use std::collections::btree_map::Entry;
 use std::collections::{BTreeMap, BTreeSet};
 use std::convert::TryInto;
-use std::os::unix::io::IntoRawFd;
 
-use base::{error, PollContext};
+use backend::*;
+use base::{error, IntoRawDescriptor, WaitContext};
 
 use crate::virtio::resource_bridge::{self, ResourceInfo, ResourceRequestSocket};
 use crate::virtio::video::async_cmd_desc_map::AsyncCmdDescMap;
@@ -23,7 +23,9 @@
 use crate::virtio::video::protocol;
 use crate::virtio::video::response::CmdResponse;
 
+mod backend;
 mod capability;
+
 use capability::*;
 
 type StreamId = u32;
@@ -74,7 +76,7 @@
     // destroyed.
     eos_resource_id: Option<OutputResourceId>,
 
-    // This is a flag that shows whether libvda's set_output_buffer_count is called.
+    // This is a flag that shows whether the device's set_output_buffer_count is called.
     // This will be set to true when ResourceCreate for OutputBuffer is called for the first time.
     //
     // TODO(b/1518105): This field is added as a hack because the current virtio-video v3 spec
@@ -154,10 +156,10 @@
     }
 }
 
-// Context is associated with one `libvda::Session`, which corresponds to one stream from the
+// Context is associated with one `DecoderSession`, which corresponds to one stream from the
 // virtio-video's point of view.
 #[derive(Default)]
-struct Context {
+struct Context<S: DecoderSession> {
     stream_id: StreamId,
 
     in_params: Params,
@@ -165,9 +167,14 @@
 
     in_res: InputResources,
     out_res: OutputResources,
+
+    // Set the flag if we need to clear output resource when the output queue is cleared next time.
+    is_clear_out_res_needed: bool,
+
+    session: Option<S>,
 }
 
-impl Context {
+impl<S: DecoderSession> Context<S> {
     fn new(stream_id: StreamId, format: Format) -> Self {
         Context {
             stream_id,
@@ -179,7 +186,10 @@
                 ..Default::default()
             },
             out_params: Default::default(),
-            ..Default::default()
+            in_res: Default::default(),
+            out_res: Default::default(),
+            is_clear_out_res_needed: false,
+            session: None,
         }
     }
 
@@ -217,7 +227,7 @@
     }
 
     /*
-     * Functions handling libvda events.
+     * Functions handling decoder events.
      */
 
     fn handle_provide_picture_buffers(
@@ -225,16 +235,13 @@
         min_num_buffers: u32,
         width: i32,
         height: i32,
-        visible_rect_left: i32,
-        visible_rect_top: i32,
-        visible_rect_right: i32,
-        visible_rect_bottom: i32,
+        visible_rect: Rect,
     ) {
         // We only support NV12.
         let format = Some(Format::NV12);
 
-        let rect_width: u32 = (visible_rect_right - visible_rect_left) as u32;
-        let rect_height: u32 = (visible_rect_bottom - visible_rect_top) as u32;
+        let rect_width: u32 = (visible_rect.right - visible_rect.left) as u32;
+        let rect_height: u32 = (visible_rect.bottom - visible_rect.top) as u32;
 
         let plane_size = rect_width * rect_height;
         let stride = rect_width;
@@ -252,8 +259,8 @@
             min_buffers: min_num_buffers + 1,
             max_buffers: 32,
             crop: Crop {
-                left: visible_rect_left as u32,
-                top: visible_rect_top as u32,
+                left: visible_rect.left as u32,
+                top: visible_rect.top as u32,
                 width: rect_width,
                 height: rect_height,
             },
@@ -261,6 +268,12 @@
             // No need to set `frame_rate`, as it's only for the encoder.
             ..Default::default()
         };
+
+        // That eos_resource_id has value means there are previous output resources.
+        // Clear the output resources when the output queue is cleared next time.
+        if self.out_res.eos_resource_id.is_some() {
+            self.is_clear_out_res_needed = true;
+        }
     }
 
     fn handle_picture_ready(
@@ -295,13 +308,18 @@
 }
 
 /// A thin wrapper of a map of contexts with error handlings.
-#[derive(Default)]
-struct ContextMap {
-    map: BTreeMap<StreamId, Context>,
+struct ContextMap<S: DecoderSession> {
+    map: BTreeMap<StreamId, Context<S>>,
 }
 
-impl ContextMap {
-    fn insert(&mut self, ctx: Context) -> VideoResult<()> {
+impl<S: DecoderSession> ContextMap<S> {
+    fn new() -> Self {
+        ContextMap {
+            map: Default::default(),
+        }
+    }
+
+    fn insert(&mut self, ctx: Context<S>) -> VideoResult<()> {
         match self.map.entry(ctx.stream_id) {
             Entry::Vacant(e) => {
                 e.insert(ctx);
@@ -314,14 +332,14 @@
         }
     }
 
-    fn get(&self, stream_id: &StreamId) -> VideoResult<&Context> {
+    fn get(&self, stream_id: &StreamId) -> VideoResult<&Context<S>> {
         self.map.get(stream_id).ok_or_else(|| {
             error!("failed to get context of stream {}", *stream_id);
             VideoError::InvalidStreamId(*stream_id)
         })
     }
 
-    fn get_mut(&mut self, stream_id: &StreamId) -> VideoResult<&mut Context> {
+    fn get_mut(&mut self, stream_id: &StreamId) -> VideoResult<&mut Context<S>> {
         self.map.get_mut(stream_id).ok_or_else(|| {
             error!("failed to get context of stream {}", *stream_id);
             VideoError::InvalidStreamId(*stream_id)
@@ -329,59 +347,14 @@
     }
 }
 
-/// A thin wrapper of a map of libvda sesssions with error handlings.
-#[derive(Default)]
-struct SessionMap<'a> {
-    map: BTreeMap<u32, libvda::decode::Session<'a>>,
-}
-
-impl<'a> SessionMap<'a> {
-    fn contains_key(&self, stream_id: StreamId) -> bool {
-        self.map.contains_key(&stream_id)
-    }
-
-    fn get(&self, stream_id: &StreamId) -> VideoResult<&libvda::decode::Session<'a>> {
-        self.map.get(&stream_id).ok_or_else(|| {
-            error!("failed to get libvda session {}", *stream_id);
-            VideoError::InvalidStreamId(*stream_id)
-        })
-    }
-
-    fn get_mut(&mut self, stream_id: &StreamId) -> VideoResult<&mut libvda::decode::Session<'a>> {
-        self.map.get_mut(&stream_id).ok_or_else(|| {
-            error!("failed to get libvda session {}", *stream_id);
-            VideoError::InvalidStreamId(*stream_id)
-        })
-    }
-
-    fn insert(
-        &mut self,
-        stream_id: StreamId,
-        session: libvda::decode::Session<'a>,
-    ) -> Option<libvda::decode::Session<'a>> {
-        self.map.insert(stream_id, session)
-    }
-}
-
-/// Represents information of a decoder backed with `libvda`.
-pub struct Decoder<'a> {
-    vda: &'a libvda::decode::VdaInstance,
+/// Represents information of a decoder backed by a `DecoderBackend`.
+pub struct Decoder<D: DecoderBackend> {
+    decoder: D,
     capability: Capability,
-    contexts: ContextMap,
-    sessions: SessionMap<'a>,
+    contexts: ContextMap<D::Session>,
 }
 
-impl<'a> Decoder<'a> {
-    pub fn new(vda: &'a libvda::decode::VdaInstance) -> Self {
-        let capability = Capability::new(vda.get_capabilities());
-        Decoder {
-            vda,
-            capability,
-            contexts: Default::default(),
-            sessions: Default::default(),
-        }
-    }
-
+impl<'a, D: DecoderBackend> Decoder<D> {
     /*
      * Functions processing virtio-video commands.
      */
@@ -397,7 +370,7 @@
 
     fn create_stream(&mut self, stream_id: StreamId, coded_format: Format) -> VideoResult<()> {
         // Create an instance of `Context`.
-        // Note that `libvda::Session` will be created not here but at the first call of
+        // Note that the `DecoderSession` will be created not here but at the first call of
         // `ResourceCreate`. This is because we need to fix a coded format for it, which
         // will be set by `SetParams`.
         self.contexts.insert(Context::new(stream_id, coded_format))
@@ -407,43 +380,26 @@
         if self.contexts.map.remove(&stream_id).is_none() {
             error!("Tried to destroy an invalid stream context {}", stream_id);
         }
-
-        // Close a libVDA session, as closing will be done in `Drop` for `session`.
-        // Note that `sessions` doesn't have an instance for `stream_id` if the
-        // first `ResourceCreate` haven't been called yet.
-        self.sessions.map.remove(&stream_id);
     }
 
     fn create_session(
-        vda: &'a libvda::decode::VdaInstance,
-        poll_ctx: &PollContext<Token>,
-        ctx: &Context,
+        decoder: &D,
+        wait_ctx: &WaitContext<Token>,
+        ctx: &Context<D::Session>,
         stream_id: StreamId,
-    ) -> VideoResult<libvda::decode::Session<'a>> {
-        let profile = match ctx.in_params.format {
-            Some(Format::VP8) => Ok(libvda::Profile::VP8),
-            Some(Format::VP9) => Ok(libvda::Profile::VP9Profile0),
-            Some(Format::H264) => Ok(libvda::Profile::H264ProfileBaseline),
-            Some(f) => {
-                error!("specified format is invalid for bitstream: {}", f);
-                Err(VideoError::InvalidParameter)
-            }
+    ) -> VideoResult<D::Session> {
+        let format = match ctx.in_params.format {
+            Some(f) => f,
             None => {
                 error!("bitstream format is not specified");
-                Err(VideoError::InvalidParameter)
+                return Err(VideoError::InvalidParameter);
             }
-        }?;
+        };
 
-        let session = vda.open_session(profile).map_err(|e| {
-            error!(
-                "failed to open a session {} for {:?}: {}",
-                stream_id, profile, e
-            );
-            VideoError::InvalidOperation
-        })?;
+        let session = decoder.new_session(format)?;
 
-        poll_ctx
-            .add(session.pipe(), Token::Event { id: stream_id })
+        wait_ctx
+            .add(session.event_pipe(), Token::Event { id: stream_id })
             .map_err(|e| {
                 error!(
                     "failed to add FD to poll context for session {}: {}",
@@ -457,7 +413,7 @@
 
     fn create_resource(
         &mut self,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
         stream_id: StreamId,
         queue_type: QueueType,
         resource_id: ResourceId,
@@ -465,11 +421,15 @@
     ) -> VideoResult<()> {
         let ctx = self.contexts.get_mut(&stream_id)?;
 
-        // Create a instance of `libvda::Session` at the first time `ResourceCreate` is
+        // Create a instance of `DecoderSession` at the first time `ResourceCreate` is
         // called here.
-        if !self.sessions.contains_key(stream_id) {
-            let session = Self::create_session(self.vda, poll_ctx, ctx, stream_id)?;
-            self.sessions.insert(stream_id, session);
+        if ctx.session.is_none() {
+            ctx.session = Some(Self::create_session(
+                &self.decoder,
+                wait_ctx,
+                ctx,
+                stream_id,
+            )?);
         }
 
         ctx.register_buffer(queue_type, resource_id, &uuid);
@@ -521,19 +481,20 @@
         timestamp: u64,
         data_sizes: Vec<u32>,
     ) -> VideoResult<()> {
-        let session = self.sessions.get(&stream_id)?;
         let ctx = self.contexts.get_mut(&stream_id)?;
+        let session = ctx.session.as_ref().ok_or(VideoError::InvalidOperation)?;
 
         if data_sizes.len() != 1 {
             error!("num_data_sizes must be 1 but {}", data_sizes.len());
             return Err(VideoError::InvalidOperation);
         }
 
-        // Take an ownership of this file by `into_raw_fd()` as this file will be closed by libvda.
+        // Take an ownership of this file by `into_raw_descriptor()` as this file will be closed
+        // by the `DecoderBackend`.
         let fd = ctx
             .get_resource_info(QueueType::Input, resource_bridge, resource_id)?
             .file
-            .into_raw_fd();
+            .into_raw_descriptor();
 
         // Register a mapping of timestamp to resource_id
         if let Some(old_resource_id) = ctx
@@ -554,16 +515,12 @@
         // a guest passes to a driver as a 32-bit integer in our implementation.
         // So, overflow must not happen in this conversion.
         let ts_sec: i32 = (timestamp / 1_000_000_000) as i32;
-        session
-            .decode(
-                ts_sec,
-                fd,            // fd
-                0,             // offset is always 0 due to the driver implementation.
-                data_sizes[0], // bytes_used
-            )
-            .map_err(VideoError::VdaError)?;
-
-        Ok(())
+        session.decode(
+            ts_sec,
+            fd,            // fd
+            0,             // offset is always 0 due to the driver implementation.
+            data_sizes[0], // bytes_used
+        )
     }
 
     fn queue_output_resource(
@@ -572,8 +529,8 @@
         stream_id: StreamId,
         resource_id: ResourceId,
     ) -> VideoResult<()> {
-        let session = self.sessions.get(&stream_id)?;
         let ctx = self.contexts.get_mut(&stream_id)?;
+        let session = ctx.session.as_ref().ok_or(VideoError::InvalidOperation)?;
 
         // Check if the current pixel format is set to NV12.
         match ctx.out_params.format {
@@ -596,18 +553,16 @@
                 // Don't enqueue this resource to the host.
                 Ok(())
             }
-            QueueOutputResourceResult::Reused(buffer_id) => session
-                .reuse_output_buffer(buffer_id)
-                .map_err(VideoError::VdaError),
+            QueueOutputResourceResult::Reused(buffer_id) => session.reuse_output_buffer(buffer_id),
             QueueOutputResourceResult::Registered(buffer_id) => {
                 let resource_info =
                     ctx.get_resource_info(QueueType::Output, resource_bridge, resource_id)?;
                 let planes = vec![
-                    libvda::FramePlane {
+                    FramePlane {
                         offset: resource_info.planes[0].offset as i32,
                         stride: resource_info.planes[0].stride as i32,
                     },
-                    libvda::FramePlane {
+                    FramePlane {
                         offset: resource_info.planes[1].offset as i32,
                         stride: resource_info.planes[1].stride as i32,
                     },
@@ -621,17 +576,13 @@
                     // TODO(b/1518105): This is a hack due to the lack of way of telling a number of
                     // frame buffers explictly in virtio-video v3 RFC. Once we have the way,
                     // set_output_buffer_count should be called with a value passed by the guest.
-                    session
-                        .set_output_buffer_count(OUTPUT_BUFFER_COUNT)
-                        .map_err(VideoError::VdaError)?;
+                    session.set_output_buffer_count(OUTPUT_BUFFER_COUNT)?;
                 }
 
-                // Take ownership of this file by `into_raw_fd()` as this
+                // Take ownership of this file by `into_raw_descriptor()` as this
                 // file will be closed by libvda.
-                let fd = resource_info.file.into_raw_fd();
-                session
-                    .use_output_buffer(buffer_id as i32, libvda::PixelFormat::NV12, fd, &planes)
-                    .map_err(VideoError::VdaError)
+                let fd = resource_info.file.into_raw_descriptor();
+                session.use_output_buffer(buffer_id as i32, Format::NV12, fd, &planes)
             }
         }
     }
@@ -653,7 +604,7 @@
         let ctx = self.contexts.get_mut(&stream_id)?;
         match queue_type {
             QueueType::Input => {
-                if self.sessions.contains_key(stream_id) {
+                if ctx.session.is_some() {
                     error!("parameter for input cannot be changed once decoding started");
                     return Err(VideoError::InvalidParameter);
                 }
@@ -719,16 +670,17 @@
     }
 
     fn drain_stream(&mut self, stream_id: StreamId) -> VideoResult<()> {
-        self.sessions
+        self.contexts
             .get(&stream_id)?
+            .session
+            .as_ref()
+            .ok_or(VideoError::InvalidOperation)?
             .flush()
-            .map_err(VideoError::VdaError)?;
-        Ok(())
     }
 
     fn clear_queue(&mut self, stream_id: StreamId, queue_type: QueueType) -> VideoResult<()> {
         let ctx = self.contexts.get_mut(&stream_id)?;
-        let session = self.sessions.get(&stream_id)?;
+        let session = ctx.session.as_ref().ok_or(VideoError::InvalidOperation)?;
 
         // TODO(b/153406792): Though QUEUE_CLEAR is defined as a per-queue command in the
         // specification, the VDA's `Reset()` clears the input buffers and may (or may not) drop
@@ -738,22 +690,25 @@
         // REQBUFS(0). To handle this problem correctly, we need to make libvda expose
         // DismissPictureBuffer() method.
         match queue_type {
-            QueueType::Input => {
-                session.reset().map_err(VideoError::VdaError)?;
-            }
+            QueueType::Input => session.reset()?,
             QueueType::Output => {
-                ctx.out_res = Default::default();
+                if std::mem::replace(&mut ctx.is_clear_out_res_needed, false) {
+                    ctx.out_res = Default::default();
+                } else {
+                    ctx.out_res.queued_res_ids.clear();
+                }
             }
-        }
+        };
+
         Ok(())
     }
 }
 
-impl<'a> Device for Decoder<'a> {
+impl<D: DecoderBackend> Device for Decoder<D> {
     fn process_cmd(
         &mut self,
         cmd: VideoCmd,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
         resource_bridge: &ResourceRequestSocket,
     ) -> VideoResult<VideoCmdResponseType> {
         use VideoCmd::*;
@@ -780,7 +735,7 @@
                 // ignore `plane_offsets` as we use `resource_info` given by `resource_bridge` instead.
                 ..
             } => {
-                self.create_resource(poll_ctx, stream_id, queue_type, resource_id, uuid)?;
+                self.create_resource(wait_ctx, stream_id, queue_type, resource_id, uuid)?;
                 Ok(Sync(CmdResponse::NoData))
             }
             ResourceDestroyAll {
@@ -882,15 +837,19 @@
         // result that would allow us to return an error to the caller.
 
         use crate::virtio::video::device::VideoEvtResponseType::*;
-        use libvda::decode::Event::*;
 
-        let session = match self.sessions.get_mut(&stream_id) {
-            Ok(s) => s,
+        let ctx = match self.contexts.get_mut(&stream_id) {
+            Ok(ctx) => ctx,
             Err(e) => {
-                error!(
-                    "an event notified for an unknown session {}: {}",
-                    stream_id, e
-                );
+                error!("failed to get a context for session {}: {}", stream_id, e);
+                return None;
+            }
+        };
+
+        let session = match ctx.session.as_mut() {
+            Some(s) => s,
+            None => {
+                error!("session not yet created for context {}", stream_id);
                 return None;
             }
         };
@@ -903,50 +862,31 @@
             }
         };
 
-        let ctx = match self.contexts.get_mut(&stream_id) {
-            Ok(ctx) => ctx,
-            Err(_) => {
-                error!(
-                    "failed to get a context for session {}: {:?}",
-                    stream_id, event
-                );
-                return None;
-            }
-        };
-
         let event_responses = match event {
-            ProvidePictureBuffers {
+            DecoderEvent::ProvidePictureBuffers {
                 min_num_buffers,
                 width,
                 height,
-                visible_rect_left,
-                visible_rect_top,
-                visible_rect_right,
-                visible_rect_bottom,
+                visible_rect,
             } => {
-                ctx.handle_provide_picture_buffers(
-                    min_num_buffers,
-                    width,
-                    height,
-                    visible_rect_left,
-                    visible_rect_top,
-                    visible_rect_right,
-                    visible_rect_bottom,
-                );
+                ctx.handle_provide_picture_buffers(min_num_buffers, width, height, visible_rect);
                 vec![Event(VideoEvt {
                     typ: EvtType::DecResChanged,
                     stream_id,
                 })]
             }
-            PictureReady {
-                buffer_id, // FrameBufferId
+            DecoderEvent::PictureReady {
+                picture_buffer_id, // FrameBufferId
                 bitstream_id: ts_sec,
-                left,
-                top,
-                right,
-                bottom,
+                visible_rect,
             } => {
-                let resource_id = ctx.handle_picture_ready(buffer_id, left, top, right, bottom)?;
+                let resource_id = ctx.handle_picture_ready(
+                    picture_buffer_id,
+                    visible_rect.left,
+                    visible_rect.top,
+                    visible_rect.right,
+                    visible_rect.bottom,
+                )?;
                 let async_response = AsyncCmdResponse::from_response(
                     AsyncCmdTag::Queue {
                         stream_id,
@@ -964,7 +904,7 @@
                 );
                 vec![AsyncCmd(async_response)]
             }
-            NotifyEndOfBitstreamBuffer { bitstream_id } => {
+            DecoderEvent::NotifyEndOfBitstreamBuffer(bitstream_id) => {
                 let resource_id = ctx.handle_notify_end_of_bitstream_buffer(bitstream_id)?;
                 let async_response = AsyncCmdResponse::from_response(
                     AsyncCmdTag::Queue {
@@ -980,9 +920,9 @@
                 );
                 vec![AsyncCmd(async_response)]
             }
-            FlushResponse(flush_response) => {
-                match flush_response {
-                    libvda::decode::Response::Success => {
+            DecoderEvent::FlushCompleted(flush_result) => {
+                match flush_result {
+                    Ok(()) => {
                         let eos_resource_id = match ctx.out_res.dequeue_eos_resource_id() {
                             Some(r) => r,
                             None => {
@@ -1018,27 +958,27 @@
                             )),
                         ]
                     }
-                    _ => {
+                    Err(error) => {
                         // TODO(b/151810591): If `resp` is `libvda::decode::Response::Canceled`,
                         // we should notify it to the driver in some way.
                         error!(
                             "failed to 'Flush' in VDA (stream id {}): {:?}",
-                            stream_id, flush_response
+                            stream_id, error
                         );
                         vec![AsyncCmd(AsyncCmdResponse::from_error(
                             AsyncCmdTag::Drain { stream_id },
-                            VideoError::VdaFailure(flush_response),
+                            error,
                         ))]
                     }
                 }
             }
-            ResetResponse(reset_response) => {
+            DecoderEvent::ResetCompleted(reset_result) => {
                 let tag = AsyncCmdTag::Clear {
                     stream_id,
                     queue_type: QueueType::Input,
                 };
-                match reset_response {
-                    libvda::decode::Response::Success => {
+                match reset_result {
+                    Ok(()) => {
                         let mut responses: Vec<_> = desc_map
                             .create_cancellation_responses(
                                 &stream_id,
@@ -1054,20 +994,17 @@
                         )));
                         responses
                     }
-                    _ => {
+                    Err(error) => {
                         error!(
                             "failed to 'Reset' in VDA (stream id {}): {:?}",
-                            stream_id, reset_response
+                            stream_id, error
                         );
-                        vec![AsyncCmd(AsyncCmdResponse::from_error(
-                            tag,
-                            VideoError::VdaFailure(reset_response),
-                        ))]
+                        vec![AsyncCmd(AsyncCmdResponse::from_error(tag, error))]
                     }
                 }
             }
-            NotifyError(resp) => {
-                error!("an error is notified by VDA: {}", resp);
+            DecoderEvent::NotifyError(error) => {
+                error!("an error is notified by VDA: {}", error);
                 vec![Event(VideoEvt {
                     typ: EvtType::Error,
                     stream_id,
@@ -1078,3 +1015,15 @@
         Some(event_responses)
     }
 }
+
+/// Create a new decoder instance using a Libvda decoder instance to perform
+/// the decoding.
+impl<'a> Decoder<&'a libvda::decode::VdaInstance> {
+    pub fn new(vda: &'a libvda::decode::VdaInstance) -> Self {
+        Decoder {
+            decoder: vda,
+            capability: Capability::new(vda.get_capabilities()),
+            contexts: ContextMap::new(),
+        }
+    }
+}
diff --git a/devices/src/virtio/video/device.rs b/devices/src/virtio/video/device.rs
index bd45390..d51405c 100644
--- a/devices/src/virtio/video/device.rs
+++ b/devices/src/virtio/video/device.rs
@@ -4,7 +4,7 @@
 
 //! Definition of the trait `Device` that each backend video device must implement.
 
-use base::{PollContext, PollToken};
+use base::{PollToken, WaitContext};
 
 use crate::virtio::resource_bridge::ResourceRequestSocket;
 use crate::virtio::video::async_cmd_desc_map::AsyncCmdDescMap;
@@ -94,14 +94,14 @@
     /// Processes a virtio-video command.
     /// If the command expects a synchronous response, it returns a response as `VideoCmdResponseType::Sync`.
     /// Otherwise, it returns a name of the descriptor chain that will be used when a response is prepared.
-    /// Implementations of this method is passed a PollContext object which can be used to add or remove
+    /// Implementations of this method is passed a WaitContext object which can be used to add or remove
     /// FDs to poll. It is expected that only Token::Event items would be added. When a Token::Event
     /// event arrives, process_event() will be invoked.
     /// TODO(b/149720783): Make this an async function.
     fn process_cmd(
         &mut self,
         cmd: VideoCmd,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
         resource_bridge: &ResourceRequestSocket,
     ) -> VideoResult<VideoCmdResponseType>;
 
diff --git a/devices/src/virtio/video/encoder/libvda_encoder.rs b/devices/src/virtio/video/encoder/libvda_encoder.rs
index cc5b76f..3f17938 100644
--- a/devices/src/virtio/video/encoder/libvda_encoder.rs
+++ b/devices/src/virtio/video/encoder/libvda_encoder.rs
@@ -5,11 +5,10 @@
 use std::collections::btree_map::Entry;
 use std::collections::BTreeMap;
 use std::fs::File;
-use std::os::unix::io::IntoRawFd;
 
 use libvda::encode::{EncodeCapabilities, VeaImplType, VeaInstance};
 
-use base::{error, warn};
+use base::{error, warn, IntoRawDescriptor};
 
 use crate::virtio::video::encoder::encoder::*;
 use crate::virtio::video::format::{Format, FormatDesc, FormatRange, FrameFormat, Level, Profile};
@@ -52,12 +51,12 @@
                     frame_formats: vec![FrameFormat {
                         width: FormatRange {
                             min: 2,
-                            max: 1920,
+                            max: 4096,
                             step: 1,
                         },
                         height: FormatRange {
                             min: 2,
-                            max: 1080,
+                            max: 4096,
                             step: 1,
                         },
                         bitrates: vec![FormatRange {
@@ -281,7 +280,7 @@
         self.session
             .encode(
                 input_buffer_id as i32,
-                resource.into_raw_fd(),
+                resource.into_raw_descriptor(),
                 &libvda_planes,
                 timestamp as i64,
                 force_keyframe,
@@ -298,7 +297,12 @@
         self.next_output_buffer_id = self.next_output_buffer_id.wrapping_add(1);
 
         self.session
-            .use_output_buffer(output_buffer_id as i32, file.into_raw_fd(), offset, size)
+            .use_output_buffer(
+                output_buffer_id as i32,
+                file.into_raw_descriptor(),
+                offset,
+                size,
+            )
             .map_err(|e| EncoderError::Implementation(Box::new(e)))?;
 
         Ok(output_buffer_id)
diff --git a/devices/src/virtio/video/encoder/mod.rs b/devices/src/virtio/video/encoder/mod.rs
index f2e795a..f70734b 100644
--- a/devices/src/virtio/video/encoder/mod.rs
+++ b/devices/src/virtio/video/encoder/mod.rs
@@ -11,7 +11,7 @@
 pub use encoder::EncoderError;
 pub use libvda_encoder::LibvdaEncoder;
 
-use base::{error, warn, PollContext};
+use base::{error, warn, WaitContext};
 use std::collections::{BTreeMap, BTreeSet};
 
 use crate::virtio::resource_bridge::{self, ResourceRequestSocket};
@@ -168,7 +168,7 @@
     fn set_encode_session<U: Encoder<Session = T>>(
         &mut self,
         encoder: &mut U,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
     ) -> VideoResult<()> {
         if self.encoder_session.is_some() {
             error!(
@@ -190,7 +190,7 @@
 
         let event_pipe = new_session.event_pipe();
 
-        poll_ctx
+        wait_ctx
             .add(event_pipe, Token::Event { id: self.id })
             .map_err(|e| {
                 error!(
@@ -204,10 +204,10 @@
         Ok(())
     }
 
-    fn clear_encode_session(&mut self, poll_ctx: &PollContext<Token>) -> VideoResult<()> {
+    fn clear_encode_session(&mut self, wait_ctx: &WaitContext<Token>) -> VideoResult<()> {
         if let Some(session) = self.encoder_session.take() {
             let event_pipe = session.event_pipe();
-            poll_ctx.delete(event_pipe).map_err(|e| {
+            wait_ctx.delete(event_pipe).map_err(|e| {
                 error!(
                     "stream: {}: failed to remove fd from poll context: {}",
                     self.id, e
@@ -573,7 +573,7 @@
 
     fn resource_create(
         &mut self,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
         resource_bridge: &ResourceRequestSocket,
         stream_id: u32,
         queue_type: QueueType,
@@ -589,7 +589,7 @@
         if !stream.has_encode_session() {
             // No encode session would have been created upon the first
             // QBUF if there was no previous S_FMT call.
-            stream.set_encode_session(&mut self.encoder, poll_ctx)?;
+            stream.set_encode_session(&mut self.encoder, wait_ctx)?;
         }
 
         let num_planes = plane_offsets.len();
@@ -924,7 +924,7 @@
 
     fn set_params(
         &mut self,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
         stream_id: u32,
         queue_type: QueueType,
         format: Option<Format>,
@@ -991,7 +991,7 @@
         // callback which has output buffer size info, in order to populate
         // dst_params to have the correct size on subsequent GetParams (G_FMT) calls.
         if stream.encoder_session.is_some() {
-            stream.clear_encode_session(poll_ctx)?;
+            stream.clear_encode_session(wait_ctx)?;
             if !stream.received_input_buffers_event {
                 // This could happen if two SetParams calls are occuring at the same time.
                 // For example, the user calls SetParams for the input queue on one thread,
@@ -1006,7 +1006,7 @@
                 warn!("New encoder session being created while waiting for RequireInputBuffers.")
             }
         }
-        stream.set_encode_session(&mut self.encoder, poll_ctx)?;
+        stream.set_encode_session(&mut self.encoder, wait_ctx)?;
         Ok(VideoCmdResponseType::Sync(CmdResponse::NoData))
     }
 
@@ -1082,10 +1082,9 @@
         match ctrl_val {
             CtrlVal::Bitrate(bitrate) => {
                 if let Some(ref mut encoder_session) = stream.encoder_session {
-                    if let Err(e) = encoder_session.request_encoding_params_change(
-                        stream.dst_bitrate,
-                        stream.dst_params.frame_rate,
-                    ) {
+                    if let Err(e) = encoder_session
+                        .request_encoding_params_change(bitrate, stream.dst_params.frame_rate)
+                    {
                         error!(
                             "failed to dynamically request encoding params change: {}",
                             e
@@ -1146,7 +1145,7 @@
     fn process_cmd(
         &mut self,
         req: VideoCmd,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
         resource_bridge: &ResourceRequestSocket,
     ) -> VideoResult<VideoCmdResponseType> {
         match req {
@@ -1164,7 +1163,7 @@
                 plane_offsets,
                 uuid,
             } => self.resource_create(
-                poll_ctx,
+                wait_ctx,
                 resource_bridge,
                 stream_id,
                 queue_type,
@@ -1207,7 +1206,7 @@
                         ..
                     },
             } => self.set_params(
-                poll_ctx,
+                wait_ctx,
                 stream_id,
                 queue_type,
                 format,
diff --git a/devices/src/virtio/video/error.rs b/devices/src/virtio/video/error.rs
index 11256d3..49e456a 100644
--- a/devices/src/virtio/video/error.rs
+++ b/devices/src/virtio/video/error.rs
@@ -61,6 +61,12 @@
     }
 }
 
+impl From<libvda::Error> for VideoError {
+    fn from(error: libvda::Error) -> Self {
+        VideoError::VdaError(error)
+    }
+}
+
 impl std::error::Error for VideoError {}
 
 pub type VideoResult<T> = Result<T, VideoError>;
diff --git a/devices/src/virtio/video/format.rs b/devices/src/virtio/video/format.rs
index 7a5e4ed..a7d61ce 100644
--- a/devices/src/virtio/video/format.rs
+++ b/devices/src/virtio/video/format.rs
@@ -273,3 +273,12 @@
         Some(format) => (format.width.max, format.height.max),
     }
 }
+
+/// A rectangle used to describe portions of a frame.
+#[derive(Debug)]
+pub struct Rect {
+    pub left: i32,
+    pub top: i32,
+    pub right: i32,
+    pub bottom: i32,
+}
diff --git a/devices/src/virtio/video/mod.rs b/devices/src/virtio/video/mod.rs
index 637324b..e7c95f0 100644
--- a/devices/src/virtio/video/mod.rs
+++ b/devices/src/virtio/video/mod.rs
@@ -8,10 +8,9 @@
 //! [v3 RFC]: https://markmail.org/thread/wxdne5re7aaugbjg
 
 use std::fmt::{self, Display};
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::thread;
 
-use base::{error, Error as SysError, Event};
+use base::{error, AsRawDescriptor, Error as SysError, Event, RawDescriptor};
 use data_model::{DataInit, Le32};
 use vm_memory::GuestMemory;
 
@@ -45,14 +44,14 @@
 /// An error indicating something went wrong in virtio-video's worker.
 #[derive(Debug)]
 pub enum Error {
-    /// Creating PollContext failed.
-    PollContextCreationFailed(SysError),
+    /// Creating WaitContext failed.
+    WaitContextCreationFailed(SysError),
     /// A DescriptorChain contains invalid data.
     InvalidDescriptorChain(DescriptorError),
     /// No available descriptor in which an event is written to.
     DescriptorNotAvailable,
     /// Error while polling for events.
-    PollError(SysError),
+    WaitError(SysError),
     /// Failed to read a virtio-video command.
     ReadFailure(ReadCmdError),
     /// Got response for an unexpected asynchronous command.
@@ -68,12 +67,12 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use Error::*;
         match self {
-            PollContextCreationFailed(e) => write!(f, "failed to create PollContext: {}", e),
+            WaitContextCreationFailed(e) => write!(f, "failed to create WaitContext: {}", e),
             InvalidDescriptorChain(e) => write!(f, "DescriptorChain contains invalid data: {}", e),
             DescriptorNotAvailable => {
                 write!(f, "no available descriptor in which an event is written to")
             }
-            PollError(err) => write!(f, "failed to poll events: {}", err),
+            WaitError(err) => write!(f, "failed to wait for events: {}", err),
             ReadFailure(e) => write!(f, "failed to read a command from the guest: {}", e),
             UnexpectedResponse(tag) => {
                 write!(f, "got a response for an untracked command: {:?}", tag)
@@ -128,12 +127,12 @@
 }
 
 impl VirtioDevice for VideoDevice {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
         if let Some(resource_bridge) = &self.resource_bridge {
-            keep_fds.push(resource_bridge.as_raw_fd());
+            keep_rds.push(resource_bridge.as_raw_descriptor());
         }
-        keep_fds
+        keep_rds
     }
 
     fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/video/worker.rs b/devices/src/virtio/video/worker.rs
index fcdd1cd..0bac2fc 100644
--- a/devices/src/virtio/video/worker.rs
+++ b/devices/src/virtio/video/worker.rs
@@ -6,7 +6,7 @@
 
 use std::collections::VecDeque;
 
-use base::{error, Event, PollContext};
+use base::{error, info, Event, WaitContext};
 use vm_memory::GuestMemory;
 
 use crate::virtio::queue::{DescriptorChain, Queue};
@@ -14,7 +14,7 @@
 use crate::virtio::video::async_cmd_desc_map::AsyncCmdDescMap;
 use crate::virtio::video::command::{QueueType, VideoCmd};
 use crate::virtio::video::device::{
-    AsyncCmdResponse, Device, Token, VideoCmdResponseType, VideoEvtResponseType,
+    AsyncCmdResponse, AsyncCmdTag, Device, Token, VideoCmdResponseType, VideoEvtResponseType,
 };
 use crate::virtio::video::event::{self, EvtType, VideoEvt};
 use crate::virtio::video::response::{self, Response};
@@ -82,7 +82,7 @@
     fn handle_command_desc<T: Device>(
         &self,
         device: &mut T,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
         desc_map: &mut AsyncCmdDescMap,
         desc: DescriptorChain,
     ) -> Result<VecDeque<WritableResp>> {
@@ -133,7 +133,7 @@
         }
 
         // Process the command by the device.
-        let process_cmd_response = device.process_cmd(cmd, &poll_ctx, &self.resource_bridge);
+        let process_cmd_response = device.process_cmd(cmd, &wait_ctx, &self.resource_bridge);
         match process_cmd_response {
             Ok(VideoCmdResponseType::Sync(r)) => {
                 responses.push_back((desc, r));
@@ -158,12 +158,12 @@
         &self,
         cmd_queue: &mut Queue,
         device: &mut T,
-        poll_ctx: &PollContext<Token>,
+        wait_ctx: &WaitContext<Token>,
         desc_map: &mut AsyncCmdDescMap,
     ) -> Result<()> {
         let _ = self.cmd_evt.read();
         while let Some(desc) = cmd_queue.pop(&self.mem) {
-            let mut resps = self.handle_command_desc(device, poll_ctx, desc_map, desc)?;
+            let mut resps = self.handle_command_desc(device, wait_ctx, desc_map, desc)?;
             self.write_responses(cmd_queue, &mut resps)?;
         }
         Ok(())
@@ -187,17 +187,29 @@
                             tag,
                             response: cmd_result,
                         } = async_response;
-                        let desc = desc_map
-                            .remove(&tag)
-                            .ok_or_else(|| Error::UnexpectedResponse(tag))?;
-                        let cmd_response = match cmd_result {
-                            Ok(r) => r,
-                            Err(e) => {
-                                error!("returning async error response: {}", &e);
-                                e.into()
+                        match desc_map.remove(&tag) {
+                            Some(desc) => {
+                                let cmd_response = match cmd_result {
+                                    Ok(r) => r,
+                                    Err(e) => {
+                                        error!("returning async error response: {}", &e);
+                                        e.into()
+                                    }
+                                };
+                                responses.push_back((desc, cmd_response))
                             }
-                        };
-                        responses.push_back((desc, cmd_response));
+                            None => match tag {
+                                // TODO(b/153406792): Drain is cancelled by clearing either of the
+                                // stream's queues. To work around a limitation in the VDA api, the
+                                // output queue is cleared synchronously without going through VDA.
+                                // Because of this, the cancellation response from VDA for the
+                                // input queue might fail to find the drain's AsyncCmdTag.
+                                AsyncCmdTag::Drain { stream_id: _ } => {
+                                    info!("ignoring unknown drain response");
+                                }
+                                _ => return Err(Error::UnexpectedResponse(tag)),
+                            },
+                        }
                     }
                     VideoEvtResponseType::Event(evt) => {
                         self.write_event(event_queue, evt)?;
@@ -228,27 +240,27 @@
         mut event_queue: Queue,
         mut device: T,
     ) -> Result<()> {
-        let poll_ctx: PollContext<Token> = PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
             (&self.cmd_evt, Token::CmdQueue),
             (&self.event_evt, Token::EventQueue),
             (&self.kill_evt, Token::Kill),
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
         ])
-        .map_err(Error::PollContextCreationFailed)?;
+        .map_err(Error::WaitContextCreationFailed)?;
 
         // Stores descriptors in which responses for asynchronous commands will be written.
         let mut desc_map: AsyncCmdDescMap = Default::default();
 
         loop {
-            let poll_events = poll_ctx.wait().map_err(Error::PollError)?;
+            let wait_events = wait_ctx.wait().map_err(Error::WaitError)?;
 
-            for poll_event in poll_events.iter_readable() {
-                match poll_event.token() {
+            for wait_event in wait_events.iter().filter(|e| e.is_readable) {
+                match wait_event.token {
                     Token::CmdQueue => {
                         self.handle_command_queue(
                             &mut cmd_queue,
                             &mut device,
-                            &poll_ctx,
+                            &wait_ctx,
                             &mut desc_map,
                         )?;
                     }
diff --git a/devices/src/virtio/virtio_device.rs b/devices/src/virtio/virtio_device.rs
index 36f5a56..c181b44 100644
--- a/devices/src/virtio/virtio_device.rs
+++ b/devices/src/virtio/virtio_device.rs
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std::os::unix::io::RawFd;
-
-use base::Event;
+use base::{Event, RawDescriptor};
 use vm_memory::GuestMemory;
 
 use super::*;
@@ -28,7 +26,7 @@
 
     /// A vector of device-specific file descriptors that must be kept open
     /// after jailing. Must be called before the process is jailed.
-    fn keep_fds(&self) -> Vec<RawFd>;
+    fn keep_rds(&self) -> Vec<RawDescriptor>;
 
     /// The virtio device type.
     fn device_type(&self) -> u32;
diff --git a/devices/src/virtio/virtio_pci_common_config.rs b/devices/src/virtio/virtio_pci_common_config.rs
index 41d9799..ea8975f 100644
--- a/devices/src/virtio/virtio_pci_common_config.rs
+++ b/devices/src/virtio/virtio_pci_common_config.rs
@@ -243,8 +243,7 @@
 mod tests {
     use super::*;
 
-    use base::Event;
-    use std::os::unix::io::RawFd;
+    use base::{Event, RawDescriptor};
     use vm_memory::GuestMemory;
 
     struct DummyDevice(u32);
@@ -252,7 +251,7 @@
     const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE];
     const DUMMY_FEATURES: u64 = 0x5555_aaaa;
     impl VirtioDevice for DummyDevice {
-        fn keep_fds(&self) -> Vec<RawFd> {
+        fn keep_rds(&self) -> Vec<RawDescriptor> {
             Vec::new()
         }
         fn device_type(&self) -> u32 {
diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs
index e1654a8..7d0913a 100644
--- a/devices/src/virtio/virtio_pci_device.rs
+++ b/devices/src/virtio/virtio_pci_device.rs
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Arc;
 use sync::Mutex;
 
-use base::{warn, Event, Result};
+use base::{warn, AsRawDescriptor, Event, RawDescriptor, Result};
 use data_model::{DataInit, Le32};
 use hypervisor::Datamatch;
 use libc::ERANGE;
@@ -17,8 +16,8 @@
 use super::*;
 use crate::pci::{
     MsixCap, MsixConfig, PciAddress, PciBarConfiguration, PciCapability, PciCapabilityID,
-    PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciInterruptPin,
-    PciSubclass,
+    PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciDisplaySubclass, PciHeaderType,
+    PciInterruptPin, PciSubclass,
 };
 use vm_control::VmIrqRequestSocket;
 
@@ -236,6 +235,17 @@
 
         let pci_device_id = VIRTIO_PCI_DEVICE_ID_BASE + device.device_type() as u16;
 
+        let (pci_device_class, pci_device_subclass) = match device.device_type() {
+            TYPE_GPU => (
+                PciClassCode::DisplayController,
+                &PciDisplaySubclass::Other as &dyn PciSubclass,
+            ),
+            _ => (
+                PciClassCode::Other,
+                &PciVirtioSubclass::NonTransitionalBase as &dyn PciSubclass,
+            ),
+        };
+
         let num_queues = device.queue_max_sizes().len();
 
         // One MSI-X vector per queue plus one for configuration changes.
@@ -245,8 +255,8 @@
         let config_regs = PciConfiguration::new(
             VIRTIO_PCI_VENDOR_ID,
             pci_device_id,
-            PciClassCode::Other,
-            &PciVirtioSubclass::NonTransitionalBase,
+            pci_device_class,
+            pci_device_subclass,
             None,
             PciHeaderType::Device,
             VIRTIO_PCI_VENDOR_ID,
@@ -386,17 +396,17 @@
         self.pci_address = Some(address);
     }
 
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut fds = self.device.keep_fds();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut rds = self.device.keep_rds();
         if let Some(interrupt_evt) = &self.interrupt_evt {
-            fds.push(interrupt_evt.as_raw_fd());
+            rds.push(interrupt_evt.as_raw_descriptor());
         }
         if let Some(interrupt_resample_evt) = &self.interrupt_resample_evt {
-            fds.push(interrupt_resample_evt.as_raw_fd());
+            rds.push(interrupt_resample_evt.as_raw_descriptor());
         }
-        let fd = self.msix_config.lock().get_msi_socket();
-        fds.push(fd);
-        fds
+        let descriptor = self.msix_config.lock().get_msi_socket();
+        rds.push(descriptor);
+        rds
     }
 
     fn assign_irq(
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs
index b11f1fa..d2c5180 100644
--- a/devices/src/virtio/wl.rs
+++ b/devices/src/virtio/wl.rs
@@ -39,7 +39,6 @@
 use std::mem::size_of;
 #[cfg(feature = "wl-dmabuf")]
 use std::os::raw::{c_uint, c_ulonglong};
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::os::unix::net::UnixStream;
 use std::path::{Path, PathBuf};
 use std::rc::Rc;
@@ -57,7 +56,8 @@
 use base::ioctl_iow_nr;
 use base::{
     error, pipe, round_up_to_page_size, warn, AsRawDescriptor, Error, Event, FileFlags,
-    PollContext, PollToken, Result, ScmSocket, SharedMemory, SharedMemoryUnix,
+    FromRawDescriptor, PollToken, RawDescriptor, Result, ScmSocket, SharedMemory, SharedMemoryUnix,
+    WaitContext,
 };
 use msg_socket::{MsgError, MsgReceiver, MsgSender};
 #[cfg(feature = "wl-dmabuf")]
@@ -70,7 +70,7 @@
 use super::resource_bridge::*;
 use super::{DescriptorChain, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_WL};
 use vm_control::{
-    MaybeOwnedFd, MemSlot, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse,
+    MaybeOwnedDescriptor, MemSlot, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse,
 };
 
 const VIRTWL_SEND_MAX_ALLOCS: usize = 28;
@@ -273,7 +273,7 @@
     WritePipe(io::Error),
     RecvVfd(Error),
     ReadPipe(io::Error),
-    PollContextAdd(Error),
+    WaitContextAdd(Error),
     DmabufSync(io::Error),
     FromSharedMemory(Error),
     WriteResponse(io::Error),
@@ -300,7 +300,7 @@
             WritePipe(e) => write!(f, "failed to write to a pipe: {}", e),
             RecvVfd(e) => write!(f, "failed to recv on a socket: {}", e),
             ReadPipe(e) => write!(f, "failed to read a pipe: {}", e),
-            PollContextAdd(e) => write!(f, "failed to listen to FD on poll context: {}", e),
+            WaitContextAdd(e) => write!(f, "failed to listen to descriptor on wait context: {}", e),
             DmabufSync(e) => write!(f, "failed to synchronize DMABuf access: {}", e),
             FromSharedMemory(e) => {
                 write!(f, "failed to create shared memory from descriptor: {}", e)
@@ -531,16 +531,16 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "WlVfd {{")?;
         if let Some(s) = &self.socket {
-            write!(f, " socket: {}", s.as_raw_fd())?;
+            write!(f, " socket: {}", s.as_raw_descriptor())?;
         }
         if let Some((slot, pfn, _)) = &self.slot {
             write!(f, " slot: {} pfn: {}", slot, pfn)?;
         }
         if let Some(s) = &self.remote_pipe {
-            write!(f, " remote: {}", s.as_raw_fd())?;
+            write!(f, " remote: {}", s.as_raw_descriptor())?;
         }
         if let Some((_, s)) = &self.local_pipe {
-            write!(f, " local: {}", s.as_raw_fd())?;
+            write!(f, " local: {}", s.as_raw_descriptor())?;
         }
         write!(f, " }}")
     }
@@ -560,7 +560,7 @@
             SharedMemory::named("virtwl_alloc", size_page_aligned).map_err(WlError::NewAlloc)?;
 
         let register_response = vm.request(VmMemoryRequest::RegisterMemory(
-            MaybeOwnedFd::Borrowed(vfd_shm.as_raw_descriptor()),
+            MaybeOwnedDescriptor::Borrowed(vfd_shm.as_raw_descriptor()),
             vfd_shm.size() as usize,
         ))?;
         match register_response {
@@ -589,13 +589,14 @@
             })?;
         match allocate_and_register_gpu_memory_response {
             VmMemoryResponse::AllocateAndRegisterGpuMemory {
-                fd: MaybeOwnedFd::Owned(file),
+                descriptor: MaybeOwnedDescriptor::Owned(file),
                 pfn,
                 slot,
                 desc,
             } => {
                 let mut vfd = WlVfd::default();
-                let vfd_shm = SharedMemory::from_file(file).map_err(WlError::NewAlloc)?;
+                let vfd_shm =
+                    SharedMemory::from_safe_descriptor(file).map_err(WlError::NewAlloc)?;
                 vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm));
                 vfd.slot = Some((slot, pfn, vm));
                 vfd.is_dmabuf = true;
@@ -612,12 +613,12 @@
         }
 
         match &self.guest_shared_memory {
-            Some((_, fd)) => {
+            Some((_, descriptor)) => {
                 let sync = dma_buf_sync {
                     flags: flags as u64,
                 };
-                // Safe as fd is a valid dmabuf and incorrect flags will return an error.
-                if unsafe { ioctl_with_ref(fd, DMA_BUF_IOCTL_SYNC(), &sync) } < 0 {
+                // Safe as descriptor is a valid dmabuf and incorrect flags will return an error.
+                if unsafe { ioctl_with_ref(descriptor, DMA_BUF_IOCTL_SYNC(), &sync) } < 0 {
                     Err(WlError::DmabufSync(io::Error::last_os_error()))
                 } else {
                     Ok(())
@@ -643,17 +644,17 @@
         Ok(vfd)
     }
 
-    fn from_file(vm: VmRequester, mut fd: File) -> WlResult<WlVfd> {
+    fn from_file(vm: VmRequester, mut descriptor: File) -> WlResult<WlVfd> {
         // We need to determine if the given file is more like shared memory or a pipe/socket. A
         // quick and easy check is to seek to the end of the file. If it works we assume it's not a
         // pipe/socket because those have no end. We can even use that seek location as an indicator
         // for how big the shared memory chunk to map into guest memory is. If seeking to the end
         // fails, we assume it's a socket or pipe with read/write semantics.
-        match fd.seek(SeekFrom::End(0)) {
+        match descriptor.seek(SeekFrom::End(0)) {
             Ok(fd_size) => {
                 let size = round_up_to_page_size(fd_size as usize) as u64;
                 let register_response = vm.request(VmMemoryRequest::RegisterMemory(
-                    MaybeOwnedFd::Borrowed(fd.as_raw_fd()),
+                    MaybeOwnedDescriptor::Borrowed(descriptor.as_raw_descriptor()),
                     size as usize,
                 ))?;
 
@@ -662,7 +663,8 @@
                         let mut vfd = WlVfd::default();
                         vfd.guest_shared_memory = Some((
                             size,
-                            SharedMemory::from_file(fd).map_err(WlError::FromSharedMemory)?,
+                            SharedMemory::from_file(descriptor)
+                                .map_err(WlError::FromSharedMemory)?,
                         ));
                         vfd.slot = Some((slot, pfn, vm));
                         Ok(vfd)
@@ -671,14 +673,14 @@
                 }
             }
             _ => {
-                let flags = match FileFlags::from_file(&fd) {
+                let flags = match FileFlags::from_file(&descriptor) {
                     Ok(FileFlags::Read) => VIRTIO_WL_VFD_READ,
                     Ok(FileFlags::Write) => VIRTIO_WL_VFD_WRITE,
                     Ok(FileFlags::ReadWrite) => VIRTIO_WL_VFD_READ | VIRTIO_WL_VFD_WRITE,
                     _ => 0,
                 };
                 let mut vfd = WlVfd::default();
-                vfd.local_pipe = Some((flags, fd));
+                vfd.local_pipe = Some((flags, descriptor));
                 Ok(vfd)
             }
         }
@@ -715,34 +717,37 @@
     }
 
     // The FD that gets sent if this VFD is sent over a socket.
-    fn send_fd(&self) -> Option<RawFd> {
+    fn send_descriptor(&self) -> Option<RawDescriptor> {
         self.guest_shared_memory
             .as_ref()
-            .map(|(_, fd)| fd.as_raw_descriptor())
-            .or(self.socket.as_ref().map(|s| s.as_raw_fd()))
-            .or(self.remote_pipe.as_ref().map(|p| p.as_raw_fd()))
+            .map(|(_, shm)| shm.as_raw_descriptor())
+            .or(self.socket.as_ref().map(|s| s.as_raw_descriptor()))
+            .or(self.remote_pipe.as_ref().map(|p| p.as_raw_descriptor()))
     }
 
     // The FD that is used for polling for events on this VFD.
-    fn poll_fd(&self) -> Option<&dyn AsRawFd> {
+    fn wait_descriptor(&self) -> Option<&dyn AsRawDescriptor> {
         self.socket
             .as_ref()
-            .map(|s| s as &dyn AsRawFd)
-            .or(self.local_pipe.as_ref().map(|(_, p)| p as &dyn AsRawFd))
+            .map(|s| s as &dyn AsRawDescriptor)
+            .or(self
+                .local_pipe
+                .as_ref()
+                .map(|(_, p)| p as &dyn AsRawDescriptor))
     }
 
     // Sends data/files from the guest to the host over this VFD.
-    fn send(&mut self, fds: &[RawFd], data: &mut Reader) -> WlResult<WlResp> {
+    fn send(&mut self, rds: &[RawDescriptor], data: &mut Reader) -> WlResult<WlResp> {
         if let Some(socket) = &self.socket {
             socket
-                .send_with_fds(&data.get_remaining(), fds)
+                .send_with_fds(&data.get_remaining(), rds)
                 .map_err(WlError::SendVfd)?;
             // All remaining data in `data` is now considered consumed.
             data.consume(::std::usize::MAX);
             Ok(WlResp::Ok)
         } else if let Some((_, local_pipe)) = &mut self.local_pipe {
-            // Impossible to send fds over a simple pipe.
-            if !fds.is_empty() {
+            // Impossible to send descriptors over a simple pipe.
+            if !rds.is_empty() {
                 return Ok(WlResp::InvalidType);
             }
             data.read_to(local_pipe, usize::max_value())
@@ -772,7 +777,7 @@
                 in_file_queue.extend(
                     fd_buf[..file_count]
                         .iter()
-                        .map(|&fd| unsafe { File::from_raw_fd(fd) }),
+                        .map(|&descriptor| unsafe { File::from_raw_descriptor(descriptor) }),
                 );
                 return Ok(buf);
             }
@@ -828,7 +833,7 @@
     vm: VmRequester,
     resource_bridge: Option<ResourceRequestSocket>,
     use_transition_flags: bool,
-    poll_ctx: PollContext<u32>,
+    wait_ctx: WaitContext<u32>,
     vfds: Map<u32, WlVfd>,
     next_vfd_id: u32,
     in_file_queue: Vec<File>,
@@ -848,7 +853,7 @@
             wayland_paths,
             vm: VmRequester::new(vm_socket),
             resource_bridge,
-            poll_ctx: PollContext::new().expect("failed to create PollContext"),
+            wait_ctx: WaitContext::new().expect("failed to create WaitContext"),
             use_transition_flags,
             vfds: Map::new(),
             next_vfd_id: NEXT_VFD_ID_BASE,
@@ -881,9 +886,9 @@
                 } else {
                     return Ok(WlResp::InvalidFlags);
                 };
-                self.poll_ctx
-                    .add(vfd.poll_fd().unwrap(), id)
-                    .map_err(WlError::PollContextAdd)?;
+                self.wait_ctx
+                    .add(vfd.wait_descriptor().unwrap(), id)
+                    .map_err(WlError::WaitContextAdd)?;
                 let resp = WlResp::VfdNew {
                     id,
                     flags: 0,
@@ -985,9 +990,9 @@
                         .get(name)
                         .ok_or(WlError::UnknownSocketName(name.to_string()))?,
                 )?);
-                self.poll_ctx
-                    .add(vfd.poll_fd().unwrap(), id)
-                    .map_err(WlError::PollContextAdd)?;
+                self.wait_ctx
+                    .add(vfd.wait_descriptor().unwrap(), id)
+                    .map_err(WlError::WaitContextAdd)?;
                 Ok(WlResp::VfdNew {
                     id,
                     flags,
@@ -1000,8 +1005,8 @@
         }
     }
 
-    fn process_poll_context(&mut self) {
-        let events = match self.poll_ctx.wait_timeout(Duration::from_secs(0)) {
+    fn process_wait_context(&mut self) {
+        let events = match self.wait_ctx.wait_timeout(Duration::from_secs(0)) {
             Ok(v) => v.to_owned(),
             Err(e) => {
                 error!("failed polling for vfd evens: {}", e);
@@ -1009,17 +1014,19 @@
             }
         };
 
-        for event in events.as_ref().iter_readable() {
-            if let Err(e) = self.recv(event.token()) {
+        for event in events.iter().filter(|e| e.is_readable) {
+            if let Err(e) = self.recv(event.token) {
                 error!("failed to recv from vfd: {}", e)
             }
         }
 
-        for event in events.as_ref().iter_hungup() {
-            if !event.readable() {
-                let vfd_id = event.token();
-                if let Some(fd) = self.vfds.get(&vfd_id).and_then(|vfd| vfd.poll_fd()) {
-                    if let Err(e) = self.poll_ctx.delete(fd) {
+        for event in events.iter().filter(|e| e.is_hungup) {
+            if !event.is_readable {
+                let vfd_id = event.token;
+                if let Some(descriptor) =
+                    self.vfds.get(&vfd_id).and_then(|vfd| vfd.wait_descriptor())
+                {
+                    if let Err(e) = self.wait_ctx.delete(descriptor) {
                         warn!("failed to remove hungup vfd from poll context: {}", e);
                     }
                 }
@@ -1072,15 +1079,15 @@
         }
 
         // Next stage collects corresponding file descriptors for each id.
-        let mut fds = [0; VIRTWL_SEND_MAX_ALLOCS];
+        let mut rds = [0; VIRTWL_SEND_MAX_ALLOCS];
         #[cfg(feature = "gpu")]
         let mut bridged_files = Vec::new();
-        for (&send_vfd_id, fd) in send_vfd_ids[..vfd_count].iter().zip(fds.iter_mut()) {
+        for (&send_vfd_id, descriptor) in send_vfd_ids[..vfd_count].iter().zip(rds.iter_mut()) {
             let id = send_vfd_id.id.to_native();
             match send_vfd_id.kind.to_native() {
                 VIRTIO_WL_CTRL_VFD_SEND_KIND_LOCAL => match self.vfds.get(&id) {
-                    Some(vfd) => match vfd.send_fd() {
-                        Some(vfd_fd) => *fd = vfd_fd,
+                    Some(vfd) => match vfd.send_descriptor() {
+                        Some(vfd_fd) => *descriptor = vfd_fd,
                         None => return Ok(WlResp::InvalidType),
                     },
                     None => {
@@ -1093,7 +1100,7 @@
                     let sock = self.resource_bridge.as_ref().unwrap();
                     match get_resource_info(sock, id) {
                         Ok(info) => {
-                            *fd = info.file.as_raw_fd();
+                            *descriptor = info.file.as_raw_descriptor();
                             bridged_files.push(info.file);
                         }
                         Err(ResourceBridgeError::InvalidResource(id)) => {
@@ -1125,7 +1132,7 @@
 
         // Final stage sends file descriptors and data to the target vfd's socket.
         match self.vfds.get_mut(&vfd_id) {
-            Some(vfd) => match vfd.send(&fds[..vfd_count], reader)? {
+            Some(vfd) => match vfd.send(&rds[..vfd_count], reader)? {
                 WlResp::Ok => {}
                 _ => return Ok(WlResp::InvalidType),
             },
@@ -1154,10 +1161,10 @@
         }
         for file in self.in_file_queue.drain(..) {
             let vfd = WlVfd::from_file(self.vm.clone(), file)?;
-            if let Some(poll_fd) = vfd.poll_fd() {
-                self.poll_ctx
-                    .add(poll_fd, self.next_vfd_id)
-                    .map_err(WlError::PollContextAdd)?;
+            if let Some(wait_descriptor) = vfd.wait_descriptor() {
+                self.wait_ctx
+                    .add(wait_descriptor, self.next_vfd_id)
+                    .map_err(WlError::WaitContextAdd)?;
             }
             self.vfds.insert(self.next_vfd_id, vfd);
             self.in_queue.push_back((
@@ -1391,24 +1398,24 @@
             InterruptResample,
         }
 
-        let poll_ctx: PollContext<Token> = match PollContext::build_with(&[
+        let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
             (&in_queue_evt, Token::InQueue),
             (&out_queue_evt, Token::OutQueue),
             (&kill_evt, Token::Kill),
-            (&self.state.poll_ctx, Token::State),
+            (&self.state.wait_ctx, Token::State),
             (self.interrupt.get_resample_evt(), Token::InterruptResample),
         ]) {
             Ok(pc) => pc,
             Err(e) => {
-                error!("failed creating PollContext: {}", e);
+                error!("failed creating WaitContext: {}", e);
                 return;
             }
         };
 
-        'poll: loop {
+        'wait: loop {
             let mut signal_used_in = false;
             let mut signal_used_out = false;
-            let events = match poll_ctx.wait() {
+            let events = match wait_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed polling for events: {}", e);
@@ -1417,7 +1424,7 @@
             };
 
             for event in &events {
-                match event.token() {
+                match event.token {
                     Token::InQueue => {
                         let _ = in_queue_evt.read();
                         // Used to buffer descriptor indexes that are invalid for our uses.
@@ -1482,8 +1489,8 @@
                             }
                         }
                     }
-                    Token::Kill => break 'poll,
-                    Token::State => self.state.process_poll_context(),
+                    Token::Kill => break 'wait,
+                    Token::State => self.state.process_wait_context(),
                     Token::InterruptResample => {
                         self.interrupt.interrupt_resample();
                     }
@@ -1580,17 +1587,17 @@
 }
 
 impl VirtioDevice for Wl {
-    fn keep_fds(&self) -> Vec<RawFd> {
-        let mut keep_fds = Vec::new();
+    fn keep_rds(&self) -> Vec<RawDescriptor> {
+        let mut keep_rds = Vec::new();
 
         if let Some(vm_socket) = &self.vm_socket {
-            keep_fds.push(vm_socket.as_raw_fd());
+            keep_rds.push(vm_socket.as_raw_descriptor());
         }
         if let Some(resource_bridge) = &self.resource_bridge {
-            keep_fds.push(resource_bridge.as_raw_fd());
+            keep_rds.push(resource_bridge.as_raw_descriptor());
         }
 
-        keep_fds
+        keep_rds
     }
 
     fn device_type(&self) -> u32 {
diff --git a/disk/Android.bp b/disk/Android.bp
index 4c281e4..53dda95 100644
--- a/disk/Android.bp
+++ b/disk/Android.bp
@@ -41,7 +41,7 @@
     srcs: ["src/disk.rs"],
     edition: "2018",
     features: [
-        "composite-disk",
+        "composite-disk",  // Added manually
     ],
     rustlibs: [
         "libbase_rust",
@@ -49,8 +49,8 @@
         "libdata_model",
         "libfutures",
         "liblibc",
-        "libprotobuf",
-        "libprotos",
+        "libprotobuf",  // Added manually
+        "libprotos",  // Added manually
         "libtempfile",
         "libvm_memory",
     ],
@@ -96,7 +96,7 @@
 //   remain-0.2.2
 //   remove_dir_all-0.5.3
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   tempfile-3.1.0
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
diff --git a/disk/src/composite.rs b/disk/src/composite.rs
index 0fff37c..a23eca0 100644
--- a/disk/src/composite.rs
+++ b/disk/src/composite.rs
@@ -7,12 +7,11 @@
 use std::fs::{File, OpenOptions};
 use std::io::{self, ErrorKind, Read, Seek, SeekFrom};
 use std::ops::Range;
-use std::os::unix::io::RawFd;
 
 use crate::{create_disk_file, DiskFile, DiskGetLen, ImageType};
 use base::{
     AsRawDescriptors, FileAllocate, FileReadWriteAtVolatile, FileSetLen, FileSync, PunchHole,
-    WriteZeroesAt,
+    RawDescriptor, WriteZeroesAt,
 };
 use data_model::VolatileSlice;
 use protos::cdisk_spec;
@@ -331,7 +330,7 @@
 }
 
 impl AsRawDescriptors for CompositeDiskFile {
-    fn as_raw_descriptors(&self) -> Vec<RawFd> {
+    fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
         self.component_disks
             .iter()
             .map(|d| d.file.as_raw_descriptors())
@@ -343,9 +342,8 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use base::SharedMemory;
+    use base::{AsRawDescriptor, SharedMemory};
     use data_model::VolatileMemory;
-    use std::os::unix::io::AsRawFd;
 
     #[test]
     fn block_duplicate_offset_disks() {
@@ -410,7 +408,11 @@
         let file1: File = SharedMemory::new(None).unwrap().into();
         let file2: File = SharedMemory::new(None).unwrap().into();
         let file3: File = SharedMemory::new(None).unwrap().into();
-        let mut in_fds = vec![file1.as_raw_fd(), file2.as_raw_fd(), file3.as_raw_fd()];
+        let mut in_fds = vec![
+            file1.as_raw_descriptor(),
+            file2.as_raw_descriptor(),
+            file3.as_raw_descriptor(),
+        ];
         in_fds.sort();
         let disk_part1 = ComponentDiskPart {
             file: Box::new(file1),
diff --git a/disk/src/disk.rs b/disk/src/disk.rs
index 70d58b2..3fa5fca 100644
--- a/disk/src/disk.rs
+++ b/disk/src/disk.rs
@@ -382,7 +382,7 @@
 impl TryFrom<File> for SingleFileDisk {
     type Error = Error;
     fn try_from(inner: File) -> Result<Self> {
-        cros_async::new(inner)
+        cros_async::async_from(inner)
             .map_err(Error::CreateSingleFileDisk)
             .map(|inner| SingleFileDisk { inner })
     }
diff --git a/disk/src/qcow/mod.rs b/disk/src/qcow/mod.rs
index 2ef00ab..2b649a3 100644
--- a/disk/src/qcow/mod.rs
+++ b/disk/src/qcow/mod.rs
@@ -7,8 +7,8 @@
 mod vec_cache;
 
 use base::{
-    error, AsRawDescriptors, FileAllocate, FileReadWriteAtVolatile, FileReadWriteVolatile,
-    FileSetLen, FileSync, PunchHole, SeekHole, WriteZeroesAt,
+    error, AsRawDescriptor, AsRawDescriptors, FileAllocate, FileReadWriteAtVolatile,
+    FileReadWriteVolatile, FileSetLen, FileSync, PunchHole, RawDescriptor, SeekHole, WriteZeroesAt,
 };
 use data_model::{VolatileMemory, VolatileSlice};
 use libc::{EINVAL, ENOSPC, ENOTSUP};
@@ -19,7 +19,6 @@
 use std::fs::{File, OpenOptions};
 use std::io::{self, Read, Seek, SeekFrom, Write};
 use std::mem::size_of;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::str;
 
 use crate::qcow::qcow_raw_file::QcowRawFile;
@@ -1534,12 +1533,12 @@
 }
 
 impl AsRawDescriptors for QcowFile {
-    fn as_raw_descriptors(&self) -> Vec<RawFd> {
-        let mut fds = vec![self.raw_file.file().as_raw_fd()];
+    fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
+        let mut descriptors = vec![self.raw_file.file().as_raw_descriptor()];
         if let Some(backing) = &self.backing_file {
-            fds.append(&mut backing.as_raw_descriptors());
+            descriptors.append(&mut backing.as_raw_descriptors());
         }
-        fds
+        descriptors
     }
 }
 
diff --git a/docker/checkout_commits.env b/docker/checkout_commits.env
index 5556e5a..2c7ad0d 100644
--- a/docker/checkout_commits.env
+++ b/docker/checkout_commits.env
@@ -1,7 +1,7 @@
 MESON_COMMIT=a1a8772034aef90e8d58230d8bcfce54ab27bf6a
 LIBEPOXY_COMMIT=34ecb908b044446226f4cf8829419664ae0ca544
 TPM2_COMMIT=a9bc45bb7fafc65ea8a787894434d409f533b1f1
-PLATFORM2_COMMIT=a386d01923b4d03e939560c09b326e5f38ec2ecc
+PLATFORM2_COMMIT=2079dd5fcd61f1ac39e2fc16595956617f3f1e9e
 ADHD_COMMIT=932f912aa5a0c25c1d5806aa4ad9d8d4d4d98e84
 DRM_COMMIT=00320d7d68ddc7d815d073bb7c92d9a1f9bb8c31
 MINIJAIL_COMMIT=85d797ecbfd7aefbb9486afeaed3cf5f74858562
diff --git a/enumn/Android.bp b/enumn/Android.bp
index 6daf181..8a202f0 100644
--- a/enumn/Android.bp
+++ b/enumn/Android.bp
@@ -31,5 +31,5 @@
 // dependent_library ["feature_list"]
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   syn-1.0.53 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
 //   unicode-xid-0.2.1 "default"
diff --git a/fuse/Android.bp b/fuse/Android.bp
new file mode 100644
index 0000000..8e4260b
--- /dev/null
+++ b/fuse/Android.bp
@@ -0,0 +1,81 @@
+// This file is generated by cargo2android.py --run --device --tests --dependencies --global_defaults=crosvm_defaults --add_workspace.
+
+rust_defaults {
+    name: "fuse_defaults",
+    defaults: ["crosvm_defaults"],
+    crate_name: "fuse",
+    srcs: ["src/lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    edition: "2018",
+    rustlibs: [
+        "libbase_rust",
+        "libbitflags",
+        "libdata_model",
+        "liblibc",
+        "libthiserror",
+    ],
+    proc_macros: ["libenumn"],
+}
+
+rust_test_host {
+    name: "fuse_host_test_src_lib",
+    defaults: ["fuse_defaults"],
+}
+
+rust_test {
+    name: "fuse_device_test_src_lib",
+    defaults: ["fuse_defaults"],
+}
+
+rust_library {
+    name: "libfuse_rust",
+    defaults: ["crosvm_defaults"],
+    stem: "libfuse",
+    host_supported: true,
+    crate_name: "fuse",
+    srcs: ["src/lib.rs"],
+    edition: "2018",
+    rustlibs: [
+        "libbase_rust",
+        "libbitflags",
+        "libdata_model",
+        "liblibc",
+        "libthiserror",
+    ],
+    proc_macros: ["libenumn"],
+}
+
+// dependent_library ["feature_list"]
+//   ../assertions/src/lib.rs
+//   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
+//   ../data_model/src/lib.rs
+//   ../enumn/src/lib.rs
+//   ../io_uring/src/lib.rs
+//   ../sync/src/lib.rs
+//   ../sys_util/poll_token_derive/poll_token_derive.rs
+//   ../sys_util/src/lib.rs
+//   ../syscall_defines/src/lib.rs
+//   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   bitflags-1.2.1 "default"
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
+//   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
+//   proc-macro2-1.0.24 "default,proc-macro"
+//   quote-1.0.7 "default,proc-macro"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
+//   unicode-xid-0.2.1 "default"
diff --git a/fuse/Cargo.toml b/fuse/Cargo.toml
new file mode 100644
index 0000000..94226b5
--- /dev/null
+++ b/fuse/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "fuse"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+base = { path = "../base" }
+bitflags = "1"
+data_model = { path = "../data_model" }
+enumn = { path = "../enumn" }
+libc = "*"
+thiserror = "1.0.20"
diff --git a/fuse/TEST_MAPPING b/fuse/TEST_MAPPING
new file mode 100644
index 0000000..bc91121
--- /dev/null
+++ b/fuse/TEST_MAPPING
@@ -0,0 +1,13 @@
+// Generated by cargo2android.py for tests in Android.bp
+{
+  "presubmit": [
+    {
+      "host": true,
+      "name": "fuse_host_test_src_lib"
+// Presubmit tries to run x86, but we only support 64-bit builds.
+//    },
+//    {
+//      "name": "fuse_device_test_src_lib"
+    }
+  ]
+}
diff --git a/devices/src/virtio/fs/filesystem.rs b/fuse/src/filesystem.rs
similarity index 98%
rename from devices/src/virtio/fs/filesystem.rs
rename to fuse/src/filesystem.rs
index 94a4546..0cb0d60 100644
--- a/devices/src/virtio/fs/filesystem.rs
+++ b/fuse/src/filesystem.rs
@@ -9,9 +9,11 @@
 use std::mem;
 use std::time::Duration;
 
-use crate::virtio::fs::fuse;
+use crate::sys;
 
-pub use fuse::{FsOptions, IoctlFlags, IoctlIovec, OpenOptions, SetattrValid, ROOT_ID};
+pub use crate::sys::{FsOptions, IoctlFlags, IoctlIovec, OpenOptions, SetattrValid, ROOT_ID};
+
+const MAX_BUFFER_SIZE: u32 = 1 << 20;
 
 /// Information about a path in the filesystem.
 pub struct Entry {
@@ -43,9 +45,9 @@
     pub entry_timeout: Duration,
 }
 
-impl From<Entry> for fuse::EntryOut {
-    fn from(entry: Entry) -> fuse::EntryOut {
-        fuse::EntryOut {
+impl From<Entry> for sys::EntryOut {
+    fn from(entry: Entry) -> sys::EntryOut {
+        sys::EntryOut {
             nodeid: entry.inode,
             generation: entry.generation,
             entry_valid: entry.entry_timeout.as_secs(),
@@ -322,8 +324,8 @@
     pub pid: libc::pid_t,
 }
 
-impl From<fuse::InHeader> for Context {
-    fn from(source: fuse::InHeader) -> Self {
+impl From<sys::InHeader> for Context {
+    fn from(source: sys::InHeader) -> Self {
         Context {
             uid: source.uid,
             gid: source.gid,
@@ -374,6 +376,12 @@
     /// details.
     type DirIter: DirectoryIterator;
 
+    /// Maximum size of the buffer that the filesystem can generate data to, including the header.
+    /// This corresponds to max_write in the initialization.
+    fn max_buffer_size(&self) -> u32 {
+        MAX_BUFFER_SIZE
+    }
+
     /// Initialize the file system.
     ///
     /// This method is called when a connection to the FUSE kernel module is first established. The
diff --git a/devices/src/virtio/fs/fuzzing.rs b/fuse/src/fuzzing.rs
similarity index 72%
rename from devices/src/virtio/fs/fuzzing.rs
rename to fuse/src/fuzzing.rs
index b8802bc..a12ada8 100644
--- a/devices/src/virtio/fs/fuzzing.rs
+++ b/fuse/src/fuzzing.rs
@@ -2,9 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use crate::virtio::fs::filesystem::{DirEntry, DirectoryIterator, FileSystem};
-use crate::virtio::fs::server::Server;
-use crate::virtio::{Reader, Writer};
+use crate::filesystem::{DirEntry, DirectoryIterator, FileSystem, ZeroCopyReader, ZeroCopyWriter};
+use crate::server::{Reader, Server, Writer};
 
 // Use a file system that does nothing since we are fuzzing the server implementation.
 struct NullFs;
@@ -22,7 +21,7 @@
 }
 
 /// Fuzz the server implementation.
-pub fn fuzz_server(r: Reader, w: Writer) {
+pub fn fuzz_server<R: Reader + ZeroCopyReader, W: Writer + ZeroCopyWriter>(r: R, w: W) {
     let server = Server::new(NullFs);
 
     let _ = server.handle_message(r, w);
diff --git a/fuse/src/lib.rs b/fuse/src/lib.rs
new file mode 100644
index 0000000..8da2265
--- /dev/null
+++ b/fuse/src/lib.rs
@@ -0,0 +1,62 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::ffi::FromBytesWithNulError;
+use std::io;
+
+use thiserror::Error as ThisError;
+
+pub mod filesystem;
+#[cfg(fuzzing)]
+pub mod fuzzing;
+pub mod mount;
+mod server;
+#[allow(dead_code)]
+pub mod sys;
+pub mod worker;
+
+pub use mount::mount;
+pub use server::{Reader, Server, Writer};
+
+/// Errors that may occur during the creation or operation of an Fs device.
+#[derive(ThisError, Debug)]
+pub enum Error {
+    /// A request is missing readable descriptors.
+    /// Failed to decode protocol messages.
+    #[error("failed to decode fuse message: {0}")]
+    DecodeMessage(io::Error),
+    /// Failed to encode protocol messages.
+    #[error("failed to encode fuse message: {0}")]
+    EncodeMessage(io::Error),
+    /// Failed to flush protocol messages.
+    #[error("failed to flush fuse message: {0}")]
+    FlushMessage(io::Error),
+    /// Failed to set up FUSE endpoint to talk with.
+    #[error("failed to set up FUSE endpoint to talk with: {0}")]
+    EndpointSetup(io::Error),
+    /// One or more parameters are missing.
+    #[error("one or more parameters are missing")]
+    MissingParameter,
+    /// A C string parameter is invalid.
+    #[error("a c string parameter is invalid: {0}")]
+    InvalidCString(FromBytesWithNulError),
+    /// The `len` field of the header is too small.
+    #[error("the `len` field of the header is too small")]
+    InvalidHeaderLength,
+    /// The `size` field of the `SetxattrIn` message does not match the length
+    /// of the decoded value.
+    #[error(
+        "The `size` field of the `SetxattrIn` message does not match the\
+             length of the decoded value: size = {0}, value.len() = {1}"
+    )]
+    InvalidXattrSize(u32, usize),
+    /// Requested too many `iovec`s for an `ioctl` retry.
+    #[error(
+        "requested too many `iovec`s for an `ioctl` retry reply: requested\
+            {0}, max: {1}"
+    )]
+    TooManyIovecs(usize, usize),
+}
+
+pub type Result<T> = ::std::result::Result<T, Error>;
diff --git a/fuse/src/mount.rs b/fuse/src/mount.rs
new file mode 100644
index 0000000..66276d4
--- /dev/null
+++ b/fuse/src/mount.rs
@@ -0,0 +1,126 @@
+// 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::{CString, OsStr};
+use std::fmt;
+use std::io;
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::RawFd;
+
+/// Mount options to pass to mount(2) for a FUSE filesystem. See the [official document](
+/// https://www.kernel.org/doc/html/latest/filesystems/fuse.html#mount-options) for the
+/// descriptions.
+pub enum MountOption {
+    FD(RawFd),
+    RootMode(u32),
+    UserId(libc::uid_t),
+    GroupId(libc::gid_t),
+    DefaultPermissions,
+    AllowOther,
+    MaxRead(u32),
+    BlockSize(u32),
+}
+
+// Implement Display for ToString to convert to actual mount options.
+impl fmt::Display for MountOption {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match &self {
+            MountOption::FD(fd) => write!(f, "fd={}", fd),
+            MountOption::RootMode(mode) => write!(f, "rootmode={:o}", mode),
+            MountOption::UserId(uid) => write!(f, "user_id={}", uid),
+            MountOption::GroupId(gid) => write!(f, "group_id={}", gid),
+            MountOption::DefaultPermissions => write!(f, "default_permissions"),
+            MountOption::AllowOther => write!(f, "allow_other"),
+            MountOption::MaxRead(size) => write!(f, "max_read={}", size),
+            MountOption::BlockSize(size) => write!(f, "blksize={}", size),
+        }
+    }
+}
+
+fn join_mount_options(options: &[MountOption]) -> String {
+    if !options.is_empty() {
+        let mut concat = options[0].to_string();
+        for opt in &options[1..] {
+            concat.push(',');
+            concat.push_str(&opt.to_string());
+        }
+        concat
+    } else {
+        String::new()
+    }
+}
+
+/// Initiates a FUSE mount at `mountpoint` directory with `flags` and `options` via mount(2). The
+/// caller should provide a file descriptor (backed by /dev/fuse) with `MountOption::FD`. After
+/// this function completes, the FUSE filesystem can start to handle the requests, e.g. via
+/// `fuse::worker::start_message_loop()`.
+///
+/// This operation requires CAP_SYS_ADMIN privilege, but the privilege can be dropped afterward.
+pub fn mount<P: AsRef<OsStr>>(
+    mountpoint: P,
+    name: &str,
+    flags: libc::c_ulong,
+    options: &[MountOption],
+) -> Result<(), io::Error> {
+    let mount_name = CString::new(name.as_bytes())?;
+    let fs_type = CString::new(String::from("fuse.") + name)?;
+    let mountpoint = CString::new(mountpoint.as_ref().as_bytes())?;
+    let mount_options = CString::new(join_mount_options(options))?;
+
+    // Safe because pointer arguments all points to null-terminiated CStrings.
+    let retval = unsafe {
+        libc::mount(
+            mount_name.as_ptr(),
+            mountpoint.as_ptr(),
+            fs_type.as_ptr(),
+            flags,
+            mount_options.as_ptr() as *const std::ffi::c_void,
+        )
+    };
+    if retval < 0 {
+        Err(io::Error::last_os_error())
+    } else {
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn basic_options_concatenate_in_order() {
+        assert_eq!("".to_string(), join_mount_options(&[]));
+
+        assert_eq!(
+            "fd=42".to_string(),
+            join_mount_options(&[MountOption::FD(42),])
+        );
+
+        assert_eq!(
+            "fd=42,rootmode=40111,allow_other,user_id=12,group_id=34,max_read=4096".to_string(),
+            join_mount_options(&[
+                MountOption::FD(42),
+                MountOption::RootMode(
+                    libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH
+                ),
+                MountOption::AllowOther,
+                MountOption::UserId(12),
+                MountOption::GroupId(34),
+                MountOption::MaxRead(4096),
+            ])
+        );
+
+        assert_eq!(
+            "fd=42,default_permissions,user_id=12,group_id=34,max_read=4096".to_string(),
+            join_mount_options(&[
+                MountOption::FD(42),
+                MountOption::DefaultPermissions,
+                MountOption::UserId(12),
+                MountOption::GroupId(34),
+                MountOption::MaxRead(4096),
+            ])
+        );
+    }
+}
diff --git a/devices/src/virtio/fs/server.rs b/fuse/src/server.rs
similarity index 78%
rename from devices/src/virtio/fs/server.rs
rename to fuse/src/server.rs
index 6aefcdc..77894bf 100644
--- a/devices/src/virtio/fs/server.rs
+++ b/fuse/src/server.rs
@@ -3,55 +3,46 @@
 // found in the LICENSE file.
 
 use std::ffi::CStr;
-use std::fs::File;
-use std::io::{self, Read, Write};
+use std::io;
 use std::mem::{size_of, MaybeUninit};
 use std::time::Duration;
 
 use base::error;
 use data_model::DataInit;
 
-use crate::virtio::fs::filesystem::{
+use crate::filesystem::{
     Context, DirEntry, DirectoryIterator, Entry, FileSystem, GetxattrReply, IoctlReply,
     ListxattrReply, ZeroCopyReader, ZeroCopyWriter,
 };
-use crate::virtio::fs::fuse::*;
-use crate::virtio::fs::{Error, Result};
-use crate::virtio::{Reader, Writer};
+use crate::sys::*;
+use crate::{Error, Result};
 
-const MAX_BUFFER_SIZE: u32 = 1 << 20;
 const DIRENT_PADDING: [u8; 8] = [0; 8];
 
-struct ZCReader(Reader);
+/// A trait for reading from the underlying FUSE endpoint.
+pub trait Reader: io::Read {}
 
-impl ZeroCopyReader for ZCReader {
-    fn read_to(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize> {
-        self.0.read_to_at(f, count, off)
-    }
-}
+/// A trait for writing to the underlying FUSE endpoint. The FUSE device expects the write
+/// operation to happen in one write transaction. Since there are cases when data needs to be
+/// generated earlier than the header, it implies the writer implementation to keep an internal
+/// buffer. The buffer then can be flushed once header and data are both prepared.
+pub trait Writer: io::Write {
+    /// Allows a closure to generate and write data at the current writer's offset. The current
+    /// writer is passed as a mutable reference to the closure. As an example, this provides an
+    /// adapter for the read implementation of a filesystem to write directly to the final buffer
+    /// without generating the FUSE header first.
+    ///
+    /// Notes: An alternative implementation would be to return a slightly different writer for the
+    /// API client to write to the offset. Since the API needs to be called for more than one time,
+    /// it imposes some complexity to deal with borrowing and mutability. The current approach
+    /// simply does not need to create a different writer, thus no need to deal with the mentioned
+    /// complexity.
+    fn write_at<F>(&mut self, offset: usize, f: F) -> io::Result<usize>
+    where
+        F: Fn(&mut Self) -> io::Result<usize>;
 
-impl io::Read for ZCReader {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        self.0.read(buf)
-    }
-}
-
-struct ZCWriter(Writer);
-
-impl ZeroCopyWriter for ZCWriter {
-    fn write_from(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize> {
-        self.0.write_from_at(f, count, off)
-    }
-}
-
-impl io::Write for ZCWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.0.write(buf)
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        self.0.flush()
-    }
+    /// Checks if the writer can still accept certain amount of data.
+    fn has_sufficient_buffer(&self, size: u32) -> bool;
 }
 
 pub struct Server<F: FileSystem + Sync> {
@@ -63,10 +54,14 @@
         Server { fs }
     }
 
-    pub fn handle_message(&self, mut r: Reader, w: Writer) -> Result<usize> {
+    pub fn handle_message<R: Reader + ZeroCopyReader, W: Writer + ZeroCopyWriter>(
+        &self,
+        mut r: R,
+        w: W,
+    ) -> Result<usize> {
         let in_header = InHeader::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
-        if in_header.len > MAX_BUFFER_SIZE {
+        if in_header.len > self.fs.max_buffer_size() {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -127,7 +122,7 @@
         }
     }
 
-    fn lookup(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn lookup<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let namelen = (in_header.len as usize)
             .checked_sub(size_of::<InHeader>())
             .ok_or(Error::InvalidHeaderLength)?;
@@ -152,7 +147,7 @@
         }
     }
 
-    fn forget(&self, in_header: InHeader, mut r: Reader) -> Result<usize> {
+    fn forget<R: Reader>(&self, in_header: InHeader, mut r: R) -> Result<usize> {
         let ForgetIn { nlookup } = ForgetIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         self.fs
@@ -162,7 +157,7 @@
         Ok(0)
     }
 
-    fn getattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn getattr<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let GetattrIn {
             flags,
             dummy: _,
@@ -192,7 +187,7 @@
         }
     }
 
-    fn setattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn setattr<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let setattr_in = SetattrIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         let handle = if setattr_in.valid & FATTR_FH != 0 {
@@ -225,7 +220,7 @@
         }
     }
 
-    fn readlink(&self, in_header: InHeader, w: Writer) -> Result<usize> {
+    fn readlink<W: Writer>(&self, in_header: InHeader, w: W) -> Result<usize> {
         match self
             .fs
             .readlink(Context::from(in_header), in_header.nodeid.into())
@@ -238,7 +233,7 @@
         }
     }
 
-    fn symlink(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn symlink<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         // Unfortunately the name and linkname are encoded one after another and
         // separated by a nul character.
         let len = (in_header.len as usize)
@@ -276,7 +271,7 @@
         }
     }
 
-    fn mknod(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn mknod<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let MknodIn {
             mode, rdev, umask, ..
         } = MknodIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
@@ -315,7 +310,7 @@
         }
     }
 
-    fn mkdir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn mkdir<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let MkdirIn { mode, umask } = MkdirIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         let buflen = (in_header.len as usize)
@@ -351,7 +346,7 @@
         }
     }
 
-    fn unlink(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn unlink<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let namelen = (in_header.len as usize)
             .checked_sub(size_of::<InHeader>())
             .ok_or(Error::InvalidHeaderLength)?;
@@ -370,7 +365,7 @@
         }
     }
 
-    fn rmdir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn rmdir<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let namelen = (in_header.len as usize)
             .checked_sub(size_of::<InHeader>())
             .ok_or(Error::InvalidHeaderLength)?;
@@ -389,14 +384,14 @@
         }
     }
 
-    fn do_rename(
+    fn do_rename<R: Reader, W: Writer>(
         &self,
         in_header: InHeader,
         msg_size: usize,
         newdir: u64,
         flags: u32,
-        mut r: Reader,
-        w: Writer,
+        mut r: R,
+        w: W,
     ) -> Result<usize> {
         let buflen = (in_header.len as usize)
             .checked_sub(size_of::<InHeader>())
@@ -429,13 +424,13 @@
         }
     }
 
-    fn rename(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn rename<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let RenameIn { newdir } = RenameIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         self.do_rename(in_header, size_of::<RenameIn>(), newdir, 0, r, w)
     }
 
-    fn rename2(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn rename2<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let Rename2In { newdir, flags, .. } =
             Rename2In::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
@@ -444,7 +439,7 @@
         self.do_rename(in_header, size_of::<Rename2In>(), newdir, flags, r, w)
     }
 
-    fn link(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn link<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let LinkIn { oldnodeid } = LinkIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         let namelen = (in_header.len as usize)
@@ -471,7 +466,7 @@
         }
     }
 
-    fn open(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn open<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let OpenIn { flags, .. } = OpenIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         match self
@@ -491,7 +486,12 @@
         }
     }
 
-    fn read(&self, in_header: InHeader, mut r: Reader, mut w: Writer) -> Result<usize> {
+    fn read<R: Reader, W: ZeroCopyWriter + Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        mut w: W,
+    ) -> Result<usize> {
         let ReadIn {
             fh,
             offset,
@@ -502,7 +502,7 @@
             ..
         } = ReadIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
-        if size > MAX_BUFFER_SIZE {
+        if size > self.fs.max_buffer_size() {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -516,19 +516,19 @@
             None
         };
 
-        // Split the writer into 2 pieces: one for the `OutHeader` and the rest for the data.
-        let data_writer = ZCWriter(w.split_at(size_of::<OutHeader>()));
-
-        match self.fs.read(
-            Context::from(in_header),
-            in_header.nodeid.into(),
-            fh.into(),
-            data_writer,
-            size,
-            offset,
-            owner,
-            flags,
-        ) {
+        // Skip for the header size to write the data first.
+        match w.write_at(size_of::<OutHeader>(), |writer| {
+            self.fs.read(
+                Context::from(in_header),
+                in_header.nodeid.into(),
+                fh.into(),
+                writer,
+                size,
+                offset,
+                owner,
+                flags,
+            )
+        }) {
             Ok(count) => {
                 // Don't use `reply_ok` because we need to set a custom size length for the
                 // header.
@@ -539,13 +539,19 @@
                 };
 
                 w.write_all(out.as_slice()).map_err(Error::EncodeMessage)?;
+                w.flush().map_err(Error::FlushMessage)?;
                 Ok(out.len as usize)
             }
             Err(e) => reply_error(e, in_header.unique, w),
         }
     }
 
-    fn write(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn write<R: Reader + ZeroCopyReader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        w: W,
+    ) -> Result<usize> {
         let WriteIn {
             fh,
             offset,
@@ -556,7 +562,7 @@
             ..
         } = WriteIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
-        if size > MAX_BUFFER_SIZE {
+        if size > self.fs.max_buffer_size() {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -572,13 +578,11 @@
 
         let delayed_write = write_flags & WRITE_CACHE != 0;
 
-        let data_reader = ZCReader(r);
-
         match self.fs.write(
             Context::from(in_header),
             in_header.nodeid.into(),
             fh.into(),
-            data_reader,
+            r,
             size,
             offset,
             owner,
@@ -597,7 +601,7 @@
         }
     }
 
-    fn statfs(&self, in_header: InHeader, w: Writer) -> Result<usize> {
+    fn statfs<W: Writer>(&self, in_header: InHeader, w: W) -> Result<usize> {
         match self
             .fs
             .statfs(Context::from(in_header), in_header.nodeid.into())
@@ -607,7 +611,7 @@
         }
     }
 
-    fn release(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn release<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let ReleaseIn {
             fh,
             flags,
@@ -637,7 +641,7 @@
         }
     }
 
-    fn fsync(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn fsync<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let FsyncIn {
             fh, fsync_flags, ..
         } = FsyncIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
@@ -654,7 +658,7 @@
         }
     }
 
-    fn setxattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn setxattr<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let SetxattrIn { size, flags } =
             SetxattrIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
@@ -678,7 +682,7 @@
         let (name, value) = buf.split_at(split_pos);
 
         if size != value.len() as u32 {
-            return Err(Error::InvalidXattrSize((size, value.len())));
+            return Err(Error::InvalidXattrSize(size, value.len()));
         }
 
         match self.fs.setxattr(
@@ -693,7 +697,7 @@
         }
     }
 
-    fn getxattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn getxattr<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let GetxattrIn { size, .. } =
             GetxattrIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
@@ -706,7 +710,7 @@
 
         r.read_exact(&mut name).map_err(Error::DecodeMessage)?;
 
-        if size > MAX_BUFFER_SIZE {
+        if size > self.fs.max_buffer_size() {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -733,11 +737,16 @@
         }
     }
 
-    fn listxattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn listxattr<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        w: W,
+    ) -> Result<usize> {
         let GetxattrIn { size, .. } =
             GetxattrIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
-        if size > MAX_BUFFER_SIZE {
+        if size > self.fs.max_buffer_size() {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -762,7 +771,12 @@
         }
     }
 
-    fn removexattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn removexattr<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        w: W,
+    ) -> Result<usize> {
         let namelen = (in_header.len as usize)
             .checked_sub(size_of::<InHeader>())
             .ok_or(Error::InvalidHeaderLength)?;
@@ -783,7 +797,7 @@
         }
     }
 
-    fn flush(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn flush<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let FlushIn {
             fh,
             unused: _,
@@ -802,7 +816,7 @@
         }
     }
 
-    fn init(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn init<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let InitIn {
             major,
             minor,
@@ -873,7 +887,7 @@
                     flags: enabled.bits(),
                     max_background: ::std::u16::MAX,
                     congestion_threshold: (::std::u16::MAX / 4) * 3,
-                    max_write: MAX_BUFFER_SIZE,
+                    max_write: self.fs.max_buffer_size(),
                     time_gran: 1, // nanoseconds
                     ..Default::default()
                 };
@@ -884,7 +898,7 @@
         }
     }
 
-    fn opendir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn opendir<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let OpenIn { flags, .. } = OpenIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         match self
@@ -904,12 +918,17 @@
         }
     }
 
-    fn readdir(&self, in_header: InHeader, mut r: Reader, mut w: Writer) -> Result<usize> {
+    fn readdir<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        mut w: W,
+    ) -> Result<usize> {
         let ReadIn {
             fh, offset, size, ..
         } = ReadIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
-        if size > MAX_BUFFER_SIZE {
+        if size > self.fs.max_buffer_size() {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -917,8 +936,7 @@
             );
         }
 
-        let available_bytes = w.available_bytes();
-        if available_bytes < size as usize {
+        if !w.has_sufficient_buffer(size) {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -927,31 +945,36 @@
         }
 
         // Skip over enough bytes for the header.
-        let mut cursor = w.split_at(size_of::<OutHeader>());
         let unique = in_header.unique;
-
-        match self.fs.readdir(
-            Context::from(in_header),
-            in_header.nodeid.into(),
-            fh.into(),
-            size,
-            offset,
-        ) {
-            Ok(mut entries) => {
-                let mut total_written = 0;
-                while let Some(dirent) = entries.next() {
-                    let remaining = (size as usize).saturating_sub(total_written);
-                    match add_dirent(&mut cursor, remaining, dirent, None) {
-                        // No more space left in the buffer.
-                        Ok(0) => break,
-                        Ok(bytes_written) => {
-                            total_written += bytes_written;
+        let result = w.write_at(size_of::<OutHeader>(), |cursor| {
+            match self.fs.readdir(
+                Context::from(in_header),
+                in_header.nodeid.into(),
+                fh.into(),
+                size,
+                offset,
+            ) {
+                Ok(mut entries) => {
+                    let mut total_written = 0;
+                    while let Some(dirent) = entries.next() {
+                        let remaining = (size as usize).saturating_sub(total_written);
+                        match add_dirent(cursor, remaining, dirent, None) {
+                            // No more space left in the buffer.
+                            Ok(0) => break,
+                            Ok(bytes_written) => {
+                                total_written += bytes_written;
+                            }
+                            Err(e) => return Err(e),
                         }
-                        Err(e) => return reply_error(e, unique, w),
                     }
+                    Ok(total_written)
                 }
-                reply_readdir(total_written, unique, w)
+                Err(e) => Err(e),
             }
+        });
+
+        match result {
+            Ok(total_written) => reply_readdir(total_written, unique, w),
             Err(e) => reply_error(e, unique, w),
         }
     }
@@ -986,12 +1009,17 @@
         Ok((dir_entry, entry))
     }
 
-    fn readdirplus(&self, in_header: InHeader, mut r: Reader, mut w: Writer) -> Result<usize> {
+    fn readdirplus<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        mut w: W,
+    ) -> Result<usize> {
         let ReadIn {
             fh, offset, size, ..
         } = ReadIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
-        if size > MAX_BUFFER_SIZE {
+        if size > self.fs.max_buffer_size() {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -999,8 +1027,7 @@
             );
         }
 
-        let available_bytes = w.available_bytes();
-        if available_bytes < size as usize {
+        if !w.has_sufficient_buffer(size) {
             return reply_error(
                 io::Error::from_raw_os_error(libc::ENOMEM),
                 in_header.unique,
@@ -1009,63 +1036,72 @@
         }
 
         // Skip over enough bytes for the header.
-        let mut cursor = w.split_at(size_of::<OutHeader>());
         let unique = in_header.unique;
-
-        match self.fs.readdir(
-            Context::from(in_header),
-            in_header.nodeid.into(),
-            fh.into(),
-            size,
-            offset,
-        ) {
-            Ok(mut entries) => {
-                let mut total_written = 0;
-                while let Some(dirent) = entries.next() {
-                    let mut entry_inode = None;
-                    match self.handle_dirent(&in_header, dirent).and_then(|(d, e)| {
-                        entry_inode = Some(e.inode);
-                        let remaining = (size as usize).saturating_sub(total_written);
-                        add_dirent(&mut cursor, remaining, d, Some(e))
-                    }) {
-                        Ok(0) => {
-                            // No more space left in the buffer but we need to undo the lookup
-                            // that created the Entry or we will end up with mismatched lookup
-                            // counts.
-                            if let Some(inode) = entry_inode {
-                                self.fs.forget(Context::from(in_header), inode.into(), 1);
+        let result = w.write_at(size_of::<OutHeader>(), |cursor| {
+            match self.fs.readdir(
+                Context::from(in_header),
+                in_header.nodeid.into(),
+                fh.into(),
+                size,
+                offset,
+            ) {
+                Ok(mut entries) => {
+                    let mut total_written = 0;
+                    while let Some(dirent) = entries.next() {
+                        let mut entry_inode = None;
+                        match self.handle_dirent(&in_header, dirent).and_then(|(d, e)| {
+                            entry_inode = Some(e.inode);
+                            let remaining = (size as usize).saturating_sub(total_written);
+                            add_dirent(cursor, remaining, d, Some(e))
+                        }) {
+                            Ok(0) => {
+                                // No more space left in the buffer but we need to undo the lookup
+                                // that created the Entry or we will end up with mismatched lookup
+                                // counts.
+                                if let Some(inode) = entry_inode {
+                                    self.fs.forget(Context::from(in_header), inode.into(), 1);
+                                }
+                                break;
                             }
-                            break;
-                        }
-                        Ok(bytes_written) => {
-                            total_written += bytes_written;
-                        }
-                        Err(e) => {
-                            if let Some(inode) = entry_inode {
-                                self.fs.forget(Context::from(in_header), inode.into(), 1);
+                            Ok(bytes_written) => {
+                                total_written += bytes_written;
                             }
+                            Err(e) => {
+                                if let Some(inode) = entry_inode {
+                                    self.fs.forget(Context::from(in_header), inode.into(), 1);
+                                }
 
-                            if total_written == 0 {
-                                // We haven't filled any entries yet so we can just propagate
-                                // the error.
-                                return reply_error(e, unique, w);
+                                if total_written == 0 {
+                                    // We haven't filled any entries yet so we can just propagate
+                                    // the error.
+                                    return Err(e);
+                                }
+
+                                // We already filled in some entries. Returning an error now will
+                                // cause lookup count mismatches for those entries so just return
+                                // whatever we already have.
+                                break;
                             }
-
-                            // We already filled in some entries. Returning an error now will
-                            // cause lookup count mismatches for those entries so just return
-                            // whatever we already have.
-                            break;
                         }
                     }
+                    Ok(total_written)
                 }
-
-                reply_readdir(total_written, unique, w)
+                Err(e) => Err(e),
             }
+        });
+
+        match result {
+            Ok(total_written) => reply_readdir(total_written, unique, w),
             Err(e) => reply_error(e, unique, w),
         }
     }
 
-    fn releasedir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn releasedir<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        w: W,
+    ) -> Result<usize> {
         let ReleaseIn { fh, flags, .. } =
             ReleaseIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
@@ -1080,7 +1116,7 @@
         }
     }
 
-    fn fsyncdir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn fsyncdir<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let FsyncIn {
             fh, fsync_flags, ..
         } = FsyncIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
@@ -1097,7 +1133,7 @@
         }
     }
 
-    fn getlk(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
+    fn getlk<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
         if let Err(e) = self.fs.getlk() {
             reply_error(e, in_header.unique, w)
         } else {
@@ -1105,7 +1141,7 @@
         }
     }
 
-    fn setlk(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
+    fn setlk<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
         if let Err(e) = self.fs.setlk() {
             reply_error(e, in_header.unique, w)
         } else {
@@ -1113,7 +1149,7 @@
         }
     }
 
-    fn setlkw(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
+    fn setlkw<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
         if let Err(e) = self.fs.setlkw() {
             reply_error(e, in_header.unique, w)
         } else {
@@ -1121,7 +1157,7 @@
         }
     }
 
-    fn access(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn access<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let AccessIn { mask, .. } = AccessIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         match self
@@ -1133,7 +1169,7 @@
         }
     }
 
-    fn create(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn create<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let CreateIn {
             flags, mode, umask, ..
         } = CreateIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
@@ -1196,7 +1232,7 @@
         Ok(0)
     }
 
-    fn bmap(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
+    fn bmap<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
         if let Err(e) = self.fs.bmap() {
             reply_error(e, in_header.unique, w)
         } else {
@@ -1211,7 +1247,7 @@
         Ok(0)
     }
 
-    fn ioctl(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn ioctl<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
         let IoctlIn {
             fh,
             flags,
@@ -1243,7 +1279,7 @@
         }
     }
 
-    fn poll(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
+    fn poll<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
         if let Err(e) = self.fs.poll() {
             reply_error(e, in_header.unique, w)
         } else {
@@ -1251,7 +1287,12 @@
         }
     }
 
-    fn notify_reply(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
+    fn notify_reply<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut _r: R,
+        w: W,
+    ) -> Result<usize> {
         if let Err(e) = self.fs.notify_reply() {
             reply_error(e, in_header.unique, w)
         } else {
@@ -1259,12 +1300,17 @@
         }
     }
 
-    fn batch_forget(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn batch_forget<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        w: W,
+    ) -> Result<usize> {
         let BatchForgetIn { count, .. } =
             BatchForgetIn::from_reader(&mut r).map_err(Error::DecodeMessage)?;
 
         if let Some(size) = (count as usize).checked_mul(size_of::<ForgetOne>()) {
-            if size > MAX_BUFFER_SIZE as usize {
+            if size > self.fs.max_buffer_size() as usize {
                 return reply_error(
                     io::Error::from_raw_os_error(libc::ENOMEM),
                     in_header.unique,
@@ -1294,7 +1340,12 @@
         Ok(0)
     }
 
-    fn fallocate(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn fallocate<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        w: W,
+    ) -> Result<usize> {
         let FallocateIn {
             fh,
             offset,
@@ -1316,7 +1367,7 @@
         }
     }
 
-    fn lseek(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
+    fn lseek<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
         if let Err(e) = self.fs.lseek() {
             reply_error(e, in_header.unique, w)
         } else {
@@ -1324,7 +1375,12 @@
         }
     }
 
-    fn copy_file_range(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+    fn copy_file_range<R: Reader, W: Writer>(
+        &self,
+        in_header: InHeader,
+        mut r: R,
+        w: W,
+    ) -> Result<usize> {
         let CopyFileRangeIn {
             fh_src,
             off_src,
@@ -1359,19 +1415,19 @@
     }
 }
 
-fn retry_ioctl(
+fn retry_ioctl<W: Writer>(
     unique: u64,
     input: Vec<IoctlIovec>,
     output: Vec<IoctlIovec>,
-    mut w: Writer,
+    mut w: W,
 ) -> Result<usize> {
     // We don't need to check for overflow here because if adding these 2 values caused an overflow
     // we would have run out of memory before reaching this point.
     if input.len() + output.len() > IOCTL_MAX_IOV {
-        return Err(Error::TooManyIovecs((
+        return Err(Error::TooManyIovecs(
             input.len() + output.len(),
             IOCTL_MAX_IOV,
-        )));
+        ));
     }
 
     let len = size_of::<OutHeader>()
@@ -1399,11 +1455,12 @@
         w.write_all(i.as_slice()).map_err(Error::EncodeMessage)?;
     }
 
+    w.flush().map_err(Error::FlushMessage)?;
     debug_assert_eq!(len, total_bytes);
     Ok(len)
 }
 
-fn finish_ioctl(unique: u64, res: io::Result<Vec<u8>>, w: Writer) -> Result<usize> {
+fn finish_ioctl<W: Writer>(unique: u64, res: io::Result<Vec<u8>>, w: W) -> Result<usize> {
     let (out, data) = match res {
         Ok(data) => {
             let out = IoctlOut {
@@ -1423,7 +1480,7 @@
     reply_ok(Some(out), data.as_ref().map(|d| &d[..]), unique, w)
 }
 
-fn reply_readdir(len: usize, unique: u64, mut w: Writer) -> Result<usize> {
+fn reply_readdir<W: Writer>(len: usize, unique: u64, mut w: W) -> Result<usize> {
     let out = OutHeader {
         len: (size_of::<OutHeader>() + len) as u32,
         error: 0,
@@ -1431,14 +1488,15 @@
     };
 
     w.write_all(out.as_slice()).map_err(Error::EncodeMessage)?;
+    w.flush().map_err(Error::FlushMessage)?;
     Ok(out.len as usize)
 }
 
-fn reply_ok<T: DataInit>(
+fn reply_ok<T: DataInit, W: Writer>(
     out: Option<T>,
     data: Option<&[u8]>,
     unique: u64,
-    mut w: Writer,
+    mut w: W,
 ) -> Result<usize> {
     let mut len = size_of::<OutHeader>();
 
@@ -1470,11 +1528,12 @@
         w.write_all(data).map_err(Error::EncodeMessage)?;
     }
 
+    w.flush().map_err(Error::FlushMessage)?;
     debug_assert_eq!(len, total_bytes);
     Ok(len)
 }
 
-fn reply_error(e: io::Error, unique: u64, mut w: Writer) -> Result<usize> {
+fn reply_error<W: Writer>(e: io::Error, unique: u64, mut w: W) -> Result<usize> {
     let header = OutHeader {
         len: size_of::<OutHeader>() as u32,
         error: -e.raw_os_error().unwrap_or(libc::EIO),
@@ -1483,6 +1542,7 @@
 
     w.write_all(header.as_slice())
         .map_err(Error::EncodeMessage)?;
+    w.flush().map_err(Error::FlushMessage)?;
 
     Ok(header.len as usize)
 }
@@ -1493,8 +1553,8 @@
     CStr::from_bytes_with_nul(buf).map_err(Error::InvalidCString)
 }
 
-fn add_dirent(
-    cursor: &mut Writer,
+fn add_dirent<W: Writer>(
+    cursor: &mut W,
     max: usize,
     d: DirEntry,
     entry: Option<Entry>,
diff --git a/devices/src/virtio/fs/fuse.rs b/fuse/src/sys.rs
similarity index 100%
rename from devices/src/virtio/fs/fuse.rs
rename to fuse/src/sys.rs
diff --git a/fuse/src/worker.rs b/fuse/src/worker.rs
new file mode 100644
index 0000000..334738a
--- /dev/null
+++ b/fuse/src/worker.rs
@@ -0,0 +1,137 @@
+// 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::fs::File;
+use std::io::{self, BufRead, BufReader, Cursor, Read, Write};
+use std::mem::size_of;
+use std::os::unix::fs::FileExt;
+
+use crate::filesystem::{FileSystem, ZeroCopyReader, ZeroCopyWriter};
+use crate::server::{Reader, Server, Writer};
+use crate::sys;
+use crate::{Error, Result};
+
+struct DevFuseReader<'a> {
+    // File representing /dev/fuse for reading, with sufficient buffer to accommodate a FUSE read
+    // transaction.
+    reader: &'a mut BufReader<File>,
+}
+
+impl<'a> DevFuseReader<'a> {
+    pub fn new(reader: &'a mut BufReader<File>) -> Self {
+        DevFuseReader { reader }
+    }
+}
+
+impl Read for DevFuseReader<'_> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.reader.read(buf)
+    }
+}
+
+impl Reader for DevFuseReader<'_> {}
+
+impl ZeroCopyReader for DevFuseReader<'_> {
+    fn read_to(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize> {
+        let buf = self.reader.fill_buf()?;
+        let end = std::cmp::min(count, buf.len());
+        let written = f.write_at(&buf[..end], off)?;
+        self.reader.consume(written);
+        Ok(written)
+    }
+}
+
+struct DevFuseWriter<'a> {
+    // File representing /dev/fuse for writing.
+    dev_fuse: &'a mut File,
+
+    // An internal buffer to allow generating data and header out of order, such that they can be
+    // flushed at once. This is wrapped by a cursor for tracking the current written position.
+    write_buf: &'a mut Cursor<Vec<u8>>,
+}
+
+impl<'a> DevFuseWriter<'a> {
+    pub fn new(dev_fuse: &'a mut File, write_buf: &'a mut Cursor<Vec<u8>>) -> Self {
+        debug_assert_eq!(write_buf.position(), 0);
+
+        DevFuseWriter {
+            dev_fuse,
+            write_buf,
+        }
+    }
+}
+
+impl Write for DevFuseWriter<'_> {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.write_buf.write(buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        self.dev_fuse.write_all(&self.write_buf.get_ref()[..])?;
+        self.write_buf.set_position(0);
+        self.write_buf.get_mut().clear();
+        Ok(())
+    }
+}
+
+impl Writer for DevFuseWriter<'_> {
+    fn write_at<F>(&mut self, offset: usize, f: F) -> io::Result<usize>
+    where
+        F: Fn(&mut Self) -> io::Result<usize>,
+    {
+        // Restore the cursor for idempotent.
+        let original = self.write_buf.position();
+        self.write_buf.set_position(offset as u64);
+        let r = f(self);
+        self.write_buf.set_position(original);
+        r
+    }
+
+    fn has_sufficient_buffer(&self, size: u32) -> bool {
+        (self.write_buf.position() as usize + size as usize) < self.write_buf.get_ref().capacity()
+    }
+}
+
+impl ZeroCopyWriter for DevFuseWriter<'_> {
+    fn write_from(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize> {
+        let pos = self.write_buf.position() as usize;
+        let end = pos + count;
+        let buf = self.write_buf.get_mut();
+
+        let old_end = buf.len();
+        buf.resize(end, 0);
+        let read = f.read_at(&mut buf[pos..end], off)?;
+
+        let new_end = pos + read;
+        debug_assert!(new_end >= old_end);
+        buf.truncate(new_end);
+        self.write_buf.set_position(new_end as u64);
+        Ok(read)
+    }
+}
+
+/// Start the FUSE message handling loop. Returns when an error happens.
+pub fn start_message_loop<F: FileSystem + Sync>(
+    dev_fuse: File,
+    max_write: u32,
+    max_read: u32,
+    fs: F,
+) -> Result<()> {
+    let server = Server::new(fs);
+    let mut buf_reader = BufReader::with_capacity(
+        max_write as usize + size_of::<sys::InHeader>() + size_of::<sys::WriteIn>(),
+        dev_fuse.try_clone().map_err(Error::EndpointSetup)?,
+    );
+
+    let mut write_buf = Cursor::new(Vec::with_capacity(max_read as usize));
+    let mut wfile = dev_fuse.try_clone().map_err(Error::EndpointSetup)?;
+    loop {
+        let dev_fuse_reader = DevFuseReader::new(&mut buf_reader);
+        let dev_fuse_writer = DevFuseWriter::new(&mut wfile, &mut write_buf);
+
+        if let Err(e) = server.handle_message(dev_fuse_reader, dev_fuse_writer) {
+            return Err(e);
+        }
+    }
+}
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index 1a10b2a..ab2e065 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -9,6 +9,7 @@
 data_model = { path = "../data_model" }
 devices = { path = "../devices" }
 disk = { path = "../disk" }
+fuse = { path = "../fuse" }
 kernel_loader = { path = "../kernel_loader" }
 libc = "*"
 rand = "0.6"
diff --git a/fuzz/block_fuzzer.rs b/fuzz/block_fuzzer.rs
index 0cd5b07..ee193e4 100644
--- a/fuzz/block_fuzzer.rs
+++ b/fuzz/block_fuzzer.rs
@@ -6,7 +6,6 @@
 
 use std::io::{Cursor, Read, Seek, SeekFrom};
 use std::mem::size_of;
-use std::os::unix::io::{AsRawFd, FromRawFd};
 use std::sync::atomic::AtomicUsize;
 use std::sync::Arc;
 
@@ -76,10 +75,9 @@
     q.max_size = QUEUE_SIZE;
 
     let queue_evts: Vec<Event> = vec![Event::new().unwrap()];
-    let queue_fd = queue_evts[0].as_raw_fd();
-    let queue_evt = unsafe { Event::from_raw_fd(libc::dup(queue_fd)) };
+    let queue_evt = queue_evts[0].try_clone().unwrap();
 
-    let features = base_features();
+    let features = base_features(false);
 
     let disk_file = tempfile::tempfile().unwrap();
     let mut block = Block::new(features, Box::new(disk_file), false, true, 512, None).unwrap();
diff --git a/fuzz/fs_server_fuzzer.rs b/fuzz/fs_server_fuzzer.rs
index 1d0d211..f1154c5 100644
--- a/fuzz/fs_server_fuzzer.rs
+++ b/fuzz/fs_server_fuzzer.rs
@@ -7,8 +7,8 @@
 use std::convert::TryInto;
 
 use cros_fuzz::fuzz_target;
-use devices::virtio::fs::fuzzing::fuzz_server;
 use devices::virtio::{create_descriptor_chain, DescriptorType, Reader, Writer};
+use fuse::fuzzing::fuzz_server;
 use vm_memory::{GuestAddress, GuestMemory};
 
 const MEM_SIZE: u64 = 256 * 1024 * 1024;
diff --git a/gpu_buffer/Android.bp b/gpu_buffer/Android.bp
index 572ca42..514eaf8 100644
--- a/gpu_buffer/Android.bp
+++ b/gpu_buffer/Android.bp
@@ -62,14 +62,31 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/gpu_display/Android.bp b/gpu_display/Android.bp
index cae43f1..368259b 100644
--- a/gpu_display/Android.bp
+++ b/gpu_display/Android.bp
@@ -97,16 +97,33 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../linux_input_sys/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
 //   cc-1.0.25
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/gpu_display/src/event_device.rs b/gpu_display/src/event_device.rs
index 5aae55c..4453c84 100644
--- a/gpu_display/src/event_device.rs
+++ b/gpu_display/src/event_device.rs
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use base::{AsRawDescriptor, RawDescriptor};
 use data_model::DataInit;
 use linux_input_sys::{virtio_input_event, InputEventDecoder};
 use std::collections::VecDeque;
 use std::io::{self, Error, ErrorKind, Read, Write};
 use std::iter::ExactSizeIterator;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::os::unix::net::UnixStream;
 
 const EVENT_SIZE: usize = virtio_input_event::SIZE;
@@ -150,8 +150,8 @@
     }
 }
 
-impl AsRawFd for EventDevice {
-    fn as_raw_fd(&self) -> RawFd {
-        self.event_socket.as_raw_fd()
+impl AsRawDescriptor for EventDevice {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.event_socket.as_raw_descriptor()
     }
 }
diff --git a/gpu_display/src/gpu_display_stub.rs b/gpu_display/src/gpu_display_stub.rs
index 70e7187..09aa03d 100644
--- a/gpu_display/src/gpu_display_stub.rs
+++ b/gpu_display/src/gpu_display_stub.rs
@@ -4,11 +4,10 @@
 
 use std::collections::BTreeMap;
 use std::num::NonZeroU32;
-use std::os::unix::io::{AsRawFd, RawFd};
 
 use crate::{DisplayT, EventDevice, GpuDisplayError, GpuDisplayFramebuffer};
 
-use base::Event;
+use base::{AsRawDescriptor, Event, RawDescriptor};
 use data_model::VolatileSlice;
 
 type SurfaceId = NonZeroU32;
@@ -130,7 +129,7 @@
 }
 
 pub struct DisplayStub {
-    /// This event is never triggered and is used solely to fulfill AsRawFd.
+    /// This event is never triggered and is used solely to fulfill AsRawDescriptor.
     event: Event,
     surfaces: SurfacesHelper,
 }
@@ -185,7 +184,7 @@
 
     fn import_dmabuf(
         &mut self,
-        _fd: RawFd,
+        _fd: RawDescriptor,
         _offset: u32,
         _stride: u32,
         _modifiers: u64,
@@ -225,8 +224,8 @@
     }
 }
 
-impl AsRawFd for DisplayStub {
-    fn as_raw_fd(&self) -> RawFd {
-        self.event.as_raw_fd()
+impl AsRawDescriptor for DisplayStub {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.event.as_raw_descriptor()
     }
 }
diff --git a/gpu_display/src/gpu_display_wl.rs b/gpu_display/src/gpu_display_wl.rs
index 28d9a1d..d6365ac 100644
--- a/gpu_display/src/gpu_display_wl.rs
+++ b/gpu_display/src/gpu_display_wl.rs
@@ -17,12 +17,12 @@
 use std::cell::Cell;
 use std::collections::HashMap;
 use std::ffi::{CStr, CString};
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::path::Path;
 use std::ptr::{null, null_mut};
 
 use base::{
-    round_up_to_page_size, AsRawDescriptor, MemoryMapping, MemoryMappingBuilder, SharedMemory,
+    round_up_to_page_size, AsRawDescriptor, MemoryMapping, MemoryMappingBuilder, RawDescriptor,
+    SharedMemory,
 };
 use data_model::VolatileMemory;
 
@@ -84,7 +84,7 @@
 
 /// A connection to the compositor and associated collection of state.
 ///
-/// The user of `GpuDisplay` can use `AsRawFd` to poll on the compositor connection's file
+/// The user of `GpuDisplay` can use `AsRawDescriptor` to poll on the compositor connection's file
 /// descriptor. When the connection is readable, `dispatch_events` can be called to process it.
 pub struct DisplayWl {
     dmabufs: HashMap<u32, DwlDmabuf>,
@@ -146,7 +146,7 @@
 impl DisplayT for DisplayWl {
     fn import_dmabuf(
         &mut self,
-        fd: RawFd,
+        fd: RawDescriptor,
         offset: u32,
         stride: u32,
         modifiers: u64,
@@ -352,8 +352,8 @@
     }
 }
 
-impl AsRawFd for DisplayWl {
-    fn as_raw_fd(&self) -> RawFd {
+impl AsRawDescriptor for DisplayWl {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
         // Safe given that the context pointer is valid.
         unsafe { dwl_context_fd(self.ctx.0) }
     }
diff --git a/gpu_display/src/gpu_display_x.rs b/gpu_display/src/gpu_display_x.rs
index 54f7c5c..5d6a9b6 100644
--- a/gpu_display/src/gpu_display_x.rs
+++ b/gpu_display/src/gpu_display_x.rs
@@ -18,7 +18,6 @@
 use std::mem::{transmute_copy, zeroed};
 use std::num::NonZeroU32;
 use std::os::raw::c_ulong;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::ptr::{null, null_mut, NonNull};
 use std::rc::Rc;
 use std::time::Duration;
@@ -30,7 +29,7 @@
     EventDeviceKind, GpuDisplayError, GpuDisplayFramebuffer,
 };
 
-use base::{error, PollContext, PollToken, WatchingEvents};
+use base::{error, AsRawDescriptor, EventType, PollToken, RawDescriptor, WaitContext};
 use data_model::VolatileSlice;
 
 const BUFFER_COUNT: usize = 2;
@@ -95,8 +94,8 @@
     }
 }
 
-impl AsRawFd for XDisplay {
-    fn as_raw_fd(&self) -> RawFd {
+impl AsRawDescriptor for XDisplay {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
         unsafe { xlib::XConnectionNumber(self.as_ptr()) }
     }
 }
@@ -542,7 +541,7 @@
 }
 
 pub struct DisplayX {
-    poll_ctx: PollContext<DisplayXPollToken>,
+    wait_ctx: WaitContext<DisplayXPollToken>,
     display: XDisplay,
     screen: XScreen,
     visual: *mut xlib::Visual,
@@ -553,7 +552,7 @@
 
 impl DisplayX {
     pub fn open_display(display: Option<&str>) -> Result<DisplayX, GpuDisplayError> {
-        let poll_ctx = PollContext::new().map_err(|_| GpuDisplayError::Allocate)?;
+        let wait_ctx = WaitContext::new().map_err(|_| GpuDisplayError::Allocate)?;
 
         let display_cstr = match display.map(CString::new) {
             Some(Ok(s)) => Some(s),
@@ -573,7 +572,7 @@
                 None => return Err(GpuDisplayError::Connect),
             };
 
-            poll_ctx
+            wait_ctx
                 .add(&display, DisplayXPollToken::Display)
                 .map_err(|_| GpuDisplayError::Allocate)?;
 
@@ -618,7 +617,7 @@
             x_free(visual_info);
 
             Ok(DisplayX {
-                poll_ctx,
+                wait_ctx,
                 display,
                 screen,
                 visual,
@@ -675,28 +674,28 @@
     }
 
     fn handle_poll_ctx(&mut self) -> base::Result<()> {
-        let poll_events = self.poll_ctx.wait_timeout(Duration::default())?.to_owned();
-        for poll_event in poll_events.as_ref().iter_writable() {
-            if let DisplayXPollToken::EventDevice { event_device_id } = poll_event.token() {
+        let wait_events = self.wait_ctx.wait_timeout(Duration::default())?;
+        for wait_event in wait_events.iter().filter(|e| e.is_writable) {
+            if let DisplayXPollToken::EventDevice { event_device_id } = wait_event.token {
                 if let Some(event_device) = self.event_device_mut(event_device_id) {
                     if !event_device.flush_buffered_events()? {
                         continue;
                     }
                 }
                 // Although this looks exactly like the previous if-block, we need to reborrow self
-                // as immutable in order to make use of self.poll_ctx.
+                // as immutable in order to make use of self.wait_ctx.
                 if let Some(event_device) = self.event_device(event_device_id) {
-                    self.poll_ctx.modify(
+                    self.wait_ctx.modify(
                         event_device,
-                        WatchingEvents::empty().set_read(),
+                        EventType::Read,
                         DisplayXPollToken::EventDevice { event_device_id },
                     )?;
                 }
             }
         }
 
-        for poll_event in poll_events.as_ref().iter_readable() {
-            match poll_event.token() {
+        for wait_event in wait_events.iter().filter(|e| e.is_readable) {
+            match wait_event.token {
                 DisplayXPollToken::Display => self.dispatch_display_events(),
                 DisplayXPollToken::EventDevice { event_device_id } => {
                     self.handle_event_device(event_device_id)
@@ -772,7 +771,7 @@
     #[allow(unused_variables)]
     fn import_dmabuf(
         &mut self,
-        fd: RawFd,
+        fd: RawDescriptor,
         offset: u32,
         stride: u32,
         modifiers: u64,
@@ -802,7 +801,7 @@
     fn import_event_device(&mut self, event_device: EventDevice) -> Result<u32, GpuDisplayError> {
         let new_event_device_id = self.next_id;
 
-        self.poll_ctx
+        self.wait_ctx
             .add(
                 &event_device,
                 DisplayXPollToken::EventDevice {
@@ -836,8 +835,8 @@
     }
 }
 
-impl AsRawFd for DisplayX {
-    fn as_raw_fd(&self) -> RawFd {
-        self.poll_ctx.as_raw_fd()
+impl AsRawDescriptor for DisplayX {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.wait_ctx.as_raw_descriptor()
     }
 }
diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs
index 94b5547..0ffae87 100644
--- a/gpu_display/src/lib.rs
+++ b/gpu_display/src/lib.rs
@@ -5,10 +5,9 @@
 //! Crate for displaying simple surfaces and GPU buffers over wayland.
 
 use std::fmt::{self, Display};
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::path::Path;
 
-use base::Error as SysError;
+use base::{AsRawDescriptor, Error as SysError, RawDescriptor};
 use data_model::VolatileSlice;
 
 mod event_device;
@@ -119,10 +118,10 @@
     }
 }
 
-trait DisplayT: AsRawFd {
+trait DisplayT: AsRawDescriptor {
     fn import_dmabuf(
         &mut self,
-        fd: RawFd,
+        fd: RawDescriptor,
         offset: u32,
         stride: u32,
         modifiers: u64,
@@ -164,7 +163,7 @@
 
 /// A connection to the compositor and associated collection of state.
 ///
-/// The user of `GpuDisplay` can use `AsRawFd` to poll on the compositor connection's file
+/// The user of `GpuDisplay` can use `AsRawDescriptor` to poll on the compositor connection's file
 /// descriptor. When the connection is readable, `dispatch_events` can be called to process it.
 pub struct GpuDisplay {
     inner: Box<dyn DisplayT>,
@@ -213,7 +212,7 @@
     /// Imports a dmabuf to the compositor for use as a surface buffer and returns a handle to it.
     pub fn import_dmabuf(
         &mut self,
-        fd: RawFd,
+        fd: RawDescriptor,
         offset: u32,
         stride: u32,
         modifiers: u64,
@@ -325,8 +324,8 @@
     }
 }
 
-impl AsRawFd for GpuDisplay {
-    fn as_raw_fd(&self) -> RawFd {
-        self.inner.as_raw_fd()
+impl AsRawDescriptor for GpuDisplay {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.inner.as_raw_descriptor()
     }
 }
diff --git a/gpu_renderer/Android.bp b/gpu_renderer/Android.bp
index 97c04a7..2b62bd4 100644
--- a/gpu_renderer/Android.bp
+++ b/gpu_renderer/Android.bp
@@ -107,7 +107,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/gpu_renderer/src/lib.rs b/gpu_renderer/src/lib.rs
index c735170..ade794b 100644
--- a/gpu_renderer/src/lib.rs
+++ b/gpu_renderer/src/lib.rs
@@ -8,7 +8,7 @@
 mod generated;
 mod vsnprintf;
 
-use base::{ExternalMapping, ExternalMappingError, ExternalMappingResult};
+use base::{ExternalMapping, ExternalMappingError, ExternalMappingResult, FromRawDescriptor};
 use std::cell::RefCell;
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
 use std::ffi::CString;
@@ -16,7 +16,6 @@
 use std::fs::File;
 use std::mem::{size_of, transmute};
 use std::os::raw::{c_char, c_void};
-use std::os::unix::io::FromRawFd;
 use std::ptr::null_mut;
 use std::rc::Rc;
 use std::result;
@@ -228,6 +227,12 @@
         self.set_flag(GFXSTREAM_RENDERER_FLAGS_NO_VK_BIT, !v)
     }
 
+    #[cfg(feature = "gfxstream")]
+    pub fn use_guest_angle(self, v: bool) -> RendererFlags {
+        const GFXSTREAM_RENDERER_FLAGS_GUEST_USES_ANGLE: u32 = 1 << 21;
+        self.set_flag(GFXSTREAM_RENDERER_FLAGS_GUEST_USES_ANGLE, v)
+    }
+
     pub fn use_external_blob(self, v: bool) -> RendererFlags {
         self.set_flag(VIRGL_RENDERER_USE_EXTERNAL_BLOB, v)
     }
@@ -640,7 +645,7 @@
                 return Err(Error::Unsupported);
             }
 
-            let dmabuf = unsafe { File::from_raw_fd(fd) };
+            let dmabuf = unsafe { File::from_raw_descriptor(fd) };
             Ok(dmabuf)
         }
         #[cfg(not(feature = "virtio-gpu-next"))]
@@ -668,7 +673,7 @@
 
         // Safe because the FD was just returned by a successful virglrenderer call so it must
         // be valid and owned by us.
-        let dmabuf = unsafe { File::from_raw_fd(query.out_fds[0]) };
+        let dmabuf = unsafe { File::from_raw_descriptor(query.out_fds[0]) };
         Ok(dmabuf)
     }
 
diff --git a/hypervisor/Android.bp b/hypervisor/Android.bp
index 92e5dae..a040ab9 100644
--- a/hypervisor/Android.bp
+++ b/hypervisor/Android.bp
@@ -75,6 +75,7 @@
 //   ../tempfile/src/lib.rs
 //   ../vm_memory/src/lib.rs
 //   async-trait-0.1.42
+//   base-0.1.0
 //   downcast-rs-1.2.0 "default,std"
 //   futures-0.3.8 "alloc,async-await,default,executor,futures-executor,std"
 //   futures-channel-0.3.8 "alloc,futures-sink,sink,std"
@@ -97,7 +98,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/hypervisor/src/aarch64.rs b/hypervisor/src/aarch64.rs
index 584e983..da253dc 100644
--- a/hypervisor/src/aarch64.rs
+++ b/hypervisor/src/aarch64.rs
@@ -7,6 +7,12 @@
 
 use crate::{Hypervisor, IrqRoute, IrqSource, IrqSourceChip, Vcpu, Vm};
 
+/// Represents a version of Power State Coordination Interface (PSCI).
+pub struct PsciVersion {
+    pub major: u32,
+    pub minor: u32,
+}
+
 /// A wrapper for using a VM on aarch64 and getting/setting its state.
 pub trait VmAArch64: Vm {
     /// Gets the `Hypervisor` that created this VM.
@@ -30,6 +36,13 @@
     /// Sets the value of a register on this VCPU.  `reg_id` is the register ID, as specified in the
     /// KVM API documentation for KVM_SET_ONE_REG.
     fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()>;
+
+    /// Gets the value of a register on this VCPU.  `reg_id` is the register ID, as specified in the
+    /// KVM API documentation for KVM_GET_ONE_REG.
+    fn get_one_reg(&self, reg_id: u64) -> Result<u64>;
+
+    /// Gets the current PSCI version.
+    fn get_psci_version(&self) -> Result<PsciVersion>;
 }
 
 impl_downcast!(VcpuAArch64);
diff --git a/hypervisor/src/kvm/aarch64.rs b/hypervisor/src/kvm/aarch64.rs
index 8efde70..a88bda1 100644
--- a/hypervisor/src/kvm/aarch64.rs
+++ b/hypervisor/src/kvm/aarch64.rs
@@ -9,7 +9,8 @@
 
 use super::{KvmVcpu, KvmVm};
 use crate::{
-    ClockState, DeviceKind, Hypervisor, IrqSourceChip, VcpuAArch64, VcpuFeature, VmAArch64, VmCap,
+    ClockState, DeviceKind, Hypervisor, IrqSourceChip, PsciVersion, VcpuAArch64, VcpuFeature,
+    VmAArch64, VmCap,
 };
 
 impl KvmVm {
@@ -156,6 +157,42 @@
             errno_result()
         }
     }
+
+    fn get_one_reg(&self, reg_id: u64) -> Result<u64> {
+        let val: u64 = 0;
+        let mut onereg = kvm_one_reg {
+            id: reg_id,
+            addr: (&val as *const u64) as u64,
+        };
+
+        // Safe because we allocated the struct and we know the kernel will read exactly the size of
+        // the struct.
+        let ret = unsafe { ioctl_with_ref(self, KVM_GET_ONE_REG(), &mut onereg) };
+        if ret == 0 {
+            Ok(val)
+        } else {
+            return errno_result();
+        }
+    }
+
+    fn get_psci_version(&self) -> Result<PsciVersion> {
+        // The definition of KVM_REG_ARM_PSCI_VERSION is in arch/arm64/include/uapi/asm/kvm.h.
+        const KVM_REG_ARM_PSCI_VERSION: u64 =
+            KVM_REG_ARM64 | (KVM_REG_SIZE_U64 as u64) | (KVM_REG_ARM_FW as u64);
+
+        match self.get_one_reg(KVM_REG_ARM_PSCI_VERSION) {
+            Ok(v) => {
+                let major = (v >> PSCI_VERSION_MAJOR_SHIFT) as u32;
+                let minor = (v as u32) & PSCI_VERSION_MINOR_MASK;
+                Ok(PsciVersion { major, minor })
+            }
+            Err(_) => {
+                // When `KVM_REG_ARM_PSCI_VERSION` is not supported, we can return PSCI 0.2, as vCPU
+                // has been initialized with `KVM_ARM_VCPU_PSCI_0_2` successfully.
+                Ok(PsciVersion { major: 0, minor: 2 })
+            }
+        }
+    }
 }
 
 // This function translates an IrqSrouceChip to the kvm u32 equivalent. It has a different
diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs
index bfb9bb1..ba06d4f 100644
--- a/hypervisor/src/kvm/mod.rs
+++ b/hypervisor/src/kvm/mod.rs
@@ -13,12 +13,11 @@
 use x86_64::*;
 
 use std::cell::RefCell;
-use std::cmp::{min, Ordering};
+use std::cmp::{min, Reverse};
 use std::collections::{BTreeMap, BinaryHeap};
 use std::convert::TryFrom;
 use std::mem::{size_of, ManuallyDrop};
 use std::os::raw::{c_char, c_int, c_ulong, c_void};
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::ptr::copy_nonoverlapping;
 use std::sync::atomic::AtomicU64;
 use std::sync::Arc;
@@ -126,12 +125,6 @@
     }
 }
 
-impl AsRawFd for Kvm {
-    fn as_raw_fd(&self) -> RawFd {
-        self.kvm.as_raw_descriptor()
-    }
-}
-
 impl Hypervisor for Kvm {
     fn try_clone(&self) -> Result<Self> {
         Ok(Kvm {
@@ -151,31 +144,14 @@
     }
 }
 
-// Used to invert the order when stored in a max-heap.
-#[derive(Copy, Clone, Eq, PartialEq)]
-struct MemSlotOrd(MemSlot);
-
-impl Ord for MemSlotOrd {
-    fn cmp(&self, other: &MemSlotOrd) -> Ordering {
-        // Notice the order is inverted so the lowest magnitude slot has the highest priority in a
-        // max-heap.
-        other.0.cmp(&self.0)
-    }
-}
-
-impl PartialOrd for MemSlotOrd {
-    fn partial_cmp(&self, other: &MemSlotOrd) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
 /// A wrapper around creating and using a KVM VM.
 pub struct KvmVm {
     kvm: Kvm,
     vm: SafeDescriptor,
     guest_mem: GuestMemory,
     mem_regions: Arc<Mutex<BTreeMap<MemSlot, Box<dyn MappedRegion>>>>,
-    mem_slot_gaps: Arc<Mutex<BinaryHeap<MemSlotOrd>>>,
+    /// A min heap of MemSlot numbers that were used and then removed and can now be re-used
+    mem_slot_gaps: Arc<Mutex<BinaryHeap<Reverse<MemSlot>>>>,
 }
 
 impl KvmVm {
@@ -214,11 +190,10 @@
     }
 
     fn create_vcpu(&self, id: usize) -> Result<KvmVcpu> {
-        let id = c_ulong::try_from(id).unwrap();
         let run_mmap_size = self.kvm.get_vcpu_mmap_size()?;
 
         // Safe because we know that our file is a VM fd and we verify the return result.
-        let fd = unsafe { ioctl_with_val(self, KVM_CREATE_VCPU(), id) };
+        let fd = unsafe { ioctl_with_val(self, KVM_CREATE_VCPU(), c_ulong::try_from(id).unwrap()) };
         if fd < 0 {
             return errno_result();
         }
@@ -235,6 +210,7 @@
         Ok(KvmVcpu {
             vm: self.vm.try_clone()?,
             vcpu,
+            id,
             run_mmap,
             vcpu_run_handle_fingerprint: Default::default(),
         })
@@ -278,14 +254,14 @@
         resample_evt: Option<&Event>,
     ) -> Result<()> {
         let mut irqfd = kvm_irqfd {
-            fd: evt.as_raw_fd() as u32,
+            fd: evt.as_raw_descriptor() as u32,
             gsi,
             ..Default::default()
         };
 
         if let Some(r_evt) = resample_evt {
             irqfd.flags = KVM_IRQFD_FLAG_RESAMPLE;
-            irqfd.resamplefd = r_evt.as_raw_fd() as u32;
+            irqfd.resamplefd = r_evt.as_raw_descriptor() as u32;
         }
 
         // Safe because we know that our file is a VM fd, we know the kernel will only read the
@@ -305,7 +281,7 @@
     /// `register_irqfd`.
     pub fn unregister_irqfd(&self, gsi: u32, evt: &Event) -> Result<()> {
         let irqfd = kvm_irqfd {
-            fd: evt.as_raw_fd() as u32,
+            fd: evt.as_raw_descriptor() as u32,
             gsi,
             flags: KVM_IRQFD_FLAG_DEASSIGN,
             ..Default::default()
@@ -385,7 +361,7 @@
                 IoEventAddress::Pio(p) => p as u64,
                 IoEventAddress::Mmio(m) => m,
             },
-            fd: evt.as_raw_fd(),
+            fd: evt.as_raw_descriptor(),
             flags,
             ..Default::default()
         };
@@ -468,7 +444,7 @@
         };
 
         if let Err(e) = res {
-            gaps.push(MemSlotOrd(slot));
+            gaps.push(Reverse(slot));
             return Err(e);
         }
         regions.insert(slot, mem);
@@ -496,7 +472,7 @@
         unsafe {
             set_user_memory_region(&self.vm, slot, false, false, 0, 0, std::ptr::null_mut())?;
         }
-        self.mem_slot_gaps.lock().push(MemSlotOrd(slot));
+        self.mem_slot_gaps.lock().push(Reverse(slot));
         // This remove will always succeed because of the contains_key check above.
         Ok(regions.remove(&slot).unwrap())
     }
@@ -553,7 +529,7 @@
     }
 
     fn register_ioevent(
-        &self,
+        &mut self,
         evt: &Event,
         addr: IoEventAddress,
         datamatch: Datamatch,
@@ -562,7 +538,7 @@
     }
 
     fn unregister_ioevent(
-        &self,
+        &mut self,
         evt: &Event,
         addr: IoEventAddress,
         datamatch: Datamatch,
@@ -570,6 +546,11 @@
         self.ioeventfd(evt, addr, datamatch, true)
     }
 
+    fn handle_io_events(&self, _addr: IoEventAddress, _data: &[u8]) -> Result<()> {
+        // KVM delivers IO events in-kernel with ioeventfds, so this is a no-op
+        Ok(())
+    }
+
     fn get_pvclock(&self) -> Result<ClockState> {
         self.get_pvclock_arch()
     }
@@ -585,16 +566,11 @@
     }
 }
 
-impl AsRawFd for KvmVm {
-    fn as_raw_fd(&self) -> RawFd {
-        self.vm.as_raw_descriptor()
-    }
-}
-
 /// A wrapper around using a KVM Vcpu.
 pub struct KvmVcpu {
     vm: SafeDescriptor,
     vcpu: SafeDescriptor,
+    id: usize,
     run_mmap: MemoryMapping,
     vcpu_run_handle_fingerprint: Arc<AtomicU64>,
 }
@@ -619,6 +595,7 @@
         Ok(KvmVcpu {
             vm,
             vcpu,
+            id: self.id,
             run_mmap,
             vcpu_run_handle_fingerprint,
         })
@@ -680,6 +657,10 @@
         Ok(ManuallyDrop::into_inner(vcpu_run_handle))
     }
 
+    fn id(&self) -> usize {
+        self.id
+    }
+
     #[allow(clippy::cast_ptr_alignment)]
     fn set_immediate_exit(&self, exit: bool) {
         // Safe because we know we mapped enough memory to hold the kvm_run struct because the
@@ -706,11 +687,6 @@
         f
     }
 
-    fn handle_io_events(&self, _addr: IoEventAddress) -> Result<()> {
-        // KVM delivers IO events in-kernel with ioeventfds, so this is a no-op
-        Ok(())
-    }
-
     #[allow(clippy::cast_ptr_alignment)]
     fn set_data(&self, data: &[u8]) -> Result<()> {
         // Safe because we know we mapped enough memory to hold the kvm_run struct because the
@@ -996,8 +972,8 @@
     }
 }
 
-impl AsRawFd for KvmVcpu {
-    fn as_raw_fd(&self) -> RawFd {
+impl AsRawDescriptor for KvmVcpu {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
         self.vcpu.as_raw_descriptor()
     }
 }
@@ -1040,7 +1016,7 @@
                         address_lo: *address as u32,
                         address_hi: (*address >> 32) as u32,
                         data: *data,
-                        pad: 0,
+                        ..Default::default()
                     },
                 },
                 ..Default::default()
@@ -1109,8 +1085,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use base::{pagesize, MemoryMappingArena, MemoryMappingBuilder};
-    use std::os::unix::io::FromRawFd;
+    use base::{pagesize, FromRawDescriptor, MemoryMappingArena, MemoryMappingBuilder};
     use std::thread;
     use vm_memory::GuestAddress;
 
@@ -1313,7 +1288,7 @@
         vm.register_irqfd(4, &evtfd1, Some(&evtfd2)).unwrap();
         vm.unregister_irqfd(4, &evtfd1).unwrap();
         // Ensures the ioctl is actually reading the resamplefd.
-        vm.register_irqfd(4, &evtfd1, Some(unsafe { &Event::from_raw_fd(-1) }))
+        vm.register_irqfd(4, &evtfd1, Some(unsafe { &Event::from_raw_descriptor(-1) }))
             .unwrap_err();
     }
 
@@ -1339,7 +1314,7 @@
     fn register_ioevent() {
         let kvm = Kvm::new().unwrap();
         let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
-        let vm = KvmVm::new(&kvm, gm).unwrap();
+        let mut vm = KvmVm::new(&kvm, gm).unwrap();
         let evtfd = Event::new().unwrap();
         vm.register_ioevent(&evtfd, IoEventAddress::Pio(0xf4), Datamatch::AnyLength)
             .unwrap();
@@ -1375,7 +1350,7 @@
     fn unregister_ioevent() {
         let kvm = Kvm::new().unwrap();
         let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
-        let vm = KvmVm::new(&kvm, gm).unwrap();
+        let mut vm = KvmVm::new(&kvm, gm).unwrap();
         let evtfd = Event::new().unwrap();
         vm.register_ioevent(&evtfd, IoEventAddress::Pio(0xf4), Datamatch::AnyLength)
             .unwrap();
diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs
index ae9535e..3123380 100644
--- a/hypervisor/src/kvm/x86_64.rs
+++ b/hypervisor/src/kvm/x86_64.rs
@@ -4,13 +4,12 @@
 
 use base::IoctlNr;
 use std::convert::TryInto;
-use std::os::unix::io::AsRawFd;
 
 use libc::E2BIG;
 
 use base::{
     errno_result, error, ioctl, ioctl_with_mut_ptr, ioctl_with_mut_ref, ioctl_with_ptr,
-    ioctl_with_ref, ioctl_with_val, Error, MappedRegion, Result,
+    ioctl_with_ref, ioctl_with_val, AsRawDescriptor, Error, MappedRegion, Result,
 };
 use data_model::vec_with_array_field;
 use kvm_sys::*;
@@ -26,8 +25,8 @@
 
 type KvmCpuId = kvm::CpuId;
 
-fn get_cpuid_with_initial_capacity<T: AsRawFd>(
-    fd: &T,
+fn get_cpuid_with_initial_capacity<T: AsRawDescriptor>(
+    descriptor: &T,
     kind: IoctlNr,
     initial_capacity: usize,
 ) -> Result<CpuId> {
@@ -40,7 +39,7 @@
             // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the
             // memory allocated for the struct. The limit is read from nent within KvmCpuId,
             // which is set to the allocated size above.
-            ioctl_with_mut_ptr(fd, kind, kvm_cpuid.as_mut_ptr())
+            ioctl_with_mut_ptr(descriptor, kind, kvm_cpuid.as_mut_ptr())
         };
         if ret < 0 {
             let err = Error::last();
@@ -344,12 +343,12 @@
 
 impl VcpuX86_64 for KvmVcpu {
     #[allow(clippy::cast_ptr_alignment)]
-    fn request_interrupt_window(&self) {
+    fn set_interrupt_window_requested(&self, requested: bool) {
         // Safe because we know we mapped enough memory to hold the kvm_run struct because the
         // kernel told us how large it was. The pointer is page aligned so casting to a different
         // type is well defined, hence the clippy allow attribute.
         let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut kvm_run) };
-        run.request_interrupt_window = 1;
+        run.request_interrupt_window = if requested { 1 } else { 0 };
     }
 
     #[allow(clippy::cast_ptr_alignment)]
@@ -377,6 +376,16 @@
         }
     }
 
+    fn inject_nmi(&self) -> Result<()> {
+        // Safe because we know that our file is a VCPU fd.
+        let ret = unsafe { ioctl(self, KVM_NMI()) };
+        if ret == 0 {
+            Ok(())
+        } else {
+            errno_result()
+        }
+    }
+
     fn get_regs(&self) -> Result<Regs> {
         // Safe because we know that our file is a VCPU fd, we know the kernel will only read the
         // correct amount of memory from our pointer, and we verify the return result.
@@ -555,6 +564,45 @@
         const KVM_MAX_ENTRIES: usize = 256;
         get_cpuid_with_initial_capacity(self, KVM_GET_SUPPORTED_HV_CPUID(), KVM_MAX_ENTRIES)
     }
+
+    fn set_guest_debug(&self, addrs: &[GuestAddress], enable_singlestep: bool) -> Result<()> {
+        use kvm_sys::*;
+        let mut dbg: kvm_guest_debug = Default::default();
+
+        if addrs.len() > 4 {
+            error!(
+                "Support 4 breakpoints at most but {} addresses are passed",
+                addrs.len()
+            );
+            return Err(base::Error::new(libc::EINVAL));
+        }
+
+        dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
+        if enable_singlestep {
+            dbg.control |= KVM_GUESTDBG_SINGLESTEP;
+        }
+
+        // Set bits 9 and 10.
+        // bit 9: GE (global exact breakpoint enable) flag.
+        // bit 10: always 1.
+        dbg.arch.debugreg[7] = 0x0600;
+
+        for i in 0..addrs.len() {
+            dbg.arch.debugreg[i] = addrs[i].0;
+            // Set global breakpoint enable flag
+            dbg.arch.debugreg[7] |= 2 << (i * 2);
+        }
+
+        let ret = unsafe {
+            // Here we trust the kernel not to read past the end of the kvm_guest_debug struct.
+            ioctl_with_ref(self, KVM_SET_GUEST_DEBUG(), &dbg)
+        };
+        if ret == 0 {
+            Ok(())
+        } else {
+            errno_result()
+        }
+    }
 }
 
 impl KvmVcpu {
diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs
index 9f63ac6..c6cf45a 100644
--- a/hypervisor/src/lib.rs
+++ b/hypervisor/src/lib.rs
@@ -12,7 +12,7 @@
 
 use std::os::raw::c_int;
 
-use base::{Event, MappedRegion, Result, SafeDescriptor};
+use base::{Event, MappedRegion, RawDescriptor, Result, SafeDescriptor};
 use msg_socket::MsgOnSocket;
 use vm_memory::{GuestAddress, GuestMemory};
 
@@ -109,7 +109,7 @@
     /// In all cases where `evt` is signaled, the ordinary vmexit to userspace that would be
     /// triggered is prevented.
     fn register_ioevent(
-        &self,
+        &mut self,
         evt: &Event,
         addr: IoEventAddress,
         datamatch: Datamatch,
@@ -120,12 +120,18 @@
     /// The `evt`, `addr`, and `datamatch` set must be the same as the ones passed into
     /// `register_ioevent`.
     fn unregister_ioevent(
-        &self,
+        &mut self,
         evt: &Event,
         addr: IoEventAddress,
         datamatch: Datamatch,
     ) -> Result<()>;
 
+    /// Trigger any matching registered io events based on an MMIO or PIO write at `addr`. The
+    /// `data` slice represents the contents and length of the write, which is used to compare with
+    /// the registered io events' Datamatch values. If the hypervisor does in-kernel IO event
+    /// delivery, this is a no-op.
+    fn handle_io_events(&self, addr: IoEventAddress, data: &[u8]) -> Result<()>;
+
     /// Retrieves the current timestamp of the paravirtual clock as seen by the current guest.
     /// Only works on VMs that support `VmCap::PvClock`.
     fn get_pvclock(&self) -> Result<ClockState>;
@@ -215,6 +221,9 @@
     /// `take_run_handle` for this `Vcpu`.
     fn run(&self, run_handle: &VcpuRunHandle) -> Result<VcpuExit>;
 
+    /// Returns the vcpu id.
+    fn id(&self) -> usize;
+
     /// Sets the bit that requests an immediate exit.
     fn set_immediate_exit(&self, exit: bool);
 
@@ -227,10 +236,6 @@
     /// signal-safe way when called.
     fn set_local_immediate_exit_fn(&self) -> extern "C" fn();
 
-    /// Trigger any io events based on the memory mapped IO at `addr`.  If the hypervisor does
-    /// in-kernel IO event delivery, this is a no-op.
-    fn handle_io_events(&self, addr: IoEventAddress) -> Result<()>;
-
     /// Sets the data received by a mmio read, ioport in, or hypercall instruction.
     ///
     /// This function should be called after `Vcpu::run` returns an `VcpuExit::IoIn`,
@@ -254,13 +259,14 @@
 downcast_rs::impl_downcast!(sync Vcpu);
 
 /// An address either in programmable I/O space or in memory mapped I/O space.
-#[derive(Copy, Clone, Debug, MsgOnSocket)]
+#[derive(Copy, Clone, Debug, MsgOnSocket, PartialEq, Eq, std::hash::Hash)]
 pub enum IoEventAddress {
     Pio(u64),
     Mmio(u64),
 }
 
 /// Used in `Vm::register_ioevent` to indicate a size and optionally value to match.
+#[derive(PartialEq, Eq)]
 pub enum Datamatch {
     AnyLength,
     U8(Option<u8>),
diff --git a/hypervisor/src/x86_64.rs b/hypervisor/src/x86_64.rs
index 7b9a7dc..ae4cb44 100644
--- a/hypervisor/src/x86_64.rs
+++ b/hypervisor/src/x86_64.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 base::{error, Result};
+use base::{error, RawDescriptor, Result};
 use bit_field::*;
 use downcast_rs::impl_downcast;
 use msg_socket::MsgOnSocket;
@@ -39,8 +39,9 @@
 
 /// A wrapper around creating and using a VCPU on x86_64.
 pub trait VcpuX86_64: Vcpu {
-    /// Request the VCPU to exit when it becomes possible to inject interrupts into the guest.
-    fn request_interrupt_window(&self);
+    /// Sets or clears the flag that requests the VCPU to exit when it becomes possible to inject
+    /// interrupts into the guest.
+    fn set_interrupt_window_requested(&self, requested: bool);
 
     /// Checks if we can inject an interrupt into the VCPU.
     fn ready_for_interrupt(&self) -> bool;
@@ -48,6 +49,9 @@
     /// Injects interrupt vector `irq` into the VCPU.
     fn interrupt(&self, irq: u32) -> Result<()>;
 
+    /// Injects a non-maskable interrupt into the VCPU.
+    fn inject_nmi(&self) -> Result<()>;
+
     /// Gets the VCPU general purpose registers.
     fn get_regs(&self) -> Result<Regs>;
 
@@ -90,6 +94,9 @@
 
     /// Gets the system emulated hyper-v CPUID values.
     fn get_hyperv_cpuid(&self) -> Result<CpuId>;
+
+    /// Sets up debug registers and configure vcpu for handling guest debug events.
+    fn set_guest_debug(&self, addrs: &[GuestAddress], enable_singlestep: bool) -> Result<()>;
 }
 
 impl_downcast!(VcpuX86_64);
@@ -154,6 +161,10 @@
     External = 0b111,
 }
 
+// These MSI structures are for Intel's implementation of MSI.  The PCI spec defines most of MSI,
+// but the Intel spec defines the format of messages for raising interrupts.  The PCI spec defines
+// three u32s -- the address, address_high, and data -- but Intel only makes use of the address and
+// data.  The Intel portion of the specification is in Volume 3 section 10.11.
 #[bitfield]
 #[derive(Clone, Copy, PartialEq, Eq)]
 pub struct MsiAddressMessage {
@@ -174,7 +185,8 @@
     #[bits = 3]
     pub delivery_mode: DeliveryMode,
     pub reserved: BitField3,
-    pub level: BitField1,
+    #[bits = 1]
+    pub level: Level,
     #[bits = 1]
     pub trigger: TriggerMode,
     pub reserved2: BitField16,
@@ -187,6 +199,14 @@
     Pending = 1,
 }
 
+/// The level of a level-triggered interrupt: asserted or deasserted.
+#[bitfield]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Level {
+    Deassert = 0,
+    Assert = 1,
+}
+
 /// Represents a IOAPIC redirection table entry.
 #[bitfield]
 #[derive(Clone, Copy, Default, PartialEq, Eq)]
diff --git a/io_uring/Android.bp b/io_uring/Android.bp
index 35033d0..e19df03 100644
--- a/io_uring/Android.bp
+++ b/io_uring/Android.bp
@@ -1,4 +1,4 @@
-// This file is generated by cargo2android.py --run --device --tests --dependencies --global_defaults=crosvm_defaults --add_workspace.
+// This file is generated by cargo2android.py --run --device --tests --dependencies --global_defaults=crosvm_defaults.
 
 rust_defaults {
     name: "io_uring_defaults",
@@ -9,8 +9,8 @@
     auto_gen_config: true,
     edition: "2018",
     rustlibs: [
-        "libbase_rust",
         "liblibc",
+        "libsys_util",
         "libsyscall_defines",
         "libtempfile",
     ],
@@ -34,15 +34,14 @@
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
-        "libbase_rust",
         "liblibc",
+        "libsys_util",
         "libsyscall_defines",
     ],
 }
 
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
-//   ../base/src/lib.rs
 //   ../data_model/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
@@ -52,5 +51,5 @@
 //   libc-0.2.80 "default,std"
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   syn-1.0.53 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
 //   unicode-xid-0.2.1 "default"
diff --git a/io_uring/Cargo.toml b/io_uring/Cargo.toml
index ad3a815..1b633d3 100644
--- a/io_uring/Cargo.toml
+++ b/io_uring/Cargo.toml
@@ -7,7 +7,7 @@
 [dependencies]
 libc = "*"
 syscall_defines = { path = "../syscall_defines" }
-base = { path = "../base" }
+sys_util = { path = "../sys_util" }
 
 [dev-dependencies]
 tempfile = { path = "../tempfile" }
diff --git a/io_uring/src/uring.rs b/io_uring/src/uring.rs
index cc6ed0f..48461c3 100644
--- a/io_uring/src/uring.rs
+++ b/io_uring/src/uring.rs
@@ -14,7 +14,7 @@
 use std::ptr::null_mut;
 use std::sync::atomic::{AtomicU32, Ordering};
 
-use base::{MappedRegion, MemoryMapping, MemoryMappingBuilder, WatchingEvents};
+use sys_util::{MappedRegion, MemoryMapping, Protection, WatchingEvents};
 
 use crate::bindings::*;
 use crate::syscalls::*;
@@ -30,11 +30,11 @@
     /// The call to `io_uring_setup` failed with the given errno.
     Setup(libc::c_int),
     /// Failed to map the completion ring.
-    MappingCompleteRing(base::MmapError),
+    MappingCompleteRing(sys_util::MmapError),
     /// Failed to map the submit ring.
-    MappingSubmitRing(base::MmapError),
+    MappingSubmitRing(sys_util::MmapError),
     /// Failed to map submit entries.
-    MappingSubmitEntries(base::MmapError),
+    MappingSubmitEntries(sys_util::MmapError),
     /// Too many ops are already queued.
     NoSpace,
 }
@@ -78,7 +78,7 @@
 /// # use std::fs::File;
 /// # use std::os::unix::io::AsRawFd;
 /// # use std::path::Path;
-/// # use base::WatchingEvents;
+/// # use sys_util::WatchingEvents;
 /// # use io_uring::URingContext;
 /// let f = File::open(Path::new("/dev/zero")).unwrap();
 /// let mut uring = URingContext::new(16).unwrap();
@@ -121,40 +121,40 @@
             // Safe because we trust the kernel to set valid sizes in `io_uring_setup` and any error
             // is checked.
             let submit_ring = SubmitQueueState::new(
-                MemoryMappingBuilder::new(
+                MemoryMapping::from_fd_offset_protection_populate(
+                    &ring_file,
                     ring_params.sq_off.array as usize
                         + ring_params.sq_entries as usize * std::mem::size_of::<u32>(),
+                    u64::from(IORING_OFF_SQ_RING),
+                    Protection::read_write(),
+                    true,
                 )
-                .from_descriptor(&ring_file)
-                .offset(u64::from(IORING_OFF_SQ_RING))
-                .populate()
-                .build()
                 .map_err(Error::MappingSubmitRing)?,
                 &ring_params,
             );
 
             let num_sqe = ring_params.sq_entries as usize;
             let submit_queue_entries = SubmitQueueEntries {
-                mmap: MemoryMappingBuilder::new(
+                mmap: MemoryMapping::from_fd_offset_protection_populate(
+                    &ring_file,
                     ring_params.sq_entries as usize * std::mem::size_of::<io_uring_sqe>(),
+                    u64::from(IORING_OFF_SQES),
+                    Protection::read_write(),
+                    true,
                 )
-                .from_descriptor(&ring_file)
-                .offset(u64::from(IORING_OFF_SQES))
-                .populate()
-                .build()
                 .map_err(Error::MappingSubmitEntries)?,
                 len: num_sqe,
             };
 
             let complete_ring = CompleteQueueState::new(
-                MemoryMappingBuilder::new(
+                MemoryMapping::from_fd_offset_protection_populate(
+                    &ring_file,
                     ring_params.cq_off.cqes as usize
                         + ring_params.cq_entries as usize * std::mem::size_of::<io_uring_cqe>(),
+                    u64::from(IORING_OFF_CQ_RING),
+                    Protection::read_write(),
+                    true,
                 )
-                .from_descriptor(&ring_file)
-                .offset(u64::from(IORING_OFF_CQ_RING))
-                .populate()
-                .build()
                 .map_err(Error::MappingCompleteRing)?,
                 &ring_params,
             );
@@ -735,7 +735,7 @@
     use std::path::{Path, PathBuf};
     use std::time::Duration;
 
-    use base::PollContext;
+    use sys_util::PollContext;
     use tempfile::{tempfile, TempDir};
 
     use super::*;
diff --git a/kernel_cmdline/src/kernel_cmdline.rs b/kernel_cmdline/src/kernel_cmdline.rs
index d2777c7..c836e12 100644
--- a/kernel_cmdline/src/kernel_cmdline.rs
+++ b/kernel_cmdline/src/kernel_cmdline.rs
@@ -39,10 +39,7 @@
 pub type Result<T> = result::Result<T, Error>;
 
 fn valid_char(c: char) -> bool {
-    match c {
-        ' '..='~' => true,
-        _ => false,
-    }
+    matches!(c, ' '..='~')
 }
 
 fn valid_str(s: &str) -> Result<()> {
diff --git a/kernel_loader/Android.bp b/kernel_loader/Android.bp
index f9ec170..609785d 100644
--- a/kernel_loader/Android.bp
+++ b/kernel_loader/Android.bp
@@ -76,7 +76,7 @@
 //   rand_core-0.5.1 "alloc,getrandom,std"
 //   remove_dir_all-0.5.3
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   tempfile-3.1.0
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
diff --git a/kvm/Android.bp b/kvm/Android.bp
index addc6fa..ba3751e 100644
--- a/kvm/Android.bp
+++ b/kvm/Android.bp
@@ -118,6 +118,7 @@
 //   ../tempfile/src/lib.rs
 //   ../vm_memory/src/lib.rs
 //   async-trait-0.1.42
+//   base-0.1.0
 //   futures-0.3.8 "alloc,async-await,default,executor,futures-executor,std"
 //   futures-channel-0.3.8 "alloc,futures-sink,sink,std"
 //   futures-core-0.3.8 "alloc,std"
@@ -139,7 +140,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index 04b964f..c2ac848 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -13,12 +13,11 @@
 use std::mem::size_of;
 use std::ops::{Deref, DerefMut};
 use std::os::raw::*;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::ptr::copy_nonoverlapping;
 use std::sync::Arc;
 use sync::Mutex;
 
-use base::{AsRawDescriptor, RawDescriptor};
+use base::{AsRawDescriptor, FromRawDescriptor, RawDescriptor};
 use data_model::vec_with_array_field;
 
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
@@ -44,7 +43,7 @@
     Err(Error::last())
 }
 
-unsafe fn set_user_memory_region<F: AsRawFd>(
+unsafe fn set_user_memory_region<F: AsRawDescriptor>(
     fd: &F,
     slot: u32,
     read_only: bool,
@@ -103,7 +102,7 @@
         }
         // Safe because we verify that ret is valid and we own the fd.
         Ok(Kvm {
-            kvm: unsafe { File::from_raw_fd(ret) },
+            kvm: unsafe { File::from_raw_descriptor(ret) },
         })
     }
 
@@ -194,9 +193,9 @@
     }
 }
 
-impl AsRawFd for Kvm {
-    fn as_raw_fd(&self) -> RawFd {
-        self.kvm.as_raw_fd()
+impl AsRawDescriptor for Kvm {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.kvm.as_raw_descriptor()
     }
 }
 
@@ -271,7 +270,7 @@
         let ret = unsafe { ioctl(kvm, KVM_CREATE_VM()) };
         if ret >= 0 {
             // Safe because we verify the value of ret and we are the owners of the fd.
-            let vm_file = unsafe { File::from_raw_fd(ret) };
+            let vm_file = unsafe { File::from_raw_descriptor(ret) };
             guest_mem.with_regions(|index, guest_addr, size, host_addr, _| {
                 unsafe {
                     // Safe because the guest regions are guaranteed not to overlap.
@@ -712,7 +711,7 @@
                 IoeventAddress::Pio(p) => p as u64,
                 IoeventAddress::Mmio(m) => m,
             },
-            fd: evt.as_raw_fd(),
+            fd: evt.as_raw_descriptor(),
             flags,
             ..Default::default()
         };
@@ -742,8 +741,8 @@
     ) -> Result<()> {
         let irqfd = kvm_irqfd {
             flags: KVM_IRQFD_FLAG_RESAMPLE,
-            fd: evt.as_raw_fd() as u32,
-            resamplefd: resample_evt.as_raw_fd() as u32,
+            fd: evt.as_raw_descriptor() as u32,
+            resamplefd: resample_evt.as_raw_descriptor() as u32,
             gsi,
             ..Default::default()
         };
@@ -770,7 +769,7 @@
     ))]
     pub fn unregister_irqfd(&self, evt: &Event, gsi: u32) -> Result<()> {
         let irqfd = kvm_irqfd {
-            fd: evt.as_raw_fd() as u32,
+            fd: evt.as_raw_descriptor() as u32,
             gsi,
             flags: KVM_IRQFD_FLAG_DEASSIGN,
             ..Default::default()
@@ -837,9 +836,9 @@
     }
 }
 
-impl AsRawFd for Vm {
-    fn as_raw_fd(&self) -> RawFd {
-        self.vm.as_raw_fd()
+impl AsRawDescriptor for Vm {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.vm.as_raw_descriptor()
     }
 }
 
@@ -948,7 +947,7 @@
 
         // Wrap the vcpu now in case the following ? returns early. This is safe because we verified
         // the value of the fd and we own the fd.
-        let vcpu = unsafe { File::from_raw_fd(vcpu_fd) };
+        let vcpu = unsafe { File::from_raw_descriptor(vcpu_fd) };
 
         let run_mmap = MemoryMappingBuilder::new(run_mmap_size)
             .from_descriptor(&vcpu)
@@ -1458,12 +1457,6 @@
     }
 }
 
-impl AsRawFd for Vcpu {
-    fn as_raw_fd(&self) -> RawFd {
-        self.vcpu.as_raw_fd()
-    }
-}
-
 impl AsRawDescriptor for Vcpu {
     fn as_raw_descriptor(&self) -> RawDescriptor {
         self.vcpu.as_raw_descriptor()
@@ -1629,8 +1622,8 @@
     }
 }
 
-impl AsRawFd for RunnableVcpu {
-    fn as_raw_fd(&self) -> RawFd {
+impl AsRawDescriptor for RunnableVcpu {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
         self.vcpu.as_raw_descriptor()
     }
 }
@@ -1941,7 +1934,7 @@
         vm.register_irqfd_resample(&evtfd1, &evtfd2, 4).unwrap();
         vm.unregister_irqfd(&evtfd1, 4).unwrap();
         // Ensures the ioctl is actually reading the resamplefd.
-        vm.register_irqfd_resample(&evtfd1, unsafe { &Event::from_raw_fd(-1) }, 4)
+        vm.register_irqfd_resample(&evtfd1, unsafe { &Event::from_raw_descriptor(-1) }, 4)
             .unwrap_err();
     }
 
diff --git a/kvm_sys/Android.bp b/kvm_sys/Android.bp
index b83c3c1..d7c0801 100644
--- a/kvm_sys/Android.bp
+++ b/kvm_sys/Android.bp
@@ -68,14 +68,31 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/kvm_sys/src/aarch64/bindings.rs b/kvm_sys/src/aarch64/bindings.rs
index 0dd7f5a..7e5303a 100644
--- a/kvm_sys/src/aarch64/bindings.rs
+++ b/kvm_sys/src/aarch64/bindings.rs
@@ -2,15 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/* automatically generated by rust-bindgen */
+/*
+ * automatically generated by bindgen 0.49.2.
+ * From chromeos-linux v5.4 include/linux/kvm.h
+ * $ cd /path/to/kernel/v5.4/
+ * $ make headers_install ARCH=arm64 INSTALL_HDR_PATH=arm64_v5_4_headers
+ * $ cd arm64_v5_4_headers
+ * $ bindgen --with-derive-default -o bindings.rs include/linux/kvm.h -- -Iinclude
+ */
 
 #[repr(C)]
 #[derive(Default)]
-pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
 impl<T> __IncompleteArrayField<T> {
     #[inline]
     pub fn new() -> Self {
-        __IncompleteArrayField(::std::marker::PhantomData)
+        __IncompleteArrayField(::std::marker::PhantomData, [])
     }
     #[inline]
     pub unsafe fn as_ptr(&self) -> *const T {
@@ -30,7 +37,7 @@
     }
 }
 impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
-    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         fmt.write_str("__IncompleteArrayField")
     }
 }
@@ -40,455 +47,634 @@
         Self::new()
     }
 }
-impl<T> ::std::marker::Copy for __IncompleteArrayField<T> {}
-pub const __BITS_PER_LONG: ::std::os::raw::c_uint = 64;
-pub const __FD_SETSIZE: ::std::os::raw::c_uint = 1024;
-pub const _IOC_NRBITS: ::std::os::raw::c_uint = 8;
-pub const _IOC_TYPEBITS: ::std::os::raw::c_uint = 8;
-pub const _IOC_SIZEBITS: ::std::os::raw::c_uint = 14;
-pub const _IOC_DIRBITS: ::std::os::raw::c_uint = 2;
-pub const _IOC_NRMASK: ::std::os::raw::c_uint = 255;
-pub const _IOC_TYPEMASK: ::std::os::raw::c_uint = 255;
-pub const _IOC_SIZEMASK: ::std::os::raw::c_uint = 16383;
-pub const _IOC_DIRMASK: ::std::os::raw::c_uint = 3;
-pub const _IOC_NRSHIFT: ::std::os::raw::c_uint = 0;
-pub const _IOC_TYPESHIFT: ::std::os::raw::c_uint = 8;
-pub const _IOC_SIZESHIFT: ::std::os::raw::c_uint = 16;
-pub const _IOC_DIRSHIFT: ::std::os::raw::c_uint = 30;
-pub const _IOC_NONE: ::std::os::raw::c_uint = 0;
-pub const _IOC_WRITE: ::std::os::raw::c_uint = 1;
-pub const _IOC_READ: ::std::os::raw::c_uint = 2;
-pub const IOC_IN: ::std::os::raw::c_uint = 1073741824;
-pub const IOC_OUT: ::std::os::raw::c_uint = 2147483648;
-pub const IOC_INOUT: ::std::os::raw::c_uint = 3221225472;
-pub const IOCSIZE_MASK: ::std::os::raw::c_uint = 1073676288;
-pub const IOCSIZE_SHIFT: ::std::os::raw::c_uint = 16;
-pub const KVM_SPSR_EL1: ::std::os::raw::c_uint = 0;
-pub const KVM_SPSR_SVC: ::std::os::raw::c_uint = 0;
-pub const KVM_SPSR_ABT: ::std::os::raw::c_uint = 1;
-pub const KVM_SPSR_UND: ::std::os::raw::c_uint = 2;
-pub const KVM_SPSR_IRQ: ::std::os::raw::c_uint = 3;
-pub const KVM_SPSR_FIQ: ::std::os::raw::c_uint = 4;
-pub const KVM_NR_SPSR: ::std::os::raw::c_uint = 5;
-pub const PSCI_0_2_FN_BASE: ::std::os::raw::c_uint = 2214592512;
-pub const PSCI_0_2_64BIT: ::std::os::raw::c_uint = 1073741824;
-pub const PSCI_0_2_FN64_BASE: ::std::os::raw::c_uint = 3288334336;
-pub const PSCI_0_2_POWER_STATE_ID_MASK: ::std::os::raw::c_uint = 65535;
-pub const PSCI_0_2_POWER_STATE_ID_SHIFT: ::std::os::raw::c_uint = 0;
-pub const PSCI_0_2_POWER_STATE_TYPE_SHIFT: ::std::os::raw::c_uint = 16;
-pub const PSCI_0_2_POWER_STATE_TYPE_MASK: ::std::os::raw::c_uint = 65536;
-pub const PSCI_0_2_POWER_STATE_AFFL_SHIFT: ::std::os::raw::c_uint = 24;
-pub const PSCI_0_2_POWER_STATE_AFFL_MASK: ::std::os::raw::c_uint = 50331648;
-pub const PSCI_1_0_EXT_POWER_STATE_ID_MASK: ::std::os::raw::c_uint = 268435455;
-pub const PSCI_1_0_EXT_POWER_STATE_ID_SHIFT: ::std::os::raw::c_uint = 0;
-pub const PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT: ::std::os::raw::c_uint = 30;
-pub const PSCI_1_0_EXT_POWER_STATE_TYPE_MASK: ::std::os::raw::c_uint = 1073741824;
-pub const PSCI_0_2_AFFINITY_LEVEL_ON: ::std::os::raw::c_uint = 0;
-pub const PSCI_0_2_AFFINITY_LEVEL_OFF: ::std::os::raw::c_uint = 1;
-pub const PSCI_0_2_AFFINITY_LEVEL_ON_PENDING: ::std::os::raw::c_uint = 2;
-pub const PSCI_0_2_TOS_UP_MIGRATE: ::std::os::raw::c_uint = 0;
-pub const PSCI_0_2_TOS_UP_NO_MIGRATE: ::std::os::raw::c_uint = 1;
-pub const PSCI_0_2_TOS_MP: ::std::os::raw::c_uint = 2;
-pub const PSCI_VERSION_MAJOR_SHIFT: ::std::os::raw::c_uint = 16;
-pub const PSCI_VERSION_MINOR_MASK: ::std::os::raw::c_uint = 65535;
-pub const PSCI_VERSION_MAJOR_MASK: ::std::os::raw::c_int = -65536;
-pub const PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT: ::std::os::raw::c_uint = 1;
-pub const PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK: ::std::os::raw::c_uint = 2;
-pub const PSCI_RET_SUCCESS: ::std::os::raw::c_uint = 0;
-pub const PSCI_RET_NOT_SUPPORTED: ::std::os::raw::c_int = -1;
-pub const PSCI_RET_INVALID_PARAMS: ::std::os::raw::c_int = -2;
-pub const PSCI_RET_DENIED: ::std::os::raw::c_int = -3;
-pub const PSCI_RET_ALREADY_ON: ::std::os::raw::c_int = -4;
-pub const PSCI_RET_ON_PENDING: ::std::os::raw::c_int = -5;
-pub const PSCI_RET_INTERNAL_FAILURE: ::std::os::raw::c_int = -6;
-pub const PSCI_RET_NOT_PRESENT: ::std::os::raw::c_int = -7;
-pub const PSCI_RET_DISABLED: ::std::os::raw::c_int = -8;
-pub const PSCI_RET_INVALID_ADDRESS: ::std::os::raw::c_int = -9;
-pub const HWCAP_FP: ::std::os::raw::c_uint = 1;
-pub const HWCAP_ASIMD: ::std::os::raw::c_uint = 2;
-pub const HWCAP_EVTSTRM: ::std::os::raw::c_uint = 4;
-pub const HWCAP_AES: ::std::os::raw::c_uint = 8;
-pub const HWCAP_PMULL: ::std::os::raw::c_uint = 16;
-pub const HWCAP_SHA1: ::std::os::raw::c_uint = 32;
-pub const HWCAP_SHA2: ::std::os::raw::c_uint = 64;
-pub const HWCAP_CRC32: ::std::os::raw::c_uint = 128;
-pub const HWCAP_ATOMICS: ::std::os::raw::c_uint = 256;
-pub const PSR_MODE_EL0t: ::std::os::raw::c_uint = 0;
-pub const PSR_MODE_EL1t: ::std::os::raw::c_uint = 4;
-pub const PSR_MODE_EL1h: ::std::os::raw::c_uint = 5;
-pub const PSR_MODE_EL2t: ::std::os::raw::c_uint = 8;
-pub const PSR_MODE_EL2h: ::std::os::raw::c_uint = 9;
-pub const PSR_MODE_EL3t: ::std::os::raw::c_uint = 12;
-pub const PSR_MODE_EL3h: ::std::os::raw::c_uint = 13;
-pub const PSR_MODE_MASK: ::std::os::raw::c_uint = 15;
-pub const PSR_MODE32_BIT: ::std::os::raw::c_uint = 16;
-pub const PSR_F_BIT: ::std::os::raw::c_uint = 64;
-pub const PSR_I_BIT: ::std::os::raw::c_uint = 128;
-pub const PSR_A_BIT: ::std::os::raw::c_uint = 256;
-pub const PSR_D_BIT: ::std::os::raw::c_uint = 512;
-pub const PSR_PAN_BIT: ::std::os::raw::c_uint = 4194304;
-pub const PSR_Q_BIT: ::std::os::raw::c_uint = 134217728;
-pub const PSR_V_BIT: ::std::os::raw::c_uint = 268435456;
-pub const PSR_C_BIT: ::std::os::raw::c_uint = 536870912;
-pub const PSR_Z_BIT: ::std::os::raw::c_uint = 1073741824;
-pub const PSR_N_BIT: ::std::os::raw::c_uint = 2147483648;
-pub const PSR_f: ::std::os::raw::c_uint = 4278190080;
-pub const PSR_s: ::std::os::raw::c_uint = 16711680;
-pub const PSR_x: ::std::os::raw::c_uint = 65280;
-pub const PSR_c: ::std::os::raw::c_uint = 255;
-pub const KVM_ARM_TARGET_AEM_V8: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_TARGET_FOUNDATION_V8: ::std::os::raw::c_uint = 1;
-pub const KVM_ARM_TARGET_CORTEX_A57: ::std::os::raw::c_uint = 2;
-pub const KVM_ARM_TARGET_XGENE_POTENZA: ::std::os::raw::c_uint = 3;
-pub const KVM_ARM_TARGET_CORTEX_A53: ::std::os::raw::c_uint = 4;
-pub const KVM_ARM_TARGET_GENERIC_V8: ::std::os::raw::c_uint = 5;
-pub const KVM_ARM_NUM_TARGETS: ::std::os::raw::c_uint = 6;
-pub const KVM_ARM_DEVICE_TYPE_SHIFT: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_DEVICE_TYPE_MASK: ::std::os::raw::c_uint = 65535;
-pub const KVM_ARM_DEVICE_ID_SHIFT: ::std::os::raw::c_uint = 16;
-pub const KVM_ARM_DEVICE_ID_MASK: ::std::os::raw::c_uint = 4294901760;
-pub const KVM_ARM_DEVICE_VGIC_V2: ::std::os::raw::c_uint = 0;
-pub const KVM_VGIC_V2_ADDR_TYPE_DIST: ::std::os::raw::c_uint = 0;
-pub const KVM_VGIC_V2_ADDR_TYPE_CPU: ::std::os::raw::c_uint = 1;
-pub const KVM_VGIC_V2_DIST_SIZE: ::std::os::raw::c_uint = 4096;
-pub const KVM_VGIC_V2_CPU_SIZE: ::std::os::raw::c_uint = 8192;
-pub const KVM_VGIC_V3_ADDR_TYPE_DIST: ::std::os::raw::c_uint = 2;
-pub const KVM_VGIC_V3_ADDR_TYPE_REDIST: ::std::os::raw::c_uint = 3;
-pub const KVM_ARM_VCPU_POWER_OFF: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_VCPU_EL1_32BIT: ::std::os::raw::c_uint = 1;
-pub const KVM_ARM_VCPU_PSCI_0_2: ::std::os::raw::c_uint = 2;
-pub const KVM_ARM_VCPU_PMU_V3: ::std::os::raw::c_uint = 3;
-pub const KVM_ARM_VCPU_PMU_V3_CTRL: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_VCPU_PMU_V3_IRQ: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_VCPU_PMU_V3_INIT: ::std::os::raw::c_uint = 1;
-pub const KVM_ARM_MAX_DBG_REGS: ::std::os::raw::c_uint = 16;
-pub const KVM_GUESTDBG_USE_SW_BP: ::std::os::raw::c_uint = 65536;
-pub const KVM_GUESTDBG_USE_HW: ::std::os::raw::c_uint = 131072;
-pub const KVM_REG_ARM_COPROC_MASK: ::std::os::raw::c_uint = 268369920;
-pub const KVM_REG_ARM_COPROC_SHIFT: ::std::os::raw::c_uint = 16;
-pub const KVM_REG_ARM_CORE: ::std::os::raw::c_uint = 1048576;
-pub const KVM_REG_ARM_DEMUX: ::std::os::raw::c_uint = 1114112;
-pub const KVM_REG_ARM_DEMUX_ID_MASK: ::std::os::raw::c_uint = 65280;
-pub const KVM_REG_ARM_DEMUX_ID_SHIFT: ::std::os::raw::c_uint = 8;
-pub const KVM_REG_ARM_DEMUX_ID_CCSIDR: ::std::os::raw::c_uint = 0;
-pub const KVM_REG_ARM_DEMUX_VAL_MASK: ::std::os::raw::c_uint = 255;
-pub const KVM_REG_ARM_DEMUX_VAL_SHIFT: ::std::os::raw::c_uint = 0;
-pub const KVM_REG_ARM64_SYSREG: ::std::os::raw::c_uint = 1245184;
-pub const KVM_REG_ARM64_SYSREG_OP0_MASK: ::std::os::raw::c_uint = 49152;
-pub const KVM_REG_ARM64_SYSREG_OP0_SHIFT: ::std::os::raw::c_uint = 14;
-pub const KVM_REG_ARM64_SYSREG_OP1_MASK: ::std::os::raw::c_uint = 14336;
-pub const KVM_REG_ARM64_SYSREG_OP1_SHIFT: ::std::os::raw::c_uint = 11;
-pub const KVM_REG_ARM64_SYSREG_CRN_MASK: ::std::os::raw::c_uint = 1920;
-pub const KVM_REG_ARM64_SYSREG_CRN_SHIFT: ::std::os::raw::c_uint = 7;
-pub const KVM_REG_ARM64_SYSREG_CRM_MASK: ::std::os::raw::c_uint = 120;
-pub const KVM_REG_ARM64_SYSREG_CRM_SHIFT: ::std::os::raw::c_uint = 3;
-pub const KVM_REG_ARM64_SYSREG_OP2_MASK: ::std::os::raw::c_uint = 7;
-pub const KVM_REG_ARM64_SYSREG_OP2_SHIFT: ::std::os::raw::c_uint = 0;
-pub const KVM_DEV_ARM_VGIC_GRP_ADDR: ::std::os::raw::c_uint = 0;
-pub const KVM_DEV_ARM_VGIC_GRP_DIST_REGS: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_ARM_VGIC_GRP_CPU_REGS: ::std::os::raw::c_uint = 2;
-pub const KVM_DEV_ARM_VGIC_CPUID_SHIFT: ::std::os::raw::c_uint = 32;
-pub const KVM_DEV_ARM_VGIC_CPUID_MASK: ::std::os::raw::c_ulonglong = 1095216660480;
-pub const KVM_DEV_ARM_VGIC_OFFSET_SHIFT: ::std::os::raw::c_uint = 0;
-pub const KVM_DEV_ARM_VGIC_OFFSET_MASK: ::std::os::raw::c_uint = 4294967295;
-pub const KVM_DEV_ARM_VGIC_GRP_NR_IRQS: ::std::os::raw::c_uint = 3;
-pub const KVM_DEV_ARM_VGIC_GRP_CTRL: ::std::os::raw::c_uint = 4;
-pub const KVM_DEV_ARM_VGIC_CTRL_INIT: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_IRQ_TYPE_SHIFT: ::std::os::raw::c_uint = 24;
-pub const KVM_ARM_IRQ_TYPE_MASK: ::std::os::raw::c_uint = 255;
-pub const KVM_ARM_IRQ_VCPU_SHIFT: ::std::os::raw::c_uint = 16;
-pub const KVM_ARM_IRQ_VCPU_MASK: ::std::os::raw::c_uint = 255;
-pub const KVM_ARM_IRQ_NUM_SHIFT: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_IRQ_NUM_MASK: ::std::os::raw::c_uint = 65535;
-pub const KVM_ARM_IRQ_TYPE_CPU: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_IRQ_TYPE_SPI: ::std::os::raw::c_uint = 1;
-pub const KVM_ARM_IRQ_TYPE_PPI: ::std::os::raw::c_uint = 2;
-pub const KVM_ARM_IRQ_CPU_IRQ: ::std::os::raw::c_uint = 0;
-pub const KVM_ARM_IRQ_CPU_FIQ: ::std::os::raw::c_uint = 1;
-pub const KVM_ARM_IRQ_GIC_MAX: ::std::os::raw::c_uint = 127;
-pub const KVM_NR_IRQCHIPS: ::std::os::raw::c_uint = 1;
-pub const KVM_PSCI_FN_BASE: ::std::os::raw::c_uint = 2512501342;
-pub const KVM_PSCI_RET_SUCCESS: ::std::os::raw::c_uint = 0;
-pub const KVM_PSCI_RET_NI: ::std::os::raw::c_int = -1;
-pub const KVM_PSCI_RET_INVAL: ::std::os::raw::c_int = -2;
-pub const KVM_PSCI_RET_DENIED: ::std::os::raw::c_int = -3;
-pub const KVM_API_VERSION: ::std::os::raw::c_uint = 12;
-pub const KVM_TRC_SHIFT: ::std::os::raw::c_uint = 16;
-pub const KVM_TRC_ENTRYEXIT: ::std::os::raw::c_uint = 65536;
-pub const KVM_TRC_HANDLER: ::std::os::raw::c_uint = 131072;
-pub const KVM_TRC_VMENTRY: ::std::os::raw::c_uint = 65537;
-pub const KVM_TRC_VMEXIT: ::std::os::raw::c_uint = 65538;
-pub const KVM_TRC_PAGE_FAULT: ::std::os::raw::c_uint = 131073;
-pub const KVM_TRC_HEAD_SIZE: ::std::os::raw::c_uint = 12;
-pub const KVM_TRC_CYCLE_SIZE: ::std::os::raw::c_uint = 8;
-pub const KVM_TRC_EXTRA_MAX: ::std::os::raw::c_uint = 7;
-pub const KVM_TRC_INJ_VIRQ: ::std::os::raw::c_uint = 131074;
-pub const KVM_TRC_REDELIVER_EVT: ::std::os::raw::c_uint = 131075;
-pub const KVM_TRC_PEND_INTR: ::std::os::raw::c_uint = 131076;
-pub const KVM_TRC_IO_READ: ::std::os::raw::c_uint = 131077;
-pub const KVM_TRC_IO_WRITE: ::std::os::raw::c_uint = 131078;
-pub const KVM_TRC_CR_READ: ::std::os::raw::c_uint = 131079;
-pub const KVM_TRC_CR_WRITE: ::std::os::raw::c_uint = 131080;
-pub const KVM_TRC_DR_READ: ::std::os::raw::c_uint = 131081;
-pub const KVM_TRC_DR_WRITE: ::std::os::raw::c_uint = 131082;
-pub const KVM_TRC_MSR_READ: ::std::os::raw::c_uint = 131083;
-pub const KVM_TRC_MSR_WRITE: ::std::os::raw::c_uint = 131084;
-pub const KVM_TRC_CPUID: ::std::os::raw::c_uint = 131085;
-pub const KVM_TRC_INTR: ::std::os::raw::c_uint = 131086;
-pub const KVM_TRC_NMI: ::std::os::raw::c_uint = 131087;
-pub const KVM_TRC_VMMCALL: ::std::os::raw::c_uint = 131088;
-pub const KVM_TRC_HLT: ::std::os::raw::c_uint = 131089;
-pub const KVM_TRC_CLTS: ::std::os::raw::c_uint = 131090;
-pub const KVM_TRC_LMSW: ::std::os::raw::c_uint = 131091;
-pub const KVM_TRC_APIC_ACCESS: ::std::os::raw::c_uint = 131092;
-pub const KVM_TRC_TDP_FAULT: ::std::os::raw::c_uint = 131093;
-pub const KVM_TRC_GTLB_WRITE: ::std::os::raw::c_uint = 131094;
-pub const KVM_TRC_STLB_WRITE: ::std::os::raw::c_uint = 131095;
-pub const KVM_TRC_STLB_INVAL: ::std::os::raw::c_uint = 131096;
-pub const KVM_TRC_PPC_INSTR: ::std::os::raw::c_uint = 131097;
-pub const KVM_MEM_LOG_DIRTY_PAGES: ::std::os::raw::c_uint = 1;
-pub const KVM_MEM_READONLY: ::std::os::raw::c_uint = 2;
-pub const KVM_PIT_SPEAKER_DUMMY: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_GET_SKEYS_NONE: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_SKEYS_MAX: ::std::os::raw::c_uint = 1048576;
-pub const KVM_EXIT_UNKNOWN: ::std::os::raw::c_uint = 0;
-pub const KVM_EXIT_EXCEPTION: ::std::os::raw::c_uint = 1;
-pub const KVM_EXIT_IO: ::std::os::raw::c_uint = 2;
-pub const KVM_EXIT_HYPERCALL: ::std::os::raw::c_uint = 3;
-pub const KVM_EXIT_DEBUG: ::std::os::raw::c_uint = 4;
-pub const KVM_EXIT_HLT: ::std::os::raw::c_uint = 5;
-pub const KVM_EXIT_MMIO: ::std::os::raw::c_uint = 6;
-pub const KVM_EXIT_IRQ_WINDOW_OPEN: ::std::os::raw::c_uint = 7;
-pub const KVM_EXIT_SHUTDOWN: ::std::os::raw::c_uint = 8;
-pub const KVM_EXIT_FAIL_ENTRY: ::std::os::raw::c_uint = 9;
-pub const KVM_EXIT_INTR: ::std::os::raw::c_uint = 10;
-pub const KVM_EXIT_SET_TPR: ::std::os::raw::c_uint = 11;
-pub const KVM_EXIT_TPR_ACCESS: ::std::os::raw::c_uint = 12;
-pub const KVM_EXIT_S390_SIEIC: ::std::os::raw::c_uint = 13;
-pub const KVM_EXIT_S390_RESET: ::std::os::raw::c_uint = 14;
-pub const KVM_EXIT_DCR: ::std::os::raw::c_uint = 15;
-pub const KVM_EXIT_NMI: ::std::os::raw::c_uint = 16;
-pub const KVM_EXIT_INTERNAL_ERROR: ::std::os::raw::c_uint = 17;
-pub const KVM_EXIT_OSI: ::std::os::raw::c_uint = 18;
-pub const KVM_EXIT_PAPR_HCALL: ::std::os::raw::c_uint = 19;
-pub const KVM_EXIT_S390_UCONTROL: ::std::os::raw::c_uint = 20;
-pub const KVM_EXIT_WATCHDOG: ::std::os::raw::c_uint = 21;
-pub const KVM_EXIT_S390_TSCH: ::std::os::raw::c_uint = 22;
-pub const KVM_EXIT_EPR: ::std::os::raw::c_uint = 23;
-pub const KVM_EXIT_SYSTEM_EVENT: ::std::os::raw::c_uint = 24;
-pub const KVM_EXIT_S390_STSI: ::std::os::raw::c_uint = 25;
-pub const KVM_EXIT_IOAPIC_EOI: ::std::os::raw::c_uint = 26;
-pub const KVM_EXIT_HYPERV: ::std::os::raw::c_uint = 27;
-pub const KVM_INTERNAL_ERROR_EMULATION: ::std::os::raw::c_uint = 1;
-pub const KVM_INTERNAL_ERROR_SIMUL_EX: ::std::os::raw::c_uint = 2;
-pub const KVM_INTERNAL_ERROR_DELIVERY_EV: ::std::os::raw::c_uint = 3;
-pub const KVM_EXIT_IO_IN: ::std::os::raw::c_uint = 0;
-pub const KVM_EXIT_IO_OUT: ::std::os::raw::c_uint = 1;
-pub const KVM_EXIT_HYPERV_SYNIC: ::std::os::raw::c_uint = 1;
-pub const KVM_EXIT_HYPERV_HCALL: ::std::os::raw::c_uint = 2;
-pub const KVM_S390_RESET_POR: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_RESET_CLEAR: ::std::os::raw::c_uint = 2;
-pub const KVM_S390_RESET_SUBSYSTEM: ::std::os::raw::c_uint = 4;
-pub const KVM_S390_RESET_CPU_INIT: ::std::os::raw::c_uint = 8;
-pub const KVM_S390_RESET_IPL: ::std::os::raw::c_uint = 16;
-pub const KVM_SYSTEM_EVENT_SHUTDOWN: ::std::os::raw::c_uint = 1;
-pub const KVM_SYSTEM_EVENT_RESET: ::std::os::raw::c_uint = 2;
-pub const KVM_SYSTEM_EVENT_CRASH: ::std::os::raw::c_uint = 3;
-pub const KVM_S390_MEMOP_LOGICAL_READ: ::std::os::raw::c_uint = 0;
-pub const KVM_S390_MEMOP_LOGICAL_WRITE: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_MEMOP_F_CHECK_ONLY: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_MEMOP_F_INJECT_EXCEPTION: ::std::os::raw::c_uint = 2;
-pub const KVM_MP_STATE_RUNNABLE: ::std::os::raw::c_uint = 0;
-pub const KVM_MP_STATE_UNINITIALIZED: ::std::os::raw::c_uint = 1;
-pub const KVM_MP_STATE_INIT_RECEIVED: ::std::os::raw::c_uint = 2;
-pub const KVM_MP_STATE_HALTED: ::std::os::raw::c_uint = 3;
-pub const KVM_MP_STATE_SIPI_RECEIVED: ::std::os::raw::c_uint = 4;
-pub const KVM_MP_STATE_STOPPED: ::std::os::raw::c_uint = 5;
-pub const KVM_MP_STATE_CHECK_STOP: ::std::os::raw::c_uint = 6;
-pub const KVM_MP_STATE_OPERATING: ::std::os::raw::c_uint = 7;
-pub const KVM_MP_STATE_LOAD: ::std::os::raw::c_uint = 8;
-pub const KVM_S390_SIGP_STOP: ::std::os::raw::c_uint = 4294836224;
-pub const KVM_S390_PROGRAM_INT: ::std::os::raw::c_uint = 4294836225;
-pub const KVM_S390_SIGP_SET_PREFIX: ::std::os::raw::c_uint = 4294836226;
-pub const KVM_S390_RESTART: ::std::os::raw::c_uint = 4294836227;
-pub const KVM_S390_INT_PFAULT_INIT: ::std::os::raw::c_uint = 4294836228;
-pub const KVM_S390_INT_PFAULT_DONE: ::std::os::raw::c_uint = 4294836229;
-pub const KVM_S390_MCHK: ::std::os::raw::c_uint = 4294840320;
-pub const KVM_S390_INT_CLOCK_COMP: ::std::os::raw::c_uint = 4294905860;
-pub const KVM_S390_INT_CPU_TIMER: ::std::os::raw::c_uint = 4294905861;
-pub const KVM_S390_INT_VIRTIO: ::std::os::raw::c_uint = 4294911491;
-pub const KVM_S390_INT_SERVICE: ::std::os::raw::c_uint = 4294910977;
-pub const KVM_S390_INT_EMERGENCY: ::std::os::raw::c_uint = 4294906369;
-pub const KVM_S390_INT_EXTERNAL_CALL: ::std::os::raw::c_uint = 4294906370;
-pub const KVM_S390_INT_IO_MIN: ::std::os::raw::c_uint = 0;
-pub const KVM_S390_INT_IO_MAX: ::std::os::raw::c_uint = 4294836223;
-pub const KVM_S390_INT_IO_AI_MASK: ::std::os::raw::c_uint = 67108864;
-pub const KVM_S390_STOP_FLAG_STORE_STATUS: ::std::os::raw::c_uint = 1;
-pub const KVM_GUESTDBG_ENABLE: ::std::os::raw::c_uint = 1;
-pub const KVM_GUESTDBG_SINGLESTEP: ::std::os::raw::c_uint = 2;
-pub const KVM_PPC_PAGE_SIZES_MAX_SZ: ::std::os::raw::c_uint = 8;
-pub const KVM_PPC_PAGE_SIZES_REAL: ::std::os::raw::c_uint = 1;
-pub const KVM_PPC_1T_SEGMENTS: ::std::os::raw::c_uint = 2;
-pub const KVM_PPC_PVINFO_FLAGS_EV_IDLE: ::std::os::raw::c_uint = 1;
-pub const KVMIO: ::std::os::raw::c_uint = 174;
-pub const KVM_VM_S390_UCONTROL: ::std::os::raw::c_uint = 1;
-pub const KVM_VM_PPC_HV: ::std::os::raw::c_uint = 1;
-pub const KVM_VM_PPC_PR: ::std::os::raw::c_uint = 2;
-pub const KVM_S390_SIE_PAGE_OFFSET: ::std::os::raw::c_uint = 1;
-pub const KVM_CAP_IRQCHIP: ::std::os::raw::c_uint = 0;
-pub const KVM_CAP_HLT: ::std::os::raw::c_uint = 1;
-pub const KVM_CAP_MMU_SHADOW_CACHE_CONTROL: ::std::os::raw::c_uint = 2;
-pub const KVM_CAP_USER_MEMORY: ::std::os::raw::c_uint = 3;
-pub const KVM_CAP_SET_TSS_ADDR: ::std::os::raw::c_uint = 4;
-pub const KVM_CAP_VAPIC: ::std::os::raw::c_uint = 6;
-pub const KVM_CAP_EXT_CPUID: ::std::os::raw::c_uint = 7;
-pub const KVM_CAP_CLOCKSOURCE: ::std::os::raw::c_uint = 8;
-pub const KVM_CAP_NR_VCPUS: ::std::os::raw::c_uint = 9;
-pub const KVM_CAP_NR_MEMSLOTS: ::std::os::raw::c_uint = 10;
-pub const KVM_CAP_PIT: ::std::os::raw::c_uint = 11;
-pub const KVM_CAP_NOP_IO_DELAY: ::std::os::raw::c_uint = 12;
-pub const KVM_CAP_PV_MMU: ::std::os::raw::c_uint = 13;
-pub const KVM_CAP_MP_STATE: ::std::os::raw::c_uint = 14;
-pub const KVM_CAP_COALESCED_MMIO: ::std::os::raw::c_uint = 15;
-pub const KVM_CAP_SYNC_MMU: ::std::os::raw::c_uint = 16;
-pub const KVM_CAP_IOMMU: ::std::os::raw::c_uint = 18;
-pub const KVM_CAP_DESTROY_MEMORY_REGION_WORKS: ::std::os::raw::c_uint = 21;
-pub const KVM_CAP_USER_NMI: ::std::os::raw::c_uint = 22;
-pub const KVM_CAP_SET_GUEST_DEBUG: ::std::os::raw::c_uint = 23;
-pub const KVM_CAP_IRQ_ROUTING: ::std::os::raw::c_uint = 25;
-pub const KVM_CAP_IRQ_INJECT_STATUS: ::std::os::raw::c_uint = 26;
-pub const KVM_CAP_ASSIGN_DEV_IRQ: ::std::os::raw::c_uint = 29;
-pub const KVM_CAP_JOIN_MEMORY_REGIONS_WORKS: ::std::os::raw::c_uint = 30;
-pub const KVM_CAP_IRQFD: ::std::os::raw::c_uint = 32;
-pub const KVM_CAP_SET_BOOT_CPU_ID: ::std::os::raw::c_uint = 34;
-pub const KVM_CAP_IOEVENTFD: ::std::os::raw::c_uint = 36;
-pub const KVM_CAP_SET_IDENTITY_MAP_ADDR: ::std::os::raw::c_uint = 37;
-pub const KVM_CAP_ADJUST_CLOCK: ::std::os::raw::c_uint = 39;
-pub const KVM_CAP_INTERNAL_ERROR_DATA: ::std::os::raw::c_uint = 40;
-pub const KVM_CAP_S390_PSW: ::std::os::raw::c_uint = 42;
-pub const KVM_CAP_PPC_SEGSTATE: ::std::os::raw::c_uint = 43;
-pub const KVM_CAP_HYPERV: ::std::os::raw::c_uint = 44;
-pub const KVM_CAP_HYPERV_VAPIC: ::std::os::raw::c_uint = 45;
-pub const KVM_CAP_HYPERV_SPIN: ::std::os::raw::c_uint = 46;
-pub const KVM_CAP_PCI_SEGMENT: ::std::os::raw::c_uint = 47;
-pub const KVM_CAP_PPC_PAIRED_SINGLES: ::std::os::raw::c_uint = 48;
-pub const KVM_CAP_INTR_SHADOW: ::std::os::raw::c_uint = 49;
-pub const KVM_CAP_X86_ROBUST_SINGLESTEP: ::std::os::raw::c_uint = 51;
-pub const KVM_CAP_PPC_OSI: ::std::os::raw::c_uint = 52;
-pub const KVM_CAP_PPC_UNSET_IRQ: ::std::os::raw::c_uint = 53;
-pub const KVM_CAP_ENABLE_CAP: ::std::os::raw::c_uint = 54;
-pub const KVM_CAP_PPC_GET_PVINFO: ::std::os::raw::c_uint = 57;
-pub const KVM_CAP_PPC_IRQ_LEVEL: ::std::os::raw::c_uint = 58;
-pub const KVM_CAP_ASYNC_PF: ::std::os::raw::c_uint = 59;
-pub const KVM_CAP_TSC_CONTROL: ::std::os::raw::c_uint = 60;
-pub const KVM_CAP_GET_TSC_KHZ: ::std::os::raw::c_uint = 61;
-pub const KVM_CAP_PPC_BOOKE_SREGS: ::std::os::raw::c_uint = 62;
-pub const KVM_CAP_SPAPR_TCE: ::std::os::raw::c_uint = 63;
-pub const KVM_CAP_PPC_SMT: ::std::os::raw::c_uint = 64;
-pub const KVM_CAP_PPC_RMA: ::std::os::raw::c_uint = 65;
-pub const KVM_CAP_MAX_VCPUS: ::std::os::raw::c_uint = 66;
-pub const KVM_CAP_PPC_HIOR: ::std::os::raw::c_uint = 67;
-pub const KVM_CAP_PPC_PAPR: ::std::os::raw::c_uint = 68;
-pub const KVM_CAP_SW_TLB: ::std::os::raw::c_uint = 69;
-pub const KVM_CAP_ONE_REG: ::std::os::raw::c_uint = 70;
-pub const KVM_CAP_S390_GMAP: ::std::os::raw::c_uint = 71;
-pub const KVM_CAP_TSC_DEADLINE_TIMER: ::std::os::raw::c_uint = 72;
-pub const KVM_CAP_S390_UCONTROL: ::std::os::raw::c_uint = 73;
-pub const KVM_CAP_SYNC_REGS: ::std::os::raw::c_uint = 74;
-pub const KVM_CAP_PCI_2_3: ::std::os::raw::c_uint = 75;
-pub const KVM_CAP_KVMCLOCK_CTRL: ::std::os::raw::c_uint = 76;
-pub const KVM_CAP_SIGNAL_MSI: ::std::os::raw::c_uint = 77;
-pub const KVM_CAP_PPC_GET_SMMU_INFO: ::std::os::raw::c_uint = 78;
-pub const KVM_CAP_S390_COW: ::std::os::raw::c_uint = 79;
-pub const KVM_CAP_PPC_ALLOC_HTAB: ::std::os::raw::c_uint = 80;
-pub const KVM_CAP_READONLY_MEM: ::std::os::raw::c_uint = 81;
-pub const KVM_CAP_IRQFD_RESAMPLE: ::std::os::raw::c_uint = 82;
-pub const KVM_CAP_PPC_BOOKE_WATCHDOG: ::std::os::raw::c_uint = 83;
-pub const KVM_CAP_PPC_HTAB_FD: ::std::os::raw::c_uint = 84;
-pub const KVM_CAP_S390_CSS_SUPPORT: ::std::os::raw::c_uint = 85;
-pub const KVM_CAP_PPC_EPR: ::std::os::raw::c_uint = 86;
-pub const KVM_CAP_ARM_PSCI: ::std::os::raw::c_uint = 87;
-pub const KVM_CAP_ARM_SET_DEVICE_ADDR: ::std::os::raw::c_uint = 88;
-pub const KVM_CAP_DEVICE_CTRL: ::std::os::raw::c_uint = 89;
-pub const KVM_CAP_IRQ_MPIC: ::std::os::raw::c_uint = 90;
-pub const KVM_CAP_PPC_RTAS: ::std::os::raw::c_uint = 91;
-pub const KVM_CAP_IRQ_XICS: ::std::os::raw::c_uint = 92;
-pub const KVM_CAP_ARM_EL1_32BIT: ::std::os::raw::c_uint = 93;
-pub const KVM_CAP_SPAPR_MULTITCE: ::std::os::raw::c_uint = 94;
-pub const KVM_CAP_EXT_EMUL_CPUID: ::std::os::raw::c_uint = 95;
-pub const KVM_CAP_HYPERV_TIME: ::std::os::raw::c_uint = 96;
-pub const KVM_CAP_IOAPIC_POLARITY_IGNORED: ::std::os::raw::c_uint = 97;
-pub const KVM_CAP_ENABLE_CAP_VM: ::std::os::raw::c_uint = 98;
-pub const KVM_CAP_S390_IRQCHIP: ::std::os::raw::c_uint = 99;
-pub const KVM_CAP_IOEVENTFD_NO_LENGTH: ::std::os::raw::c_uint = 100;
-pub const KVM_CAP_VM_ATTRIBUTES: ::std::os::raw::c_uint = 101;
-pub const KVM_CAP_ARM_PSCI_0_2: ::std::os::raw::c_uint = 102;
-pub const KVM_CAP_PPC_FIXUP_HCALL: ::std::os::raw::c_uint = 103;
-pub const KVM_CAP_PPC_ENABLE_HCALL: ::std::os::raw::c_uint = 104;
-pub const KVM_CAP_CHECK_EXTENSION_VM: ::std::os::raw::c_uint = 105;
-pub const KVM_CAP_S390_USER_SIGP: ::std::os::raw::c_uint = 106;
-pub const KVM_CAP_S390_VECTOR_REGISTERS: ::std::os::raw::c_uint = 107;
-pub const KVM_CAP_S390_MEM_OP: ::std::os::raw::c_uint = 108;
-pub const KVM_CAP_S390_USER_STSI: ::std::os::raw::c_uint = 109;
-pub const KVM_CAP_S390_SKEYS: ::std::os::raw::c_uint = 110;
-pub const KVM_CAP_MIPS_FPU: ::std::os::raw::c_uint = 111;
-pub const KVM_CAP_MIPS_MSA: ::std::os::raw::c_uint = 112;
-pub const KVM_CAP_S390_INJECT_IRQ: ::std::os::raw::c_uint = 113;
-pub const KVM_CAP_S390_IRQ_STATE: ::std::os::raw::c_uint = 114;
-pub const KVM_CAP_PPC_HWRNG: ::std::os::raw::c_uint = 115;
-pub const KVM_CAP_DISABLE_QUIRKS: ::std::os::raw::c_uint = 116;
-pub const KVM_CAP_X86_SMM: ::std::os::raw::c_uint = 117;
-pub const KVM_CAP_MULTI_ADDRESS_SPACE: ::std::os::raw::c_uint = 118;
-pub const KVM_CAP_GUEST_DEBUG_HW_BPS: ::std::os::raw::c_uint = 119;
-pub const KVM_CAP_GUEST_DEBUG_HW_WPS: ::std::os::raw::c_uint = 120;
-pub const KVM_CAP_SPLIT_IRQCHIP: ::std::os::raw::c_uint = 121;
-pub const KVM_CAP_IOEVENTFD_ANY_LENGTH: ::std::os::raw::c_uint = 122;
-pub const KVM_CAP_ARM_PMU_V3: ::std::os::raw::c_uint = 126;
-pub const KVM_CAP_IMMEDIATE_EXIT: ::std::os::raw::c_uint = 136;
-pub const KVM_IRQ_ROUTING_IRQCHIP: ::std::os::raw::c_uint = 1;
-pub const KVM_IRQ_ROUTING_MSI: ::std::os::raw::c_uint = 2;
-pub const KVM_IRQ_ROUTING_S390_ADAPTER: ::std::os::raw::c_uint = 3;
-pub const KVM_IRQFD_FLAG_DEASSIGN: ::std::os::raw::c_uint = 1;
-pub const KVM_IRQFD_FLAG_RESAMPLE: ::std::os::raw::c_uint = 2;
-pub const KVM_MMU_FSL_BOOKE_NOHV: ::std::os::raw::c_uint = 0;
-pub const KVM_MMU_FSL_BOOKE_HV: ::std::os::raw::c_uint = 1;
-pub const KVM_REG_ARCH_MASK: ::std::os::raw::c_longlong = -72057594037927936;
-pub const KVM_REG_GENERIC: ::std::os::raw::c_uint = 0;
-pub const KVM_REG_PPC: ::std::os::raw::c_ulonglong = 1152921504606846976;
-pub const KVM_REG_X86: ::std::os::raw::c_ulonglong = 2305843009213693952;
-pub const KVM_REG_IA64: ::std::os::raw::c_ulonglong = 3458764513820540928;
-pub const KVM_REG_ARM: ::std::os::raw::c_ulonglong = 4611686018427387904;
-pub const KVM_REG_S390: ::std::os::raw::c_ulonglong = 5764607523034234880;
-pub const KVM_REG_ARM64: ::std::os::raw::c_ulonglong = 6917529027641081856;
-pub const KVM_REG_MIPS: ::std::os::raw::c_ulonglong = 8070450532247928832;
-pub const KVM_REG_SIZE_SHIFT: ::std::os::raw::c_uint = 52;
-pub const KVM_REG_SIZE_MASK: ::std::os::raw::c_ulonglong = 67553994410557440;
-pub const KVM_REG_SIZE_U8: ::std::os::raw::c_uint = 0;
-pub const KVM_REG_SIZE_U16: ::std::os::raw::c_ulonglong = 4503599627370496;
-pub const KVM_REG_SIZE_U32: ::std::os::raw::c_ulonglong = 9007199254740992;
-pub const KVM_REG_SIZE_U64: ::std::os::raw::c_ulonglong = 13510798882111488;
-pub const KVM_REG_SIZE_U128: ::std::os::raw::c_ulonglong = 18014398509481984;
-pub const KVM_REG_SIZE_U256: ::std::os::raw::c_ulonglong = 22517998136852480;
-pub const KVM_REG_SIZE_U512: ::std::os::raw::c_ulonglong = 27021597764222976;
-pub const KVM_REG_SIZE_U1024: ::std::os::raw::c_ulonglong = 31525197391593472;
-pub const KVM_CREATE_DEVICE_TEST: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_VFIO_GROUP: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_VFIO_GROUP_ADD: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_VFIO_GROUP_DEL: ::std::os::raw::c_uint = 2;
-pub const KVM_S390_STORE_STATUS_NOADDR: ::std::os::raw::c_int = -1;
-pub const KVM_S390_STORE_STATUS_PREFIXED: ::std::os::raw::c_int = -2;
-pub const KVM_DEV_ASSIGN_ENABLE_IOMMU: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_ASSIGN_PCI_2_3: ::std::os::raw::c_uint = 2;
-pub const KVM_DEV_ASSIGN_MASK_INTX: ::std::os::raw::c_uint = 4;
-pub const KVM_DEV_IRQ_HOST_INTX: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_IRQ_HOST_MSI: ::std::os::raw::c_uint = 2;
-pub const KVM_DEV_IRQ_HOST_MSIX: ::std::os::raw::c_uint = 4;
-pub const KVM_DEV_IRQ_GUEST_INTX: ::std::os::raw::c_uint = 256;
-pub const KVM_DEV_IRQ_GUEST_MSI: ::std::os::raw::c_uint = 512;
-pub const KVM_DEV_IRQ_GUEST_MSIX: ::std::os::raw::c_uint = 1024;
-pub const KVM_DEV_IRQ_HOST_MASK: ::std::os::raw::c_uint = 255;
-pub const KVM_DEV_IRQ_GUEST_MASK: ::std::os::raw::c_uint = 65280;
-pub const KVM_MAX_MSIX_PER_DEV: ::std::os::raw::c_uint = 256;
+pub const __BITS_PER_LONG: u32 = 64;
+pub const __FD_SETSIZE: u32 = 1024;
+pub const _IOC_NRBITS: u32 = 8;
+pub const _IOC_TYPEBITS: u32 = 8;
+pub const _IOC_SIZEBITS: u32 = 14;
+pub const _IOC_DIRBITS: u32 = 2;
+pub const _IOC_NRMASK: u32 = 255;
+pub const _IOC_TYPEMASK: u32 = 255;
+pub const _IOC_SIZEMASK: u32 = 16383;
+pub const _IOC_DIRMASK: u32 = 3;
+pub const _IOC_NRSHIFT: u32 = 0;
+pub const _IOC_TYPESHIFT: u32 = 8;
+pub const _IOC_SIZESHIFT: u32 = 16;
+pub const _IOC_DIRSHIFT: u32 = 30;
+pub const _IOC_NONE: u32 = 0;
+pub const _IOC_WRITE: u32 = 1;
+pub const _IOC_READ: u32 = 2;
+pub const IOC_IN: u32 = 1073741824;
+pub const IOC_OUT: u32 = 2147483648;
+pub const IOC_INOUT: u32 = 3221225472;
+pub const IOCSIZE_MASK: u32 = 1073676288;
+pub const IOCSIZE_SHIFT: u32 = 16;
+pub const KVM_SPSR_EL1: u32 = 0;
+pub const KVM_SPSR_SVC: u32 = 0;
+pub const KVM_SPSR_ABT: u32 = 1;
+pub const KVM_SPSR_UND: u32 = 2;
+pub const KVM_SPSR_IRQ: u32 = 3;
+pub const KVM_SPSR_FIQ: u32 = 4;
+pub const KVM_NR_SPSR: u32 = 5;
+pub const PSCI_0_2_FN_BASE: u32 = 2214592512;
+pub const PSCI_0_2_64BIT: u32 = 1073741824;
+pub const PSCI_0_2_FN64_BASE: u32 = 3288334336;
+pub const PSCI_0_2_POWER_STATE_ID_MASK: u32 = 65535;
+pub const PSCI_0_2_POWER_STATE_ID_SHIFT: u32 = 0;
+pub const PSCI_0_2_POWER_STATE_TYPE_SHIFT: u32 = 16;
+pub const PSCI_0_2_POWER_STATE_TYPE_MASK: u32 = 65536;
+pub const PSCI_0_2_POWER_STATE_AFFL_SHIFT: u32 = 24;
+pub const PSCI_0_2_POWER_STATE_AFFL_MASK: u32 = 50331648;
+pub const PSCI_1_0_EXT_POWER_STATE_ID_MASK: u32 = 268435455;
+pub const PSCI_1_0_EXT_POWER_STATE_ID_SHIFT: u32 = 0;
+pub const PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT: u32 = 30;
+pub const PSCI_1_0_EXT_POWER_STATE_TYPE_MASK: u32 = 1073741824;
+pub const PSCI_0_2_AFFINITY_LEVEL_ON: u32 = 0;
+pub const PSCI_0_2_AFFINITY_LEVEL_OFF: u32 = 1;
+pub const PSCI_0_2_AFFINITY_LEVEL_ON_PENDING: u32 = 2;
+pub const PSCI_0_2_TOS_UP_MIGRATE: u32 = 0;
+pub const PSCI_0_2_TOS_UP_NO_MIGRATE: u32 = 1;
+pub const PSCI_0_2_TOS_MP: u32 = 2;
+pub const PSCI_VERSION_MAJOR_SHIFT: u32 = 16;
+pub const PSCI_VERSION_MINOR_MASK: u32 = 65535;
+pub const PSCI_VERSION_MAJOR_MASK: i32 = -65536;
+pub const PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT: u32 = 1;
+pub const PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK: u32 = 2;
+pub const PSCI_1_0_SUSPEND_MODE_PC: u32 = 0;
+pub const PSCI_1_0_SUSPEND_MODE_OSI: u32 = 1;
+pub const PSCI_RET_SUCCESS: u32 = 0;
+pub const PSCI_RET_NOT_SUPPORTED: i32 = -1;
+pub const PSCI_RET_INVALID_PARAMS: i32 = -2;
+pub const PSCI_RET_DENIED: i32 = -3;
+pub const PSCI_RET_ALREADY_ON: i32 = -4;
+pub const PSCI_RET_ON_PENDING: i32 = -5;
+pub const PSCI_RET_INTERNAL_FAILURE: i32 = -6;
+pub const PSCI_RET_NOT_PRESENT: i32 = -7;
+pub const PSCI_RET_DISABLED: i32 = -8;
+pub const PSCI_RET_INVALID_ADDRESS: i32 = -9;
+pub const HWCAP_FP: u32 = 1;
+pub const HWCAP_ASIMD: u32 = 2;
+pub const HWCAP_EVTSTRM: u32 = 4;
+pub const HWCAP_AES: u32 = 8;
+pub const HWCAP_PMULL: u32 = 16;
+pub const HWCAP_SHA1: u32 = 32;
+pub const HWCAP_SHA2: u32 = 64;
+pub const HWCAP_CRC32: u32 = 128;
+pub const HWCAP_ATOMICS: u32 = 256;
+pub const HWCAP_FPHP: u32 = 512;
+pub const HWCAP_ASIMDHP: u32 = 1024;
+pub const HWCAP_CPUID: u32 = 2048;
+pub const HWCAP_ASIMDRDM: u32 = 4096;
+pub const HWCAP_JSCVT: u32 = 8192;
+pub const HWCAP_FCMA: u32 = 16384;
+pub const HWCAP_LRCPC: u32 = 32768;
+pub const HWCAP_DCPOP: u32 = 65536;
+pub const HWCAP_SHA3: u32 = 131072;
+pub const HWCAP_SM3: u32 = 262144;
+pub const HWCAP_SM4: u32 = 524288;
+pub const HWCAP_ASIMDDP: u32 = 1048576;
+pub const HWCAP_SHA512: u32 = 2097152;
+pub const HWCAP_SVE: u32 = 4194304;
+pub const HWCAP_ASIMDFHM: u32 = 8388608;
+pub const HWCAP_DIT: u32 = 16777216;
+pub const HWCAP_USCAT: u32 = 33554432;
+pub const HWCAP_ILRCPC: u32 = 67108864;
+pub const HWCAP_FLAGM: u32 = 134217728;
+pub const HWCAP_SSBS: u32 = 268435456;
+pub const HWCAP_SB: u32 = 536870912;
+pub const HWCAP_PACA: u32 = 1073741824;
+pub const HWCAP_PACG: u32 = 2147483648;
+pub const HWCAP2_DCPODP: u32 = 1;
+pub const HWCAP2_SVE2: u32 = 2;
+pub const HWCAP2_SVEAES: u32 = 4;
+pub const HWCAP2_SVEPMULL: u32 = 8;
+pub const HWCAP2_SVEBITPERM: u32 = 16;
+pub const HWCAP2_SVESHA3: u32 = 32;
+pub const HWCAP2_SVESM4: u32 = 64;
+pub const HWCAP2_FLAGM2: u32 = 128;
+pub const HWCAP2_FRINT: u32 = 256;
+pub const HWCAP2_RNG: u32 = 512;
+pub const __SVE_VQ_BYTES: u32 = 16;
+pub const __SVE_VQ_MIN: u32 = 1;
+pub const __SVE_VQ_MAX: u32 = 512;
+pub const __SVE_VL_MIN: u32 = 16;
+pub const __SVE_VL_MAX: u32 = 8192;
+pub const __SVE_NUM_ZREGS: u32 = 32;
+pub const __SVE_NUM_PREGS: u32 = 16;
+pub const __SVE_ZREGS_OFFSET: u32 = 0;
+pub const PSR_MODE_EL0t: u32 = 0;
+pub const PSR_MODE_EL1t: u32 = 4;
+pub const PSR_MODE_EL1h: u32 = 5;
+pub const PSR_MODE_EL2t: u32 = 8;
+pub const PSR_MODE_EL2h: u32 = 9;
+pub const PSR_MODE_EL3t: u32 = 12;
+pub const PSR_MODE_EL3h: u32 = 13;
+pub const PSR_MODE_MASK: u32 = 15;
+pub const PSR_MODE32_BIT: u32 = 16;
+pub const PSR_F_BIT: u32 = 64;
+pub const PSR_I_BIT: u32 = 128;
+pub const PSR_A_BIT: u32 = 256;
+pub const PSR_D_BIT: u32 = 512;
+pub const PSR_SSBS_BIT: u32 = 4096;
+pub const PSR_PAN_BIT: u32 = 4194304;
+pub const PSR_UAO_BIT: u32 = 8388608;
+pub const PSR_DIT_BIT: u32 = 16777216;
+pub const PSR_V_BIT: u32 = 268435456;
+pub const PSR_C_BIT: u32 = 536870912;
+pub const PSR_Z_BIT: u32 = 1073741824;
+pub const PSR_N_BIT: u32 = 2147483648;
+pub const PSR_f: u32 = 4278190080;
+pub const PSR_s: u32 = 16711680;
+pub const PSR_x: u32 = 65280;
+pub const PSR_c: u32 = 255;
+pub const PTRACE_SYSEMU: u32 = 31;
+pub const PTRACE_SYSEMU_SINGLESTEP: u32 = 32;
+pub const SVE_PT_REGS_MASK: u32 = 1;
+pub const SVE_PT_REGS_FPSIMD: u32 = 0;
+pub const SVE_PT_REGS_SVE: u32 = 1;
+pub const SVE_PT_VL_INHERIT: u32 = 2;
+pub const SVE_PT_VL_ONEXEC: u32 = 4;
+pub const KVM_COALESCED_MMIO_PAGE_OFFSET: u32 = 1;
+pub const KVM_ARM_TARGET_AEM_V8: u32 = 0;
+pub const KVM_ARM_TARGET_FOUNDATION_V8: u32 = 1;
+pub const KVM_ARM_TARGET_CORTEX_A57: u32 = 2;
+pub const KVM_ARM_TARGET_XGENE_POTENZA: u32 = 3;
+pub const KVM_ARM_TARGET_CORTEX_A53: u32 = 4;
+pub const KVM_ARM_TARGET_GENERIC_V8: u32 = 5;
+pub const KVM_ARM_NUM_TARGETS: u32 = 6;
+pub const KVM_ARM_DEVICE_TYPE_SHIFT: u32 = 0;
+pub const KVM_ARM_DEVICE_TYPE_MASK: u32 = 65535;
+pub const KVM_ARM_DEVICE_ID_SHIFT: u32 = 16;
+pub const KVM_ARM_DEVICE_ID_MASK: u32 = 4294901760;
+pub const KVM_ARM_DEVICE_VGIC_V2: u32 = 0;
+pub const KVM_VGIC_V2_ADDR_TYPE_DIST: u32 = 0;
+pub const KVM_VGIC_V2_ADDR_TYPE_CPU: u32 = 1;
+pub const KVM_VGIC_V2_DIST_SIZE: u32 = 4096;
+pub const KVM_VGIC_V2_CPU_SIZE: u32 = 8192;
+pub const KVM_VGIC_V3_ADDR_TYPE_DIST: u32 = 2;
+pub const KVM_VGIC_V3_ADDR_TYPE_REDIST: u32 = 3;
+pub const KVM_VGIC_ITS_ADDR_TYPE: u32 = 4;
+pub const KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION: u32 = 5;
+pub const KVM_ARM_VCPU_POWER_OFF: u32 = 0;
+pub const KVM_ARM_VCPU_EL1_32BIT: u32 = 1;
+pub const KVM_ARM_VCPU_PSCI_0_2: u32 = 2;
+pub const KVM_ARM_VCPU_PMU_V3: u32 = 3;
+pub const KVM_ARM_VCPU_SVE: u32 = 4;
+pub const KVM_ARM_VCPU_PTRAUTH_ADDRESS: u32 = 5;
+pub const KVM_ARM_VCPU_PTRAUTH_GENERIC: u32 = 6;
+pub const KVM_ARM_MAX_DBG_REGS: u32 = 16;
+pub const KVM_GUESTDBG_USE_SW_BP: u32 = 65536;
+pub const KVM_GUESTDBG_USE_HW: u32 = 131072;
+pub const KVM_REG_ARM_COPROC_MASK: u32 = 268369920;
+pub const KVM_REG_ARM_COPROC_SHIFT: u32 = 16;
+pub const KVM_REG_ARM_CORE: u32 = 1048576;
+pub const KVM_REG_ARM_DEMUX: u32 = 1114112;
+pub const KVM_REG_ARM_DEMUX_ID_MASK: u32 = 65280;
+pub const KVM_REG_ARM_DEMUX_ID_SHIFT: u32 = 8;
+pub const KVM_REG_ARM_DEMUX_ID_CCSIDR: u32 = 0;
+pub const KVM_REG_ARM_DEMUX_VAL_MASK: u32 = 255;
+pub const KVM_REG_ARM_DEMUX_VAL_SHIFT: u32 = 0;
+pub const KVM_REG_ARM64_SYSREG: u32 = 1245184;
+pub const KVM_REG_ARM64_SYSREG_OP0_MASK: u32 = 49152;
+pub const KVM_REG_ARM64_SYSREG_OP0_SHIFT: u32 = 14;
+pub const KVM_REG_ARM64_SYSREG_OP1_MASK: u32 = 14336;
+pub const KVM_REG_ARM64_SYSREG_OP1_SHIFT: u32 = 11;
+pub const KVM_REG_ARM64_SYSREG_CRN_MASK: u32 = 1920;
+pub const KVM_REG_ARM64_SYSREG_CRN_SHIFT: u32 = 7;
+pub const KVM_REG_ARM64_SYSREG_CRM_MASK: u32 = 120;
+pub const KVM_REG_ARM64_SYSREG_CRM_SHIFT: u32 = 3;
+pub const KVM_REG_ARM64_SYSREG_OP2_MASK: u32 = 7;
+pub const KVM_REG_ARM64_SYSREG_OP2_SHIFT: u32 = 0;
+pub const KVM_REG_ARM_FW: u32 = 1310720;
+pub const KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL: u32 = 0;
+pub const KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL: u32 = 1;
+pub const KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED: u32 = 2;
+pub const KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL: u32 = 0;
+pub const KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN: u32 = 1;
+pub const KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL: u32 = 2;
+pub const KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED: u32 = 3;
+pub const KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED: u32 = 16;
+pub const KVM_REG_ARM64_SVE: u32 = 1376256;
+pub const KVM_REG_ARM64_SVE_ZREG_BASE: u32 = 0;
+pub const KVM_REG_ARM64_SVE_PREG_BASE: u32 = 1024;
+pub const KVM_REG_ARM64_SVE_FFR_BASE: u32 = 1536;
+pub const KVM_ARM64_SVE_NUM_ZREGS: u32 = 32;
+pub const KVM_ARM64_SVE_NUM_PREGS: u32 = 16;
+pub const KVM_ARM64_SVE_MAX_SLICES: u32 = 32;
+pub const KVM_ARM64_SVE_VQ_MIN: u32 = 1;
+pub const KVM_ARM64_SVE_VQ_MAX: u32 = 512;
+pub const KVM_ARM64_SVE_VLS_WORDS: u32 = 8;
+pub const KVM_DEV_ARM_VGIC_GRP_ADDR: u32 = 0;
+pub const KVM_DEV_ARM_VGIC_GRP_DIST_REGS: u32 = 1;
+pub const KVM_DEV_ARM_VGIC_GRP_CPU_REGS: u32 = 2;
+pub const KVM_DEV_ARM_VGIC_CPUID_SHIFT: u32 = 32;
+pub const KVM_DEV_ARM_VGIC_CPUID_MASK: u64 = 1095216660480;
+pub const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
+pub const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: i64 = -4294967296;
+pub const KVM_DEV_ARM_VGIC_OFFSET_SHIFT: u32 = 0;
+pub const KVM_DEV_ARM_VGIC_OFFSET_MASK: u32 = 4294967295;
+pub const KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK: u32 = 65535;
+pub const KVM_DEV_ARM_VGIC_GRP_NR_IRQS: u32 = 3;
+pub const KVM_DEV_ARM_VGIC_GRP_CTRL: u32 = 4;
+pub const KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: u32 = 5;
+pub const KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: u32 = 6;
+pub const KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: u32 = 7;
+pub const KVM_DEV_ARM_VGIC_GRP_ITS_REGS: u32 = 8;
+pub const KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT: u32 = 10;
+pub const KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK: u32 = 4294966272;
+pub const KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK: u32 = 1023;
+pub const VGIC_LEVEL_INFO_LINE_LEVEL: u32 = 0;
+pub const KVM_DEV_ARM_VGIC_CTRL_INIT: u32 = 0;
+pub const KVM_DEV_ARM_ITS_SAVE_TABLES: u32 = 1;
+pub const KVM_DEV_ARM_ITS_RESTORE_TABLES: u32 = 2;
+pub const KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES: u32 = 3;
+pub const KVM_DEV_ARM_ITS_CTRL_RESET: u32 = 4;
+pub const KVM_ARM_VCPU_PMU_V3_CTRL: u32 = 0;
+pub const KVM_ARM_VCPU_PMU_V3_IRQ: u32 = 0;
+pub const KVM_ARM_VCPU_PMU_V3_INIT: u32 = 1;
+pub const KVM_ARM_VCPU_TIMER_CTRL: u32 = 1;
+pub const KVM_ARM_VCPU_TIMER_IRQ_VTIMER: u32 = 0;
+pub const KVM_ARM_VCPU_TIMER_IRQ_PTIMER: u32 = 1;
+pub const KVM_ARM_IRQ_VCPU2_SHIFT: u32 = 28;
+pub const KVM_ARM_IRQ_VCPU2_MASK: u32 = 15;
+pub const KVM_ARM_IRQ_TYPE_SHIFT: u32 = 24;
+pub const KVM_ARM_IRQ_TYPE_MASK: u32 = 15;
+pub const KVM_ARM_IRQ_VCPU_SHIFT: u32 = 16;
+pub const KVM_ARM_IRQ_VCPU_MASK: u32 = 255;
+pub const KVM_ARM_IRQ_NUM_SHIFT: u32 = 0;
+pub const KVM_ARM_IRQ_NUM_MASK: u32 = 65535;
+pub const KVM_ARM_IRQ_TYPE_CPU: u32 = 0;
+pub const KVM_ARM_IRQ_TYPE_SPI: u32 = 1;
+pub const KVM_ARM_IRQ_TYPE_PPI: u32 = 2;
+pub const KVM_ARM_IRQ_CPU_IRQ: u32 = 0;
+pub const KVM_ARM_IRQ_CPU_FIQ: u32 = 1;
+pub const KVM_ARM_IRQ_GIC_MAX: u32 = 127;
+pub const KVM_NR_IRQCHIPS: u32 = 1;
+pub const KVM_PSCI_FN_BASE: u32 = 2512501342;
+pub const KVM_PSCI_RET_SUCCESS: u32 = 0;
+pub const KVM_PSCI_RET_NI: i32 = -1;
+pub const KVM_PSCI_RET_INVAL: i32 = -2;
+pub const KVM_PSCI_RET_DENIED: i32 = -3;
+pub const KVM_API_VERSION: u32 = 12;
+pub const KVM_TRC_SHIFT: u32 = 16;
+pub const KVM_TRC_ENTRYEXIT: u32 = 65536;
+pub const KVM_TRC_HANDLER: u32 = 131072;
+pub const KVM_TRC_VMENTRY: u32 = 65537;
+pub const KVM_TRC_VMEXIT: u32 = 65538;
+pub const KVM_TRC_PAGE_FAULT: u32 = 131073;
+pub const KVM_TRC_HEAD_SIZE: u32 = 12;
+pub const KVM_TRC_CYCLE_SIZE: u32 = 8;
+pub const KVM_TRC_EXTRA_MAX: u32 = 7;
+pub const KVM_TRC_INJ_VIRQ: u32 = 131074;
+pub const KVM_TRC_REDELIVER_EVT: u32 = 131075;
+pub const KVM_TRC_PEND_INTR: u32 = 131076;
+pub const KVM_TRC_IO_READ: u32 = 131077;
+pub const KVM_TRC_IO_WRITE: u32 = 131078;
+pub const KVM_TRC_CR_READ: u32 = 131079;
+pub const KVM_TRC_CR_WRITE: u32 = 131080;
+pub const KVM_TRC_DR_READ: u32 = 131081;
+pub const KVM_TRC_DR_WRITE: u32 = 131082;
+pub const KVM_TRC_MSR_READ: u32 = 131083;
+pub const KVM_TRC_MSR_WRITE: u32 = 131084;
+pub const KVM_TRC_CPUID: u32 = 131085;
+pub const KVM_TRC_INTR: u32 = 131086;
+pub const KVM_TRC_NMI: u32 = 131087;
+pub const KVM_TRC_VMMCALL: u32 = 131088;
+pub const KVM_TRC_HLT: u32 = 131089;
+pub const KVM_TRC_CLTS: u32 = 131090;
+pub const KVM_TRC_LMSW: u32 = 131091;
+pub const KVM_TRC_APIC_ACCESS: u32 = 131092;
+pub const KVM_TRC_TDP_FAULT: u32 = 131093;
+pub const KVM_TRC_GTLB_WRITE: u32 = 131094;
+pub const KVM_TRC_STLB_WRITE: u32 = 131095;
+pub const KVM_TRC_STLB_INVAL: u32 = 131096;
+pub const KVM_TRC_PPC_INSTR: u32 = 131097;
+pub const KVM_MEM_LOG_DIRTY_PAGES: u32 = 1;
+pub const KVM_MEM_READONLY: u32 = 2;
+pub const KVM_PIT_SPEAKER_DUMMY: u32 = 1;
+pub const KVM_S390_CMMA_PEEK: u32 = 1;
+pub const KVM_EXIT_HYPERV_SYNIC: u32 = 1;
+pub const KVM_EXIT_HYPERV_HCALL: u32 = 2;
+pub const KVM_S390_GET_SKEYS_NONE: u32 = 1;
+pub const KVM_S390_SKEYS_MAX: u32 = 1048576;
+pub const KVM_EXIT_UNKNOWN: u32 = 0;
+pub const KVM_EXIT_EXCEPTION: u32 = 1;
+pub const KVM_EXIT_IO: u32 = 2;
+pub const KVM_EXIT_HYPERCALL: u32 = 3;
+pub const KVM_EXIT_DEBUG: u32 = 4;
+pub const KVM_EXIT_HLT: u32 = 5;
+pub const KVM_EXIT_MMIO: u32 = 6;
+pub const KVM_EXIT_IRQ_WINDOW_OPEN: u32 = 7;
+pub const KVM_EXIT_SHUTDOWN: u32 = 8;
+pub const KVM_EXIT_FAIL_ENTRY: u32 = 9;
+pub const KVM_EXIT_INTR: u32 = 10;
+pub const KVM_EXIT_SET_TPR: u32 = 11;
+pub const KVM_EXIT_TPR_ACCESS: u32 = 12;
+pub const KVM_EXIT_S390_SIEIC: u32 = 13;
+pub const KVM_EXIT_S390_RESET: u32 = 14;
+pub const KVM_EXIT_DCR: u32 = 15;
+pub const KVM_EXIT_NMI: u32 = 16;
+pub const KVM_EXIT_INTERNAL_ERROR: u32 = 17;
+pub const KVM_EXIT_OSI: u32 = 18;
+pub const KVM_EXIT_PAPR_HCALL: u32 = 19;
+pub const KVM_EXIT_S390_UCONTROL: u32 = 20;
+pub const KVM_EXIT_WATCHDOG: u32 = 21;
+pub const KVM_EXIT_S390_TSCH: u32 = 22;
+pub const KVM_EXIT_EPR: u32 = 23;
+pub const KVM_EXIT_SYSTEM_EVENT: u32 = 24;
+pub const KVM_EXIT_S390_STSI: u32 = 25;
+pub const KVM_EXIT_IOAPIC_EOI: u32 = 26;
+pub const KVM_EXIT_HYPERV: u32 = 27;
+pub const KVM_INTERNAL_ERROR_EMULATION: u32 = 1;
+pub const KVM_INTERNAL_ERROR_SIMUL_EX: u32 = 2;
+pub const KVM_INTERNAL_ERROR_DELIVERY_EV: u32 = 3;
+pub const KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON: u32 = 4;
+pub const KVM_EXIT_IO_IN: u32 = 0;
+pub const KVM_EXIT_IO_OUT: u32 = 1;
+pub const KVM_S390_RESET_POR: u32 = 1;
+pub const KVM_S390_RESET_CLEAR: u32 = 2;
+pub const KVM_S390_RESET_SUBSYSTEM: u32 = 4;
+pub const KVM_S390_RESET_CPU_INIT: u32 = 8;
+pub const KVM_S390_RESET_IPL: u32 = 16;
+pub const KVM_SYSTEM_EVENT_SHUTDOWN: u32 = 1;
+pub const KVM_SYSTEM_EVENT_RESET: u32 = 2;
+pub const KVM_SYSTEM_EVENT_CRASH: u32 = 3;
+pub const SYNC_REGS_SIZE_BYTES: u32 = 2048;
+pub const KVM_S390_MEMOP_LOGICAL_READ: u32 = 0;
+pub const KVM_S390_MEMOP_LOGICAL_WRITE: u32 = 1;
+pub const KVM_S390_MEMOP_F_CHECK_ONLY: u32 = 1;
+pub const KVM_S390_MEMOP_F_INJECT_EXCEPTION: u32 = 2;
+pub const KVM_MP_STATE_RUNNABLE: u32 = 0;
+pub const KVM_MP_STATE_UNINITIALIZED: u32 = 1;
+pub const KVM_MP_STATE_INIT_RECEIVED: u32 = 2;
+pub const KVM_MP_STATE_HALTED: u32 = 3;
+pub const KVM_MP_STATE_SIPI_RECEIVED: u32 = 4;
+pub const KVM_MP_STATE_STOPPED: u32 = 5;
+pub const KVM_MP_STATE_CHECK_STOP: u32 = 6;
+pub const KVM_MP_STATE_OPERATING: u32 = 7;
+pub const KVM_MP_STATE_LOAD: u32 = 8;
+pub const KVM_S390_SIGP_STOP: u32 = 4294836224;
+pub const KVM_S390_PROGRAM_INT: u32 = 4294836225;
+pub const KVM_S390_SIGP_SET_PREFIX: u32 = 4294836226;
+pub const KVM_S390_RESTART: u32 = 4294836227;
+pub const KVM_S390_INT_PFAULT_INIT: u32 = 4294836228;
+pub const KVM_S390_INT_PFAULT_DONE: u32 = 4294836229;
+pub const KVM_S390_MCHK: u32 = 4294840320;
+pub const KVM_S390_INT_CLOCK_COMP: u32 = 4294905860;
+pub const KVM_S390_INT_CPU_TIMER: u32 = 4294905861;
+pub const KVM_S390_INT_VIRTIO: u32 = 4294911491;
+pub const KVM_S390_INT_SERVICE: u32 = 4294910977;
+pub const KVM_S390_INT_EMERGENCY: u32 = 4294906369;
+pub const KVM_S390_INT_EXTERNAL_CALL: u32 = 4294906370;
+pub const KVM_S390_INT_IO_MIN: u32 = 0;
+pub const KVM_S390_INT_IO_MAX: u32 = 4294836223;
+pub const KVM_S390_INT_IO_AI_MASK: u32 = 67108864;
+pub const KVM_S390_PGM_FLAGS_ILC_VALID: u32 = 1;
+pub const KVM_S390_PGM_FLAGS_ILC_0: u32 = 2;
+pub const KVM_S390_PGM_FLAGS_ILC_1: u32 = 4;
+pub const KVM_S390_PGM_FLAGS_ILC_MASK: u32 = 6;
+pub const KVM_S390_PGM_FLAGS_NO_REWIND: u32 = 8;
+pub const KVM_S390_STOP_FLAG_STORE_STATUS: u32 = 1;
+pub const KVM_GUESTDBG_ENABLE: u32 = 1;
+pub const KVM_GUESTDBG_SINGLESTEP: u32 = 2;
+pub const KVM_X86_DISABLE_EXITS_MWAIT: u32 = 1;
+pub const KVM_X86_DISABLE_EXITS_HLT: u32 = 2;
+pub const KVM_X86_DISABLE_EXITS_PAUSE: u32 = 4;
+pub const KVM_X86_DISABLE_EXITS_CSTATE: u32 = 8;
+pub const KVM_X86_DISABLE_VALID_EXITS: u32 = 15;
+pub const KVM_PPC_PVINFO_FLAGS_EV_IDLE: u32 = 1;
+pub const KVM_PPC_PAGE_SIZES_MAX_SZ: u32 = 8;
+pub const KVM_PPC_PAGE_SIZES_REAL: u32 = 1;
+pub const KVM_PPC_1T_SEGMENTS: u32 = 2;
+pub const KVM_PPC_NO_HASH: u32 = 4;
+pub const KVMIO: u32 = 174;
+pub const KVM_VM_S390_UCONTROL: u32 = 1;
+pub const KVM_VM_PPC_HV: u32 = 1;
+pub const KVM_VM_PPC_PR: u32 = 2;
+pub const KVM_VM_MIPS_AUTO: u32 = 0;
+pub const KVM_VM_MIPS_VZ: u32 = 1;
+pub const KVM_VM_MIPS_TE: u32 = 2;
+pub const KVM_S390_SIE_PAGE_OFFSET: u32 = 1;
+pub const KVM_VM_TYPE_ARM_IPA_SIZE_MASK: u32 = 255;
+pub const KVM_CAP_IRQCHIP: u32 = 0;
+pub const KVM_CAP_HLT: u32 = 1;
+pub const KVM_CAP_MMU_SHADOW_CACHE_CONTROL: u32 = 2;
+pub const KVM_CAP_USER_MEMORY: u32 = 3;
+pub const KVM_CAP_SET_TSS_ADDR: u32 = 4;
+pub const KVM_CAP_VAPIC: u32 = 6;
+pub const KVM_CAP_EXT_CPUID: u32 = 7;
+pub const KVM_CAP_CLOCKSOURCE: u32 = 8;
+pub const KVM_CAP_NR_VCPUS: u32 = 9;
+pub const KVM_CAP_NR_MEMSLOTS: u32 = 10;
+pub const KVM_CAP_PIT: u32 = 11;
+pub const KVM_CAP_NOP_IO_DELAY: u32 = 12;
+pub const KVM_CAP_PV_MMU: u32 = 13;
+pub const KVM_CAP_MP_STATE: u32 = 14;
+pub const KVM_CAP_COALESCED_MMIO: u32 = 15;
+pub const KVM_CAP_SYNC_MMU: u32 = 16;
+pub const KVM_CAP_IOMMU: u32 = 18;
+pub const KVM_CAP_DESTROY_MEMORY_REGION_WORKS: u32 = 21;
+pub const KVM_CAP_USER_NMI: u32 = 22;
+pub const KVM_CAP_SET_GUEST_DEBUG: u32 = 23;
+pub const KVM_CAP_IRQ_ROUTING: u32 = 25;
+pub const KVM_CAP_IRQ_INJECT_STATUS: u32 = 26;
+pub const KVM_CAP_ASSIGN_DEV_IRQ: u32 = 29;
+pub const KVM_CAP_JOIN_MEMORY_REGIONS_WORKS: u32 = 30;
+pub const KVM_CAP_IRQFD: u32 = 32;
+pub const KVM_CAP_SET_BOOT_CPU_ID: u32 = 34;
+pub const KVM_CAP_IOEVENTFD: u32 = 36;
+pub const KVM_CAP_SET_IDENTITY_MAP_ADDR: u32 = 37;
+pub const KVM_CAP_ADJUST_CLOCK: u32 = 39;
+pub const KVM_CAP_INTERNAL_ERROR_DATA: u32 = 40;
+pub const KVM_CAP_VCPU_EVENTS: u32 = 41;
+pub const KVM_CAP_S390_PSW: u32 = 42;
+pub const KVM_CAP_PPC_SEGSTATE: u32 = 43;
+pub const KVM_CAP_HYPERV: u32 = 44;
+pub const KVM_CAP_HYPERV_VAPIC: u32 = 45;
+pub const KVM_CAP_HYPERV_SPIN: u32 = 46;
+pub const KVM_CAP_PCI_SEGMENT: u32 = 47;
+pub const KVM_CAP_PPC_PAIRED_SINGLES: u32 = 48;
+pub const KVM_CAP_INTR_SHADOW: u32 = 49;
+pub const KVM_CAP_X86_ROBUST_SINGLESTEP: u32 = 51;
+pub const KVM_CAP_PPC_OSI: u32 = 52;
+pub const KVM_CAP_PPC_UNSET_IRQ: u32 = 53;
+pub const KVM_CAP_ENABLE_CAP: u32 = 54;
+pub const KVM_CAP_PPC_GET_PVINFO: u32 = 57;
+pub const KVM_CAP_PPC_IRQ_LEVEL: u32 = 58;
+pub const KVM_CAP_ASYNC_PF: u32 = 59;
+pub const KVM_CAP_TSC_CONTROL: u32 = 60;
+pub const KVM_CAP_GET_TSC_KHZ: u32 = 61;
+pub const KVM_CAP_PPC_BOOKE_SREGS: u32 = 62;
+pub const KVM_CAP_SPAPR_TCE: u32 = 63;
+pub const KVM_CAP_PPC_SMT: u32 = 64;
+pub const KVM_CAP_PPC_RMA: u32 = 65;
+pub const KVM_CAP_MAX_VCPUS: u32 = 66;
+pub const KVM_CAP_PPC_HIOR: u32 = 67;
+pub const KVM_CAP_PPC_PAPR: u32 = 68;
+pub const KVM_CAP_SW_TLB: u32 = 69;
+pub const KVM_CAP_ONE_REG: u32 = 70;
+pub const KVM_CAP_S390_GMAP: u32 = 71;
+pub const KVM_CAP_TSC_DEADLINE_TIMER: u32 = 72;
+pub const KVM_CAP_S390_UCONTROL: u32 = 73;
+pub const KVM_CAP_SYNC_REGS: u32 = 74;
+pub const KVM_CAP_PCI_2_3: u32 = 75;
+pub const KVM_CAP_KVMCLOCK_CTRL: u32 = 76;
+pub const KVM_CAP_SIGNAL_MSI: u32 = 77;
+pub const KVM_CAP_PPC_GET_SMMU_INFO: u32 = 78;
+pub const KVM_CAP_S390_COW: u32 = 79;
+pub const KVM_CAP_PPC_ALLOC_HTAB: u32 = 80;
+pub const KVM_CAP_READONLY_MEM: u32 = 81;
+pub const KVM_CAP_IRQFD_RESAMPLE: u32 = 82;
+pub const KVM_CAP_PPC_BOOKE_WATCHDOG: u32 = 83;
+pub const KVM_CAP_PPC_HTAB_FD: u32 = 84;
+pub const KVM_CAP_S390_CSS_SUPPORT: u32 = 85;
+pub const KVM_CAP_PPC_EPR: u32 = 86;
+pub const KVM_CAP_ARM_PSCI: u32 = 87;
+pub const KVM_CAP_ARM_SET_DEVICE_ADDR: u32 = 88;
+pub const KVM_CAP_DEVICE_CTRL: u32 = 89;
+pub const KVM_CAP_IRQ_MPIC: u32 = 90;
+pub const KVM_CAP_PPC_RTAS: u32 = 91;
+pub const KVM_CAP_IRQ_XICS: u32 = 92;
+pub const KVM_CAP_ARM_EL1_32BIT: u32 = 93;
+pub const KVM_CAP_SPAPR_MULTITCE: u32 = 94;
+pub const KVM_CAP_EXT_EMUL_CPUID: u32 = 95;
+pub const KVM_CAP_HYPERV_TIME: u32 = 96;
+pub const KVM_CAP_IOAPIC_POLARITY_IGNORED: u32 = 97;
+pub const KVM_CAP_ENABLE_CAP_VM: u32 = 98;
+pub const KVM_CAP_S390_IRQCHIP: u32 = 99;
+pub const KVM_CAP_IOEVENTFD_NO_LENGTH: u32 = 100;
+pub const KVM_CAP_VM_ATTRIBUTES: u32 = 101;
+pub const KVM_CAP_ARM_PSCI_0_2: u32 = 102;
+pub const KVM_CAP_PPC_FIXUP_HCALL: u32 = 103;
+pub const KVM_CAP_PPC_ENABLE_HCALL: u32 = 104;
+pub const KVM_CAP_CHECK_EXTENSION_VM: u32 = 105;
+pub const KVM_CAP_S390_USER_SIGP: u32 = 106;
+pub const KVM_CAP_S390_VECTOR_REGISTERS: u32 = 107;
+pub const KVM_CAP_S390_MEM_OP: u32 = 108;
+pub const KVM_CAP_S390_USER_STSI: u32 = 109;
+pub const KVM_CAP_S390_SKEYS: u32 = 110;
+pub const KVM_CAP_MIPS_FPU: u32 = 111;
+pub const KVM_CAP_MIPS_MSA: u32 = 112;
+pub const KVM_CAP_S390_INJECT_IRQ: u32 = 113;
+pub const KVM_CAP_S390_IRQ_STATE: u32 = 114;
+pub const KVM_CAP_PPC_HWRNG: u32 = 115;
+pub const KVM_CAP_DISABLE_QUIRKS: u32 = 116;
+pub const KVM_CAP_X86_SMM: u32 = 117;
+pub const KVM_CAP_MULTI_ADDRESS_SPACE: u32 = 118;
+pub const KVM_CAP_GUEST_DEBUG_HW_BPS: u32 = 119;
+pub const KVM_CAP_GUEST_DEBUG_HW_WPS: u32 = 120;
+pub const KVM_CAP_SPLIT_IRQCHIP: u32 = 121;
+pub const KVM_CAP_IOEVENTFD_ANY_LENGTH: u32 = 122;
+pub const KVM_CAP_HYPERV_SYNIC: u32 = 123;
+pub const KVM_CAP_S390_RI: u32 = 124;
+pub const KVM_CAP_SPAPR_TCE_64: u32 = 125;
+pub const KVM_CAP_ARM_PMU_V3: u32 = 126;
+pub const KVM_CAP_VCPU_ATTRIBUTES: u32 = 127;
+pub const KVM_CAP_MAX_VCPU_ID: u32 = 128;
+pub const KVM_CAP_X2APIC_API: u32 = 129;
+pub const KVM_CAP_S390_USER_INSTR0: u32 = 130;
+pub const KVM_CAP_MSI_DEVID: u32 = 131;
+pub const KVM_CAP_PPC_HTM: u32 = 132;
+pub const KVM_CAP_SPAPR_RESIZE_HPT: u32 = 133;
+pub const KVM_CAP_PPC_MMU_RADIX: u32 = 134;
+pub const KVM_CAP_PPC_MMU_HASH_V3: u32 = 135;
+pub const KVM_CAP_IMMEDIATE_EXIT: u32 = 136;
+pub const KVM_CAP_MIPS_VZ: u32 = 137;
+pub const KVM_CAP_MIPS_TE: u32 = 138;
+pub const KVM_CAP_MIPS_64BIT: u32 = 139;
+pub const KVM_CAP_S390_GS: u32 = 140;
+pub const KVM_CAP_S390_AIS: u32 = 141;
+pub const KVM_CAP_SPAPR_TCE_VFIO: u32 = 142;
+pub const KVM_CAP_X86_DISABLE_EXITS: u32 = 143;
+pub const KVM_CAP_ARM_USER_IRQ: u32 = 144;
+pub const KVM_CAP_S390_CMMA_MIGRATION: u32 = 145;
+pub const KVM_CAP_PPC_FWNMI: u32 = 146;
+pub const KVM_CAP_PPC_SMT_POSSIBLE: u32 = 147;
+pub const KVM_CAP_HYPERV_SYNIC2: u32 = 148;
+pub const KVM_CAP_HYPERV_VP_INDEX: u32 = 149;
+pub const KVM_CAP_S390_AIS_MIGRATION: u32 = 150;
+pub const KVM_CAP_PPC_GET_CPU_CHAR: u32 = 151;
+pub const KVM_CAP_S390_BPB: u32 = 152;
+pub const KVM_CAP_GET_MSR_FEATURES: u32 = 153;
+pub const KVM_CAP_HYPERV_EVENTFD: u32 = 154;
+pub const KVM_CAP_HYPERV_TLBFLUSH: u32 = 155;
+pub const KVM_CAP_S390_HPAGE_1M: u32 = 156;
+pub const KVM_CAP_NESTED_STATE: u32 = 157;
+pub const KVM_CAP_ARM_INJECT_SERROR_ESR: u32 = 158;
+pub const KVM_CAP_MSR_PLATFORM_INFO: u32 = 159;
+pub const KVM_CAP_PPC_NESTED_HV: u32 = 160;
+pub const KVM_CAP_HYPERV_SEND_IPI: u32 = 161;
+pub const KVM_CAP_COALESCED_PIO: u32 = 162;
+pub const KVM_CAP_HYPERV_ENLIGHTENED_VMCS: u32 = 163;
+pub const KVM_CAP_EXCEPTION_PAYLOAD: u32 = 164;
+pub const KVM_CAP_ARM_VM_IPA_SIZE: u32 = 165;
+pub const KVM_CAP_MANUAL_DIRTY_LOG_PROTECT: u32 = 166;
+pub const KVM_CAP_HYPERV_CPUID: u32 = 167;
+pub const KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2: u32 = 168;
+pub const KVM_CAP_PPC_IRQ_XIVE: u32 = 169;
+pub const KVM_CAP_ARM_SVE: u32 = 170;
+pub const KVM_CAP_ARM_PTRAUTH_ADDRESS: u32 = 171;
+pub const KVM_CAP_ARM_PTRAUTH_GENERIC: u32 = 172;
+pub const KVM_CAP_PMU_EVENT_FILTER: u32 = 173;
+pub const KVM_CAP_ARM_IRQ_LINE_LAYOUT_2: u32 = 174;
+pub const KVM_CAP_HYPERV_DIRECT_TLBFLUSH: u32 = 175;
+pub const KVM_IRQ_ROUTING_IRQCHIP: u32 = 1;
+pub const KVM_IRQ_ROUTING_MSI: u32 = 2;
+pub const KVM_IRQ_ROUTING_S390_ADAPTER: u32 = 3;
+pub const KVM_IRQ_ROUTING_HV_SINT: u32 = 4;
+pub const KVM_IRQFD_FLAG_DEASSIGN: u32 = 1;
+pub const KVM_IRQFD_FLAG_RESAMPLE: u32 = 2;
+pub const KVM_CLOCK_TSC_STABLE: u32 = 2;
+pub const KVM_MMU_FSL_BOOKE_NOHV: u32 = 0;
+pub const KVM_MMU_FSL_BOOKE_HV: u32 = 1;
+pub const KVM_REG_ARCH_MASK: i64 = -72057594037927936;
+pub const KVM_REG_GENERIC: u32 = 0;
+pub const KVM_REG_PPC: u64 = 1152921504606846976;
+pub const KVM_REG_X86: u64 = 2305843009213693952;
+pub const KVM_REG_IA64: u64 = 3458764513820540928;
+pub const KVM_REG_ARM: u64 = 4611686018427387904;
+pub const KVM_REG_S390: u64 = 5764607523034234880;
+pub const KVM_REG_ARM64: u64 = 6917529027641081856;
+pub const KVM_REG_MIPS: u64 = 8070450532247928832;
+pub const KVM_REG_RISCV: i64 = -9223372036854775808;
+pub const KVM_REG_SIZE_SHIFT: u32 = 52;
+pub const KVM_REG_SIZE_MASK: u64 = 67553994410557440;
+pub const KVM_REG_SIZE_U8: u32 = 0;
+pub const KVM_REG_SIZE_U16: u64 = 4503599627370496;
+pub const KVM_REG_SIZE_U32: u64 = 9007199254740992;
+pub const KVM_REG_SIZE_U64: u64 = 13510798882111488;
+pub const KVM_REG_SIZE_U128: u64 = 18014398509481984;
+pub const KVM_REG_SIZE_U256: u64 = 22517998136852480;
+pub const KVM_REG_SIZE_U512: u64 = 27021597764222976;
+pub const KVM_REG_SIZE_U1024: u64 = 31525197391593472;
+pub const KVM_REG_SIZE_U2048: u64 = 36028797018963968;
+pub const KVM_MSI_VALID_DEVID: u32 = 1;
+pub const KVM_CREATE_DEVICE_TEST: u32 = 1;
+pub const KVM_DEV_VFIO_GROUP: u32 = 1;
+pub const KVM_DEV_VFIO_GROUP_ADD: u32 = 1;
+pub const KVM_DEV_VFIO_GROUP_DEL: u32 = 2;
+pub const KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE: u32 = 3;
+pub const KVM_S390_STORE_STATUS_NOADDR: i32 = -1;
+pub const KVM_S390_STORE_STATUS_PREFIXED: i32 = -2;
+pub const KVM_DEV_ASSIGN_ENABLE_IOMMU: u32 = 1;
+pub const KVM_DEV_ASSIGN_PCI_2_3: u32 = 2;
+pub const KVM_DEV_ASSIGN_MASK_INTX: u32 = 4;
+pub const KVM_DEV_IRQ_HOST_INTX: u32 = 1;
+pub const KVM_DEV_IRQ_HOST_MSI: u32 = 2;
+pub const KVM_DEV_IRQ_HOST_MSIX: u32 = 4;
+pub const KVM_DEV_IRQ_GUEST_INTX: u32 = 256;
+pub const KVM_DEV_IRQ_GUEST_MSI: u32 = 512;
+pub const KVM_DEV_IRQ_GUEST_MSIX: u32 = 1024;
+pub const KVM_DEV_IRQ_HOST_MASK: u32 = 255;
+pub const KVM_DEV_IRQ_GUEST_MASK: u32 = 65280;
+pub const KVM_MAX_MSIX_PER_DEV: u32 = 256;
+pub const KVM_X2APIC_API_USE_32BIT_IDS: u32 = 1;
+pub const KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK: u32 = 2;
+pub const KVM_ARM_DEV_EL1_VTIMER: u32 = 1;
+pub const KVM_ARM_DEV_EL1_PTIMER: u32 = 2;
+pub const KVM_ARM_DEV_PMU: u32 = 4;
+pub const KVM_HYPERV_CONN_ID_MASK: u32 = 16777215;
+pub const KVM_HYPERV_EVENTFD_DEASSIGN: u32 = 1;
 pub type __s8 = ::std::os::raw::c_schar;
 pub type __u8 = ::std::os::raw::c_uchar;
 pub type __s16 = ::std::os::raw::c_short;
@@ -578,6 +764,7 @@
 pub type __kernel_off_t = __kernel_long_t;
 pub type __kernel_loff_t = ::std::os::raw::c_longlong;
 pub type __kernel_time_t = __kernel_long_t;
+pub type __kernel_time64_t = ::std::os::raw::c_longlong;
 pub type __kernel_clock_t = __kernel_long_t;
 pub type __kernel_timer_t = ::std::os::raw::c_int;
 pub type __kernel_clockid_t = ::std::os::raw::c_int;
@@ -592,6 +779,7 @@
 pub type __be64 = __u64;
 pub type __sum16 = __u16;
 pub type __wsum = __u32;
+pub type __poll_t = ::std::os::raw::c_uint;
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct user_pt_regs {
@@ -654,12 +842,13 @@
     );
 }
 #[repr(C)]
+#[repr(align(16))]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct user_fpsimd_state {
     pub vregs: [__uint128_t; 32usize],
     pub fpsr: __u32,
     pub fpcr: __u32,
-    pub __bindgen_padding_0: u64,
+    pub __reserved: [__u32; 2usize],
 }
 #[test]
 fn bindgen_test_layout_user_fpsimd_state() {
@@ -669,6 +858,11 @@
         concat!("Size of: ", stringify!(user_fpsimd_state))
     );
     assert_eq!(
+        ::std::mem::align_of::<user_fpsimd_state>(),
+        16usize,
+        concat!("Alignment of ", stringify!(user_fpsimd_state))
+    );
+    assert_eq!(
         unsafe { &(*(::std::ptr::null::<user_fpsimd_state>())).vregs as *const _ as usize },
         0usize,
         concat!(
@@ -698,6 +892,16 @@
             stringify!(fpcr)
         )
     );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_fpsimd_state>())).__reserved as *const _ as usize },
+        520usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_fpsimd_state),
+            "::",
+            stringify!(__reserved)
+        )
+    );
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
@@ -810,6 +1014,220 @@
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
+pub struct user_sve_header {
+    pub size: __u32,
+    pub max_size: __u32,
+    pub vl: __u16,
+    pub max_vl: __u16,
+    pub flags: __u16,
+    pub __reserved: __u16,
+}
+#[test]
+fn bindgen_test_layout_user_sve_header() {
+    assert_eq!(
+        ::std::mem::size_of::<user_sve_header>(),
+        16usize,
+        concat!("Size of: ", stringify!(user_sve_header))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<user_sve_header>(),
+        4usize,
+        concat!("Alignment of ", stringify!(user_sve_header))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_sve_header>())).size as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_sve_header),
+            "::",
+            stringify!(size)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_sve_header>())).max_size as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_sve_header),
+            "::",
+            stringify!(max_size)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_sve_header>())).vl as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_sve_header),
+            "::",
+            stringify!(vl)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_sve_header>())).max_vl as *const _ as usize },
+        10usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_sve_header),
+            "::",
+            stringify!(max_vl)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_sve_header>())).flags as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_sve_header),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_sve_header>())).__reserved as *const _ as usize },
+        14usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_sve_header),
+            "::",
+            stringify!(__reserved)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct user_pac_mask {
+    pub data_mask: __u64,
+    pub insn_mask: __u64,
+}
+#[test]
+fn bindgen_test_layout_user_pac_mask() {
+    assert_eq!(
+        ::std::mem::size_of::<user_pac_mask>(),
+        16usize,
+        concat!("Size of: ", stringify!(user_pac_mask))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<user_pac_mask>(),
+        8usize,
+        concat!("Alignment of ", stringify!(user_pac_mask))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_pac_mask>())).data_mask as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_pac_mask),
+            "::",
+            stringify!(data_mask)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_pac_mask>())).insn_mask as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_pac_mask),
+            "::",
+            stringify!(insn_mask)
+        )
+    );
+}
+#[repr(C)]
+#[repr(align(16))]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct user_pac_address_keys {
+    pub apiakey: __uint128_t,
+    pub apibkey: __uint128_t,
+    pub apdakey: __uint128_t,
+    pub apdbkey: __uint128_t,
+}
+#[test]
+fn bindgen_test_layout_user_pac_address_keys() {
+    assert_eq!(
+        ::std::mem::size_of::<user_pac_address_keys>(),
+        64usize,
+        concat!("Size of: ", stringify!(user_pac_address_keys))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<user_pac_address_keys>(),
+        16usize,
+        concat!("Alignment of ", stringify!(user_pac_address_keys))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_pac_address_keys>())).apiakey as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_pac_address_keys),
+            "::",
+            stringify!(apiakey)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_pac_address_keys>())).apibkey as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_pac_address_keys),
+            "::",
+            stringify!(apibkey)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_pac_address_keys>())).apdakey as *const _ as usize },
+        32usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_pac_address_keys),
+            "::",
+            stringify!(apdakey)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_pac_address_keys>())).apdbkey as *const _ as usize },
+        48usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_pac_address_keys),
+            "::",
+            stringify!(apdbkey)
+        )
+    );
+}
+#[repr(C)]
+#[repr(align(16))]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct user_pac_generic_keys {
+    pub apgakey: __uint128_t,
+}
+#[test]
+fn bindgen_test_layout_user_pac_generic_keys() {
+    assert_eq!(
+        ::std::mem::size_of::<user_pac_generic_keys>(),
+        16usize,
+        concat!("Size of: ", stringify!(user_pac_generic_keys))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<user_pac_generic_keys>(),
+        16usize,
+        concat!("Alignment of ", stringify!(user_pac_generic_keys))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<user_pac_generic_keys>())).apgakey as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(user_pac_generic_keys),
+            "::",
+            stringify!(apgakey)
+        )
+    );
+}
+#[repr(C)]
+#[repr(align(16))]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_regs {
     pub regs: user_pt_regs,
     pub sp_el1: __u64,
@@ -826,6 +1244,11 @@
         concat!("Size of: ", stringify!(kvm_regs))
     );
     assert_eq!(
+        ::std::mem::align_of::<kvm_regs>(),
+        16usize,
+        concat!("Alignment of ", stringify!(kvm_regs))
+    );
+    assert_eq!(
         unsafe { &(*(::std::ptr::null::<kvm_regs>())).regs as *const _ as usize },
         0usize,
         concat!(
@@ -1049,19 +1472,31 @@
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_sync_regs {}
+pub struct kvm_sync_regs {
+    pub device_irq_level: __u64,
+}
 #[test]
 fn bindgen_test_layout_kvm_sync_regs() {
     assert_eq!(
         ::std::mem::size_of::<kvm_sync_regs>(),
-        0usize,
+        8usize,
         concat!("Size of: ", stringify!(kvm_sync_regs))
     );
     assert_eq!(
         ::std::mem::align_of::<kvm_sync_regs>(),
-        1usize,
+        8usize,
         concat!("Alignment of ", stringify!(kvm_sync_regs))
     );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sync_regs>())).device_irq_level as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sync_regs),
+            "::",
+            stringify!(device_irq_level)
+        )
+    );
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
@@ -1081,6 +1516,117 @@
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vcpu_events {
+    pub exception: kvm_vcpu_events__bindgen_ty_1,
+    pub reserved: [__u32; 12usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vcpu_events__bindgen_ty_1 {
+    pub serror_pending: __u8,
+    pub serror_has_esr: __u8,
+    pub pad: [__u8; 6usize],
+    pub serror_esr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_vcpu_events__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_vcpu_events__bindgen_ty_1>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_vcpu_events__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_vcpu_events__bindgen_ty_1>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_vcpu_events__bindgen_ty_1))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).serror_pending as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vcpu_events__bindgen_ty_1),
+            "::",
+            stringify!(serror_pending)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).serror_has_esr as *const _
+                as usize
+        },
+        1usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vcpu_events__bindgen_ty_1),
+            "::",
+            stringify!(serror_has_esr)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).pad as *const _ as usize
+        },
+        2usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vcpu_events__bindgen_ty_1),
+            "::",
+            stringify!(pad)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).serror_esr as *const _
+                as usize
+        },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vcpu_events__bindgen_ty_1),
+            "::",
+            stringify!(serror_esr)
+        )
+    );
+}
+#[test]
+fn bindgen_test_layout_kvm_vcpu_events() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_vcpu_events>(),
+        64usize,
+        concat!("Size of: ", stringify!(kvm_vcpu_events))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_vcpu_events>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_vcpu_events))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_vcpu_events>())).exception as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vcpu_events),
+            "::",
+            stringify!(exception)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_vcpu_events>())).reserved as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vcpu_events),
+            "::",
+            stringify!(reserved)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_user_trace_setup {
     pub buf_size: __u32,
     pub buf_nr: __u32,
@@ -1657,6 +2203,399 @@
         )
     );
 }
+#[doc = " kvm_s390_cmma_log - Used for CMMA migration."]
+#[doc = ""]
+#[doc = " Used both for input and output."]
+#[doc = ""]
+#[doc = " @start_gfn: Guest page number to start from."]
+#[doc = " @count: Size of the result buffer."]
+#[doc = " @flags: Control operation mode via KVM_S390_CMMA_* flags"]
+#[doc = " @remaining: Used with KVM_S390_GET_CMMA_BITS. Indicates how many dirty"]
+#[doc = "             pages are still remaining."]
+#[doc = " @mask: Used with KVM_S390_SET_CMMA_BITS. Bitmap of bits to actually set"]
+#[doc = "        in the PGSTE."]
+#[doc = " @values: Pointer to the values buffer."]
+#[doc = ""]
+#[doc = " Used in KVM_S390_{G,S}ET_CMMA_BITS ioctls."]
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_s390_cmma_log {
+    pub start_gfn: __u64,
+    pub count: __u32,
+    pub flags: __u32,
+    pub __bindgen_anon_1: kvm_s390_cmma_log__bindgen_ty_1,
+    pub values: __u64,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_s390_cmma_log__bindgen_ty_1 {
+    pub remaining: __u64,
+    pub mask: __u64,
+    _bindgen_union_align: u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_cmma_log__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_s390_cmma_log__bindgen_ty_1>(),
+        8usize,
+        concat!("Size of: ", stringify!(kvm_s390_cmma_log__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_s390_cmma_log__bindgen_ty_1>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_s390_cmma_log__bindgen_ty_1))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_s390_cmma_log__bindgen_ty_1>())).remaining as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log__bindgen_ty_1),
+            "::",
+            stringify!(remaining)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_s390_cmma_log__bindgen_ty_1>())).mask as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log__bindgen_ty_1),
+            "::",
+            stringify!(mask)
+        )
+    );
+}
+impl Default for kvm_s390_cmma_log__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_cmma_log() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_s390_cmma_log>(),
+        32usize,
+        concat!("Size of: ", stringify!(kvm_s390_cmma_log))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_s390_cmma_log>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_s390_cmma_log))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_cmma_log>())).start_gfn as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log),
+            "::",
+            stringify!(start_gfn)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_cmma_log>())).count as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log),
+            "::",
+            stringify!(count)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_cmma_log>())).flags as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_cmma_log>())).values as *const _ as usize },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log),
+            "::",
+            stringify!(values)
+        )
+    );
+}
+impl Default for kvm_s390_cmma_log {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_hyperv_exit {
+    pub type_: __u32,
+    pub pad1: __u32,
+    pub u: kvm_hyperv_exit__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_hyperv_exit__bindgen_ty_1 {
+    pub synic: kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1,
+    pub hcall: kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2,
+    _bindgen_union_align: [u64; 4usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1 {
+    pub msr: __u32,
+    pub pad2: __u32,
+    pub control: __u64,
+    pub evt_page: __u64,
+    pub msg_page: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>(),
+        32usize,
+        concat!(
+            "Size of: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>(),
+        8usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).msr as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(msr)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).pad2 as *const _
+                as usize
+        },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(pad2)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).control
+                as *const _ as usize
+        },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(control)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).evt_page
+                as *const _ as usize
+        },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(evt_page)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).msg_page
+                as *const _ as usize
+        },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(msg_page)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2 {
+    pub input: __u64,
+    pub result: __u64,
+    pub params: [__u64; 2usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>(),
+        32usize,
+        concat!(
+            "Size of: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2)
+        )
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>(),
+        8usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).input
+                as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
+            "::",
+            stringify!(input)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).result
+                as *const _ as usize
+        },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
+            "::",
+            stringify!(result)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).params
+                as *const _ as usize
+        },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
+            "::",
+            stringify!(params)
+        )
+    );
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1>(),
+        32usize,
+        concat!("Size of: ", stringify!(kvm_hyperv_exit__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_hyperv_exit__bindgen_ty_1))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1>())).synic as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1),
+            "::",
+            stringify!(synic)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1>())).hcall as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1),
+            "::",
+            stringify!(hcall)
+        )
+    );
+}
+impl Default for kvm_hyperv_exit__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_exit() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_exit>(),
+        40usize,
+        concat!("Size of: ", stringify!(kvm_hyperv_exit))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_exit>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_hyperv_exit))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_exit>())).type_ as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit),
+            "::",
+            stringify!(type_)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_exit>())).pad1 as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit),
+            "::",
+            stringify!(pad1)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_exit>())).u as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit),
+            "::",
+            stringify!(u)
+        )
+    );
+}
+impl Default for kvm_hyperv_exit {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
 #[repr(C)]
 #[derive(Copy, Clone)]
 pub struct kvm_run {
@@ -1675,11 +2614,6 @@
     pub s: kvm_run__bindgen_ty_2,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_run__bindgen_ty_1__bindgen_ty_1 {
-    pub hardware_exit_reason: __u64,
-}
-#[repr(C)]
 #[derive(Copy, Clone)]
 pub union kvm_run__bindgen_ty_1 {
     pub hw: kvm_run__bindgen_ty_1__bindgen_ty_1,
@@ -1706,6 +2640,11 @@
     pub padding: [::std::os::raw::c_char; 256usize],
     _bindgen_union_align: [u64; 32usize],
 }
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_1 {
+    pub hardware_exit_reason: __u64,
+}
 #[test]
 fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_1() {
     assert_eq!(
@@ -2842,174 +3781,6 @@
         )
     );
 }
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct kvm_hyperv_exit {
-    pub type_: __u32,
-    pub pad: __u32,
-    pub u: kvm_hyperv_exit__bindgen_ty_1,
-}
-#[test]
-fn bindgen_test_layout_kvm_hyperv_exit() {
-    assert_eq!(
-        ::std::mem::size_of::<kvm_hyperv_exit>(),
-        40usize,
-        concat!("Size of: ", stringify!(kvm_hyperv_exit))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<kvm_hyperv_exit>(),
-        8usize,
-        concat!("Alignment of ", stringify!(kvm_hyperv_exit))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_hyperv_exit>())).u as *const _ as usize },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit),
-            "::",
-            stringify!(u)
-        )
-    );
-}
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub union kvm_hyperv_exit__bindgen_ty_1 {
-    pub synic: kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1,
-    pub hcall: kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2,
-}
-#[test]
-fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1() {
-    assert_eq!(
-        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1>(),
-        32usize,
-        concat!("Size of: ", stringify!(kvm_hyperv_exit__bindgen_ty_1))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1>(),
-        8usize,
-        concat!("Alignment of ", stringify!(kvm_hyperv_exit__bindgen_ty_1))
-    );
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1 {
-    pub msr: __u32,
-    pub pad: __u32,
-    pub control: __u64,
-    pub evt_page: __u64,
-    pub msg_page: __u64,
-}
-#[test]
-fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1() {
-    assert_eq!(
-        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>(),
-        32usize,
-        concat!(
-            "Size of: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1)
-        )
-    );
-    assert_eq!(
-        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>(),
-        8usize,
-        concat!(
-            "Alignment of ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).control
-                as *const _ as usize
-        },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
-            "::",
-            stringify!(control)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).evt_page
-                as *const _ as usize
-        },
-        16usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
-            "::",
-            stringify!(evt_page)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).msg_page
-                as *const _ as usize
-        },
-        24usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
-            "::",
-            stringify!(msg_page)
-        )
-    );
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2 {
-    pub input: __u64,
-    pub result: __u64,
-    pub params: [__u64; 2],
-}
-#[test]
-fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2() {
-    assert_eq!(
-        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>(),
-        32usize,
-        concat!(
-            "Size of: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2)
-        )
-    );
-    assert_eq!(
-        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>(),
-        8usize,
-        concat!(
-            "Alignment of ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).result
-                as *const _ as usize
-        },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
-            "::",
-            stringify!(result)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).params
-                as *const _ as usize
-        },
-        16usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
-            "::",
-            stringify!(params)
-        )
-    );
-}
 #[test]
 fn bindgen_test_layout_kvm_run__bindgen_ty_1() {
     assert_eq!(
@@ -3237,6 +4008,16 @@
         )
     );
     assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).hyperv as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_run__bindgen_ty_1),
+            "::",
+            stringify!(hyperv)
+        )
+    );
+    assert_eq!(
         unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).padding as *const _ as usize },
         0usize,
         concat!(
@@ -3257,7 +4038,7 @@
 pub union kvm_run__bindgen_ty_2 {
     pub regs: kvm_sync_regs,
     pub padding: [::std::os::raw::c_char; 2048usize],
-    _bindgen_union_align: [u8; 2048usize],
+    _bindgen_union_align: [u64; 256usize],
 }
 #[test]
 fn bindgen_test_layout_kvm_run__bindgen_ty_2() {
@@ -3268,7 +4049,7 @@
     );
     assert_eq!(
         ::std::mem::align_of::<kvm_run__bindgen_ty_2>(),
-        1usize,
+        8usize,
         concat!("Alignment of ", stringify!(kvm_run__bindgen_ty_2))
     );
     assert_eq!(
@@ -3440,11 +4221,68 @@
     }
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Copy, Clone)]
 pub struct kvm_coalesced_mmio_zone {
     pub addr: __u64,
     pub size: __u32,
+    pub __bindgen_anon_1: kvm_coalesced_mmio_zone__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_coalesced_mmio_zone__bindgen_ty_1 {
     pub pad: __u32,
+    pub pio: __u32,
+    _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_coalesced_mmio_zone__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_coalesced_mmio_zone__bindgen_ty_1>(),
+        4usize,
+        concat!(
+            "Size of: ",
+            stringify!(kvm_coalesced_mmio_zone__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_coalesced_mmio_zone__bindgen_ty_1>(),
+        4usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_coalesced_mmio_zone__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_coalesced_mmio_zone__bindgen_ty_1>())).pad as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_coalesced_mmio_zone__bindgen_ty_1),
+            "::",
+            stringify!(pad)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_coalesced_mmio_zone__bindgen_ty_1>())).pio as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_coalesced_mmio_zone__bindgen_ty_1),
+            "::",
+            stringify!(pio)
+        )
+    );
+}
+impl Default for kvm_coalesced_mmio_zone__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
 }
 #[test]
 fn bindgen_test_layout_kvm_coalesced_mmio_zone() {
@@ -3478,24 +4316,71 @@
             stringify!(size)
         )
     );
+}
+impl Default for kvm_coalesced_mmio_zone {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_coalesced_mmio {
+    pub phys_addr: __u64,
+    pub len: __u32,
+    pub __bindgen_anon_1: kvm_coalesced_mmio__bindgen_ty_1,
+    pub data: [__u8; 8usize],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_coalesced_mmio__bindgen_ty_1 {
+    pub pad: __u32,
+    pub pio: __u32,
+    _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_coalesced_mmio__bindgen_ty_1() {
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_zone>())).pad as *const _ as usize },
-        12usize,
+        ::std::mem::size_of::<kvm_coalesced_mmio__bindgen_ty_1>(),
+        4usize,
+        concat!("Size of: ", stringify!(kvm_coalesced_mmio__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_coalesced_mmio__bindgen_ty_1>(),
+        4usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_coalesced_mmio__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_coalesced_mmio__bindgen_ty_1>())).pad as *const _ as usize
+        },
+        0usize,
         concat!(
             "Offset of field: ",
-            stringify!(kvm_coalesced_mmio_zone),
+            stringify!(kvm_coalesced_mmio__bindgen_ty_1),
             "::",
             stringify!(pad)
         )
     );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_coalesced_mmio__bindgen_ty_1>())).pio as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_coalesced_mmio__bindgen_ty_1),
+            "::",
+            stringify!(pio)
+        )
+    );
 }
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_coalesced_mmio {
-    pub phys_addr: __u64,
-    pub len: __u32,
-    pub pad: __u32,
-    pub data: [__u8; 8usize],
+impl Default for kvm_coalesced_mmio__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
 }
 #[test]
 fn bindgen_test_layout_kvm_coalesced_mmio() {
@@ -3530,16 +4415,6 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio>())).pad as *const _ as usize },
-        12usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_coalesced_mmio),
-            "::",
-            stringify!(pad)
-        )
-    );
-    assert_eq!(
         unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio>())).data as *const _ as usize },
         16usize,
         concat!(
@@ -3550,14 +4425,16 @@
         )
     );
 }
+impl Default for kvm_coalesced_mmio {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
 #[repr(C)]
-#[derive(Debug, Default)]
 pub struct kvm_coalesced_mmio_ring {
     pub first: __u32,
     pub last: __u32,
     pub coalesced_mmio: __IncompleteArrayField<kvm_coalesced_mmio>,
-    // Manually added to work around rust bindgen issue 684
-    __force_alignment: [u64; 0],
 }
 #[test]
 fn bindgen_test_layout_kvm_coalesced_mmio_ring() {
@@ -3604,6 +4481,11 @@
         )
     );
 }
+impl Default for kvm_coalesced_mmio_ring {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_translation {
@@ -3907,6 +4789,116 @@
     }
 }
 #[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_clear_dirty_log {
+    pub slot: __u32,
+    pub num_pages: __u32,
+    pub first_page: __u64,
+    pub __bindgen_anon_1: kvm_clear_dirty_log__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_clear_dirty_log__bindgen_ty_1 {
+    pub dirty_bitmap: *mut ::std::os::raw::c_void,
+    pub padding2: __u64,
+    _bindgen_union_align: u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_clear_dirty_log__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_clear_dirty_log__bindgen_ty_1>(),
+        8usize,
+        concat!("Size of: ", stringify!(kvm_clear_dirty_log__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_clear_dirty_log__bindgen_ty_1>(),
+        8usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_clear_dirty_log__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_clear_dirty_log__bindgen_ty_1>())).dirty_bitmap as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log__bindgen_ty_1),
+            "::",
+            stringify!(dirty_bitmap)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_clear_dirty_log__bindgen_ty_1>())).padding2 as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log__bindgen_ty_1),
+            "::",
+            stringify!(padding2)
+        )
+    );
+}
+impl Default for kvm_clear_dirty_log__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[test]
+fn bindgen_test_layout_kvm_clear_dirty_log() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_clear_dirty_log>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_clear_dirty_log))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_clear_dirty_log>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_clear_dirty_log))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_clear_dirty_log>())).slot as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log),
+            "::",
+            stringify!(slot)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_clear_dirty_log>())).num_pages as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log),
+            "::",
+            stringify!(num_pages)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_clear_dirty_log>())).first_page as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log),
+            "::",
+            stringify!(first_page)
+        )
+    );
+}
+impl Default for kvm_clear_dirty_log {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
 #[derive(Debug, Default)]
 pub struct kvm_signal_mask {
     pub len: __u32,
@@ -4265,7 +5257,8 @@
     pub exc_access_id: __u8,
     pub per_access_id: __u8,
     pub op_access_id: __u8,
-    pub pad: [__u8; 3usize],
+    pub flags: __u8,
+    pub pad: [__u8; 2usize],
 }
 #[test]
 fn bindgen_test_layout_kvm_s390_pgm_info() {
@@ -4392,12 +5385,22 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).pad as *const _ as usize },
+        unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).flags as *const _ as usize },
         37usize,
         concat!(
             "Offset of field: ",
             stringify!(kvm_s390_pgm_info),
             "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).pad as *const _ as usize },
+        38usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_pgm_info),
+            "::",
             stringify!(pad)
         )
     );
@@ -4608,7 +5611,6 @@
     pub type_: __u64,
     pub u: kvm_s390_irq__bindgen_ty_1,
 }
-
 #[repr(C)]
 #[derive(Copy, Clone)]
 pub union kvm_s390_irq__bindgen_ty_1 {
@@ -4894,7 +5896,7 @@
 pub const kvm_ioeventfd_flag_nr_virtio_ccw_notify: _bindgen_ty_1 = 3;
 pub const kvm_ioeventfd_flag_nr_fast_mmio: _bindgen_ty_1 = 4;
 pub const kvm_ioeventfd_flag_nr_max: _bindgen_ty_1 = 5;
-pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
+pub type _bindgen_ty_1 = u32;
 #[repr(C)]
 #[derive(Copy, Clone)]
 pub struct kvm_ioeventfd {
@@ -5204,7 +6206,8 @@
 pub struct kvm_ppc_smmu_info {
     pub flags: __u64,
     pub slb_size: __u32,
-    pub pad: __u32,
+    pub data_keys: __u16,
+    pub instr_keys: __u16,
     pub sps: [kvm_ppc_one_seg_page_size; 8usize],
 }
 #[test]
@@ -5240,13 +6243,23 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).pad as *const _ as usize },
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).data_keys as *const _ as usize },
         12usize,
         concat!(
             "Offset of field: ",
             stringify!(kvm_ppc_smmu_info),
             "::",
-            stringify!(pad)
+            stringify!(data_keys)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).instr_keys as *const _ as usize },
+        14usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_ppc_smmu_info),
+            "::",
+            stringify!(instr_keys)
         )
     );
     assert_eq!(
@@ -5262,6 +6275,56 @@
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ppc_resize_hpt {
+    pub flags: __u64,
+    pub shift: __u32,
+    pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_resize_hpt() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_ppc_resize_hpt>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_ppc_resize_hpt))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_ppc_resize_hpt>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_ppc_resize_hpt))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_resize_hpt>())).flags as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_ppc_resize_hpt),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_resize_hpt>())).shift as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_ppc_resize_hpt),
+            "::",
+            stringify!(shift)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_resize_hpt>())).pad as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_ppc_resize_hpt),
+            "::",
+            stringify!(pad)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_irq_routing_irqchip {
     pub irqchip: __u32,
     pub pin: __u32,
@@ -5300,12 +6363,64 @@
     );
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Copy, Clone)]
 pub struct kvm_irq_routing_msi {
     pub address_lo: __u32,
     pub address_hi: __u32,
     pub data: __u32,
+    pub __bindgen_anon_1: kvm_irq_routing_msi__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_irq_routing_msi__bindgen_ty_1 {
     pub pad: __u32,
+    pub devid: __u32,
+    _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_msi__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_irq_routing_msi__bindgen_ty_1>(),
+        4usize,
+        concat!("Size of: ", stringify!(kvm_irq_routing_msi__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_irq_routing_msi__bindgen_ty_1>(),
+        4usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_irq_routing_msi__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_irq_routing_msi__bindgen_ty_1>())).pad as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_msi__bindgen_ty_1),
+            "::",
+            stringify!(pad)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_irq_routing_msi__bindgen_ty_1>())).devid as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_msi__bindgen_ty_1),
+            "::",
+            stringify!(devid)
+        )
+    );
+}
+impl Default for kvm_irq_routing_msi__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
 }
 #[test]
 fn bindgen_test_layout_kvm_irq_routing_msi() {
@@ -5349,16 +6464,11 @@
             stringify!(data)
         )
     );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_irq_routing_msi>())).pad as *const _ as usize },
-        12usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_irq_routing_msi),
-            "::",
-            stringify!(pad)
-        )
-    );
+}
+impl Default for kvm_irq_routing_msi {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
@@ -5445,6 +6555,45 @@
     );
 }
 #[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irq_routing_hv_sint {
+    pub vcpu: __u32,
+    pub sint: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_hv_sint() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_irq_routing_hv_sint>(),
+        8usize,
+        concat!("Size of: ", stringify!(kvm_irq_routing_hv_sint))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_irq_routing_hv_sint>(),
+        4usize,
+        concat!("Alignment of ", stringify!(kvm_irq_routing_hv_sint))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_irq_routing_hv_sint>())).vcpu as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_hv_sint),
+            "::",
+            stringify!(vcpu)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_irq_routing_hv_sint>())).sint as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_hv_sint),
+            "::",
+            stringify!(sint)
+        )
+    );
+}
+#[repr(C)]
 #[derive(Copy, Clone)]
 pub struct kvm_irq_routing_entry {
     pub gsi: __u32,
@@ -5459,6 +6608,7 @@
     pub irqchip: kvm_irq_routing_irqchip,
     pub msi: kvm_irq_routing_msi,
     pub adapter: kvm_irq_routing_s390_adapter,
+    pub hv_sint: kvm_irq_routing_hv_sint,
     pub pad: [__u32; 8usize],
     _bindgen_union_align: [u64; 4usize],
 }
@@ -5517,6 +6667,19 @@
     );
     assert_eq!(
         unsafe {
+            &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).hv_sint as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+            "::",
+            stringify!(hv_sint)
+        )
+    );
+    assert_eq!(
+        unsafe {
             &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).pad as *const _ as usize
         },
         0usize,
@@ -5606,8 +6769,6 @@
     pub nr: __u32,
     pub flags: __u32,
     pub entries: __IncompleteArrayField<kvm_irq_routing_entry>,
-    // Manually added to work around rust bindgen issue 684
-    __force_alignment: [u64; 0],
 }
 #[test]
 fn bindgen_test_layout_kvm_irq_routing() {
@@ -5964,7 +7125,8 @@
     pub address_hi: __u32,
     pub data: __u32,
     pub flags: __u32,
-    pub pad: [__u8; 16usize],
+    pub devid: __u32,
+    pub pad: [__u8; 12usize],
 }
 #[test]
 fn bindgen_test_layout_kvm_msi() {
@@ -6019,12 +7181,22 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_msi>())).pad as *const _ as usize },
+        unsafe { &(*(::std::ptr::null::<kvm_msi>())).devid as *const _ as usize },
         16usize,
         concat!(
             "Offset of field: ",
             stringify!(kvm_msi),
             "::",
+            stringify!(devid)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_msi>())).pad as *const _ as usize },
+        20usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_msi),
+            "::",
             stringify!(pad)
         )
     );
@@ -6186,8 +7358,49 @@
 pub const kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2: kvm_device_type = 5;
 pub const kvm_device_type_KVM_DEV_TYPE_FLIC: kvm_device_type = 6;
 pub const kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3: kvm_device_type = 7;
-pub const kvm_device_type_KVM_DEV_TYPE_MAX: kvm_device_type = 8;
-pub type kvm_device_type = ::std::os::raw::c_uint;
+pub const kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS: kvm_device_type = 8;
+pub const kvm_device_type_KVM_DEV_TYPE_XIVE: kvm_device_type = 9;
+pub const kvm_device_type_KVM_DEV_TYPE_MAX: kvm_device_type = 10;
+pub type kvm_device_type = u32;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vfio_spapr_tce {
+    pub groupfd: __s32,
+    pub tablefd: __s32,
+}
+#[test]
+fn bindgen_test_layout_kvm_vfio_spapr_tce() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_vfio_spapr_tce>(),
+        8usize,
+        concat!("Size of: ", stringify!(kvm_vfio_spapr_tce))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_vfio_spapr_tce>(),
+        4usize,
+        concat!("Alignment of ", stringify!(kvm_vfio_spapr_tce))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_vfio_spapr_tce>())).groupfd as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vfio_spapr_tce),
+            "::",
+            stringify!(groupfd)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_vfio_spapr_tce>())).tablefd as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vfio_spapr_tce),
+            "::",
+            stringify!(tablefd)
+        )
+    );
+}
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_s390_ucas_mapping {
@@ -6239,6 +7452,482 @@
     );
 }
 #[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_enc_region {
+    pub addr: __u64,
+    pub size: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_enc_region() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_enc_region>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_enc_region))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_enc_region>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_enc_region))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_enc_region>())).addr as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_enc_region),
+            "::",
+            stringify!(addr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_enc_region>())).size as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_enc_region),
+            "::",
+            stringify!(size)
+        )
+    );
+}
+pub const sev_cmd_id_KVM_SEV_INIT: sev_cmd_id = 0;
+pub const sev_cmd_id_KVM_SEV_ES_INIT: sev_cmd_id = 1;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_START: sev_cmd_id = 2;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_UPDATE_DATA: sev_cmd_id = 3;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_UPDATE_VMSA: sev_cmd_id = 4;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_SECRET: sev_cmd_id = 5;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_MEASURE: sev_cmd_id = 6;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_FINISH: sev_cmd_id = 7;
+pub const sev_cmd_id_KVM_SEV_SEND_START: sev_cmd_id = 8;
+pub const sev_cmd_id_KVM_SEV_SEND_UPDATE_DATA: sev_cmd_id = 9;
+pub const sev_cmd_id_KVM_SEV_SEND_UPDATE_VMSA: sev_cmd_id = 10;
+pub const sev_cmd_id_KVM_SEV_SEND_FINISH: sev_cmd_id = 11;
+pub const sev_cmd_id_KVM_SEV_RECEIVE_START: sev_cmd_id = 12;
+pub const sev_cmd_id_KVM_SEV_RECEIVE_UPDATE_DATA: sev_cmd_id = 13;
+pub const sev_cmd_id_KVM_SEV_RECEIVE_UPDATE_VMSA: sev_cmd_id = 14;
+pub const sev_cmd_id_KVM_SEV_RECEIVE_FINISH: sev_cmd_id = 15;
+pub const sev_cmd_id_KVM_SEV_GUEST_STATUS: sev_cmd_id = 16;
+pub const sev_cmd_id_KVM_SEV_DBG_DECRYPT: sev_cmd_id = 17;
+pub const sev_cmd_id_KVM_SEV_DBG_ENCRYPT: sev_cmd_id = 18;
+pub const sev_cmd_id_KVM_SEV_CERT_EXPORT: sev_cmd_id = 19;
+pub const sev_cmd_id_KVM_SEV_NR_MAX: sev_cmd_id = 20;
+pub type sev_cmd_id = u32;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_cmd {
+    pub id: __u32,
+    pub data: __u64,
+    pub error: __u32,
+    pub sev_fd: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_cmd() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_cmd>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_sev_cmd))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_cmd>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_cmd))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_cmd>())).id as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_cmd),
+            "::",
+            stringify!(id)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_cmd>())).data as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_cmd),
+            "::",
+            stringify!(data)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_cmd>())).error as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_cmd),
+            "::",
+            stringify!(error)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_cmd>())).sev_fd as *const _ as usize },
+        20usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_cmd),
+            "::",
+            stringify!(sev_fd)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_launch_start {
+    pub handle: __u32,
+    pub policy: __u32,
+    pub dh_uaddr: __u64,
+    pub dh_len: __u32,
+    pub session_uaddr: __u64,
+    pub session_len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_launch_start() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_launch_start>(),
+        40usize,
+        concat!("Size of: ", stringify!(kvm_sev_launch_start))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_launch_start>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_launch_start))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_start>())).handle as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(handle)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_start>())).policy as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(policy)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_start>())).dh_uaddr as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(dh_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_start>())).dh_len as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(dh_len)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_start>())).session_uaddr as *const _ as usize
+        },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(session_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_start>())).session_len as *const _ as usize
+        },
+        32usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(session_len)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_launch_update_data {
+    pub uaddr: __u64,
+    pub len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_launch_update_data() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_launch_update_data>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_sev_launch_update_data))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_launch_update_data>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_launch_update_data))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_update_data>())).uaddr as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_update_data),
+            "::",
+            stringify!(uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_update_data>())).len as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_update_data),
+            "::",
+            stringify!(len)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_launch_secret {
+    pub hdr_uaddr: __u64,
+    pub hdr_len: __u32,
+    pub guest_uaddr: __u64,
+    pub guest_len: __u32,
+    pub trans_uaddr: __u64,
+    pub trans_len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_launch_secret() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_launch_secret>(),
+        48usize,
+        concat!("Size of: ", stringify!(kvm_sev_launch_secret))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_launch_secret>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_launch_secret))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_secret>())).hdr_uaddr as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(hdr_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_secret>())).hdr_len as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(hdr_len)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_secret>())).guest_uaddr as *const _ as usize
+        },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(guest_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_secret>())).guest_len as *const _ as usize },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(guest_len)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_secret>())).trans_uaddr as *const _ as usize
+        },
+        32usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(trans_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_secret>())).trans_len as *const _ as usize },
+        40usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(trans_len)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_launch_measure {
+    pub uaddr: __u64,
+    pub len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_launch_measure() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_launch_measure>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_sev_launch_measure))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_launch_measure>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_launch_measure))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_measure>())).uaddr as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_measure),
+            "::",
+            stringify!(uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_measure>())).len as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_measure),
+            "::",
+            stringify!(len)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_guest_status {
+    pub handle: __u32,
+    pub policy: __u32,
+    pub state: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_guest_status() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_guest_status>(),
+        12usize,
+        concat!("Size of: ", stringify!(kvm_sev_guest_status))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_guest_status>(),
+        4usize,
+        concat!("Alignment of ", stringify!(kvm_sev_guest_status))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_guest_status>())).handle as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_guest_status),
+            "::",
+            stringify!(handle)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_guest_status>())).policy as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_guest_status),
+            "::",
+            stringify!(policy)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_guest_status>())).state as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_guest_status),
+            "::",
+            stringify!(state)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_dbg {
+    pub src_uaddr: __u64,
+    pub dst_uaddr: __u64,
+    pub len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_dbg() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_dbg>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_sev_dbg))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_dbg>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_dbg))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_dbg>())).src_uaddr as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_dbg),
+            "::",
+            stringify!(src_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_dbg>())).dst_uaddr as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_dbg),
+            "::",
+            stringify!(dst_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_dbg>())).len as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_dbg),
+            "::",
+            stringify!(len)
+        )
+    );
+}
+#[repr(C)]
 #[derive(Copy, Clone)]
 pub struct kvm_assigned_pci_dev {
     pub assigned_dev_id: __u32,
@@ -6578,4 +8267,65 @@
         )
     );
 }
-pub type __uint128_t = [u64; 2];
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_hyperv_eventfd {
+    pub conn_id: __u32,
+    pub fd: __s32,
+    pub flags: __u32,
+    pub padding: [__u32; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_eventfd() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_eventfd>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_hyperv_eventfd))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_eventfd>(),
+        4usize,
+        concat!("Alignment of ", stringify!(kvm_hyperv_eventfd))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_eventfd>())).conn_id as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_eventfd),
+            "::",
+            stringify!(conn_id)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_eventfd>())).fd as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_eventfd),
+            "::",
+            stringify!(fd)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_eventfd>())).flags as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_eventfd),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_eventfd>())).padding as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_eventfd),
+            "::",
+            stringify!(padding)
+        )
+    );
+}
+pub type __uint128_t = u128;
diff --git a/kvm_sys/src/x86/bindings.rs b/kvm_sys/src/x86/bindings.rs
index 629d83a..789cc2e 100644
--- a/kvm_sys/src/x86/bindings.rs
+++ b/kvm_sys/src/x86/bindings.rs
@@ -2,7 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/* automatically generated by rust-bindgen */
+/*
+ * automatically generated by bindgen 0.49.2.
+ * From chromeos-linux v5.4 include/linux/kvm.h
+ * $ cd /path/to/kernel/v5.4/
+ * $ make headers_install ARCH=x86 INSTALL_HDR_PATH=x86_v5_4_headers
+ * $ cd x86_v5_4_headers
+ * $ bindgen --with-derive-default -o bindings.rs include/linux/kvm.h -- -Iinclude
+ */
 
 #[repr(C)]
 #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
@@ -13,7 +20,6 @@
     storage: Storage,
     align: [Align; 0],
 }
-
 impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
 where
     Storage: AsRef<[u8]> + AsMut<[u8]>,
@@ -22,74 +28,78 @@
     pub fn new(storage: Storage) -> Self {
         Self { storage, align: [] }
     }
-
     #[inline]
     pub fn get_bit(&self, index: usize) -> bool {
         debug_assert!(index / 8 < self.storage.as_ref().len());
-
         let byte_index = index / 8;
         let byte = self.storage.as_ref()[byte_index];
-
-        let bit_index = index % 8;
+        let bit_index = if cfg!(target_endian = "big") {
+            7 - (index % 8)
+        } else {
+            index % 8
+        };
         let mask = 1 << bit_index;
-
         byte & mask == mask
     }
-
     #[inline]
     pub fn set_bit(&mut self, index: usize, val: bool) {
         debug_assert!(index / 8 < self.storage.as_ref().len());
-
         let byte_index = index / 8;
         let byte = &mut self.storage.as_mut()[byte_index];
-
-        let bit_index = index % 8;
+        let bit_index = if cfg!(target_endian = "big") {
+            7 - (index % 8)
+        } else {
+            index % 8
+        };
         let mask = 1 << bit_index;
-
         if val {
             *byte |= mask;
         } else {
             *byte &= !mask;
         }
     }
-
     #[inline]
     pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
         debug_assert!(bit_width <= 64);
         debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
         debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
-
         let mut val = 0;
-
         for i in 0..(bit_width as usize) {
             if self.get_bit(i + bit_offset) {
-                val |= 1 << i;
+                let index = if cfg!(target_endian = "big") {
+                    bit_width as usize - 1 - i
+                } else {
+                    i
+                };
+                val |= 1 << index;
             }
         }
-
         val
     }
-
     #[inline]
     pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
         debug_assert!(bit_width <= 64);
         debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
         debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
-
         for i in 0..(bit_width as usize) {
             let mask = 1 << i;
             let val_bit_is_set = val & mask == mask;
-            self.set_bit(i + bit_offset, val_bit_is_set);
+            let index = if cfg!(target_endian = "big") {
+                bit_width as usize - 1 - i
+            } else {
+                i
+            };
+            self.set_bit(index + bit_offset, val_bit_is_set);
         }
     }
 }
 #[repr(C)]
 #[derive(Default)]
-pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
 impl<T> __IncompleteArrayField<T> {
     #[inline]
     pub fn new() -> Self {
-        __IncompleteArrayField(::std::marker::PhantomData)
+        __IncompleteArrayField(::std::marker::PhantomData, [])
     }
     #[inline]
     pub unsafe fn as_ptr(&self) -> *const T {
@@ -109,7 +119,7 @@
     }
 }
 impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
-    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         fmt.write_str("__IncompleteArrayField")
     }
 }
@@ -119,360 +129,502 @@
         Self::new()
     }
 }
-impl<T> ::std::marker::Copy for __IncompleteArrayField<T> {}
-pub const __BITS_PER_LONG: ::std::os::raw::c_uint = 64;
-pub const __FD_SETSIZE: ::std::os::raw::c_uint = 1024;
-pub const _IOC_NRBITS: ::std::os::raw::c_uint = 8;
-pub const _IOC_TYPEBITS: ::std::os::raw::c_uint = 8;
-pub const _IOC_SIZEBITS: ::std::os::raw::c_uint = 14;
-pub const _IOC_DIRBITS: ::std::os::raw::c_uint = 2;
-pub const _IOC_NRMASK: ::std::os::raw::c_uint = 255;
-pub const _IOC_TYPEMASK: ::std::os::raw::c_uint = 255;
-pub const _IOC_SIZEMASK: ::std::os::raw::c_uint = 16383;
-pub const _IOC_DIRMASK: ::std::os::raw::c_uint = 3;
-pub const _IOC_NRSHIFT: ::std::os::raw::c_uint = 0;
-pub const _IOC_TYPESHIFT: ::std::os::raw::c_uint = 8;
-pub const _IOC_SIZESHIFT: ::std::os::raw::c_uint = 16;
-pub const _IOC_DIRSHIFT: ::std::os::raw::c_uint = 30;
-pub const _IOC_NONE: ::std::os::raw::c_uint = 0;
-pub const _IOC_WRITE: ::std::os::raw::c_uint = 1;
-pub const _IOC_READ: ::std::os::raw::c_uint = 2;
-pub const IOC_IN: ::std::os::raw::c_uint = 1073741824;
-pub const IOC_OUT: ::std::os::raw::c_uint = 2147483648;
-pub const IOC_INOUT: ::std::os::raw::c_uint = 3221225472;
-pub const IOCSIZE_MASK: ::std::os::raw::c_uint = 1073676288;
-pub const IOCSIZE_SHIFT: ::std::os::raw::c_uint = 16;
-pub const DE_VECTOR: ::std::os::raw::c_uint = 0;
-pub const DB_VECTOR: ::std::os::raw::c_uint = 1;
-pub const BP_VECTOR: ::std::os::raw::c_uint = 3;
-pub const OF_VECTOR: ::std::os::raw::c_uint = 4;
-pub const BR_VECTOR: ::std::os::raw::c_uint = 5;
-pub const UD_VECTOR: ::std::os::raw::c_uint = 6;
-pub const NM_VECTOR: ::std::os::raw::c_uint = 7;
-pub const DF_VECTOR: ::std::os::raw::c_uint = 8;
-pub const TS_VECTOR: ::std::os::raw::c_uint = 10;
-pub const NP_VECTOR: ::std::os::raw::c_uint = 11;
-pub const SS_VECTOR: ::std::os::raw::c_uint = 12;
-pub const GP_VECTOR: ::std::os::raw::c_uint = 13;
-pub const PF_VECTOR: ::std::os::raw::c_uint = 14;
-pub const MF_VECTOR: ::std::os::raw::c_uint = 16;
-pub const AC_VECTOR: ::std::os::raw::c_uint = 17;
-pub const MC_VECTOR: ::std::os::raw::c_uint = 18;
-pub const XM_VECTOR: ::std::os::raw::c_uint = 19;
-pub const VE_VECTOR: ::std::os::raw::c_uint = 20;
-pub const KVM_NR_INTERRUPTS: ::std::os::raw::c_uint = 256;
-pub const KVM_IOAPIC_NUM_PINS: ::std::os::raw::c_uint = 24;
-pub const KVM_IRQCHIP_PIC_MASTER: ::std::os::raw::c_uint = 0;
-pub const KVM_IRQCHIP_PIC_SLAVE: ::std::os::raw::c_uint = 1;
-pub const KVM_IRQCHIP_IOAPIC: ::std::os::raw::c_uint = 2;
-pub const KVM_NR_IRQCHIPS: ::std::os::raw::c_uint = 3;
-pub const KVM_RUN_X86_SMM: ::std::os::raw::c_uint = 1;
-pub const KVM_APIC_REG_SIZE: ::std::os::raw::c_uint = 1024;
-pub const KVM_CPUID_FLAG_SIGNIFCANT_INDEX: ::std::os::raw::c_uint = 1;
-pub const KVM_CPUID_FLAG_STATEFUL_FUNC: ::std::os::raw::c_uint = 2;
-pub const KVM_CPUID_FLAG_STATE_READ_NEXT: ::std::os::raw::c_uint = 4;
-pub const KVM_GUESTDBG_USE_SW_BP: ::std::os::raw::c_uint = 65536;
-pub const KVM_GUESTDBG_USE_HW_BP: ::std::os::raw::c_uint = 131072;
-pub const KVM_GUESTDBG_INJECT_DB: ::std::os::raw::c_uint = 262144;
-pub const KVM_GUESTDBG_INJECT_BP: ::std::os::raw::c_uint = 524288;
-pub const KVM_PIT_FLAGS_HPET_LEGACY: ::std::os::raw::c_uint = 1;
-pub const KVM_VCPUEVENT_VALID_NMI_PENDING: ::std::os::raw::c_uint = 1;
-pub const KVM_VCPUEVENT_VALID_SIPI_VECTOR: ::std::os::raw::c_uint = 2;
-pub const KVM_VCPUEVENT_VALID_SHADOW: ::std::os::raw::c_uint = 4;
-pub const KVM_VCPUEVENT_VALID_SMM: ::std::os::raw::c_uint = 8;
-pub const KVM_X86_SHADOW_INT_MOV_SS: ::std::os::raw::c_uint = 1;
-pub const KVM_X86_SHADOW_INT_STI: ::std::os::raw::c_uint = 2;
-pub const KVM_MAX_XCRS: ::std::os::raw::c_uint = 16;
-pub const KVM_X86_QUIRK_LINT0_REENABLED: ::std::os::raw::c_uint = 1;
-pub const KVM_X86_QUIRK_CD_NW_CLEARED: ::std::os::raw::c_uint = 2;
-pub const KVM_API_VERSION: ::std::os::raw::c_uint = 12;
-pub const KVM_TRC_SHIFT: ::std::os::raw::c_uint = 16;
-pub const KVM_TRC_ENTRYEXIT: ::std::os::raw::c_uint = 65536;
-pub const KVM_TRC_HANDLER: ::std::os::raw::c_uint = 131072;
-pub const KVM_TRC_VMENTRY: ::std::os::raw::c_uint = 65537;
-pub const KVM_TRC_VMEXIT: ::std::os::raw::c_uint = 65538;
-pub const KVM_TRC_PAGE_FAULT: ::std::os::raw::c_uint = 131073;
-pub const KVM_TRC_HEAD_SIZE: ::std::os::raw::c_uint = 12;
-pub const KVM_TRC_CYCLE_SIZE: ::std::os::raw::c_uint = 8;
-pub const KVM_TRC_EXTRA_MAX: ::std::os::raw::c_uint = 7;
-pub const KVM_TRC_INJ_VIRQ: ::std::os::raw::c_uint = 131074;
-pub const KVM_TRC_REDELIVER_EVT: ::std::os::raw::c_uint = 131075;
-pub const KVM_TRC_PEND_INTR: ::std::os::raw::c_uint = 131076;
-pub const KVM_TRC_IO_READ: ::std::os::raw::c_uint = 131077;
-pub const KVM_TRC_IO_WRITE: ::std::os::raw::c_uint = 131078;
-pub const KVM_TRC_CR_READ: ::std::os::raw::c_uint = 131079;
-pub const KVM_TRC_CR_WRITE: ::std::os::raw::c_uint = 131080;
-pub const KVM_TRC_DR_READ: ::std::os::raw::c_uint = 131081;
-pub const KVM_TRC_DR_WRITE: ::std::os::raw::c_uint = 131082;
-pub const KVM_TRC_MSR_READ: ::std::os::raw::c_uint = 131083;
-pub const KVM_TRC_MSR_WRITE: ::std::os::raw::c_uint = 131084;
-pub const KVM_TRC_CPUID: ::std::os::raw::c_uint = 131085;
-pub const KVM_TRC_INTR: ::std::os::raw::c_uint = 131086;
-pub const KVM_TRC_NMI: ::std::os::raw::c_uint = 131087;
-pub const KVM_TRC_VMMCALL: ::std::os::raw::c_uint = 131088;
-pub const KVM_TRC_HLT: ::std::os::raw::c_uint = 131089;
-pub const KVM_TRC_CLTS: ::std::os::raw::c_uint = 131090;
-pub const KVM_TRC_LMSW: ::std::os::raw::c_uint = 131091;
-pub const KVM_TRC_APIC_ACCESS: ::std::os::raw::c_uint = 131092;
-pub const KVM_TRC_TDP_FAULT: ::std::os::raw::c_uint = 131093;
-pub const KVM_TRC_GTLB_WRITE: ::std::os::raw::c_uint = 131094;
-pub const KVM_TRC_STLB_WRITE: ::std::os::raw::c_uint = 131095;
-pub const KVM_TRC_STLB_INVAL: ::std::os::raw::c_uint = 131096;
-pub const KVM_TRC_PPC_INSTR: ::std::os::raw::c_uint = 131097;
-pub const KVM_MEM_LOG_DIRTY_PAGES: ::std::os::raw::c_uint = 1;
-pub const KVM_MEM_READONLY: ::std::os::raw::c_uint = 2;
-pub const KVM_PIT_SPEAKER_DUMMY: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_GET_SKEYS_NONE: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_SKEYS_MAX: ::std::os::raw::c_uint = 1048576;
-pub const KVM_EXIT_UNKNOWN: ::std::os::raw::c_uint = 0;
-pub const KVM_EXIT_EXCEPTION: ::std::os::raw::c_uint = 1;
-pub const KVM_EXIT_IO: ::std::os::raw::c_uint = 2;
-pub const KVM_EXIT_HYPERCALL: ::std::os::raw::c_uint = 3;
-pub const KVM_EXIT_DEBUG: ::std::os::raw::c_uint = 4;
-pub const KVM_EXIT_HLT: ::std::os::raw::c_uint = 5;
-pub const KVM_EXIT_MMIO: ::std::os::raw::c_uint = 6;
-pub const KVM_EXIT_IRQ_WINDOW_OPEN: ::std::os::raw::c_uint = 7;
-pub const KVM_EXIT_SHUTDOWN: ::std::os::raw::c_uint = 8;
-pub const KVM_EXIT_FAIL_ENTRY: ::std::os::raw::c_uint = 9;
-pub const KVM_EXIT_INTR: ::std::os::raw::c_uint = 10;
-pub const KVM_EXIT_SET_TPR: ::std::os::raw::c_uint = 11;
-pub const KVM_EXIT_TPR_ACCESS: ::std::os::raw::c_uint = 12;
-pub const KVM_EXIT_S390_SIEIC: ::std::os::raw::c_uint = 13;
-pub const KVM_EXIT_S390_RESET: ::std::os::raw::c_uint = 14;
-pub const KVM_EXIT_DCR: ::std::os::raw::c_uint = 15;
-pub const KVM_EXIT_NMI: ::std::os::raw::c_uint = 16;
-pub const KVM_EXIT_INTERNAL_ERROR: ::std::os::raw::c_uint = 17;
-pub const KVM_EXIT_OSI: ::std::os::raw::c_uint = 18;
-pub const KVM_EXIT_PAPR_HCALL: ::std::os::raw::c_uint = 19;
-pub const KVM_EXIT_S390_UCONTROL: ::std::os::raw::c_uint = 20;
-pub const KVM_EXIT_WATCHDOG: ::std::os::raw::c_uint = 21;
-pub const KVM_EXIT_S390_TSCH: ::std::os::raw::c_uint = 22;
-pub const KVM_EXIT_EPR: ::std::os::raw::c_uint = 23;
-pub const KVM_EXIT_SYSTEM_EVENT: ::std::os::raw::c_uint = 24;
-pub const KVM_EXIT_S390_STSI: ::std::os::raw::c_uint = 25;
-pub const KVM_EXIT_IOAPIC_EOI: ::std::os::raw::c_uint = 26;
-pub const KVM_EXIT_HYPERV: ::std::os::raw::c_uint = 27;
-pub const KVM_INTERNAL_ERROR_EMULATION: ::std::os::raw::c_uint = 1;
-pub const KVM_INTERNAL_ERROR_SIMUL_EX: ::std::os::raw::c_uint = 2;
-pub const KVM_INTERNAL_ERROR_DELIVERY_EV: ::std::os::raw::c_uint = 3;
-pub const KVM_EXIT_IO_IN: ::std::os::raw::c_uint = 0;
-pub const KVM_EXIT_IO_OUT: ::std::os::raw::c_uint = 1;
-pub const KVM_EXIT_HYPERV_SYNIC: ::std::os::raw::c_uint = 1;
-pub const KVM_EXIT_HYPERV_HCALL: ::std::os::raw::c_uint = 2;
-pub const KVM_S390_RESET_POR: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_RESET_CLEAR: ::std::os::raw::c_uint = 2;
-pub const KVM_S390_RESET_SUBSYSTEM: ::std::os::raw::c_uint = 4;
-pub const KVM_S390_RESET_CPU_INIT: ::std::os::raw::c_uint = 8;
-pub const KVM_S390_RESET_IPL: ::std::os::raw::c_uint = 16;
-pub const KVM_SYSTEM_EVENT_SHUTDOWN: ::std::os::raw::c_uint = 1;
-pub const KVM_SYSTEM_EVENT_RESET: ::std::os::raw::c_uint = 2;
-pub const KVM_SYSTEM_EVENT_CRASH: ::std::os::raw::c_uint = 3;
-pub const KVM_S390_MEMOP_LOGICAL_READ: ::std::os::raw::c_uint = 0;
-pub const KVM_S390_MEMOP_LOGICAL_WRITE: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_MEMOP_F_CHECK_ONLY: ::std::os::raw::c_uint = 1;
-pub const KVM_S390_MEMOP_F_INJECT_EXCEPTION: ::std::os::raw::c_uint = 2;
-pub const KVM_MP_STATE_RUNNABLE: ::std::os::raw::c_uint = 0;
-pub const KVM_MP_STATE_UNINITIALIZED: ::std::os::raw::c_uint = 1;
-pub const KVM_MP_STATE_INIT_RECEIVED: ::std::os::raw::c_uint = 2;
-pub const KVM_MP_STATE_HALTED: ::std::os::raw::c_uint = 3;
-pub const KVM_MP_STATE_SIPI_RECEIVED: ::std::os::raw::c_uint = 4;
-pub const KVM_MP_STATE_STOPPED: ::std::os::raw::c_uint = 5;
-pub const KVM_MP_STATE_CHECK_STOP: ::std::os::raw::c_uint = 6;
-pub const KVM_MP_STATE_OPERATING: ::std::os::raw::c_uint = 7;
-pub const KVM_MP_STATE_LOAD: ::std::os::raw::c_uint = 8;
-pub const KVM_S390_SIGP_STOP: ::std::os::raw::c_uint = 4294836224;
-pub const KVM_S390_PROGRAM_INT: ::std::os::raw::c_uint = 4294836225;
-pub const KVM_S390_SIGP_SET_PREFIX: ::std::os::raw::c_uint = 4294836226;
-pub const KVM_S390_RESTART: ::std::os::raw::c_uint = 4294836227;
-pub const KVM_S390_INT_PFAULT_INIT: ::std::os::raw::c_uint = 4294836228;
-pub const KVM_S390_INT_PFAULT_DONE: ::std::os::raw::c_uint = 4294836229;
-pub const KVM_S390_MCHK: ::std::os::raw::c_uint = 4294840320;
-pub const KVM_S390_INT_CLOCK_COMP: ::std::os::raw::c_uint = 4294905860;
-pub const KVM_S390_INT_CPU_TIMER: ::std::os::raw::c_uint = 4294905861;
-pub const KVM_S390_INT_VIRTIO: ::std::os::raw::c_uint = 4294911491;
-pub const KVM_S390_INT_SERVICE: ::std::os::raw::c_uint = 4294910977;
-pub const KVM_S390_INT_EMERGENCY: ::std::os::raw::c_uint = 4294906369;
-pub const KVM_S390_INT_EXTERNAL_CALL: ::std::os::raw::c_uint = 4294906370;
-pub const KVM_S390_INT_IO_MIN: ::std::os::raw::c_uint = 0;
-pub const KVM_S390_INT_IO_MAX: ::std::os::raw::c_uint = 4294836223;
-pub const KVM_S390_INT_IO_AI_MASK: ::std::os::raw::c_uint = 67108864;
-pub const KVM_S390_STOP_FLAG_STORE_STATUS: ::std::os::raw::c_uint = 1;
-pub const KVM_GUESTDBG_ENABLE: ::std::os::raw::c_uint = 1;
-pub const KVM_GUESTDBG_SINGLESTEP: ::std::os::raw::c_uint = 2;
-pub const KVM_PPC_PAGE_SIZES_MAX_SZ: ::std::os::raw::c_uint = 8;
-pub const KVM_PPC_PAGE_SIZES_REAL: ::std::os::raw::c_uint = 1;
-pub const KVM_PPC_1T_SEGMENTS: ::std::os::raw::c_uint = 2;
-pub const KVM_PPC_PVINFO_FLAGS_EV_IDLE: ::std::os::raw::c_uint = 1;
-pub const KVMIO: ::std::os::raw::c_uint = 174;
-pub const KVM_VM_S390_UCONTROL: ::std::os::raw::c_uint = 1;
-pub const KVM_VM_PPC_HV: ::std::os::raw::c_uint = 1;
-pub const KVM_VM_PPC_PR: ::std::os::raw::c_uint = 2;
-pub const KVM_S390_SIE_PAGE_OFFSET: ::std::os::raw::c_uint = 1;
-pub const KVM_CAP_IRQCHIP: ::std::os::raw::c_uint = 0;
-pub const KVM_CAP_HLT: ::std::os::raw::c_uint = 1;
-pub const KVM_CAP_MMU_SHADOW_CACHE_CONTROL: ::std::os::raw::c_uint = 2;
-pub const KVM_CAP_USER_MEMORY: ::std::os::raw::c_uint = 3;
-pub const KVM_CAP_SET_TSS_ADDR: ::std::os::raw::c_uint = 4;
-pub const KVM_CAP_VAPIC: ::std::os::raw::c_uint = 6;
-pub const KVM_CAP_EXT_CPUID: ::std::os::raw::c_uint = 7;
-pub const KVM_CAP_CLOCKSOURCE: ::std::os::raw::c_uint = 8;
-pub const KVM_CAP_NR_VCPUS: ::std::os::raw::c_uint = 9;
-pub const KVM_CAP_NR_MEMSLOTS: ::std::os::raw::c_uint = 10;
-pub const KVM_CAP_PIT: ::std::os::raw::c_uint = 11;
-pub const KVM_CAP_NOP_IO_DELAY: ::std::os::raw::c_uint = 12;
-pub const KVM_CAP_PV_MMU: ::std::os::raw::c_uint = 13;
-pub const KVM_CAP_MP_STATE: ::std::os::raw::c_uint = 14;
-pub const KVM_CAP_COALESCED_MMIO: ::std::os::raw::c_uint = 15;
-pub const KVM_CAP_SYNC_MMU: ::std::os::raw::c_uint = 16;
-pub const KVM_CAP_IOMMU: ::std::os::raw::c_uint = 18;
-pub const KVM_CAP_DESTROY_MEMORY_REGION_WORKS: ::std::os::raw::c_uint = 21;
-pub const KVM_CAP_USER_NMI: ::std::os::raw::c_uint = 22;
-pub const KVM_CAP_SET_GUEST_DEBUG: ::std::os::raw::c_uint = 23;
-pub const KVM_CAP_REINJECT_CONTROL: ::std::os::raw::c_uint = 24;
-pub const KVM_CAP_IRQ_ROUTING: ::std::os::raw::c_uint = 25;
-pub const KVM_CAP_IRQ_INJECT_STATUS: ::std::os::raw::c_uint = 26;
-pub const KVM_CAP_ASSIGN_DEV_IRQ: ::std::os::raw::c_uint = 29;
-pub const KVM_CAP_JOIN_MEMORY_REGIONS_WORKS: ::std::os::raw::c_uint = 30;
-pub const KVM_CAP_MCE: ::std::os::raw::c_uint = 31;
-pub const KVM_CAP_IRQFD: ::std::os::raw::c_uint = 32;
-pub const KVM_CAP_PIT2: ::std::os::raw::c_uint = 33;
-pub const KVM_CAP_SET_BOOT_CPU_ID: ::std::os::raw::c_uint = 34;
-pub const KVM_CAP_PIT_STATE2: ::std::os::raw::c_uint = 35;
-pub const KVM_CAP_IOEVENTFD: ::std::os::raw::c_uint = 36;
-pub const KVM_CAP_SET_IDENTITY_MAP_ADDR: ::std::os::raw::c_uint = 37;
-pub const KVM_CAP_XEN_HVM: ::std::os::raw::c_uint = 38;
-pub const KVM_CAP_ADJUST_CLOCK: ::std::os::raw::c_uint = 39;
-pub const KVM_CAP_INTERNAL_ERROR_DATA: ::std::os::raw::c_uint = 40;
-pub const KVM_CAP_VCPU_EVENTS: ::std::os::raw::c_uint = 41;
-pub const KVM_CAP_S390_PSW: ::std::os::raw::c_uint = 42;
-pub const KVM_CAP_PPC_SEGSTATE: ::std::os::raw::c_uint = 43;
-pub const KVM_CAP_HYPERV: ::std::os::raw::c_uint = 44;
-pub const KVM_CAP_HYPERV_VAPIC: ::std::os::raw::c_uint = 45;
-pub const KVM_CAP_HYPERV_SPIN: ::std::os::raw::c_uint = 46;
-pub const KVM_CAP_PCI_SEGMENT: ::std::os::raw::c_uint = 47;
-pub const KVM_CAP_PPC_PAIRED_SINGLES: ::std::os::raw::c_uint = 48;
-pub const KVM_CAP_INTR_SHADOW: ::std::os::raw::c_uint = 49;
-pub const KVM_CAP_DEBUGREGS: ::std::os::raw::c_uint = 50;
-pub const KVM_CAP_X86_ROBUST_SINGLESTEP: ::std::os::raw::c_uint = 51;
-pub const KVM_CAP_PPC_OSI: ::std::os::raw::c_uint = 52;
-pub const KVM_CAP_PPC_UNSET_IRQ: ::std::os::raw::c_uint = 53;
-pub const KVM_CAP_ENABLE_CAP: ::std::os::raw::c_uint = 54;
-pub const KVM_CAP_XSAVE: ::std::os::raw::c_uint = 55;
-pub const KVM_CAP_XCRS: ::std::os::raw::c_uint = 56;
-pub const KVM_CAP_PPC_GET_PVINFO: ::std::os::raw::c_uint = 57;
-pub const KVM_CAP_PPC_IRQ_LEVEL: ::std::os::raw::c_uint = 58;
-pub const KVM_CAP_ASYNC_PF: ::std::os::raw::c_uint = 59;
-pub const KVM_CAP_TSC_CONTROL: ::std::os::raw::c_uint = 60;
-pub const KVM_CAP_GET_TSC_KHZ: ::std::os::raw::c_uint = 61;
-pub const KVM_CAP_PPC_BOOKE_SREGS: ::std::os::raw::c_uint = 62;
-pub const KVM_CAP_SPAPR_TCE: ::std::os::raw::c_uint = 63;
-pub const KVM_CAP_PPC_SMT: ::std::os::raw::c_uint = 64;
-pub const KVM_CAP_PPC_RMA: ::std::os::raw::c_uint = 65;
-pub const KVM_CAP_MAX_VCPUS: ::std::os::raw::c_uint = 66;
-pub const KVM_CAP_PPC_HIOR: ::std::os::raw::c_uint = 67;
-pub const KVM_CAP_PPC_PAPR: ::std::os::raw::c_uint = 68;
-pub const KVM_CAP_SW_TLB: ::std::os::raw::c_uint = 69;
-pub const KVM_CAP_ONE_REG: ::std::os::raw::c_uint = 70;
-pub const KVM_CAP_S390_GMAP: ::std::os::raw::c_uint = 71;
-pub const KVM_CAP_TSC_DEADLINE_TIMER: ::std::os::raw::c_uint = 72;
-pub const KVM_CAP_S390_UCONTROL: ::std::os::raw::c_uint = 73;
-pub const KVM_CAP_SYNC_REGS: ::std::os::raw::c_uint = 74;
-pub const KVM_CAP_PCI_2_3: ::std::os::raw::c_uint = 75;
-pub const KVM_CAP_KVMCLOCK_CTRL: ::std::os::raw::c_uint = 76;
-pub const KVM_CAP_SIGNAL_MSI: ::std::os::raw::c_uint = 77;
-pub const KVM_CAP_PPC_GET_SMMU_INFO: ::std::os::raw::c_uint = 78;
-pub const KVM_CAP_S390_COW: ::std::os::raw::c_uint = 79;
-pub const KVM_CAP_PPC_ALLOC_HTAB: ::std::os::raw::c_uint = 80;
-pub const KVM_CAP_READONLY_MEM: ::std::os::raw::c_uint = 81;
-pub const KVM_CAP_IRQFD_RESAMPLE: ::std::os::raw::c_uint = 82;
-pub const KVM_CAP_PPC_BOOKE_WATCHDOG: ::std::os::raw::c_uint = 83;
-pub const KVM_CAP_PPC_HTAB_FD: ::std::os::raw::c_uint = 84;
-pub const KVM_CAP_S390_CSS_SUPPORT: ::std::os::raw::c_uint = 85;
-pub const KVM_CAP_PPC_EPR: ::std::os::raw::c_uint = 86;
-pub const KVM_CAP_ARM_PSCI: ::std::os::raw::c_uint = 87;
-pub const KVM_CAP_ARM_SET_DEVICE_ADDR: ::std::os::raw::c_uint = 88;
-pub const KVM_CAP_DEVICE_CTRL: ::std::os::raw::c_uint = 89;
-pub const KVM_CAP_IRQ_MPIC: ::std::os::raw::c_uint = 90;
-pub const KVM_CAP_PPC_RTAS: ::std::os::raw::c_uint = 91;
-pub const KVM_CAP_IRQ_XICS: ::std::os::raw::c_uint = 92;
-pub const KVM_CAP_ARM_EL1_32BIT: ::std::os::raw::c_uint = 93;
-pub const KVM_CAP_SPAPR_MULTITCE: ::std::os::raw::c_uint = 94;
-pub const KVM_CAP_EXT_EMUL_CPUID: ::std::os::raw::c_uint = 95;
-pub const KVM_CAP_HYPERV_TIME: ::std::os::raw::c_uint = 96;
-pub const KVM_CAP_IOAPIC_POLARITY_IGNORED: ::std::os::raw::c_uint = 97;
-pub const KVM_CAP_ENABLE_CAP_VM: ::std::os::raw::c_uint = 98;
-pub const KVM_CAP_S390_IRQCHIP: ::std::os::raw::c_uint = 99;
-pub const KVM_CAP_IOEVENTFD_NO_LENGTH: ::std::os::raw::c_uint = 100;
-pub const KVM_CAP_VM_ATTRIBUTES: ::std::os::raw::c_uint = 101;
-pub const KVM_CAP_ARM_PSCI_0_2: ::std::os::raw::c_uint = 102;
-pub const KVM_CAP_PPC_FIXUP_HCALL: ::std::os::raw::c_uint = 103;
-pub const KVM_CAP_PPC_ENABLE_HCALL: ::std::os::raw::c_uint = 104;
-pub const KVM_CAP_CHECK_EXTENSION_VM: ::std::os::raw::c_uint = 105;
-pub const KVM_CAP_S390_USER_SIGP: ::std::os::raw::c_uint = 106;
-pub const KVM_CAP_S390_VECTOR_REGISTERS: ::std::os::raw::c_uint = 107;
-pub const KVM_CAP_S390_MEM_OP: ::std::os::raw::c_uint = 108;
-pub const KVM_CAP_S390_USER_STSI: ::std::os::raw::c_uint = 109;
-pub const KVM_CAP_S390_SKEYS: ::std::os::raw::c_uint = 110;
-pub const KVM_CAP_MIPS_FPU: ::std::os::raw::c_uint = 111;
-pub const KVM_CAP_MIPS_MSA: ::std::os::raw::c_uint = 112;
-pub const KVM_CAP_S390_INJECT_IRQ: ::std::os::raw::c_uint = 113;
-pub const KVM_CAP_S390_IRQ_STATE: ::std::os::raw::c_uint = 114;
-pub const KVM_CAP_PPC_HWRNG: ::std::os::raw::c_uint = 115;
-pub const KVM_CAP_DISABLE_QUIRKS: ::std::os::raw::c_uint = 116;
-pub const KVM_CAP_X86_SMM: ::std::os::raw::c_uint = 117;
-pub const KVM_CAP_MULTI_ADDRESS_SPACE: ::std::os::raw::c_uint = 118;
-pub const KVM_CAP_GUEST_DEBUG_HW_BPS: ::std::os::raw::c_uint = 119;
-pub const KVM_CAP_GUEST_DEBUG_HW_WPS: ::std::os::raw::c_uint = 120;
-pub const KVM_CAP_SPLIT_IRQCHIP: ::std::os::raw::c_uint = 121;
-pub const KVM_CAP_IOEVENTFD_ANY_LENGTH: ::std::os::raw::c_uint = 122;
-pub const KVM_CAP_HYPERV_SYNIC: ::std::os::raw::c_uint = 123;
-pub const KVM_CAP_ARM_PMU_V3: ::std::os::raw::c_uint = 126;
-pub const KVM_CAP_IMMEDIATE_EXIT: ::std::os::raw::c_uint = 136;
-pub const KVM_CAP_HYPERV_SYNIC2: ::std::os::raw::c_uint = 148;
-pub const KVM_IRQ_ROUTING_IRQCHIP: ::std::os::raw::c_uint = 1;
-pub const KVM_IRQ_ROUTING_MSI: ::std::os::raw::c_uint = 2;
-pub const KVM_IRQ_ROUTING_S390_ADAPTER: ::std::os::raw::c_uint = 3;
-pub const KVM_IRQFD_FLAG_DEASSIGN: ::std::os::raw::c_uint = 1;
-pub const KVM_IRQFD_FLAG_RESAMPLE: ::std::os::raw::c_uint = 2;
-pub const KVM_MMU_FSL_BOOKE_NOHV: ::std::os::raw::c_uint = 0;
-pub const KVM_MMU_FSL_BOOKE_HV: ::std::os::raw::c_uint = 1;
-pub const KVM_REG_ARCH_MASK: ::std::os::raw::c_longlong = -72057594037927936;
-pub const KVM_REG_GENERIC: ::std::os::raw::c_uint = 0;
-pub const KVM_REG_PPC: ::std::os::raw::c_ulonglong = 1152921504606846976;
-pub const KVM_REG_X86: ::std::os::raw::c_ulonglong = 2305843009213693952;
-pub const KVM_REG_IA64: ::std::os::raw::c_ulonglong = 3458764513820540928;
-pub const KVM_REG_ARM: ::std::os::raw::c_ulonglong = 4611686018427387904;
-pub const KVM_REG_S390: ::std::os::raw::c_ulonglong = 5764607523034234880;
-pub const KVM_REG_ARM64: ::std::os::raw::c_ulonglong = 6917529027641081856;
-pub const KVM_REG_MIPS: ::std::os::raw::c_ulonglong = 8070450532247928832;
-pub const KVM_REG_SIZE_SHIFT: ::std::os::raw::c_uint = 52;
-pub const KVM_REG_SIZE_MASK: ::std::os::raw::c_ulonglong = 67553994410557440;
-pub const KVM_REG_SIZE_U8: ::std::os::raw::c_uint = 0;
-pub const KVM_REG_SIZE_U16: ::std::os::raw::c_ulonglong = 4503599627370496;
-pub const KVM_REG_SIZE_U32: ::std::os::raw::c_ulonglong = 9007199254740992;
-pub const KVM_REG_SIZE_U64: ::std::os::raw::c_ulonglong = 13510798882111488;
-pub const KVM_REG_SIZE_U128: ::std::os::raw::c_ulonglong = 18014398509481984;
-pub const KVM_REG_SIZE_U256: ::std::os::raw::c_ulonglong = 22517998136852480;
-pub const KVM_REG_SIZE_U512: ::std::os::raw::c_ulonglong = 27021597764222976;
-pub const KVM_REG_SIZE_U1024: ::std::os::raw::c_ulonglong = 31525197391593472;
-pub const KVM_CREATE_DEVICE_TEST: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_VFIO_GROUP: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_VFIO_GROUP_ADD: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_VFIO_GROUP_DEL: ::std::os::raw::c_uint = 2;
-pub const KVM_S390_STORE_STATUS_NOADDR: ::std::os::raw::c_int = -1;
-pub const KVM_S390_STORE_STATUS_PREFIXED: ::std::os::raw::c_int = -2;
-pub const KVM_DEV_ASSIGN_ENABLE_IOMMU: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_ASSIGN_PCI_2_3: ::std::os::raw::c_uint = 2;
-pub const KVM_DEV_ASSIGN_MASK_INTX: ::std::os::raw::c_uint = 4;
-pub const KVM_DEV_IRQ_HOST_INTX: ::std::os::raw::c_uint = 1;
-pub const KVM_DEV_IRQ_HOST_MSI: ::std::os::raw::c_uint = 2;
-pub const KVM_DEV_IRQ_HOST_MSIX: ::std::os::raw::c_uint = 4;
-pub const KVM_DEV_IRQ_GUEST_INTX: ::std::os::raw::c_uint = 256;
-pub const KVM_DEV_IRQ_GUEST_MSI: ::std::os::raw::c_uint = 512;
-pub const KVM_DEV_IRQ_GUEST_MSIX: ::std::os::raw::c_uint = 1024;
-pub const KVM_DEV_IRQ_HOST_MASK: ::std::os::raw::c_uint = 255;
-pub const KVM_DEV_IRQ_GUEST_MASK: ::std::os::raw::c_uint = 65280;
-pub const KVM_MAX_MSIX_PER_DEV: ::std::os::raw::c_uint = 256;
+#[repr(C)]
+pub struct __BindgenUnionField<T>(::std::marker::PhantomData<T>);
+impl<T> __BindgenUnionField<T> {
+    #[inline]
+    pub fn new() -> Self {
+        __BindgenUnionField(::std::marker::PhantomData)
+    }
+    #[inline]
+    pub unsafe fn as_ref(&self) -> &T {
+        ::std::mem::transmute(self)
+    }
+    #[inline]
+    pub unsafe fn as_mut(&mut self) -> &mut T {
+        ::std::mem::transmute(self)
+    }
+}
+impl<T> ::std::default::Default for __BindgenUnionField<T> {
+    #[inline]
+    fn default() -> Self {
+        Self::new()
+    }
+}
+impl<T> ::std::clone::Clone for __BindgenUnionField<T> {
+    #[inline]
+    fn clone(&self) -> Self {
+        Self::new()
+    }
+}
+impl<T> ::std::marker::Copy for __BindgenUnionField<T> {}
+impl<T> ::std::fmt::Debug for __BindgenUnionField<T> {
+    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        fmt.write_str("__BindgenUnionField")
+    }
+}
+impl<T> ::std::hash::Hash for __BindgenUnionField<T> {
+    fn hash<H: ::std::hash::Hasher>(&self, _state: &mut H) {}
+}
+impl<T> ::std::cmp::PartialEq for __BindgenUnionField<T> {
+    fn eq(&self, _other: &__BindgenUnionField<T>) -> bool {
+        true
+    }
+}
+impl<T> ::std::cmp::Eq for __BindgenUnionField<T> {}
+pub const __BITS_PER_LONG: u32 = 64;
+pub const __FD_SETSIZE: u32 = 1024;
+pub const _IOC_NRBITS: u32 = 8;
+pub const _IOC_TYPEBITS: u32 = 8;
+pub const _IOC_SIZEBITS: u32 = 14;
+pub const _IOC_DIRBITS: u32 = 2;
+pub const _IOC_NRMASK: u32 = 255;
+pub const _IOC_TYPEMASK: u32 = 255;
+pub const _IOC_SIZEMASK: u32 = 16383;
+pub const _IOC_DIRMASK: u32 = 3;
+pub const _IOC_NRSHIFT: u32 = 0;
+pub const _IOC_TYPESHIFT: u32 = 8;
+pub const _IOC_SIZESHIFT: u32 = 16;
+pub const _IOC_DIRSHIFT: u32 = 30;
+pub const _IOC_NONE: u32 = 0;
+pub const _IOC_WRITE: u32 = 1;
+pub const _IOC_READ: u32 = 2;
+pub const IOC_IN: u32 = 1073741824;
+pub const IOC_OUT: u32 = 2147483648;
+pub const IOC_INOUT: u32 = 3221225472;
+pub const IOCSIZE_MASK: u32 = 1073676288;
+pub const IOCSIZE_SHIFT: u32 = 16;
+pub const KVM_PIO_PAGE_OFFSET: u32 = 1;
+pub const KVM_COALESCED_MMIO_PAGE_OFFSET: u32 = 2;
+pub const DE_VECTOR: u32 = 0;
+pub const DB_VECTOR: u32 = 1;
+pub const BP_VECTOR: u32 = 3;
+pub const OF_VECTOR: u32 = 4;
+pub const BR_VECTOR: u32 = 5;
+pub const UD_VECTOR: u32 = 6;
+pub const NM_VECTOR: u32 = 7;
+pub const DF_VECTOR: u32 = 8;
+pub const TS_VECTOR: u32 = 10;
+pub const NP_VECTOR: u32 = 11;
+pub const SS_VECTOR: u32 = 12;
+pub const GP_VECTOR: u32 = 13;
+pub const PF_VECTOR: u32 = 14;
+pub const MF_VECTOR: u32 = 16;
+pub const AC_VECTOR: u32 = 17;
+pub const MC_VECTOR: u32 = 18;
+pub const XM_VECTOR: u32 = 19;
+pub const VE_VECTOR: u32 = 20;
+pub const KVM_NR_INTERRUPTS: u32 = 256;
+pub const KVM_IOAPIC_NUM_PINS: u32 = 24;
+pub const KVM_IRQCHIP_PIC_MASTER: u32 = 0;
+pub const KVM_IRQCHIP_PIC_SLAVE: u32 = 1;
+pub const KVM_IRQCHIP_IOAPIC: u32 = 2;
+pub const KVM_NR_IRQCHIPS: u32 = 3;
+pub const KVM_RUN_X86_SMM: u32 = 1;
+pub const KVM_APIC_REG_SIZE: u32 = 1024;
+pub const KVM_CPUID_FLAG_SIGNIFCANT_INDEX: u32 = 1;
+pub const KVM_CPUID_FLAG_STATEFUL_FUNC: u32 = 2;
+pub const KVM_CPUID_FLAG_STATE_READ_NEXT: u32 = 4;
+pub const KVM_GUESTDBG_USE_SW_BP: u32 = 65536;
+pub const KVM_GUESTDBG_USE_HW_BP: u32 = 131072;
+pub const KVM_GUESTDBG_INJECT_DB: u32 = 262144;
+pub const KVM_GUESTDBG_INJECT_BP: u32 = 524288;
+pub const KVM_PIT_FLAGS_HPET_LEGACY: u32 = 1;
+pub const KVM_VCPUEVENT_VALID_NMI_PENDING: u32 = 1;
+pub const KVM_VCPUEVENT_VALID_SIPI_VECTOR: u32 = 2;
+pub const KVM_VCPUEVENT_VALID_SHADOW: u32 = 4;
+pub const KVM_VCPUEVENT_VALID_SMM: u32 = 8;
+pub const KVM_VCPUEVENT_VALID_PAYLOAD: u32 = 16;
+pub const KVM_X86_SHADOW_INT_MOV_SS: u32 = 1;
+pub const KVM_X86_SHADOW_INT_STI: u32 = 2;
+pub const KVM_MAX_XCRS: u32 = 16;
+pub const KVM_SYNC_X86_REGS: u32 = 1;
+pub const KVM_SYNC_X86_SREGS: u32 = 2;
+pub const KVM_SYNC_X86_EVENTS: u32 = 4;
+pub const KVM_SYNC_X86_VALID_FIELDS: u32 = 7;
+pub const KVM_X86_QUIRK_LINT0_REENABLED: u32 = 1;
+pub const KVM_X86_QUIRK_CD_NW_CLEARED: u32 = 2;
+pub const KVM_X86_QUIRK_LAPIC_MMIO_HOLE: u32 = 4;
+pub const KVM_X86_QUIRK_OUT_7E_INC_RIP: u32 = 8;
+pub const KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT: u32 = 16;
+pub const KVM_STATE_NESTED_FORMAT_VMX: u32 = 0;
+pub const KVM_STATE_NESTED_FORMAT_SVM: u32 = 1;
+pub const KVM_STATE_NESTED_GUEST_MODE: u32 = 1;
+pub const KVM_STATE_NESTED_RUN_PENDING: u32 = 2;
+pub const KVM_STATE_NESTED_EVMCS: u32 = 4;
+pub const KVM_STATE_NESTED_SMM_GUEST_MODE: u32 = 1;
+pub const KVM_STATE_NESTED_SMM_VMXON: u32 = 2;
+pub const KVM_STATE_NESTED_VMX_VMCS_SIZE: u32 = 4096;
+pub const KVM_PMU_EVENT_ALLOW: u32 = 0;
+pub const KVM_PMU_EVENT_DENY: u32 = 1;
+pub const KVM_API_VERSION: u32 = 12;
+pub const KVM_TRC_SHIFT: u32 = 16;
+pub const KVM_TRC_ENTRYEXIT: u32 = 65536;
+pub const KVM_TRC_HANDLER: u32 = 131072;
+pub const KVM_TRC_VMENTRY: u32 = 65537;
+pub const KVM_TRC_VMEXIT: u32 = 65538;
+pub const KVM_TRC_PAGE_FAULT: u32 = 131073;
+pub const KVM_TRC_HEAD_SIZE: u32 = 12;
+pub const KVM_TRC_CYCLE_SIZE: u32 = 8;
+pub const KVM_TRC_EXTRA_MAX: u32 = 7;
+pub const KVM_TRC_INJ_VIRQ: u32 = 131074;
+pub const KVM_TRC_REDELIVER_EVT: u32 = 131075;
+pub const KVM_TRC_PEND_INTR: u32 = 131076;
+pub const KVM_TRC_IO_READ: u32 = 131077;
+pub const KVM_TRC_IO_WRITE: u32 = 131078;
+pub const KVM_TRC_CR_READ: u32 = 131079;
+pub const KVM_TRC_CR_WRITE: u32 = 131080;
+pub const KVM_TRC_DR_READ: u32 = 131081;
+pub const KVM_TRC_DR_WRITE: u32 = 131082;
+pub const KVM_TRC_MSR_READ: u32 = 131083;
+pub const KVM_TRC_MSR_WRITE: u32 = 131084;
+pub const KVM_TRC_CPUID: u32 = 131085;
+pub const KVM_TRC_INTR: u32 = 131086;
+pub const KVM_TRC_NMI: u32 = 131087;
+pub const KVM_TRC_VMMCALL: u32 = 131088;
+pub const KVM_TRC_HLT: u32 = 131089;
+pub const KVM_TRC_CLTS: u32 = 131090;
+pub const KVM_TRC_LMSW: u32 = 131091;
+pub const KVM_TRC_APIC_ACCESS: u32 = 131092;
+pub const KVM_TRC_TDP_FAULT: u32 = 131093;
+pub const KVM_TRC_GTLB_WRITE: u32 = 131094;
+pub const KVM_TRC_STLB_WRITE: u32 = 131095;
+pub const KVM_TRC_STLB_INVAL: u32 = 131096;
+pub const KVM_TRC_PPC_INSTR: u32 = 131097;
+pub const KVM_MEM_LOG_DIRTY_PAGES: u32 = 1;
+pub const KVM_MEM_READONLY: u32 = 2;
+pub const KVM_PIT_SPEAKER_DUMMY: u32 = 1;
+pub const KVM_S390_CMMA_PEEK: u32 = 1;
+pub const KVM_EXIT_HYPERV_SYNIC: u32 = 1;
+pub const KVM_EXIT_HYPERV_HCALL: u32 = 2;
+pub const KVM_S390_GET_SKEYS_NONE: u32 = 1;
+pub const KVM_S390_SKEYS_MAX: u32 = 1048576;
+pub const KVM_EXIT_UNKNOWN: u32 = 0;
+pub const KVM_EXIT_EXCEPTION: u32 = 1;
+pub const KVM_EXIT_IO: u32 = 2;
+pub const KVM_EXIT_HYPERCALL: u32 = 3;
+pub const KVM_EXIT_DEBUG: u32 = 4;
+pub const KVM_EXIT_HLT: u32 = 5;
+pub const KVM_EXIT_MMIO: u32 = 6;
+pub const KVM_EXIT_IRQ_WINDOW_OPEN: u32 = 7;
+pub const KVM_EXIT_SHUTDOWN: u32 = 8;
+pub const KVM_EXIT_FAIL_ENTRY: u32 = 9;
+pub const KVM_EXIT_INTR: u32 = 10;
+pub const KVM_EXIT_SET_TPR: u32 = 11;
+pub const KVM_EXIT_TPR_ACCESS: u32 = 12;
+pub const KVM_EXIT_S390_SIEIC: u32 = 13;
+pub const KVM_EXIT_S390_RESET: u32 = 14;
+pub const KVM_EXIT_DCR: u32 = 15;
+pub const KVM_EXIT_NMI: u32 = 16;
+pub const KVM_EXIT_INTERNAL_ERROR: u32 = 17;
+pub const KVM_EXIT_OSI: u32 = 18;
+pub const KVM_EXIT_PAPR_HCALL: u32 = 19;
+pub const KVM_EXIT_S390_UCONTROL: u32 = 20;
+pub const KVM_EXIT_WATCHDOG: u32 = 21;
+pub const KVM_EXIT_S390_TSCH: u32 = 22;
+pub const KVM_EXIT_EPR: u32 = 23;
+pub const KVM_EXIT_SYSTEM_EVENT: u32 = 24;
+pub const KVM_EXIT_S390_STSI: u32 = 25;
+pub const KVM_EXIT_IOAPIC_EOI: u32 = 26;
+pub const KVM_EXIT_HYPERV: u32 = 27;
+pub const KVM_INTERNAL_ERROR_EMULATION: u32 = 1;
+pub const KVM_INTERNAL_ERROR_SIMUL_EX: u32 = 2;
+pub const KVM_INTERNAL_ERROR_DELIVERY_EV: u32 = 3;
+pub const KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON: u32 = 4;
+pub const KVM_EXIT_IO_IN: u32 = 0;
+pub const KVM_EXIT_IO_OUT: u32 = 1;
+pub const KVM_S390_RESET_POR: u32 = 1;
+pub const KVM_S390_RESET_CLEAR: u32 = 2;
+pub const KVM_S390_RESET_SUBSYSTEM: u32 = 4;
+pub const KVM_S390_RESET_CPU_INIT: u32 = 8;
+pub const KVM_S390_RESET_IPL: u32 = 16;
+pub const KVM_SYSTEM_EVENT_SHUTDOWN: u32 = 1;
+pub const KVM_SYSTEM_EVENT_RESET: u32 = 2;
+pub const KVM_SYSTEM_EVENT_CRASH: u32 = 3;
+pub const SYNC_REGS_SIZE_BYTES: u32 = 2048;
+pub const KVM_S390_MEMOP_LOGICAL_READ: u32 = 0;
+pub const KVM_S390_MEMOP_LOGICAL_WRITE: u32 = 1;
+pub const KVM_S390_MEMOP_F_CHECK_ONLY: u32 = 1;
+pub const KVM_S390_MEMOP_F_INJECT_EXCEPTION: u32 = 2;
+pub const KVM_MP_STATE_RUNNABLE: u32 = 0;
+pub const KVM_MP_STATE_UNINITIALIZED: u32 = 1;
+pub const KVM_MP_STATE_INIT_RECEIVED: u32 = 2;
+pub const KVM_MP_STATE_HALTED: u32 = 3;
+pub const KVM_MP_STATE_SIPI_RECEIVED: u32 = 4;
+pub const KVM_MP_STATE_STOPPED: u32 = 5;
+pub const KVM_MP_STATE_CHECK_STOP: u32 = 6;
+pub const KVM_MP_STATE_OPERATING: u32 = 7;
+pub const KVM_MP_STATE_LOAD: u32 = 8;
+pub const KVM_S390_SIGP_STOP: u32 = 4294836224;
+pub const KVM_S390_PROGRAM_INT: u32 = 4294836225;
+pub const KVM_S390_SIGP_SET_PREFIX: u32 = 4294836226;
+pub const KVM_S390_RESTART: u32 = 4294836227;
+pub const KVM_S390_INT_PFAULT_INIT: u32 = 4294836228;
+pub const KVM_S390_INT_PFAULT_DONE: u32 = 4294836229;
+pub const KVM_S390_MCHK: u32 = 4294840320;
+pub const KVM_S390_INT_CLOCK_COMP: u32 = 4294905860;
+pub const KVM_S390_INT_CPU_TIMER: u32 = 4294905861;
+pub const KVM_S390_INT_VIRTIO: u32 = 4294911491;
+pub const KVM_S390_INT_SERVICE: u32 = 4294910977;
+pub const KVM_S390_INT_EMERGENCY: u32 = 4294906369;
+pub const KVM_S390_INT_EXTERNAL_CALL: u32 = 4294906370;
+pub const KVM_S390_INT_IO_MIN: u32 = 0;
+pub const KVM_S390_INT_IO_MAX: u32 = 4294836223;
+pub const KVM_S390_INT_IO_AI_MASK: u32 = 67108864;
+pub const KVM_S390_PGM_FLAGS_ILC_VALID: u32 = 1;
+pub const KVM_S390_PGM_FLAGS_ILC_0: u32 = 2;
+pub const KVM_S390_PGM_FLAGS_ILC_1: u32 = 4;
+pub const KVM_S390_PGM_FLAGS_ILC_MASK: u32 = 6;
+pub const KVM_S390_PGM_FLAGS_NO_REWIND: u32 = 8;
+pub const KVM_S390_STOP_FLAG_STORE_STATUS: u32 = 1;
+pub const KVM_GUESTDBG_ENABLE: u32 = 1;
+pub const KVM_GUESTDBG_SINGLESTEP: u32 = 2;
+pub const KVM_X86_DISABLE_EXITS_MWAIT: u32 = 1;
+pub const KVM_X86_DISABLE_EXITS_HLT: u32 = 2;
+pub const KVM_X86_DISABLE_EXITS_PAUSE: u32 = 4;
+pub const KVM_X86_DISABLE_EXITS_CSTATE: u32 = 8;
+pub const KVM_X86_DISABLE_VALID_EXITS: u32 = 15;
+pub const KVM_PPC_PVINFO_FLAGS_EV_IDLE: u32 = 1;
+pub const KVM_PPC_PAGE_SIZES_MAX_SZ: u32 = 8;
+pub const KVM_PPC_PAGE_SIZES_REAL: u32 = 1;
+pub const KVM_PPC_1T_SEGMENTS: u32 = 2;
+pub const KVM_PPC_NO_HASH: u32 = 4;
+pub const KVMIO: u32 = 174;
+pub const KVM_VM_S390_UCONTROL: u32 = 1;
+pub const KVM_VM_PPC_HV: u32 = 1;
+pub const KVM_VM_PPC_PR: u32 = 2;
+pub const KVM_VM_MIPS_AUTO: u32 = 0;
+pub const KVM_VM_MIPS_VZ: u32 = 1;
+pub const KVM_VM_MIPS_TE: u32 = 2;
+pub const KVM_S390_SIE_PAGE_OFFSET: u32 = 1;
+pub const KVM_VM_TYPE_ARM_IPA_SIZE_MASK: u32 = 255;
+pub const KVM_CAP_IRQCHIP: u32 = 0;
+pub const KVM_CAP_HLT: u32 = 1;
+pub const KVM_CAP_MMU_SHADOW_CACHE_CONTROL: u32 = 2;
+pub const KVM_CAP_USER_MEMORY: u32 = 3;
+pub const KVM_CAP_SET_TSS_ADDR: u32 = 4;
+pub const KVM_CAP_VAPIC: u32 = 6;
+pub const KVM_CAP_EXT_CPUID: u32 = 7;
+pub const KVM_CAP_CLOCKSOURCE: u32 = 8;
+pub const KVM_CAP_NR_VCPUS: u32 = 9;
+pub const KVM_CAP_NR_MEMSLOTS: u32 = 10;
+pub const KVM_CAP_PIT: u32 = 11;
+pub const KVM_CAP_NOP_IO_DELAY: u32 = 12;
+pub const KVM_CAP_PV_MMU: u32 = 13;
+pub const KVM_CAP_MP_STATE: u32 = 14;
+pub const KVM_CAP_COALESCED_MMIO: u32 = 15;
+pub const KVM_CAP_SYNC_MMU: u32 = 16;
+pub const KVM_CAP_IOMMU: u32 = 18;
+pub const KVM_CAP_DESTROY_MEMORY_REGION_WORKS: u32 = 21;
+pub const KVM_CAP_USER_NMI: u32 = 22;
+pub const KVM_CAP_SET_GUEST_DEBUG: u32 = 23;
+pub const KVM_CAP_REINJECT_CONTROL: u32 = 24;
+pub const KVM_CAP_IRQ_ROUTING: u32 = 25;
+pub const KVM_CAP_IRQ_INJECT_STATUS: u32 = 26;
+pub const KVM_CAP_ASSIGN_DEV_IRQ: u32 = 29;
+pub const KVM_CAP_JOIN_MEMORY_REGIONS_WORKS: u32 = 30;
+pub const KVM_CAP_MCE: u32 = 31;
+pub const KVM_CAP_IRQFD: u32 = 32;
+pub const KVM_CAP_PIT2: u32 = 33;
+pub const KVM_CAP_SET_BOOT_CPU_ID: u32 = 34;
+pub const KVM_CAP_PIT_STATE2: u32 = 35;
+pub const KVM_CAP_IOEVENTFD: u32 = 36;
+pub const KVM_CAP_SET_IDENTITY_MAP_ADDR: u32 = 37;
+pub const KVM_CAP_XEN_HVM: u32 = 38;
+pub const KVM_CAP_ADJUST_CLOCK: u32 = 39;
+pub const KVM_CAP_INTERNAL_ERROR_DATA: u32 = 40;
+pub const KVM_CAP_VCPU_EVENTS: u32 = 41;
+pub const KVM_CAP_S390_PSW: u32 = 42;
+pub const KVM_CAP_PPC_SEGSTATE: u32 = 43;
+pub const KVM_CAP_HYPERV: u32 = 44;
+pub const KVM_CAP_HYPERV_VAPIC: u32 = 45;
+pub const KVM_CAP_HYPERV_SPIN: u32 = 46;
+pub const KVM_CAP_PCI_SEGMENT: u32 = 47;
+pub const KVM_CAP_PPC_PAIRED_SINGLES: u32 = 48;
+pub const KVM_CAP_INTR_SHADOW: u32 = 49;
+pub const KVM_CAP_DEBUGREGS: u32 = 50;
+pub const KVM_CAP_X86_ROBUST_SINGLESTEP: u32 = 51;
+pub const KVM_CAP_PPC_OSI: u32 = 52;
+pub const KVM_CAP_PPC_UNSET_IRQ: u32 = 53;
+pub const KVM_CAP_ENABLE_CAP: u32 = 54;
+pub const KVM_CAP_XSAVE: u32 = 55;
+pub const KVM_CAP_XCRS: u32 = 56;
+pub const KVM_CAP_PPC_GET_PVINFO: u32 = 57;
+pub const KVM_CAP_PPC_IRQ_LEVEL: u32 = 58;
+pub const KVM_CAP_ASYNC_PF: u32 = 59;
+pub const KVM_CAP_TSC_CONTROL: u32 = 60;
+pub const KVM_CAP_GET_TSC_KHZ: u32 = 61;
+pub const KVM_CAP_PPC_BOOKE_SREGS: u32 = 62;
+pub const KVM_CAP_SPAPR_TCE: u32 = 63;
+pub const KVM_CAP_PPC_SMT: u32 = 64;
+pub const KVM_CAP_PPC_RMA: u32 = 65;
+pub const KVM_CAP_MAX_VCPUS: u32 = 66;
+pub const KVM_CAP_PPC_HIOR: u32 = 67;
+pub const KVM_CAP_PPC_PAPR: u32 = 68;
+pub const KVM_CAP_SW_TLB: u32 = 69;
+pub const KVM_CAP_ONE_REG: u32 = 70;
+pub const KVM_CAP_S390_GMAP: u32 = 71;
+pub const KVM_CAP_TSC_DEADLINE_TIMER: u32 = 72;
+pub const KVM_CAP_S390_UCONTROL: u32 = 73;
+pub const KVM_CAP_SYNC_REGS: u32 = 74;
+pub const KVM_CAP_PCI_2_3: u32 = 75;
+pub const KVM_CAP_KVMCLOCK_CTRL: u32 = 76;
+pub const KVM_CAP_SIGNAL_MSI: u32 = 77;
+pub const KVM_CAP_PPC_GET_SMMU_INFO: u32 = 78;
+pub const KVM_CAP_S390_COW: u32 = 79;
+pub const KVM_CAP_PPC_ALLOC_HTAB: u32 = 80;
+pub const KVM_CAP_READONLY_MEM: u32 = 81;
+pub const KVM_CAP_IRQFD_RESAMPLE: u32 = 82;
+pub const KVM_CAP_PPC_BOOKE_WATCHDOG: u32 = 83;
+pub const KVM_CAP_PPC_HTAB_FD: u32 = 84;
+pub const KVM_CAP_S390_CSS_SUPPORT: u32 = 85;
+pub const KVM_CAP_PPC_EPR: u32 = 86;
+pub const KVM_CAP_ARM_PSCI: u32 = 87;
+pub const KVM_CAP_ARM_SET_DEVICE_ADDR: u32 = 88;
+pub const KVM_CAP_DEVICE_CTRL: u32 = 89;
+pub const KVM_CAP_IRQ_MPIC: u32 = 90;
+pub const KVM_CAP_PPC_RTAS: u32 = 91;
+pub const KVM_CAP_IRQ_XICS: u32 = 92;
+pub const KVM_CAP_ARM_EL1_32BIT: u32 = 93;
+pub const KVM_CAP_SPAPR_MULTITCE: u32 = 94;
+pub const KVM_CAP_EXT_EMUL_CPUID: u32 = 95;
+pub const KVM_CAP_HYPERV_TIME: u32 = 96;
+pub const KVM_CAP_IOAPIC_POLARITY_IGNORED: u32 = 97;
+pub const KVM_CAP_ENABLE_CAP_VM: u32 = 98;
+pub const KVM_CAP_S390_IRQCHIP: u32 = 99;
+pub const KVM_CAP_IOEVENTFD_NO_LENGTH: u32 = 100;
+pub const KVM_CAP_VM_ATTRIBUTES: u32 = 101;
+pub const KVM_CAP_ARM_PSCI_0_2: u32 = 102;
+pub const KVM_CAP_PPC_FIXUP_HCALL: u32 = 103;
+pub const KVM_CAP_PPC_ENABLE_HCALL: u32 = 104;
+pub const KVM_CAP_CHECK_EXTENSION_VM: u32 = 105;
+pub const KVM_CAP_S390_USER_SIGP: u32 = 106;
+pub const KVM_CAP_S390_VECTOR_REGISTERS: u32 = 107;
+pub const KVM_CAP_S390_MEM_OP: u32 = 108;
+pub const KVM_CAP_S390_USER_STSI: u32 = 109;
+pub const KVM_CAP_S390_SKEYS: u32 = 110;
+pub const KVM_CAP_MIPS_FPU: u32 = 111;
+pub const KVM_CAP_MIPS_MSA: u32 = 112;
+pub const KVM_CAP_S390_INJECT_IRQ: u32 = 113;
+pub const KVM_CAP_S390_IRQ_STATE: u32 = 114;
+pub const KVM_CAP_PPC_HWRNG: u32 = 115;
+pub const KVM_CAP_DISABLE_QUIRKS: u32 = 116;
+pub const KVM_CAP_X86_SMM: u32 = 117;
+pub const KVM_CAP_MULTI_ADDRESS_SPACE: u32 = 118;
+pub const KVM_CAP_GUEST_DEBUG_HW_BPS: u32 = 119;
+pub const KVM_CAP_GUEST_DEBUG_HW_WPS: u32 = 120;
+pub const KVM_CAP_SPLIT_IRQCHIP: u32 = 121;
+pub const KVM_CAP_IOEVENTFD_ANY_LENGTH: u32 = 122;
+pub const KVM_CAP_HYPERV_SYNIC: u32 = 123;
+pub const KVM_CAP_S390_RI: u32 = 124;
+pub const KVM_CAP_SPAPR_TCE_64: u32 = 125;
+pub const KVM_CAP_ARM_PMU_V3: u32 = 126;
+pub const KVM_CAP_VCPU_ATTRIBUTES: u32 = 127;
+pub const KVM_CAP_MAX_VCPU_ID: u32 = 128;
+pub const KVM_CAP_X2APIC_API: u32 = 129;
+pub const KVM_CAP_S390_USER_INSTR0: u32 = 130;
+pub const KVM_CAP_MSI_DEVID: u32 = 131;
+pub const KVM_CAP_PPC_HTM: u32 = 132;
+pub const KVM_CAP_SPAPR_RESIZE_HPT: u32 = 133;
+pub const KVM_CAP_PPC_MMU_RADIX: u32 = 134;
+pub const KVM_CAP_PPC_MMU_HASH_V3: u32 = 135;
+pub const KVM_CAP_IMMEDIATE_EXIT: u32 = 136;
+pub const KVM_CAP_MIPS_VZ: u32 = 137;
+pub const KVM_CAP_MIPS_TE: u32 = 138;
+pub const KVM_CAP_MIPS_64BIT: u32 = 139;
+pub const KVM_CAP_S390_GS: u32 = 140;
+pub const KVM_CAP_S390_AIS: u32 = 141;
+pub const KVM_CAP_SPAPR_TCE_VFIO: u32 = 142;
+pub const KVM_CAP_X86_DISABLE_EXITS: u32 = 143;
+pub const KVM_CAP_ARM_USER_IRQ: u32 = 144;
+pub const KVM_CAP_S390_CMMA_MIGRATION: u32 = 145;
+pub const KVM_CAP_PPC_FWNMI: u32 = 146;
+pub const KVM_CAP_PPC_SMT_POSSIBLE: u32 = 147;
+pub const KVM_CAP_HYPERV_SYNIC2: u32 = 148;
+pub const KVM_CAP_HYPERV_VP_INDEX: u32 = 149;
+pub const KVM_CAP_S390_AIS_MIGRATION: u32 = 150;
+pub const KVM_CAP_PPC_GET_CPU_CHAR: u32 = 151;
+pub const KVM_CAP_S390_BPB: u32 = 152;
+pub const KVM_CAP_GET_MSR_FEATURES: u32 = 153;
+pub const KVM_CAP_HYPERV_EVENTFD: u32 = 154;
+pub const KVM_CAP_HYPERV_TLBFLUSH: u32 = 155;
+pub const KVM_CAP_S390_HPAGE_1M: u32 = 156;
+pub const KVM_CAP_NESTED_STATE: u32 = 157;
+pub const KVM_CAP_ARM_INJECT_SERROR_ESR: u32 = 158;
+pub const KVM_CAP_MSR_PLATFORM_INFO: u32 = 159;
+pub const KVM_CAP_PPC_NESTED_HV: u32 = 160;
+pub const KVM_CAP_HYPERV_SEND_IPI: u32 = 161;
+pub const KVM_CAP_COALESCED_PIO: u32 = 162;
+pub const KVM_CAP_HYPERV_ENLIGHTENED_VMCS: u32 = 163;
+pub const KVM_CAP_EXCEPTION_PAYLOAD: u32 = 164;
+pub const KVM_CAP_ARM_VM_IPA_SIZE: u32 = 165;
+pub const KVM_CAP_MANUAL_DIRTY_LOG_PROTECT: u32 = 166;
+pub const KVM_CAP_HYPERV_CPUID: u32 = 167;
+pub const KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2: u32 = 168;
+pub const KVM_CAP_PPC_IRQ_XIVE: u32 = 169;
+pub const KVM_CAP_ARM_SVE: u32 = 170;
+pub const KVM_CAP_ARM_PTRAUTH_ADDRESS: u32 = 171;
+pub const KVM_CAP_ARM_PTRAUTH_GENERIC: u32 = 172;
+pub const KVM_CAP_PMU_EVENT_FILTER: u32 = 173;
+pub const KVM_CAP_ARM_IRQ_LINE_LAYOUT_2: u32 = 174;
+pub const KVM_CAP_HYPERV_DIRECT_TLBFLUSH: u32 = 175;
+pub const KVM_IRQ_ROUTING_IRQCHIP: u32 = 1;
+pub const KVM_IRQ_ROUTING_MSI: u32 = 2;
+pub const KVM_IRQ_ROUTING_S390_ADAPTER: u32 = 3;
+pub const KVM_IRQ_ROUTING_HV_SINT: u32 = 4;
+pub const KVM_IRQFD_FLAG_DEASSIGN: u32 = 1;
+pub const KVM_IRQFD_FLAG_RESAMPLE: u32 = 2;
+pub const KVM_CLOCK_TSC_STABLE: u32 = 2;
+pub const KVM_MMU_FSL_BOOKE_NOHV: u32 = 0;
+pub const KVM_MMU_FSL_BOOKE_HV: u32 = 1;
+pub const KVM_REG_ARCH_MASK: i64 = -72057594037927936;
+pub const KVM_REG_GENERIC: u32 = 0;
+pub const KVM_REG_PPC: u64 = 1152921504606846976;
+pub const KVM_REG_X86: u64 = 2305843009213693952;
+pub const KVM_REG_IA64: u64 = 3458764513820540928;
+pub const KVM_REG_ARM: u64 = 4611686018427387904;
+pub const KVM_REG_S390: u64 = 5764607523034234880;
+pub const KVM_REG_ARM64: u64 = 6917529027641081856;
+pub const KVM_REG_MIPS: u64 = 8070450532247928832;
+pub const KVM_REG_RISCV: i64 = -9223372036854775808;
+pub const KVM_REG_SIZE_SHIFT: u32 = 52;
+pub const KVM_REG_SIZE_MASK: u64 = 67553994410557440;
+pub const KVM_REG_SIZE_U8: u32 = 0;
+pub const KVM_REG_SIZE_U16: u64 = 4503599627370496;
+pub const KVM_REG_SIZE_U32: u64 = 9007199254740992;
+pub const KVM_REG_SIZE_U64: u64 = 13510798882111488;
+pub const KVM_REG_SIZE_U128: u64 = 18014398509481984;
+pub const KVM_REG_SIZE_U256: u64 = 22517998136852480;
+pub const KVM_REG_SIZE_U512: u64 = 27021597764222976;
+pub const KVM_REG_SIZE_U1024: u64 = 31525197391593472;
+pub const KVM_REG_SIZE_U2048: u64 = 36028797018963968;
+pub const KVM_MSI_VALID_DEVID: u32 = 1;
+pub const KVM_CREATE_DEVICE_TEST: u32 = 1;
+pub const KVM_DEV_VFIO_GROUP: u32 = 1;
+pub const KVM_DEV_VFIO_GROUP_ADD: u32 = 1;
+pub const KVM_DEV_VFIO_GROUP_DEL: u32 = 2;
+pub const KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE: u32 = 3;
+pub const KVM_S390_STORE_STATUS_NOADDR: i32 = -1;
+pub const KVM_S390_STORE_STATUS_PREFIXED: i32 = -2;
+pub const KVM_DEV_ASSIGN_ENABLE_IOMMU: u32 = 1;
+pub const KVM_DEV_ASSIGN_PCI_2_3: u32 = 2;
+pub const KVM_DEV_ASSIGN_MASK_INTX: u32 = 4;
+pub const KVM_DEV_IRQ_HOST_INTX: u32 = 1;
+pub const KVM_DEV_IRQ_HOST_MSI: u32 = 2;
+pub const KVM_DEV_IRQ_HOST_MSIX: u32 = 4;
+pub const KVM_DEV_IRQ_GUEST_INTX: u32 = 256;
+pub const KVM_DEV_IRQ_GUEST_MSI: u32 = 512;
+pub const KVM_DEV_IRQ_GUEST_MSIX: u32 = 1024;
+pub const KVM_DEV_IRQ_HOST_MASK: u32 = 255;
+pub const KVM_DEV_IRQ_GUEST_MASK: u32 = 65280;
+pub const KVM_MAX_MSIX_PER_DEV: u32 = 256;
+pub const KVM_X2APIC_API_USE_32BIT_IDS: u32 = 1;
+pub const KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK: u32 = 2;
+pub const KVM_ARM_DEV_EL1_VTIMER: u32 = 1;
+pub const KVM_ARM_DEV_EL1_PTIMER: u32 = 2;
+pub const KVM_ARM_DEV_PMU: u32 = 4;
+pub const KVM_HYPERV_CONN_ID_MASK: u32 = 16777215;
+pub const KVM_HYPERV_EVENTFD_DEASSIGN: u32 = 1;
 pub type __s8 = ::std::os::raw::c_schar;
 pub type __u8 = ::std::os::raw::c_uchar;
 pub type __s16 = ::std::os::raw::c_short;
@@ -562,6 +714,7 @@
 pub type __kernel_off_t = __kernel_long_t;
 pub type __kernel_loff_t = ::std::os::raw::c_longlong;
 pub type __kernel_time_t = __kernel_long_t;
+pub type __kernel_time64_t = ::std::os::raw::c_longlong;
 pub type __kernel_clock_t = __kernel_long_t;
 pub type __kernel_timer_t = ::std::os::raw::c_int;
 pub type __kernel_clockid_t = ::std::os::raw::c_int;
@@ -576,6 +729,7 @@
 pub type __be64 = __u64;
 pub type __sum16 = __u16;
 pub type __wsum = __u32;
+pub type __poll_t = ::std::os::raw::c_uint;
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_memory_alias {
@@ -2056,8 +2210,6 @@
     pub nmsrs: __u32,
     pub pad: __u32,
     pub entries: __IncompleteArrayField<kvm_msr_entry>,
-    // Manually added to work around rust bindgen issue 684
-    __force_alignment: [u64; 0],
 }
 #[test]
 fn bindgen_test_layout_kvm_msrs() {
@@ -2831,7 +2983,9 @@
     pub sipi_vector: __u32,
     pub flags: __u32,
     pub smi: kvm_vcpu_events__bindgen_ty_4,
-    pub reserved: [__u32; 9usize],
+    pub reserved: [__u8; 27usize],
+    pub exception_has_payload: __u8,
+    pub exception_payload: __u64,
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
@@ -2839,7 +2993,7 @@
     pub injected: __u8,
     pub nr: __u8,
     pub has_error_code: __u8,
-    pub pad: __u8,
+    pub pending: __u8,
     pub error_code: __u32,
 }
 #[test]
@@ -2893,14 +3047,14 @@
     );
     assert_eq!(
         unsafe {
-            &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).pad as *const _ as usize
+            &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).pending as *const _ as usize
         },
         3usize,
         concat!(
             "Offset of field: ",
             stringify!(kvm_vcpu_events__bindgen_ty_1),
             "::",
-            stringify!(pad)
+            stringify!(pending)
         )
     );
     assert_eq!(
@@ -3135,7 +3289,7 @@
     );
     assert_eq!(
         ::std::mem::align_of::<kvm_vcpu_events>(),
-        4usize,
+        8usize,
         concat!("Alignment of ", stringify!(kvm_vcpu_events))
     );
     assert_eq!(
@@ -3208,6 +3362,30 @@
             stringify!(reserved)
         )
     );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vcpu_events>())).exception_has_payload as *const _ as usize
+        },
+        55usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vcpu_events),
+            "::",
+            stringify!(exception_has_payload)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vcpu_events>())).exception_payload as *const _ as usize
+        },
+        56usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vcpu_events),
+            "::",
+            stringify!(exception_payload)
+        )
+    );
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
@@ -3427,19 +3605,438 @@
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_sync_regs {}
+pub struct kvm_sync_regs {
+    pub regs: kvm_regs,
+    pub sregs: kvm_sregs,
+    pub events: kvm_vcpu_events,
+}
 #[test]
 fn bindgen_test_layout_kvm_sync_regs() {
     assert_eq!(
         ::std::mem::size_of::<kvm_sync_regs>(),
-        0usize,
+        520usize,
         concat!("Size of: ", stringify!(kvm_sync_regs))
     );
     assert_eq!(
         ::std::mem::align_of::<kvm_sync_regs>(),
-        1usize,
+        8usize,
         concat!("Alignment of ", stringify!(kvm_sync_regs))
     );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sync_regs>())).regs as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sync_regs),
+            "::",
+            stringify!(regs)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sync_regs>())).sregs as *const _ as usize },
+        144usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sync_regs),
+            "::",
+            stringify!(sregs)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sync_regs>())).events as *const _ as usize },
+        456usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sync_regs),
+            "::",
+            stringify!(events)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_vmx_nested_state_data {
+    pub vmcs12: [__u8; 4096usize],
+    pub shadow_vmcs12: [__u8; 4096usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_vmx_nested_state_data() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_vmx_nested_state_data>(),
+        8192usize,
+        concat!("Size of: ", stringify!(kvm_vmx_nested_state_data))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_vmx_nested_state_data>(),
+        1usize,
+        concat!("Alignment of ", stringify!(kvm_vmx_nested_state_data))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vmx_nested_state_data>())).vmcs12 as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vmx_nested_state_data),
+            "::",
+            stringify!(vmcs12)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vmx_nested_state_data>())).shadow_vmcs12 as *const _ as usize
+        },
+        4096usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vmx_nested_state_data),
+            "::",
+            stringify!(shadow_vmcs12)
+        )
+    );
+}
+impl Default for kvm_vmx_nested_state_data {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vmx_nested_state_hdr {
+    pub vmxon_pa: __u64,
+    pub vmcs12_pa: __u64,
+    pub smm: kvm_vmx_nested_state_hdr__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vmx_nested_state_hdr__bindgen_ty_1 {
+    pub flags: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_vmx_nested_state_hdr__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_vmx_nested_state_hdr__bindgen_ty_1>(),
+        2usize,
+        concat!(
+            "Size of: ",
+            stringify!(kvm_vmx_nested_state_hdr__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_vmx_nested_state_hdr__bindgen_ty_1>(),
+        2usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_vmx_nested_state_hdr__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vmx_nested_state_hdr__bindgen_ty_1>())).flags as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vmx_nested_state_hdr__bindgen_ty_1),
+            "::",
+            stringify!(flags)
+        )
+    );
+}
+#[test]
+fn bindgen_test_layout_kvm_vmx_nested_state_hdr() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_vmx_nested_state_hdr>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_vmx_nested_state_hdr))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_vmx_nested_state_hdr>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_vmx_nested_state_hdr))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vmx_nested_state_hdr>())).vmxon_pa as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vmx_nested_state_hdr),
+            "::",
+            stringify!(vmxon_pa)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_vmx_nested_state_hdr>())).vmcs12_pa as *const _ as usize
+        },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vmx_nested_state_hdr),
+            "::",
+            stringify!(vmcs12_pa)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_vmx_nested_state_hdr>())).smm as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vmx_nested_state_hdr),
+            "::",
+            stringify!(smm)
+        )
+    );
+}
+#[repr(C)]
+pub struct kvm_nested_state {
+    pub flags: __u16,
+    pub format: __u16,
+    pub size: __u32,
+    pub hdr: kvm_nested_state__bindgen_ty_1,
+    pub data: kvm_nested_state__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_nested_state__bindgen_ty_1 {
+    pub vmx: kvm_vmx_nested_state_hdr,
+    pub pad: [__u8; 120usize],
+    _bindgen_union_align: [u64; 15usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_nested_state__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_nested_state__bindgen_ty_1>(),
+        120usize,
+        concat!("Size of: ", stringify!(kvm_nested_state__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_nested_state__bindgen_ty_1>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_nested_state__bindgen_ty_1))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_nested_state__bindgen_ty_1>())).vmx as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_nested_state__bindgen_ty_1),
+            "::",
+            stringify!(vmx)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_nested_state__bindgen_ty_1>())).pad as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_nested_state__bindgen_ty_1),
+            "::",
+            stringify!(pad)
+        )
+    );
+}
+impl Default for kvm_nested_state__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+pub struct kvm_nested_state__bindgen_ty_2 {
+    pub vmx: __BindgenUnionField<[kvm_vmx_nested_state_data; 0usize]>,
+    pub bindgen_union_field: [u8; 0usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_nested_state__bindgen_ty_2() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_nested_state__bindgen_ty_2>(),
+        0usize,
+        concat!("Size of: ", stringify!(kvm_nested_state__bindgen_ty_2))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_nested_state__bindgen_ty_2>(),
+        1usize,
+        concat!("Alignment of ", stringify!(kvm_nested_state__bindgen_ty_2))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_nested_state__bindgen_ty_2>())).vmx as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_nested_state__bindgen_ty_2),
+            "::",
+            stringify!(vmx)
+        )
+    );
+}
+impl Default for kvm_nested_state__bindgen_ty_2 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[test]
+fn bindgen_test_layout_kvm_nested_state() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_nested_state>(),
+        128usize,
+        concat!("Size of: ", stringify!(kvm_nested_state))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_nested_state>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_nested_state))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_nested_state>())).flags as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_nested_state),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_nested_state>())).format as *const _ as usize },
+        2usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_nested_state),
+            "::",
+            stringify!(format)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_nested_state>())).size as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_nested_state),
+            "::",
+            stringify!(size)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_nested_state>())).hdr as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_nested_state),
+            "::",
+            stringify!(hdr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_nested_state>())).data as *const _ as usize },
+        128usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_nested_state),
+            "::",
+            stringify!(data)
+        )
+    );
+}
+impl Default for kvm_nested_state {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_pmu_event_filter {
+    pub action: __u32,
+    pub nevents: __u32,
+    pub fixed_counter_bitmap: __u32,
+    pub flags: __u32,
+    pub pad: [__u32; 4usize],
+    pub events: __IncompleteArrayField<__u64>,
+}
+#[test]
+fn bindgen_test_layout_kvm_pmu_event_filter() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_pmu_event_filter>(),
+        32usize,
+        concat!("Size of: ", stringify!(kvm_pmu_event_filter))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_pmu_event_filter>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_pmu_event_filter))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_pmu_event_filter>())).action as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_pmu_event_filter),
+            "::",
+            stringify!(action)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_pmu_event_filter>())).nevents as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_pmu_event_filter),
+            "::",
+            stringify!(nevents)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_pmu_event_filter>())).fixed_counter_bitmap as *const _
+                as usize
+        },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_pmu_event_filter),
+            "::",
+            stringify!(fixed_counter_bitmap)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_pmu_event_filter>())).flags as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_pmu_event_filter),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_pmu_event_filter>())).pad as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_pmu_event_filter),
+            "::",
+            stringify!(pad)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_pmu_event_filter>())).events as *const _ as usize },
+        32usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_pmu_event_filter),
+            "::",
+            stringify!(events)
+        )
+    );
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
@@ -4043,6 +4640,399 @@
         )
     );
 }
+#[doc = " kvm_s390_cmma_log - Used for CMMA migration."]
+#[doc = ""]
+#[doc = " Used both for input and output."]
+#[doc = ""]
+#[doc = " @start_gfn: Guest page number to start from."]
+#[doc = " @count: Size of the result buffer."]
+#[doc = " @flags: Control operation mode via KVM_S390_CMMA_* flags"]
+#[doc = " @remaining: Used with KVM_S390_GET_CMMA_BITS. Indicates how many dirty"]
+#[doc = "             pages are still remaining."]
+#[doc = " @mask: Used with KVM_S390_SET_CMMA_BITS. Bitmap of bits to actually set"]
+#[doc = "        in the PGSTE."]
+#[doc = " @values: Pointer to the values buffer."]
+#[doc = ""]
+#[doc = " Used in KVM_S390_{G,S}ET_CMMA_BITS ioctls."]
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_s390_cmma_log {
+    pub start_gfn: __u64,
+    pub count: __u32,
+    pub flags: __u32,
+    pub __bindgen_anon_1: kvm_s390_cmma_log__bindgen_ty_1,
+    pub values: __u64,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_s390_cmma_log__bindgen_ty_1 {
+    pub remaining: __u64,
+    pub mask: __u64,
+    _bindgen_union_align: u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_cmma_log__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_s390_cmma_log__bindgen_ty_1>(),
+        8usize,
+        concat!("Size of: ", stringify!(kvm_s390_cmma_log__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_s390_cmma_log__bindgen_ty_1>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_s390_cmma_log__bindgen_ty_1))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_s390_cmma_log__bindgen_ty_1>())).remaining as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log__bindgen_ty_1),
+            "::",
+            stringify!(remaining)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_s390_cmma_log__bindgen_ty_1>())).mask as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log__bindgen_ty_1),
+            "::",
+            stringify!(mask)
+        )
+    );
+}
+impl Default for kvm_s390_cmma_log__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_cmma_log() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_s390_cmma_log>(),
+        32usize,
+        concat!("Size of: ", stringify!(kvm_s390_cmma_log))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_s390_cmma_log>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_s390_cmma_log))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_cmma_log>())).start_gfn as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log),
+            "::",
+            stringify!(start_gfn)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_cmma_log>())).count as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log),
+            "::",
+            stringify!(count)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_cmma_log>())).flags as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_cmma_log>())).values as *const _ as usize },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_cmma_log),
+            "::",
+            stringify!(values)
+        )
+    );
+}
+impl Default for kvm_s390_cmma_log {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_hyperv_exit {
+    pub type_: __u32,
+    pub pad1: __u32,
+    pub u: kvm_hyperv_exit__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_hyperv_exit__bindgen_ty_1 {
+    pub synic: kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1,
+    pub hcall: kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2,
+    _bindgen_union_align: [u64; 4usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1 {
+    pub msr: __u32,
+    pub pad2: __u32,
+    pub control: __u64,
+    pub evt_page: __u64,
+    pub msg_page: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>(),
+        32usize,
+        concat!(
+            "Size of: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>(),
+        8usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).msr as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(msr)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).pad2 as *const _
+                as usize
+        },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(pad2)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).control
+                as *const _ as usize
+        },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(control)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).evt_page
+                as *const _ as usize
+        },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(evt_page)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).msg_page
+                as *const _ as usize
+        },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
+            "::",
+            stringify!(msg_page)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2 {
+    pub input: __u64,
+    pub result: __u64,
+    pub params: [__u64; 2usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>(),
+        32usize,
+        concat!(
+            "Size of: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2)
+        )
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>(),
+        8usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).input
+                as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
+            "::",
+            stringify!(input)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).result
+                as *const _ as usize
+        },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
+            "::",
+            stringify!(result)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).params
+                as *const _ as usize
+        },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
+            "::",
+            stringify!(params)
+        )
+    );
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1>(),
+        32usize,
+        concat!("Size of: ", stringify!(kvm_hyperv_exit__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_hyperv_exit__bindgen_ty_1))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1>())).synic as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1),
+            "::",
+            stringify!(synic)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1>())).hcall as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit__bindgen_ty_1),
+            "::",
+            stringify!(hcall)
+        )
+    );
+}
+impl Default for kvm_hyperv_exit__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_exit() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_exit>(),
+        40usize,
+        concat!("Size of: ", stringify!(kvm_hyperv_exit))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_exit>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_hyperv_exit))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_exit>())).type_ as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit),
+            "::",
+            stringify!(type_)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_exit>())).pad1 as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit),
+            "::",
+            stringify!(pad1)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_exit>())).u as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_exit),
+            "::",
+            stringify!(u)
+        )
+    );
+}
+impl Default for kvm_hyperv_exit {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
 #[repr(C)]
 #[derive(Copy, Clone)]
 pub struct kvm_run {
@@ -4061,11 +5051,6 @@
     pub s: kvm_run__bindgen_ty_2,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_run__bindgen_ty_1__bindgen_ty_1 {
-    pub hardware_exit_reason: __u64,
-}
-#[repr(C)]
 #[derive(Copy, Clone)]
 pub union kvm_run__bindgen_ty_1 {
     pub hw: kvm_run__bindgen_ty_1__bindgen_ty_1,
@@ -4092,6 +5077,11 @@
     pub padding: [::std::os::raw::c_char; 256usize],
     _bindgen_union_align: [u64; 32usize],
 }
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_1 {
+    pub hardware_exit_reason: __u64,
+}
 #[test]
 fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_1() {
     assert_eq!(
@@ -5228,174 +6218,6 @@
         )
     );
 }
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct kvm_hyperv_exit {
-    pub type_: __u32,
-    pub pad: __u32,
-    pub u: kvm_hyperv_exit__bindgen_ty_1,
-}
-#[test]
-fn bindgen_test_layout_kvm_hyperv_exit() {
-    assert_eq!(
-        ::std::mem::size_of::<kvm_hyperv_exit>(),
-        40usize,
-        concat!("Size of: ", stringify!(kvm_hyperv_exit))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<kvm_hyperv_exit>(),
-        8usize,
-        concat!("Alignment of ", stringify!(kvm_hyperv_exit))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_hyperv_exit>())).u as *const _ as usize },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit),
-            "::",
-            stringify!(u)
-        )
-    );
-}
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub union kvm_hyperv_exit__bindgen_ty_1 {
-    pub synic: kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1,
-    pub hcall: kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2,
-}
-#[test]
-fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1() {
-    assert_eq!(
-        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1>(),
-        32usize,
-        concat!("Size of: ", stringify!(kvm_hyperv_exit__bindgen_ty_1))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1>(),
-        8usize,
-        concat!("Alignment of ", stringify!(kvm_hyperv_exit__bindgen_ty_1))
-    );
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1 {
-    pub msr: __u32,
-    pub pad: __u32,
-    pub control: __u64,
-    pub evt_page: __u64,
-    pub msg_page: __u64,
-}
-#[test]
-fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1() {
-    assert_eq!(
-        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>(),
-        32usize,
-        concat!(
-            "Size of: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1)
-        )
-    );
-    assert_eq!(
-        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>(),
-        8usize,
-        concat!(
-            "Alignment of ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).control
-                as *const _ as usize
-        },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
-            "::",
-            stringify!(control)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).evt_page
-                as *const _ as usize
-        },
-        16usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
-            "::",
-            stringify!(evt_page)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1>())).msg_page
-                as *const _ as usize
-        },
-        24usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_1),
-            "::",
-            stringify!(msg_page)
-        )
-    );
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2 {
-    pub input: __u64,
-    pub result: __u64,
-    pub params: [__u64; 2],
-}
-#[test]
-fn bindgen_test_layout_kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2() {
-    assert_eq!(
-        ::std::mem::size_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>(),
-        32usize,
-        concat!(
-            "Size of: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2)
-        )
-    );
-    assert_eq!(
-        ::std::mem::align_of::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>(),
-        8usize,
-        concat!(
-            "Alignment of ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).result
-                as *const _ as usize
-        },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
-            "::",
-            stringify!(result)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2>())).params
-                as *const _ as usize
-        },
-        16usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_hyperv_exit__bindgen_ty_1__bindgen_ty_2),
-            "::",
-            stringify!(params)
-        )
-    );
-}
 #[test]
 fn bindgen_test_layout_kvm_run__bindgen_ty_1() {
     assert_eq!(
@@ -5623,6 +6445,16 @@
         )
     );
     assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).hyperv as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_run__bindgen_ty_1),
+            "::",
+            stringify!(hyperv)
+        )
+    );
+    assert_eq!(
         unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).padding as *const _ as usize },
         0usize,
         concat!(
@@ -5643,7 +6475,7 @@
 pub union kvm_run__bindgen_ty_2 {
     pub regs: kvm_sync_regs,
     pub padding: [::std::os::raw::c_char; 2048usize],
-    _bindgen_union_align: [u8; 2048usize],
+    _bindgen_union_align: [u64; 256usize],
 }
 #[test]
 fn bindgen_test_layout_kvm_run__bindgen_ty_2() {
@@ -5654,7 +6486,7 @@
     );
     assert_eq!(
         ::std::mem::align_of::<kvm_run__bindgen_ty_2>(),
-        1usize,
+        8usize,
         concat!("Alignment of ", stringify!(kvm_run__bindgen_ty_2))
     );
     assert_eq!(
@@ -5826,11 +6658,68 @@
     }
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Copy, Clone)]
 pub struct kvm_coalesced_mmio_zone {
     pub addr: __u64,
     pub size: __u32,
+    pub __bindgen_anon_1: kvm_coalesced_mmio_zone__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_coalesced_mmio_zone__bindgen_ty_1 {
     pub pad: __u32,
+    pub pio: __u32,
+    _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_coalesced_mmio_zone__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_coalesced_mmio_zone__bindgen_ty_1>(),
+        4usize,
+        concat!(
+            "Size of: ",
+            stringify!(kvm_coalesced_mmio_zone__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_coalesced_mmio_zone__bindgen_ty_1>(),
+        4usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_coalesced_mmio_zone__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_coalesced_mmio_zone__bindgen_ty_1>())).pad as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_coalesced_mmio_zone__bindgen_ty_1),
+            "::",
+            stringify!(pad)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_coalesced_mmio_zone__bindgen_ty_1>())).pio as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_coalesced_mmio_zone__bindgen_ty_1),
+            "::",
+            stringify!(pio)
+        )
+    );
+}
+impl Default for kvm_coalesced_mmio_zone__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
 }
 #[test]
 fn bindgen_test_layout_kvm_coalesced_mmio_zone() {
@@ -5864,24 +6753,71 @@
             stringify!(size)
         )
     );
+}
+impl Default for kvm_coalesced_mmio_zone {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_coalesced_mmio {
+    pub phys_addr: __u64,
+    pub len: __u32,
+    pub __bindgen_anon_1: kvm_coalesced_mmio__bindgen_ty_1,
+    pub data: [__u8; 8usize],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_coalesced_mmio__bindgen_ty_1 {
+    pub pad: __u32,
+    pub pio: __u32,
+    _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_coalesced_mmio__bindgen_ty_1() {
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_zone>())).pad as *const _ as usize },
-        12usize,
+        ::std::mem::size_of::<kvm_coalesced_mmio__bindgen_ty_1>(),
+        4usize,
+        concat!("Size of: ", stringify!(kvm_coalesced_mmio__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_coalesced_mmio__bindgen_ty_1>(),
+        4usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_coalesced_mmio__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_coalesced_mmio__bindgen_ty_1>())).pad as *const _ as usize
+        },
+        0usize,
         concat!(
             "Offset of field: ",
-            stringify!(kvm_coalesced_mmio_zone),
+            stringify!(kvm_coalesced_mmio__bindgen_ty_1),
             "::",
             stringify!(pad)
         )
     );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_coalesced_mmio__bindgen_ty_1>())).pio as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_coalesced_mmio__bindgen_ty_1),
+            "::",
+            stringify!(pio)
+        )
+    );
 }
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
-pub struct kvm_coalesced_mmio {
-    pub phys_addr: __u64,
-    pub len: __u32,
-    pub pad: __u32,
-    pub data: [__u8; 8usize],
+impl Default for kvm_coalesced_mmio__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
 }
 #[test]
 fn bindgen_test_layout_kvm_coalesced_mmio() {
@@ -5916,16 +6852,6 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio>())).pad as *const _ as usize },
-        12usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_coalesced_mmio),
-            "::",
-            stringify!(pad)
-        )
-    );
-    assert_eq!(
         unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio>())).data as *const _ as usize },
         16usize,
         concat!(
@@ -5936,14 +6862,16 @@
         )
     );
 }
+impl Default for kvm_coalesced_mmio {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
 #[repr(C)]
-#[derive(Debug, Default)]
 pub struct kvm_coalesced_mmio_ring {
     pub first: __u32,
     pub last: __u32,
     pub coalesced_mmio: __IncompleteArrayField<kvm_coalesced_mmio>,
-    // Manually added to work around rust bindgen issue 684
-    __force_alignment: [u64; 0],
 }
 #[test]
 fn bindgen_test_layout_kvm_coalesced_mmio_ring() {
@@ -5990,6 +6918,11 @@
         )
     );
 }
+impl Default for kvm_coalesced_mmio_ring {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_translation {
@@ -6293,6 +7226,116 @@
     }
 }
 #[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_clear_dirty_log {
+    pub slot: __u32,
+    pub num_pages: __u32,
+    pub first_page: __u64,
+    pub __bindgen_anon_1: kvm_clear_dirty_log__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_clear_dirty_log__bindgen_ty_1 {
+    pub dirty_bitmap: *mut ::std::os::raw::c_void,
+    pub padding2: __u64,
+    _bindgen_union_align: u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_clear_dirty_log__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_clear_dirty_log__bindgen_ty_1>(),
+        8usize,
+        concat!("Size of: ", stringify!(kvm_clear_dirty_log__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_clear_dirty_log__bindgen_ty_1>(),
+        8usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_clear_dirty_log__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_clear_dirty_log__bindgen_ty_1>())).dirty_bitmap as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log__bindgen_ty_1),
+            "::",
+            stringify!(dirty_bitmap)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_clear_dirty_log__bindgen_ty_1>())).padding2 as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log__bindgen_ty_1),
+            "::",
+            stringify!(padding2)
+        )
+    );
+}
+impl Default for kvm_clear_dirty_log__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[test]
+fn bindgen_test_layout_kvm_clear_dirty_log() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_clear_dirty_log>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_clear_dirty_log))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_clear_dirty_log>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_clear_dirty_log))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_clear_dirty_log>())).slot as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log),
+            "::",
+            stringify!(slot)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_clear_dirty_log>())).num_pages as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log),
+            "::",
+            stringify!(num_pages)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_clear_dirty_log>())).first_page as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_clear_dirty_log),
+            "::",
+            stringify!(first_page)
+        )
+    );
+}
+impl Default for kvm_clear_dirty_log {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
 #[derive(Debug, Default)]
 pub struct kvm_signal_mask {
     pub len: __u32,
@@ -6651,7 +7694,8 @@
     pub exc_access_id: __u8,
     pub per_access_id: __u8,
     pub op_access_id: __u8,
-    pub pad: [__u8; 3usize],
+    pub flags: __u8,
+    pub pad: [__u8; 2usize],
 }
 #[test]
 fn bindgen_test_layout_kvm_s390_pgm_info() {
@@ -6778,12 +7822,22 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).pad as *const _ as usize },
+        unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).flags as *const _ as usize },
         37usize,
         concat!(
             "Offset of field: ",
             stringify!(kvm_s390_pgm_info),
             "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).pad as *const _ as usize },
+        38usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_s390_pgm_info),
+            "::",
             stringify!(pad)
         )
     );
@@ -7279,7 +8333,7 @@
 pub const kvm_ioeventfd_flag_nr_virtio_ccw_notify: _bindgen_ty_1 = 3;
 pub const kvm_ioeventfd_flag_nr_fast_mmio: _bindgen_ty_1 = 4;
 pub const kvm_ioeventfd_flag_nr_max: _bindgen_ty_1 = 5;
-pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
+pub type _bindgen_ty_1 = u32;
 #[repr(C)]
 #[derive(Copy, Clone)]
 pub struct kvm_ioeventfd {
@@ -7589,7 +8643,8 @@
 pub struct kvm_ppc_smmu_info {
     pub flags: __u64,
     pub slb_size: __u32,
-    pub pad: __u32,
+    pub data_keys: __u16,
+    pub instr_keys: __u16,
     pub sps: [kvm_ppc_one_seg_page_size; 8usize],
 }
 #[test]
@@ -7625,13 +8680,23 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).pad as *const _ as usize },
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).data_keys as *const _ as usize },
         12usize,
         concat!(
             "Offset of field: ",
             stringify!(kvm_ppc_smmu_info),
             "::",
-            stringify!(pad)
+            stringify!(data_keys)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).instr_keys as *const _ as usize },
+        14usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_ppc_smmu_info),
+            "::",
+            stringify!(instr_keys)
         )
     );
     assert_eq!(
@@ -7647,6 +8712,56 @@
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ppc_resize_hpt {
+    pub flags: __u64,
+    pub shift: __u32,
+    pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_resize_hpt() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_ppc_resize_hpt>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_ppc_resize_hpt))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_ppc_resize_hpt>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_ppc_resize_hpt))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_resize_hpt>())).flags as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_ppc_resize_hpt),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_resize_hpt>())).shift as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_ppc_resize_hpt),
+            "::",
+            stringify!(shift)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_ppc_resize_hpt>())).pad as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_ppc_resize_hpt),
+            "::",
+            stringify!(pad)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_irq_routing_irqchip {
     pub irqchip: __u32,
     pub pin: __u32,
@@ -7685,12 +8800,64 @@
     );
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Copy, Clone)]
 pub struct kvm_irq_routing_msi {
     pub address_lo: __u32,
     pub address_hi: __u32,
     pub data: __u32,
+    pub __bindgen_anon_1: kvm_irq_routing_msi__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_irq_routing_msi__bindgen_ty_1 {
     pub pad: __u32,
+    pub devid: __u32,
+    _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_msi__bindgen_ty_1() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_irq_routing_msi__bindgen_ty_1>(),
+        4usize,
+        concat!("Size of: ", stringify!(kvm_irq_routing_msi__bindgen_ty_1))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_irq_routing_msi__bindgen_ty_1>(),
+        4usize,
+        concat!(
+            "Alignment of ",
+            stringify!(kvm_irq_routing_msi__bindgen_ty_1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_irq_routing_msi__bindgen_ty_1>())).pad as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_msi__bindgen_ty_1),
+            "::",
+            stringify!(pad)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_irq_routing_msi__bindgen_ty_1>())).devid as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_msi__bindgen_ty_1),
+            "::",
+            stringify!(devid)
+        )
+    );
+}
+impl Default for kvm_irq_routing_msi__bindgen_ty_1 {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
 }
 #[test]
 fn bindgen_test_layout_kvm_irq_routing_msi() {
@@ -7734,16 +8901,11 @@
             stringify!(data)
         )
     );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_irq_routing_msi>())).pad as *const _ as usize },
-        12usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(kvm_irq_routing_msi),
-            "::",
-            stringify!(pad)
-        )
-    );
+}
+impl Default for kvm_irq_routing_msi {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
@@ -7830,6 +8992,45 @@
     );
 }
 #[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irq_routing_hv_sint {
+    pub vcpu: __u32,
+    pub sint: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_hv_sint() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_irq_routing_hv_sint>(),
+        8usize,
+        concat!("Size of: ", stringify!(kvm_irq_routing_hv_sint))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_irq_routing_hv_sint>(),
+        4usize,
+        concat!("Alignment of ", stringify!(kvm_irq_routing_hv_sint))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_irq_routing_hv_sint>())).vcpu as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_hv_sint),
+            "::",
+            stringify!(vcpu)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_irq_routing_hv_sint>())).sint as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_hv_sint),
+            "::",
+            stringify!(sint)
+        )
+    );
+}
+#[repr(C)]
 #[derive(Copy, Clone)]
 pub struct kvm_irq_routing_entry {
     pub gsi: __u32,
@@ -7844,6 +9045,7 @@
     pub irqchip: kvm_irq_routing_irqchip,
     pub msi: kvm_irq_routing_msi,
     pub adapter: kvm_irq_routing_s390_adapter,
+    pub hv_sint: kvm_irq_routing_hv_sint,
     pub pad: [__u32; 8usize],
     _bindgen_union_align: [u64; 4usize],
 }
@@ -7902,6 +9104,19 @@
     );
     assert_eq!(
         unsafe {
+            &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).hv_sint as *const _
+                as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+            "::",
+            stringify!(hv_sint)
+        )
+    );
+    assert_eq!(
+        unsafe {
             &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).pad as *const _ as usize
         },
         0usize,
@@ -7991,8 +9206,6 @@
     pub nr: __u32,
     pub flags: __u32,
     pub entries: __IncompleteArrayField<kvm_irq_routing_entry>,
-    // Manually added to work around rust bindgen issue 684
-    __force_alignment: [u64; 0],
 }
 #[test]
 fn bindgen_test_layout_kvm_irq_routing() {
@@ -8537,7 +9750,8 @@
     pub address_hi: __u32,
     pub data: __u32,
     pub flags: __u32,
-    pub pad: [__u8; 16usize],
+    pub devid: __u32,
+    pub pad: [__u8; 12usize],
 }
 #[test]
 fn bindgen_test_layout_kvm_msi() {
@@ -8592,12 +9806,22 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<kvm_msi>())).pad as *const _ as usize },
+        unsafe { &(*(::std::ptr::null::<kvm_msi>())).devid as *const _ as usize },
         16usize,
         concat!(
             "Offset of field: ",
             stringify!(kvm_msi),
             "::",
+            stringify!(devid)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_msi>())).pad as *const _ as usize },
+        20usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_msi),
+            "::",
             stringify!(pad)
         )
     );
@@ -8759,8 +9983,49 @@
 pub const kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2: kvm_device_type = 5;
 pub const kvm_device_type_KVM_DEV_TYPE_FLIC: kvm_device_type = 6;
 pub const kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3: kvm_device_type = 7;
-pub const kvm_device_type_KVM_DEV_TYPE_MAX: kvm_device_type = 8;
-pub type kvm_device_type = ::std::os::raw::c_uint;
+pub const kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS: kvm_device_type = 8;
+pub const kvm_device_type_KVM_DEV_TYPE_XIVE: kvm_device_type = 9;
+pub const kvm_device_type_KVM_DEV_TYPE_MAX: kvm_device_type = 10;
+pub type kvm_device_type = u32;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vfio_spapr_tce {
+    pub groupfd: __s32,
+    pub tablefd: __s32,
+}
+#[test]
+fn bindgen_test_layout_kvm_vfio_spapr_tce() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_vfio_spapr_tce>(),
+        8usize,
+        concat!("Size of: ", stringify!(kvm_vfio_spapr_tce))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_vfio_spapr_tce>(),
+        4usize,
+        concat!("Alignment of ", stringify!(kvm_vfio_spapr_tce))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_vfio_spapr_tce>())).groupfd as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vfio_spapr_tce),
+            "::",
+            stringify!(groupfd)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_vfio_spapr_tce>())).tablefd as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_vfio_spapr_tce),
+            "::",
+            stringify!(tablefd)
+        )
+    );
+}
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct kvm_s390_ucas_mapping {
@@ -8812,6 +10077,482 @@
     );
 }
 #[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_enc_region {
+    pub addr: __u64,
+    pub size: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_enc_region() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_enc_region>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_enc_region))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_enc_region>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_enc_region))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_enc_region>())).addr as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_enc_region),
+            "::",
+            stringify!(addr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_enc_region>())).size as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_enc_region),
+            "::",
+            stringify!(size)
+        )
+    );
+}
+pub const sev_cmd_id_KVM_SEV_INIT: sev_cmd_id = 0;
+pub const sev_cmd_id_KVM_SEV_ES_INIT: sev_cmd_id = 1;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_START: sev_cmd_id = 2;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_UPDATE_DATA: sev_cmd_id = 3;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_UPDATE_VMSA: sev_cmd_id = 4;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_SECRET: sev_cmd_id = 5;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_MEASURE: sev_cmd_id = 6;
+pub const sev_cmd_id_KVM_SEV_LAUNCH_FINISH: sev_cmd_id = 7;
+pub const sev_cmd_id_KVM_SEV_SEND_START: sev_cmd_id = 8;
+pub const sev_cmd_id_KVM_SEV_SEND_UPDATE_DATA: sev_cmd_id = 9;
+pub const sev_cmd_id_KVM_SEV_SEND_UPDATE_VMSA: sev_cmd_id = 10;
+pub const sev_cmd_id_KVM_SEV_SEND_FINISH: sev_cmd_id = 11;
+pub const sev_cmd_id_KVM_SEV_RECEIVE_START: sev_cmd_id = 12;
+pub const sev_cmd_id_KVM_SEV_RECEIVE_UPDATE_DATA: sev_cmd_id = 13;
+pub const sev_cmd_id_KVM_SEV_RECEIVE_UPDATE_VMSA: sev_cmd_id = 14;
+pub const sev_cmd_id_KVM_SEV_RECEIVE_FINISH: sev_cmd_id = 15;
+pub const sev_cmd_id_KVM_SEV_GUEST_STATUS: sev_cmd_id = 16;
+pub const sev_cmd_id_KVM_SEV_DBG_DECRYPT: sev_cmd_id = 17;
+pub const sev_cmd_id_KVM_SEV_DBG_ENCRYPT: sev_cmd_id = 18;
+pub const sev_cmd_id_KVM_SEV_CERT_EXPORT: sev_cmd_id = 19;
+pub const sev_cmd_id_KVM_SEV_NR_MAX: sev_cmd_id = 20;
+pub type sev_cmd_id = u32;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_cmd {
+    pub id: __u32,
+    pub data: __u64,
+    pub error: __u32,
+    pub sev_fd: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_cmd() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_cmd>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_sev_cmd))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_cmd>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_cmd))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_cmd>())).id as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_cmd),
+            "::",
+            stringify!(id)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_cmd>())).data as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_cmd),
+            "::",
+            stringify!(data)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_cmd>())).error as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_cmd),
+            "::",
+            stringify!(error)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_cmd>())).sev_fd as *const _ as usize },
+        20usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_cmd),
+            "::",
+            stringify!(sev_fd)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_launch_start {
+    pub handle: __u32,
+    pub policy: __u32,
+    pub dh_uaddr: __u64,
+    pub dh_len: __u32,
+    pub session_uaddr: __u64,
+    pub session_len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_launch_start() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_launch_start>(),
+        40usize,
+        concat!("Size of: ", stringify!(kvm_sev_launch_start))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_launch_start>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_launch_start))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_start>())).handle as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(handle)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_start>())).policy as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(policy)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_start>())).dh_uaddr as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(dh_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_start>())).dh_len as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(dh_len)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_start>())).session_uaddr as *const _ as usize
+        },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(session_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_start>())).session_len as *const _ as usize
+        },
+        32usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_start),
+            "::",
+            stringify!(session_len)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_launch_update_data {
+    pub uaddr: __u64,
+    pub len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_launch_update_data() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_launch_update_data>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_sev_launch_update_data))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_launch_update_data>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_launch_update_data))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_update_data>())).uaddr as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_update_data),
+            "::",
+            stringify!(uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_update_data>())).len as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_update_data),
+            "::",
+            stringify!(len)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_launch_secret {
+    pub hdr_uaddr: __u64,
+    pub hdr_len: __u32,
+    pub guest_uaddr: __u64,
+    pub guest_len: __u32,
+    pub trans_uaddr: __u64,
+    pub trans_len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_launch_secret() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_launch_secret>(),
+        48usize,
+        concat!("Size of: ", stringify!(kvm_sev_launch_secret))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_launch_secret>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_launch_secret))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_secret>())).hdr_uaddr as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(hdr_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_secret>())).hdr_len as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(hdr_len)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_secret>())).guest_uaddr as *const _ as usize
+        },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(guest_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_secret>())).guest_len as *const _ as usize },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(guest_len)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<kvm_sev_launch_secret>())).trans_uaddr as *const _ as usize
+        },
+        32usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(trans_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_secret>())).trans_len as *const _ as usize },
+        40usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_secret),
+            "::",
+            stringify!(trans_len)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_launch_measure {
+    pub uaddr: __u64,
+    pub len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_launch_measure() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_launch_measure>(),
+        16usize,
+        concat!("Size of: ", stringify!(kvm_sev_launch_measure))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_launch_measure>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_launch_measure))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_measure>())).uaddr as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_measure),
+            "::",
+            stringify!(uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_launch_measure>())).len as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_launch_measure),
+            "::",
+            stringify!(len)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_guest_status {
+    pub handle: __u32,
+    pub policy: __u32,
+    pub state: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_guest_status() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_guest_status>(),
+        12usize,
+        concat!("Size of: ", stringify!(kvm_sev_guest_status))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_guest_status>(),
+        4usize,
+        concat!("Alignment of ", stringify!(kvm_sev_guest_status))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_guest_status>())).handle as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_guest_status),
+            "::",
+            stringify!(handle)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_guest_status>())).policy as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_guest_status),
+            "::",
+            stringify!(policy)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_guest_status>())).state as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_guest_status),
+            "::",
+            stringify!(state)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sev_dbg {
+    pub src_uaddr: __u64,
+    pub dst_uaddr: __u64,
+    pub len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_sev_dbg() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_sev_dbg>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_sev_dbg))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_sev_dbg>(),
+        8usize,
+        concat!("Alignment of ", stringify!(kvm_sev_dbg))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_dbg>())).src_uaddr as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_dbg),
+            "::",
+            stringify!(src_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_dbg>())).dst_uaddr as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_dbg),
+            "::",
+            stringify!(dst_uaddr)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_sev_dbg>())).len as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_sev_dbg),
+            "::",
+            stringify!(len)
+        )
+    );
+}
+#[repr(C)]
 #[derive(Copy, Clone)]
 pub struct kvm_assigned_pci_dev {
     pub assigned_dev_id: __u32,
@@ -9151,3 +10892,64 @@
         )
     );
 }
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_hyperv_eventfd {
+    pub conn_id: __u32,
+    pub fd: __s32,
+    pub flags: __u32,
+    pub padding: [__u32; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_hyperv_eventfd() {
+    assert_eq!(
+        ::std::mem::size_of::<kvm_hyperv_eventfd>(),
+        24usize,
+        concat!("Size of: ", stringify!(kvm_hyperv_eventfd))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<kvm_hyperv_eventfd>(),
+        4usize,
+        concat!("Alignment of ", stringify!(kvm_hyperv_eventfd))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_eventfd>())).conn_id as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_eventfd),
+            "::",
+            stringify!(conn_id)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_eventfd>())).fd as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_eventfd),
+            "::",
+            stringify!(fd)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_eventfd>())).flags as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_eventfd),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<kvm_hyperv_eventfd>())).padding as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(kvm_hyperv_eventfd),
+            "::",
+            stringify!(padding)
+        )
+    );
+}
diff --git a/linux_input_sys/Android.bp b/linux_input_sys/Android.bp
index 5cb0418..fc8c9cc 100644
--- a/linux_input_sys/Android.bp
+++ b/linux_input_sys/Android.bp
@@ -42,14 +42,31 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/msg_socket/Android.bp b/msg_socket/Android.bp
index 6fb3edc..234dd2b 100644
--- a/msg_socket/Android.bp
+++ b/msg_socket/Android.bp
@@ -126,6 +126,7 @@
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
 //   async-trait-0.1.42
+//   base-0.1.0
 //   futures-0.3.8 "alloc,async-await,default,executor,futures-executor,std"
 //   futures-channel-0.3.8 "alloc,futures-sink,sink,std"
 //   futures-core-0.3.8 "alloc,std"
@@ -147,7 +148,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/msg_socket/msg_on_socket_derive/Android.bp b/msg_socket/msg_on_socket_derive/Android.bp
index 27ddaa3..d95b4ed 100644
--- a/msg_socket/msg_on_socket_derive/Android.bp
+++ b/msg_socket/msg_on_socket_derive/Android.bp
@@ -29,7 +29,8 @@
 }
 
 // dependent_library ["feature_list"]
+//   base-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   syn-1.0.53 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
 //   unicode-xid-0.2.1 "default"
diff --git a/msg_socket/msg_on_socket_derive/Cargo.toml b/msg_socket/msg_on_socket_derive/Cargo.toml
index 002a3ae..206c03a 100644
--- a/msg_socket/msg_on_socket_derive/Cargo.toml
+++ b/msg_socket/msg_on_socket_derive/Cargo.toml
@@ -5,6 +5,7 @@
 edition = "2018"
 
 [dependencies]
+base = "*"
 proc-macro2 = "^1"
 quote = "^1"
 syn = "^1"
diff --git a/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs b/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs
index d287ac0..98b3549 100644
--- a/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs
+++ b/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs
@@ -44,10 +44,7 @@
 }
 
 fn is_named_struct(ds: &DataStruct) -> bool {
-    match &ds.fields {
-        Fields::Named(_f) => true,
-        _ => false,
-    }
+    matches!(&ds.fields, Fields::Named(_f))
 }
 
 /************************** Named Struct Impls ********************************************/
@@ -133,8 +130,8 @@
     }
 
     quote! {
-        fn uses_fd() -> bool {
-            #(<#field_types>::uses_fd())||*
+        fn uses_descriptor() -> bool {
+            #(<#field_types>::uses_descriptor())||*
         }
     }
 }
@@ -145,7 +142,7 @@
         fn msg_size(&self) -> usize {
             #msg_size
         }
-        fn fd_count(&self) -> usize {
+        fn descriptor_count(&self) -> usize {
             #fd_count
         }
     }
@@ -174,7 +171,7 @@
     quote! {
         unsafe fn read_from_buffer(
             buffer: &[u8],
-            fds: &[std::os::unix::io::RawFd],
+            fds: &[RawDescriptor],
         ) -> msg_socket::MsgResult<(Self, usize)> {
             let mut __offset = 0usize;
             let mut __fd_offset = 0usize;
@@ -206,7 +203,7 @@
         fn write_to_buffer(
             &self,
             buffer: &mut [u8],
-            fds: &mut [std::os::unix::io::RawFd],
+            fds: &mut [RawDescriptor],
         ) -> msg_socket::MsgResult<usize> {
             let mut __offset = 0usize;
             let mut __fd_offset = 0usize;
@@ -240,13 +237,13 @@
         }
     }
 
-    if variant_field_types.len() == 0 {
+    if variant_field_types.is_empty() {
         return quote!();
     }
 
     quote! {
-        fn uses_fd() -> bool {
-            #(<#variant_field_types>::uses_fd())||*
+        fn uses_descriptor() -> bool {
+            #(<#variant_field_types>::uses_descriptor())||*
         }
     }
 }
@@ -270,7 +267,7 @@
                 msg_size_match_variants.push(v);
 
                 let v = quote! {
-                    #name::#variant_name { #(#tmp_names),* } => #(#tmp_names.fd_count())+*,
+                    #name::#variant_name { #(#tmp_names),* } => #(#tmp_names.descriptor_count())+*,
                 };
                 fd_count_match_variants.push(v);
             }
@@ -288,7 +285,7 @@
                 msg_size_match_variants.push(v);
 
                 let v = quote! {
-                    #name::#variant_name(#(#tmp_names),*) => #(#tmp_names.fd_count())+*,
+                    #name::#variant_name(#(#tmp_names),*) => #(#tmp_names.descriptor_count())+*,
                 };
                 fd_count_match_variants.push(v);
             }
@@ -308,7 +305,7 @@
                 #(#msg_size_match_variants)*
             }
         }
-        fn fd_count(&self) -> usize {
+        fn descriptor_count(&self) -> usize {
             match self {
                 #(#fd_count_match_variants)*
             }
@@ -373,7 +370,7 @@
     quote! {
         unsafe fn read_from_buffer(
             buffer: &[u8],
-            fds: &[std::os::unix::io::RawFd],
+            fds: &[RawDescriptor],
         ) -> msg_socket::MsgResult<(Self, usize)> {
             let v = buffer.get(0).ok_or(msg_socket::MsgError::WrongMsgBufferSize)?;
             match v {
@@ -449,7 +446,7 @@
         fn write_to_buffer(
             &self,
             buffer: &mut [u8],
-            fds: &mut [std::os::unix::io::RawFd],
+            fds: &mut [RawDescriptor],
         ) -> msg_socket::MsgResult<usize> {
             if buffer.is_empty() {
                 return Err(msg_socket::MsgError::WrongMsgBufferSize)
@@ -508,14 +505,14 @@
 }
 
 fn define_uses_fd_for_tuples(fields: &[StructField]) -> TokenStream {
-    if fields.len() == 0 {
+    if fields.is_empty() {
         return quote!();
     }
 
     let field_types = fields.iter().map(|f| &f.ty);
     quote! {
-        fn uses_fd() -> bool {
-            #(<#field_types>::uses_fd())||*
+        fn uses_descriptor() -> bool {
+            #(<#field_types>::uses_descriptor())||*
         }
     }
 }
@@ -534,7 +531,7 @@
     quote! {
         unsafe fn read_from_buffer(
             buffer: &[u8],
-            fds: &[std::os::unix::io::RawFd],
+            fds: &[RawDescriptor],
         ) -> msg_socket::MsgResult<(Self, usize)> {
             let mut __offset = 0usize;
             let mut __fd_offset = 0usize;
@@ -562,7 +559,7 @@
         fn write_to_buffer(
             &self,
             buffer: &mut [u8],
-            fds: &mut [std::os::unix::io::RawFd],
+            fds: &mut [RawDescriptor],
         ) -> msg_socket::MsgResult<usize> {
             let mut __offset = 0usize;
             let mut __fd_offset = 0usize;
@@ -579,13 +576,13 @@
         .filter(|f| !f.skipped)
         .map(|f| &f.member)
         .collect();
-    if fields.len() > 0 {
+    if !fields.is_empty() {
         (
             quote! {
                 #( self.#fields.msg_size() as usize )+*
             },
             quote! {
-                #( self.#fields.fd_count() as usize )+*
+                #( self.#fields.descriptor_count() as usize )+*
             },
         )
     } else {
@@ -621,29 +618,31 @@
         let input: DeriveInput = parse_quote! {
             struct MyMsg {
                 a: u8,
-                b: RawFd,
+                b: RawDescriptor,
                 c: u32,
             }
         };
 
         let expected = quote! {
             impl msg_socket::MsgOnSocket for MyMsg {
-                fn uses_fd() -> bool {
-                    <u8>::uses_fd() || <RawFd>::uses_fd() || <u32>::uses_fd()
+                fn uses_descriptor() -> bool {
+                    <u8>::uses_descriptor()
+                    || <RawDescriptor>::uses_descriptor()
+                    || <u32>::uses_descriptor()
                 }
                 fn msg_size(&self) -> usize {
                     self.a.msg_size() as usize
                         + self.b.msg_size() as usize
                         + self.c.msg_size() as usize
                 }
-                fn fd_count(&self) -> usize {
-                    self.a.fd_count() as usize
-                        + self.b.fd_count() as usize
-                        + self.c.fd_count() as usize
+                fn descriptor_count(&self) -> usize {
+                    self.a.descriptor_count() as usize
+                        + self.b.descriptor_count() as usize
+                        + self.c.descriptor_count() as usize
                 }
                 unsafe fn read_from_buffer(
                     buffer: &[u8],
-                    fds: &[std::os::unix::io::RawFd],
+                    fds: &[RawDescriptor],
                 ) -> msg_socket::MsgResult<(Self, usize)> {
                     let mut __offset = 0usize;
                     let mut __fd_offset = 0usize;
@@ -651,7 +650,8 @@
                     __offset += t.0.msg_size();
                     __fd_offset += t.1;
                     let a = t.0;
-                    let t = <RawFd>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+                    let t = <RawDescriptor>::read_from_buffer(
+                        &buffer[__offset..], &fds[__fd_offset..])?;
                     __offset += t.0.msg_size();
                     __fd_offset += t.1;
                     let b = t.0;
@@ -664,7 +664,7 @@
                 fn write_to_buffer(
                     &self,
                     buffer: &mut [u8],
-                    fds: &mut [std::os::unix::io::RawFd],
+                    fds: &mut [RawDescriptor],
                 ) -> msg_socket::MsgResult<usize> {
                     let mut __offset = 0usize;
                     let mut __fd_offset = 0usize;
@@ -700,21 +700,21 @@
 
         let expected = quote! {
             impl msg_socket::MsgOnSocket for MyMsg {
-                fn uses_fd() -> bool {
-                    <u8>::uses_fd() || <u32>::uses_fd() || <File>::uses_fd()
+                fn uses_descriptor() -> bool {
+                    <u8>::uses_descriptor() || <u32>::uses_descriptor() || <File>::uses_descriptor()
                 }
                 fn msg_size(&self) -> usize {
                     self.0.msg_size() as usize
                         + self.1.msg_size() as usize + self.2.msg_size() as usize
                 }
-                fn fd_count(&self) -> usize {
-                    self.0.fd_count() as usize
-                        + self.1.fd_count() as usize
-                        + self.2.fd_count() as usize
+                fn descriptor_count(&self) -> usize {
+                    self.0.descriptor_count() as usize
+                        + self.1.descriptor_count() as usize
+                        + self.2.descriptor_count() as usize
                 }
                 unsafe fn read_from_buffer(
                     buffer: &[u8],
-                    fds: &[std::os::unix::io::RawFd],
+                    fds: &[RawDescriptor],
                 ) -> msg_socket::MsgResult<(Self, usize)> {
                     let mut __offset = 0usize;
                     let mut __fd_offset = 0usize;
@@ -735,7 +735,7 @@
                 fn write_to_buffer(
                     &self,
                     buffer: &mut [u8],
-                    fds: &mut [std::os::unix::io::RawFd],
+                    fds: &mut [RawDescriptor],
                 ) -> msg_socket::MsgResult<usize> {
                     let mut __offset = 0usize;
                     let mut __fd_offset = 0usize;
@@ -765,15 +765,17 @@
                 B,
                 C {
                     f0: u8,
-                    f1: RawFd,
+                    f1: RawDescriptor,
                 },
             }
         };
 
         let expected = quote! {
             impl msg_socket::MsgOnSocket for MyMsg {
-                fn uses_fd() -> bool {
-                    <u8>::uses_fd() || <u8>::uses_fd() || <RawFd>::uses_fd()
+                fn uses_descriptor() -> bool {
+                    <u8>::uses_descriptor()
+                    || <u8>::uses_descriptor()
+                    || <RawDescriptor>::uses_descriptor()
                 }
                 fn msg_size(&self) -> usize {
                     1 + match self {
@@ -782,16 +784,16 @@
                         MyMsg::C { f0, f1 } => f0.msg_size() + f1.msg_size(),
                     }
                 }
-                fn fd_count(&self) -> usize {
+                fn descriptor_count(&self) -> usize {
                     match self {
-                        MyMsg::A(enum_field0) => enum_field0.fd_count(),
+                        MyMsg::A(enum_field0) => enum_field0.descriptor_count(),
                         MyMsg::B => 0,
-                        MyMsg::C { f0, f1 } => f0.fd_count() + f1.fd_count(),
+                        MyMsg::C { f0, f1 } => f0.descriptor_count() + f1.descriptor_count(),
                     }
                 }
                 unsafe fn read_from_buffer(
                     buffer: &[u8],
-                    fds: &[std::os::unix::io::RawFd],
+                    fds: &[RawDescriptor],
                 ) -> msg_socket::MsgResult<(Self, usize)> {
                     let v = buffer
                         .get(0)
@@ -814,7 +816,7 @@
                             __offset += t.0.msg_size();
                             __fd_offset += t.1;
                             let f0 = t.0;
-                            let t = <RawFd>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+                            let t = <RawDescriptor>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                             __offset += t.0.msg_size();
                             __fd_offset += t.1;
                             let f1 = t.0;
@@ -826,7 +828,7 @@
                 fn write_to_buffer(
                     &self,
                     buffer: &mut [u8],
-                    fds: &mut [std::os::unix::io::RawFd],
+                    fds: &mut [RawDescriptor],
                 ) -> msg_socket::MsgResult<usize> {
                     if buffer.is_empty() {
                         return Err(msg_socket::MsgError::WrongMsgBufferSize)
@@ -880,12 +882,12 @@
                 fn msg_size(&self) -> usize {
                     0
                 }
-                fn fd_count(&self) -> usize {
+                fn descriptor_count(&self) -> usize {
                     0
                 }
                 unsafe fn read_from_buffer(
                     buffer: &[u8],
-                    fds: &[std::os::unix::io::RawFd],
+                    fds: &[RawDescriptor],
                 ) -> msg_socket::MsgResult<(Self, usize)> {
                     let mut __offset = 0usize;
                     let mut __fd_offset = 0usize;
@@ -894,7 +896,7 @@
                 fn write_to_buffer(
                     &self,
                     buffer: &mut [u8],
-                    fds: &mut [std::os::unix::io::RawFd],
+                    fds: &mut [RawDescriptor],
                 ) -> msg_socket::MsgResult<usize> {
                     let mut __offset = 0usize;
                     let mut __fd_offset = 0usize;
diff --git a/msg_socket/src/lib.rs b/msg_socket/src/lib.rs
index 0a9dc24..db3e1d0 100644
--- a/msg_socket/src/lib.rs
+++ b/msg_socket/src/lib.rs
@@ -3,11 +3,14 @@
 // found in the LICENSE file.
 
 mod msg_on_socket;
+mod serializable_descriptors;
 
-use base::{handle_eintr, net::UnixSeqpacket, Error as SysError, ScmSocket, UnsyncMarker};
+use base::{
+    handle_eintr, net::UnixSeqpacket, AsRawDescriptor, Error as SysError, RawDescriptor, ScmSocket,
+    UnsyncMarker,
+};
 use std::io::{IoSlice, Result};
 use std::marker::PhantomData;
-use std::os::unix::io::{AsRawFd, RawFd};
 
 pub use crate::msg_on_socket::*;
 pub use msg_on_socket_derive::*;
@@ -85,15 +88,15 @@
     }
 }
 
-impl<I: MsgOnSocket, O: MsgOnSocket> AsRawFd for MsgSocket<I, O> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.sock.as_raw_fd()
+impl<I: MsgOnSocket, O: MsgOnSocket> AsRawDescriptor for MsgSocket<I, O> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.sock.as_raw_descriptor()
     }
 }
 
-impl<I: MsgOnSocket, O: MsgOnSocket> AsRawFd for &MsgSocket<I, O> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.sock.as_raw_fd()
+impl<I: MsgOnSocket, O: MsgOnSocket> AsRawDescriptor for &MsgSocket<I, O> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.sock.as_raw_descriptor()
     }
 }
 
@@ -103,9 +106,9 @@
     }
 }
 
-impl<M: MsgOnSocket> AsRawFd for Sender<M> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.sock.as_raw_fd()
+impl<M: MsgOnSocket> AsRawDescriptor for Sender<M> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.sock.as_raw_descriptor()
     }
 }
 
@@ -115,9 +118,9 @@
     }
 }
 
-impl<M: MsgOnSocket> AsRawFd for Receiver<M> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.sock.as_raw_fd()
+impl<M: MsgOnSocket> AsRawDescriptor for Receiver<M> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.sock.as_raw_descriptor()
     }
 }
 
@@ -126,18 +129,18 @@
     type M: MsgOnSocket;
     fn send(&self, msg: &Self::M) -> MsgResult<()> {
         let msg_size = msg.msg_size();
-        let fd_size = msg.fd_count();
+        let descriptor_size = msg.descriptor_count();
         let mut msg_buffer: Vec<u8> = vec![0; msg_size];
-        let mut fd_buffer: Vec<RawFd> = vec![0; fd_size];
+        let mut descriptor_buffer: Vec<RawDescriptor> = vec![0; descriptor_size];
 
-        let fd_size = msg.write_to_buffer(&mut msg_buffer, &mut fd_buffer)?;
+        let descriptor_size = msg.write_to_buffer(&mut msg_buffer, &mut descriptor_buffer)?;
         let sock: &UnixSeqpacket = self.as_ref();
-        if fd_size == 0 {
+        if descriptor_size == 0 {
             handle_eintr!(sock.send(&msg_buffer))
                 .map_err(|e| MsgError::Send(SysError::new(e.raw_os_error().unwrap_or(0))))?;
         } else {
             let ioslice = IoSlice::new(&msg_buffer[..]);
-            sock.send_with_fds(&[ioslice], &fd_buffer[0..fd_size])
+            sock.send_with_fds(&[ioslice], &descriptor_buffer[0..descriptor_size])
                 .map_err(MsgError::Send)?;
         }
         Ok(())
@@ -150,8 +153,8 @@
     fn recv(&self) -> MsgResult<Self::M> {
         let sock: &UnixSeqpacket = self.as_ref();
 
-        let (msg_buffer, fd_buffer) = {
-            if Self::M::uses_fd() {
+        let (msg_buffer, descriptor_buffer) = {
+            if Self::M::uses_descriptor() {
                 sock.recv_as_vec_with_fds()
                     .map_err(|e| MsgError::Recv(SysError::new(e.raw_os_error().unwrap_or(0))))?
             } else {
@@ -178,9 +181,10 @@
         }
 
         // Safe because fd buffer is read from socket.
-        let (v, read_fd_size) = unsafe { Self::M::read_from_buffer(&msg_buffer, &fd_buffer)? };
-        if fd_buffer.len() != read_fd_size {
-            return Err(MsgError::NotExpectFd);
+        let (v, read_descriptor_size) =
+            unsafe { Self::M::read_from_buffer(&msg_buffer, &descriptor_buffer)? };
+        if descriptor_buffer.len() != read_descriptor_size {
+            return Err(MsgError::NotExpectDescriptor);
         }
         Ok(v)
     }
@@ -211,7 +215,7 @@
     }
 
     pub async fn next(&mut self) -> MsgResult<O> {
-        let p = cros_async::new(self.inner).unwrap();
+        let p = cros_async::async_from(&self.inner.sock).unwrap();
         p.wait_readable().await.unwrap();
         self.inner.recv()
     }
diff --git a/msg_socket/src/msg_on_socket.rs b/msg_socket/src/msg_on_socket.rs
index 38ccf3c..2b890b6 100644
--- a/msg_socket/src/msg_on_socket.rs
+++ b/msg_socket/src/msg_on_socket.rs
@@ -6,15 +6,11 @@
 mod tuple;
 
 use std::fmt::{self, Display};
-use std::fs::File;
 use std::mem::{size_of, transmute_copy, MaybeUninit};
-use std::net::{TcpListener, TcpStream, UdpSocket};
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
-use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
 use std::result;
 use std::sync::Arc;
 
-use base::{Error as SysError, Event};
+use base::{Error as SysError, RawDescriptor};
 use data_model::*;
 use slice::{slice_read_helper, slice_write_helper};
 
@@ -35,14 +31,14 @@
     /// There was no data received when the socket `recv`-ed.
     RecvZero,
     /// There was no associated file descriptor received for a request that expected it.
-    ExpectFd,
+    ExpectDescriptor,
     /// There was some associated file descriptor received but not used when deserialize.
-    NotExpectFd,
+    NotExpectDescriptor,
     /// Failed to set flags on the file descriptor.
-    SettingFdFlags(SysError),
+    SettingDescriptorFlags(SysError),
     /// Trying to serialize/deserialize, but fd buffer size is too small. This typically happens
     /// when max_fd_count() returns a value that is too small.
-    WrongFdBufferSize,
+    WrongDescriptorBufferSize,
     /// Trying to serialize/deserialize, but msg buffer size is too small. This typically happens
     /// when msg_size() returns a value that is too small.
     WrongMsgBufferSize,
@@ -65,10 +61,12 @@
                 expected, actual
             ),
             RecvZero => write!(f, "received zero data"),
-            ExpectFd => write!(f, "missing associated file descriptor for request"),
-            NotExpectFd => write!(f, "unexpected file descriptor is unused"),
-            SettingFdFlags(e) => write!(f, "failed setting flags on the message FD: {}", e),
-            WrongFdBufferSize => write!(f, "fd buffer size too small"),
+            ExpectDescriptor => write!(f, "missing associated file descriptor for request"),
+            NotExpectDescriptor => write!(f, "unexpected file descriptor is unused"),
+            SettingDescriptorFlags(e) => {
+                write!(f, "failed setting flags on the message descriptor: {}", e)
+            }
+            WrongDescriptorBufferSize => write!(f, "descriptor buffer size too small"),
             WrongMsgBufferSize => write!(f, "msg buffer size too small"),
         }
     }
@@ -81,16 +79,16 @@
 ///
 /// e.g.
 /// ```
-/// use std::os::unix::io::RawFd;
+/// use base::RawDescriptor;
 /// enum Message {
 ///     VariantA(u8),
-///     VariantB(u32, RawFd),
+///     VariantB(u32, RawDescriptor),
 ///     VariantC,
 /// }
 /// ```
 ///
 /// For variant A, we need 1 byte to store its inner value.
-/// For variant B, we need 4 bytes and 1 RawFd to store its inner value.
+/// For variant B, we need 4 bytes and 1 RawDescriptor to store its inner value.
 /// For variant C, we need 0 bytes to store its inner value.
 /// When we serialize Message to (buffer, fd_buffer), we always use fixed number of bytes in
 /// the buffer. Unused buffer bytes will be padded with zero.
@@ -99,8 +97,8 @@
 /// Thus, read/write functions always the return correct count of fds in this variant. There will be
 /// no padding in fd_buffer.
 pub trait MsgOnSocket: Sized {
-    // `true` if this structure can potentially serialize fds.
-    fn uses_fd() -> bool {
+    // `true` if this structure can potentially serialize descriptors.
+    fn uses_descriptor() -> bool {
         false
     }
 
@@ -114,9 +112,9 @@
         Self::fixed_size().unwrap()
     }
 
-    /// Number of FDs in this message. This must be overridden if `uses_fd()` returns true.
-    fn fd_count(&self) -> usize {
-        assert!(!Self::uses_fd());
+    /// Number of FDs in this message. This must be overridden if `uses_descriptor()` returns true.
+    fn descriptor_count(&self) -> usize {
+        assert!(!Self::uses_descriptor());
         0
     }
     /// Returns (self, fd read count).
@@ -125,29 +123,29 @@
     ///     1. For enum, fds contains correct fd layout of the particular variant.
     ///     2. write_to_buffer is implemented correctly(put valid fds into the buffer, has no padding,
     ///        return correct count).
-    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)>;
+    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawDescriptor]) -> MsgResult<(Self, usize)>;
 
     /// Serialize self to buffers.
-    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize>;
+    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawDescriptor]) -> MsgResult<usize>;
 }
 
 impl MsgOnSocket for SysError {
     fn fixed_size() -> Option<usize> {
         Some(size_of::<u32>())
     }
-    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
         let (v, size) = u32::read_from_buffer(buffer, fds)?;
         Ok((SysError::new(v as i32), size))
     }
-    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawDescriptor]) -> MsgResult<usize> {
         let v = self.errno() as u32;
         v.write_to_buffer(buffer, fds)
     }
 }
 
 impl<T: MsgOnSocket> MsgOnSocket for Option<T> {
-    fn uses_fd() -> bool {
-        T::uses_fd()
+    fn uses_descriptor() -> bool {
+        T::uses_descriptor()
     }
 
     fn msg_size(&self) -> usize {
@@ -157,14 +155,14 @@
         }
     }
 
-    fn fd_count(&self) -> usize {
+    fn descriptor_count(&self) -> usize {
         match self {
-            Some(v) => v.fd_count(),
+            Some(v) => v.descriptor_count(),
             None => 0,
         }
     }
 
-    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
         match buffer[0] {
             0 => Ok((None, 0)),
             1 => {
@@ -175,7 +173,7 @@
         }
     }
 
-    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawDescriptor]) -> MsgResult<usize> {
         match self {
             None => {
                 buffer[0] = 0;
@@ -190,23 +188,23 @@
 }
 
 impl<T: MsgOnSocket> MsgOnSocket for Arc<T> {
-    fn uses_fd() -> bool {
-        T::uses_fd()
+    fn uses_descriptor() -> bool {
+        T::uses_descriptor()
     }
 
     fn msg_size(&self) -> usize {
         (**self).msg_size()
     }
 
-    fn fd_count(&self) -> usize {
-        (**self).fd_count()
+    fn descriptor_count(&self) -> usize {
+        (**self).descriptor_count()
     }
 
-    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
         T::read_from_buffer(buffer, fds).map(|(v, count)| (Arc::new(v), count))
     }
 
-    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawDescriptor]) -> MsgResult<usize> {
         (**self).write_to_buffer(buffer, fds)
     }
 }
@@ -216,59 +214,21 @@
         Some(0)
     }
 
-    unsafe fn read_from_buffer(_buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+    unsafe fn read_from_buffer(_buffer: &[u8], _fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
         Ok(((), 0))
     }
 
-    fn write_to_buffer(&self, _buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
+    fn write_to_buffer(&self, _buffer: &mut [u8], _fds: &mut [RawDescriptor]) -> MsgResult<usize> {
         Ok(0)
     }
 }
 
-macro_rules! rawfd_impl {
-    ($type:ident) => {
-        impl MsgOnSocket for $type {
-            fn uses_fd() -> bool {
-                true
-            }
-            fn msg_size(&self) -> usize {
-                0
-            }
-            fn fd_count(&self) -> usize {
-                1
-            }
-            unsafe fn read_from_buffer(_buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
-                if fds.len() < 1 {
-                    return Err(MsgError::ExpectFd);
-                }
-                Ok(($type::from_raw_fd(fds[0]), 1))
-            }
-            fn write_to_buffer(&self, _buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
-                if fds.is_empty() {
-                    return Err(MsgError::WrongFdBufferSize);
-                }
-                fds[0] = self.as_raw_fd();
-                Ok(1)
-            }
-        }
-    };
-}
-
-rawfd_impl!(Event);
-rawfd_impl!(File);
-rawfd_impl!(UnixStream);
-rawfd_impl!(TcpStream);
-rawfd_impl!(TcpListener);
-rawfd_impl!(UdpSocket);
-rawfd_impl!(UnixListener);
-rawfd_impl!(UnixDatagram);
-
 // usize could be different sizes on different targets. We always use u64.
 impl MsgOnSocket for usize {
     fn msg_size(&self) -> usize {
         size_of::<u64>()
     }
-    unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+    unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
         if buffer.len() < size_of::<u64>() {
             return Err(MsgError::WrongMsgBufferSize);
         }
@@ -276,7 +236,7 @@
         Ok((t as usize, 0))
     }
 
-    fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
+    fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawDescriptor]) -> MsgResult<usize> {
         if buffer.len() < size_of::<u64>() {
             return Err(MsgError::WrongMsgBufferSize);
         }
@@ -291,7 +251,7 @@
     fn msg_size(&self) -> usize {
         size_of::<u8>()
     }
-    unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+    unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
         if buffer.len() < size_of::<u8>() {
             return Err(MsgError::WrongMsgBufferSize);
         }
@@ -302,7 +262,7 @@
             _ => Err(MsgError::InvalidType),
         }
     }
-    fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
+    fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawDescriptor]) -> MsgResult<usize> {
         if buffer.len() < size_of::<u8>() {
             return Err(MsgError::WrongMsgBufferSize);
         }
@@ -318,7 +278,10 @@
                 Some(size_of::<$native_type>())
             }
 
-            unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+            unsafe fn read_from_buffer(
+                buffer: &[u8],
+                _fds: &[RawDescriptor],
+            ) -> MsgResult<(Self, usize)> {
                 if buffer.len() < size_of::<$native_type>() {
                     return Err(MsgError::WrongMsgBufferSize);
                 }
@@ -326,7 +289,11 @@
                 Ok((t.into(), 0))
             }
 
-            fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
+            fn write_to_buffer(
+                &self,
+                buffer: &mut [u8],
+                _fds: &mut [RawDescriptor],
+            ) -> MsgResult<usize> {
                 if buffer.len() < size_of::<$native_type>() {
                     return Err(MsgError::WrongMsgBufferSize);
                 }
@@ -348,7 +315,7 @@
 le_impl!(Le64, u64);
 
 fn simple_read<T: MsgOnSocket>(buffer: &[u8], offset: &mut usize) -> MsgResult<T> {
-    assert!(!T::uses_fd());
+    assert!(!T::uses_descriptor());
     // Safety for T::read_from_buffer depends on the given FDs being valid, but we pass no FDs.
     let (v, _) = unsafe { T::read_from_buffer(&buffer[*offset..], &[])? };
     *offset += v.msg_size();
@@ -356,7 +323,7 @@
 }
 
 fn simple_write<T: MsgOnSocket>(v: T, buffer: &mut [u8], offset: &mut usize) -> MsgResult<()> {
-    assert!(!T::uses_fd());
+    assert!(!T::uses_descriptor());
     v.write_to_buffer(&mut buffer[*offset..], &mut [])?;
     *offset += v.msg_size();
     Ok(())
@@ -380,8 +347,8 @@
     ($N:expr, $t: ident $($ts:ident)*)
     => {
         impl<T: MsgOnSocket + Clone> MsgOnSocket for [T; $N] {
-            fn uses_fd() -> bool {
-                T::uses_fd()
+            fn uses_descriptor() -> bool {
+                T::uses_descriptor()
             }
 
             fn fixed_size() -> Option<usize> {
@@ -395,15 +362,16 @@
                 }
             }
 
-            fn fd_count(&self) -> usize {
-                if T::uses_fd() {
-                    self.iter().map(|i| i.fd_count()).sum()
+            fn descriptor_count(&self) -> usize {
+                if T::uses_descriptor() {
+                    self.iter().map(|i| i.descriptor_count()).sum()
                 } else {
                     0
                 }
             }
 
-            unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+            unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawDescriptor])
+                    -> MsgResult<(Self, usize)> {
                 // Taken from the canonical example of initializing an array, the `assume_init` can
                 // be assumed safe because the array elements (`MaybeUninit<T>` in this case)
                 // themselves don't require initializing.
@@ -425,7 +393,7 @@
             fn write_to_buffer(
                 &self,
                 buffer: &mut [u8],
-                fds: &mut [RawFd],
+                fds: &mut [RawDescriptor],
                 ) -> MsgResult<usize> {
                 slice_write_helper(self, buffer, fds)
             }
diff --git a/msg_socket/src/msg_on_socket/slice.rs b/msg_socket/src/msg_on_socket/slice.rs
index 7b6ef28..05df5e1 100644
--- a/msg_socket/src/msg_on_socket/slice.rs
+++ b/msg_socket/src/msg_on_socket/slice.rs
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use base::RawDescriptor;
 use std::mem::{size_of, ManuallyDrop, MaybeUninit};
-use std::os::unix::io::RawFd;
 use std::ptr::drop_in_place;
 
 use crate::{MsgOnSocket, MsgResult};
@@ -17,7 +17,7 @@
 /// requirements that the `msgs` are only used on success of this function
 pub unsafe fn slice_read_helper<T: MsgOnSocket>(
     buffer: &[u8],
-    fds: &[RawFd],
+    fds: &[RawDescriptor],
     msgs: &mut [MaybeUninit<T>],
 ) -> MsgResult<usize> {
     let mut offset = 0usize;
@@ -64,7 +64,7 @@
 pub fn slice_write_helper<T: MsgOnSocket>(
     msgs: &[T],
     buffer: &mut [u8],
-    fds: &mut [RawFd],
+    fds: &mut [RawDescriptor],
 ) -> MsgResult<usize> {
     let mut offset = 0usize;
     let mut fd_offset = 0usize;
@@ -86,8 +86,8 @@
 }
 
 impl<T: MsgOnSocket> MsgOnSocket for Vec<T> {
-    fn uses_fd() -> bool {
-        T::uses_fd()
+    fn uses_descriptor() -> bool {
+        T::uses_descriptor()
     }
 
     fn fixed_size() -> Option<usize> {
@@ -102,15 +102,15 @@
         size_of::<u64>() + vec_size
     }
 
-    fn fd_count(&self) -> usize {
-        if T::uses_fd() {
-            self.iter().map(|i| i.fd_count()).sum()
+    fn descriptor_count(&self) -> usize {
+        if T::uses_descriptor() {
+            self.iter().map(|i| i.descriptor_count()).sum()
         } else {
             0
         }
     }
 
-    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
         let mut offset = 0;
         let len = simple_read::<u64>(buffer, &mut offset)? as usize;
         let mut msgs: Vec<MaybeUninit<T>> = Vec::with_capacity(len);
@@ -123,7 +123,7 @@
         ))
     }
 
-    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawDescriptor]) -> MsgResult<usize> {
         let mut offset = 0;
         simple_write(self.len() as u64, buffer, &mut offset)?;
         slice_write_helper(self, &mut buffer[offset..], fds)
diff --git a/msg_socket/src/msg_on_socket/tuple.rs b/msg_socket/src/msg_on_socket/tuple.rs
index f960ce5..90784bf 100644
--- a/msg_socket/src/msg_on_socket/tuple.rs
+++ b/msg_socket/src/msg_on_socket/tuple.rs
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use base::RawDescriptor;
 use std::mem::size_of;
-use std::os::unix::io::RawFd;
 
 use crate::{MsgOnSocket, MsgResult};
 
@@ -16,7 +16,7 @@
 
 unsafe fn tuple_read_helper<T: MsgOnSocket>(
     buffer: &[u8],
-    fds: &[RawFd],
+    fds: &[RawDescriptor],
     buffer_index: &mut usize,
     fd_index: &mut usize,
 ) -> MsgResult<T> {
@@ -36,7 +36,7 @@
 fn tuple_write_helper<T: MsgOnSocket>(
     v: &T,
     buffer: &mut [u8],
-    fds: &mut [RawFd],
+    fds: &mut [RawDescriptor],
     buffer_index: &mut usize,
     fd_index: &mut usize,
 ) -> MsgResult<()> {
@@ -59,12 +59,12 @@
     ($t: ident) => {
         #[allow(unused_variables, non_snake_case)]
         impl<$t: MsgOnSocket> MsgOnSocket for ($t,) {
-            fn uses_fd() -> bool {
-                $t::uses_fd()
+            fn uses_descriptor() -> bool {
+                $t::uses_descriptor()
             }
 
-            fn fd_count(&self) -> usize {
-                self.0.fd_count()
+            fn descriptor_count(&self) -> usize {
+                self.0.descriptor_count()
             }
 
             fn fixed_size() -> Option<usize> {
@@ -75,7 +75,7 @@
                 self.0.msg_size()
             }
 
-            unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+            unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
                 let (t, s) = $t::read_from_buffer(buffer, fds)?;
                 Ok(((t,), s))
             }
@@ -83,7 +83,7 @@
             fn write_to_buffer(
                 &self,
                 buffer: &mut [u8],
-                fds: &mut [RawFd],
+                fds: &mut [RawDescriptor],
             ) -> MsgResult<usize> {
                 self.0.write_to_buffer(buffer, fds)
             }
@@ -92,16 +92,16 @@
     ($t: ident, $($ts:ident),*) => {
         #[allow(unused_variables, non_snake_case)]
         impl<$t: MsgOnSocket $(, $ts: MsgOnSocket)*> MsgOnSocket for ($t$(, $ts)*) {
-            fn uses_fd() -> bool {
-                $t::uses_fd() $(|| $ts::uses_fd())*
+            fn uses_descriptor() -> bool {
+                $t::uses_descriptor() $(|| $ts::uses_descriptor())*
             }
 
-            fn fd_count(&self) -> usize {
-                if Self::uses_fd() {
+            fn descriptor_count(&self) -> usize {
+                if Self::uses_descriptor() {
                     return 0;
                 }
                 let ($t $(,$ts)*) = self;
-                $t.fd_count() $(+ $ts.fd_count())*
+                $t.descriptor_count() $(+ $ts.descriptor_count())*
             }
 
             fn fixed_size() -> Option<usize> {
@@ -118,7 +118,7 @@
                 tuple_size_helper($t) $(+ tuple_size_helper($ts))*
             }
 
-            unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+            unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawDescriptor]) -> MsgResult<(Self, usize)> {
                 let mut buffer_index = 0;
                 let mut fd_index = 0;
                 Ok((
@@ -137,7 +137,7 @@
             fn write_to_buffer(
                 &self,
                 buffer: &mut [u8],
-                fds: &mut [RawFd],
+                fds: &mut [RawDescriptor],
             ) -> MsgResult<usize> {
                 let mut buffer_index = 0;
                 let mut fd_index = 0;
diff --git a/msg_socket/src/serializable_descriptors.rs b/msg_socket/src/serializable_descriptors.rs
new file mode 100644
index 0000000..21ce84b
--- /dev/null
+++ b/msg_socket/src/serializable_descriptors.rs
@@ -0,0 +1,85 @@
+// 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 crate::msg_on_socket::{MsgError, MsgOnSocket, MsgResult};
+use base::{AsRawDescriptor, Event, FromRawDescriptor, RawDescriptor};
+use std::fs::File;
+use std::net::{TcpListener, TcpStream, UdpSocket};
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
+
+macro_rules! rawdescriptor_impl {
+    ($type:ident) => {
+        impl MsgOnSocket for $type {
+            fn uses_descriptor() -> bool {
+                true
+            }
+            fn msg_size(&self) -> usize {
+                0
+            }
+            fn descriptor_count(&self) -> usize {
+                1
+            }
+            unsafe fn read_from_buffer(
+                _buffer: &[u8],
+                descriptors: &[RawDescriptor],
+            ) -> MsgResult<(Self, usize)> {
+                if descriptors.len() < 1 {
+                    return Err(MsgError::ExpectDescriptor);
+                }
+                Ok(($type::from_raw_descriptor(descriptors[0]), 1))
+            }
+            fn write_to_buffer(
+                &self,
+                _buffer: &mut [u8],
+                descriptors: &mut [RawDescriptor],
+            ) -> MsgResult<usize> {
+                if descriptors.is_empty() {
+                    return Err(MsgError::WrongDescriptorBufferSize);
+                }
+                descriptors[0] = self.as_raw_descriptor();
+                Ok(1)
+            }
+        }
+    };
+}
+
+rawdescriptor_impl!(Event);
+rawdescriptor_impl!(File);
+
+macro_rules! rawfd_impl {
+    ($type:ident) => {
+        impl MsgOnSocket for $type {
+            fn uses_descriptor() -> bool {
+                true
+            }
+            fn msg_size(&self) -> usize {
+                0
+            }
+            fn descriptor_count(&self) -> usize {
+                1
+            }
+            unsafe fn read_from_buffer(_buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+                if fds.len() < 1 {
+                    return Err(MsgError::ExpectDescriptor);
+                }
+                Ok(($type::from_raw_fd(fds[0]), 1))
+            }
+            fn write_to_buffer(&self, _buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+                if fds.is_empty() {
+                    return Err(MsgError::WrongDescriptorBufferSize);
+                }
+                fds[0] = self.as_raw_fd();
+                Ok(1)
+            }
+        }
+    };
+}
+
+rawfd_impl!(UnixStream);
+rawfd_impl!(TcpStream);
+rawfd_impl!(TcpListener);
+rawfd_impl!(UdpSocket);
+rawfd_impl!(UnixListener);
+rawfd_impl!(UnixDatagram);
diff --git a/msg_socket/tests/enum.rs b/msg_socket/tests/enum.rs
index 526793b..7f5998b 100644
--- a/msg_socket/tests/enum.rs
+++ b/msg_socket/tests/enum.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 base::Event;
+use base::{Event, RawDescriptor};
 
 use msg_socket::*;
 
diff --git a/msg_socket/tests/struct.rs b/msg_socket/tests/struct.rs
index 8c97e57..8e3c93f 100644
--- a/msg_socket/tests/struct.rs
+++ b/msg_socket/tests/struct.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 base::Event;
+use base::{Event, RawDescriptor};
 
 use msg_socket::*;
 
diff --git a/msg_socket/tests/tuple.rs b/msg_socket/tests/tuple.rs
index b7e0056..ae00813 100644
--- a/msg_socket/tests/tuple.rs
+++ b/msg_socket/tests/tuple.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 base::Event;
+use base::{Event, RawDescriptor};
 use msg_socket::*;
 
 #[derive(MsgOnSocket)]
diff --git a/net_sys/Android.bp b/net_sys/Android.bp
index aa73582..c2d8b4a 100644
--- a/net_sys/Android.bp
+++ b/net_sys/Android.bp
@@ -38,14 +38,31 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/net_util/Android.bp b/net_util/Android.bp
index e95c373..66a1a8e 100644
--- a/net_util/Android.bp
+++ b/net_util/Android.bp
@@ -44,15 +44,32 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../net_sys/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/net_util/src/lib.rs b/net_util/src/lib.rs
index 483a1a0..bb17cb4 100644
--- a/net_util/src/lib.rs
+++ b/net_util/src/lib.rs
@@ -16,7 +16,10 @@
 
 use base::Error as SysError;
 use base::FileReadWriteVolatile;
-use base::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val, volatile_impl, IoctlNr};
+use base::{
+    ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val, volatile_impl, AsRawDescriptor,
+    FromRawDescriptor, IoctlNr, RawDescriptor,
+};
 
 #[derive(Debug)]
 pub enum Error {
@@ -171,8 +174,8 @@
 }
 
 impl Tap {
-    pub unsafe fn from_raw_fd(fd: RawFd) -> Result<Tap> {
-        let tap_file = File::from_raw_fd(fd);
+    pub unsafe fn from_raw_descriptor(fd: RawDescriptor) -> Result<Tap> {
+        let tap_file = File::from_raw_descriptor(fd);
 
         // Get the interface name since we will need it for some ioctls.
         let mut ifreq: net_sys::ifreq = Default::default();
@@ -203,7 +206,7 @@
         }
 
         // We just checked that the fd is valid.
-        let tuntap = unsafe { File::from_raw_fd(fd) };
+        let tuntap = unsafe { File::from_raw_descriptor(fd) };
         // ioctl is safe since we call it with a valid tap fd and check the return
         // value.
         let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_sys::TUNSETIFF(), ifreq) };
@@ -226,7 +229,7 @@
     }
 }
 
-pub trait TapT: FileReadWriteVolatile + Read + Write + AsRawFd + Send + Sized {
+pub trait TapT: FileReadWriteVolatile + Read + Write + AsRawDescriptor + Send + Sized {
     /// Create a new tap interface. Set the `vnet_hdr` flag to true to allow offloading on this tap,
     /// which will add an extra 12 byte virtio net header to incoming frames. Offloading cannot
     /// be used if `vnet_hdr` is false.
@@ -514,7 +517,13 @@
 
 impl AsRawFd for Tap {
     fn as_raw_fd(&self) -> RawFd {
-        self.tap_file.as_raw_fd()
+        self.tap_file.as_raw_descriptor()
+    }
+}
+
+impl AsRawDescriptor for Tap {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.tap_file.as_raw_descriptor()
     }
 }
 
@@ -617,7 +626,13 @@
 
     impl AsRawFd for FakeTap {
         fn as_raw_fd(&self) -> RawFd {
-            self.tap_file.as_raw_fd()
+            self.tap_file.as_raw_descriptor()
+        }
+    }
+
+    impl AsRawDescriptor for FakeTap {
+        fn as_raw_descriptor(&self) -> RawDescriptor {
+            self.tap_file.as_raw_descriptor()
         }
     }
     volatile_impl!(FakeTap);
diff --git a/resources/Android.bp b/resources/Android.bp
index 037c1c3..416210c 100644
--- a/resources/Android.bp
+++ b/resources/Android.bp
@@ -53,6 +53,7 @@
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
 //   async-trait-0.1.42
+//   base-0.1.0
 //   futures-0.3.8 "alloc,async-await,default,executor,futures-executor,std"
 //   futures-channel-0.3.8 "alloc,futures-sink,sink,std"
 //   futures-core-0.3.8 "alloc,std"
@@ -74,7 +75,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/resources/src/gpu_allocator.rs b/resources/src/gpu_allocator.rs
index 05ff2ec..70c0752 100644
--- a/resources/src/gpu_allocator.rs
+++ b/resources/src/gpu_allocator.rs
@@ -5,6 +5,7 @@
 use std::fmt::Debug;
 use std::fs::File;
 
+use base::RawDescriptor;
 #[cfg(feature = "wl-dmabuf")]
 use libc::EINVAL;
 
diff --git a/resources/src/lib.rs b/resources/src/lib.rs
index 71a4d2e..97943d0 100644
--- a/resources/src/lib.rs
+++ b/resources/src/lib.rs
@@ -10,6 +10,7 @@
 extern crate libc;
 extern crate msg_socket;
 
+use base::RawDescriptor;
 use msg_socket::MsgOnSocket;
 use std::fmt::Display;
 
diff --git a/seccomp/aarch64/9p_device.policy b/seccomp/aarch64/9p_device.policy
index dcf6ffd..27c7908 100644
--- a/seccomp/aarch64/9p_device.policy
+++ b/seccomp/aarch64/9p_device.policy
@@ -6,11 +6,13 @@
 
 @include /usr/share/policy/crosvm/common_device.policy
 
+fcntl: 1
 pread64: 1
 pwrite64: 1
 statx: 1
 fstat: 1
 ioctl: arg1 == FIOCLEX
+lseek: 1
 getdents64: 1
 fdatasync: 1
 fsync: 1
@@ -22,5 +24,5 @@
 utimensat: 1
 ftruncate: 1
 fchown: arg1 == 0xffffffff && arg2 == 0xffffffff
-statfs: 1
+fstatfs: 1
 newfstatat: 1
diff --git a/seccomp/aarch64/battery.policy b/seccomp/aarch64/battery.policy
new file mode 100644
index 0000000..a4fb9fc
--- /dev/null
+++ b/seccomp/aarch64/battery.policy
@@ -0,0 +1,5 @@
+# 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.
+
+@include /usr/share/policy/crosvm/common_device.policy
diff --git a/seccomp/arm/9p_device.policy b/seccomp/arm/9p_device.policy
index 1c26079..95d0b32 100644
--- a/seccomp/arm/9p_device.policy
+++ b/seccomp/arm/9p_device.policy
@@ -4,26 +4,27 @@
 
 @include /usr/share/policy/crosvm/common_device.policy
 
+fcntl64: 1
 pread64: 1
 pwrite64: 1
-lstat64: 1
 stat64: 1
 statx: 1
 fstat64: 1
 ioctl: arg1 == FIOCLEX
+_llseek: 1
 getdents64: 1
 fdatasync: 1
 fsync: 1
-mkdir: 1
+mkdirat: 1
 open: 1
 openat: 1
 rmdir: 1
-rename: 1
-link: 1
-unlink: 1
+renameat: 1
+linkat: 1
+unlinkat: 1
 socket: arg0 == AF_UNIX
 utimensat: 1
 ftruncate64: 1
 fchown: arg1 == 0xffffffff && arg2 == 0xffffffff
-statfs64: 1
+fstatfs64: 1
 fstatat64: 1
diff --git a/seccomp/arm/battery.policy b/seccomp/arm/battery.policy
new file mode 100644
index 0000000..a4fb9fc
--- /dev/null
+++ b/seccomp/arm/battery.policy
@@ -0,0 +1,5 @@
+# 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.
+
+@include /usr/share/policy/crosvm/common_device.policy
diff --git a/seccomp/x86_64/9p_device.policy b/seccomp/x86_64/9p_device.policy
index 114ea11..6f14c0a 100644
--- a/seccomp/x86_64/9p_device.policy
+++ b/seccomp/x86_64/9p_device.policy
@@ -7,23 +7,24 @@
 
 @include /usr/share/policy/crosvm/common_device.policy
 
+fcntl: 1
 pwrite64: 1
 stat: 1
 statx: 1
-lstat: 1
 fstat: 1
 ioctl: arg1 == FIOCLEX
-link: 1
-unlink: 1
-rename: 1
+linkat: 1
+unlinkat: 1
+renameat: 1
 pread64: 1
-getdents: 1
-mkdir: 1
+lseek: 1
+getdents64: 1
+mkdirat: 1
 rmdir: 1
 fsync: 1
 fdatasync: 1
 utimensat: 1
 ftruncate: 1
 fchown: arg1 == 0xffffffff && arg2 == 0xffffffff
-statfs: 1
+fstatfs: 1
 newfstatat: 1
diff --git a/seccomp/x86_64/battery.policy b/seccomp/x86_64/battery.policy
new file mode 100644
index 0000000..a4fb9fc
--- /dev/null
+++ b/seccomp/x86_64/battery.policy
@@ -0,0 +1,5 @@
+# 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.
+
+@include /usr/share/policy/crosvm/common_device.policy
diff --git a/src/crosvm.rs b/src/crosvm.rs
index 832257a..0260edb 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -6,7 +6,10 @@
 //! configs.
 
 pub mod argument;
-pub mod linux;
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+pub mod gdb;
+#[path = "linux.rs"]
+pub mod platform;
 #[cfg(feature = "plugin")]
 pub mod plugin;
 
@@ -23,6 +26,7 @@
 #[cfg(feature = "audio")]
 use devices::Ac97Parameters;
 use libc::{getegid, geteuid};
+use vm_control::BatteryType;
 
 static SECCOMP_POLICY_DIR: &str = "/usr/share/policy/crosvm";
 
@@ -142,7 +146,8 @@
     pub kind: SharedDirKind,
     pub uid_map: String,
     pub gid_map: String,
-    pub cfg: passthrough::Config,
+    pub fs_cfg: passthrough::Config,
+    pub p9_cfg: p9::Config,
 }
 
 impl Default for SharedDir {
@@ -153,7 +158,8 @@
             kind: Default::default(),
             uid_map: format!("0 {} 1", unsafe { geteuid() }),
             gid_map: format!("0 {} 1", unsafe { getegid() }),
-            cfg: Default::default(),
+            fs_cfg: Default::default(),
+            p9_cfg: Default::default(),
         }
     }
 }
@@ -210,6 +216,9 @@
     pub video_enc: bool,
     pub acpi_tables: Vec<PathBuf>,
     pub protected_vm: bool,
+    pub battery_type: Option<BatteryType>,
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    pub gdb: Option<u32>,
 }
 
 impl Default for Config {
@@ -265,6 +274,9 @@
             video_enc: false,
             acpi_tables: Vec::new(),
             protected_vm: false,
+            battery_type: None,
+            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+            gdb: None,
         }
     }
 }
diff --git a/src/gdb.rs b/src/gdb.rs
new file mode 100644
index 0000000..65626f5
--- /dev/null
+++ b/src/gdb.rs
@@ -0,0 +1,343 @@
+// 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::net::TcpListener;
+use std::sync::mpsc;
+use std::time::Duration;
+
+use base::{error, info};
+use msg_socket::{MsgReceiver, MsgSender};
+use sync::Mutex;
+use vm_control::{
+    VcpuControl, VcpuDebug, VcpuDebugStatus, VcpuDebugStatusMessage, VmControlRequestSocket,
+    VmRequest, VmResponse,
+};
+use vm_memory::GuestAddress;
+
+#[cfg(target_arch = "x86_64")]
+use gdbstub::arch::x86::X86_64_SSE as GdbArch;
+use gdbstub::arch::Arch;
+use gdbstub::target::ext::base::singlethread::{ResumeAction, SingleThreadOps, StopReason};
+use gdbstub::target::ext::base::BaseOps;
+use gdbstub::target::ext::breakpoints::{HwBreakpoint, HwBreakpointOps};
+use gdbstub::target::TargetError::NonFatal;
+use gdbstub::target::{Target, TargetResult};
+use gdbstub::Connection;
+use remain::sorted;
+use thiserror::Error as ThisError;
+
+#[cfg(target_arch = "x86_64")]
+type ArchUsize = u64;
+
+pub fn gdb_thread(mut gdbstub: GdbStub, port: u32) {
+    let addr = format!("0.0.0.0:{}", port);
+    let listener = match TcpListener::bind(addr.clone()) {
+        Ok(s) => s,
+        Err(e) => {
+            error!("Failed to create a TCP listener: {}", e);
+            return;
+        }
+    };
+    info!("Waiting for a GDB connection on {:?}...", addr);
+
+    let (stream, addr) = match listener.accept() {
+        Ok(v) => v,
+        Err(e) => {
+            error!("Failed to accept a connection from GDB: {}", e);
+            return;
+        }
+    };
+    info!("GDB connected from {}", addr);
+
+    let connection: Box<dyn Connection<Error = std::io::Error>> = Box::new(stream);
+    let mut gdb = gdbstub::GdbStub::new(connection);
+
+    match gdb.run(&mut gdbstub) {
+        Ok(reason) => {
+            info!("GDB session closed: {:?}", reason);
+        }
+        Err(e) => {
+            error!("error occurred in GDB session: {}", e);
+        }
+    }
+
+    // Resume the VM when GDB session is disconnected.
+    if let Err(e) = gdbstub.vm_request(VmRequest::Resume) {
+        error!("Failed to resume the VM after GDB disconnected: {}", e);
+    }
+}
+
+#[sorted]
+#[derive(ThisError, Debug)]
+enum Error {
+    /// Got an unexpected VM response.
+    #[error("Got an unexpected VM response: {0}")]
+    UnexpectedVmResponse(VmResponse),
+    /// Failed to send a vCPU request.
+    #[error("failed to send a vCPU request: {0}")]
+    VcpuRequest(mpsc::SendError<VcpuControl>),
+    /// Failed to receive a vCPU response.
+    #[error("failed to receive a vCPU response: {0}")]
+    VcpuResponse(mpsc::RecvTimeoutError),
+    /// Failed to send a VM request.
+    #[error("failed to send a VM request: {0}")]
+    VmRequest(msg_socket::MsgError),
+    /// Failed to receive a VM request.
+    #[error("failed to receive a VM response: {0}")]
+    VmResponse(msg_socket::MsgError),
+}
+type GdbResult<T> = std::result::Result<T, Error>;
+
+pub struct GdbStub {
+    vm_socket: Mutex<VmControlRequestSocket>,
+    vcpu_com: Vec<mpsc::Sender<VcpuControl>>,
+    from_vcpu: mpsc::Receiver<VcpuDebugStatusMessage>,
+
+    hw_breakpoints: Vec<GuestAddress>,
+}
+
+impl GdbStub {
+    pub fn new(
+        vm_socket: VmControlRequestSocket,
+        vcpu_com: Vec<mpsc::Sender<VcpuControl>>,
+        from_vcpu: mpsc::Receiver<VcpuDebugStatusMessage>,
+    ) -> Self {
+        GdbStub {
+            vm_socket: Mutex::new(vm_socket),
+            vcpu_com,
+            from_vcpu,
+            hw_breakpoints: Default::default(),
+        }
+    }
+
+    fn vcpu_request(&self, request: VcpuControl) -> GdbResult<VcpuDebugStatus> {
+        // We use the only one vCPU when GDB is enabled.
+        self.vcpu_com[0].send(request).map_err(Error::VcpuRequest)?;
+
+        match self.from_vcpu.recv_timeout(Duration::from_millis(500)) {
+            Ok(msg) => Ok(msg.msg),
+            Err(e) => Err(Error::VcpuResponse(e)),
+        }
+    }
+
+    fn vm_request(&self, request: VmRequest) -> GdbResult<()> {
+        let vm_socket = self.vm_socket.lock();
+        vm_socket.send(&request).map_err(Error::VmRequest)?;
+        match vm_socket.recv() {
+            Ok(VmResponse::Ok) => Ok(()),
+            Ok(r) => Err(Error::UnexpectedVmResponse(r)),
+            Err(e) => Err(Error::VmResponse(e)),
+        }
+    }
+}
+
+impl Target for GdbStub {
+    // TODO(keiichiw): Replace `()` with `X86_64CoreRegId` when we update the gdbstub crate.
+    type Arch = GdbArch<()>;
+    type Error = &'static str;
+
+    fn base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error> {
+        BaseOps::SingleThread(self)
+    }
+
+    // TODO(keiichiw): sw_breakpoint, hw_watchpoint, extended_mode, monitor_cmd, section_offsets
+    fn hw_breakpoint(&mut self) -> Option<HwBreakpointOps<Self>> {
+        Some(self)
+    }
+}
+
+impl SingleThreadOps for GdbStub {
+    fn resume(
+        &mut self,
+        action: ResumeAction,
+        check_gdb_interrupt: &mut dyn FnMut() -> bool,
+    ) -> Result<StopReason<ArchUsize>, Self::Error> {
+        let single_step = ResumeAction::Step == action;
+
+        if single_step {
+            match self.vcpu_request(VcpuControl::Debug(VcpuDebug::EnableSinglestep)) {
+                Ok(VcpuDebugStatus::CommandComplete) => {}
+                Ok(s) => {
+                    error!("Unexpected vCPU response for EnableSinglestep: {:?}", s);
+                    return Err("Unexpected vCPU response for EnableSinglestep");
+                }
+                Err(e) => {
+                    error!("Failed to request EnableSinglestep: {}", e);
+                    return Err("Failed to request EnableSinglestep");
+                }
+            };
+        }
+
+        self.vm_request(VmRequest::Resume).map_err(|e| {
+            error!("Failed to resume the target: {}", e);
+            "Failed to resume the target"
+        })?;
+
+        // Polling
+        loop {
+            match self
+                .from_vcpu
+                .recv_timeout(std::time::Duration::from_millis(100))
+            {
+                Ok(msg) => match msg.msg {
+                    VcpuDebugStatus::HitBreakPoint => {
+                        if single_step {
+                            return Ok(StopReason::DoneStep);
+                        } else {
+                            return Ok(StopReason::HwBreak);
+                        }
+                    }
+                    status => {
+                        error!("Unexpected VcpuDebugStatus: {:?}", status);
+                    }
+                },
+                Err(_) => {} // TODO(keiichiw): handle error?
+            };
+
+            if check_gdb_interrupt() {
+                self.vm_request(VmRequest::Suspend).map_err(|e| {
+                    error!("Failed to suspend the target: {}", e);
+                    "Failed to suspend the target"
+                })?;
+                return Ok(StopReason::GdbInterrupt);
+            }
+        }
+    }
+
+    fn read_registers(
+        &mut self,
+        regs: &mut <Self::Arch as Arch>::Registers,
+    ) -> TargetResult<(), Self> {
+        match self.vcpu_request(VcpuControl::Debug(VcpuDebug::ReadRegs)) {
+            Ok(VcpuDebugStatus::RegValues(r)) => {
+                *regs = r;
+                Ok(())
+            }
+            Ok(s) => {
+                error!("Unexpected vCPU response for ReadRegs: {:?}", s);
+                Err(NonFatal)
+            }
+            Err(e) => {
+                error!("Failed to request ReadRegs: {}", e);
+                Err(NonFatal)
+            }
+        }
+    }
+
+    fn write_registers(
+        &mut self,
+        regs: &<Self::Arch as Arch>::Registers,
+    ) -> TargetResult<(), Self> {
+        match self.vcpu_request(VcpuControl::Debug(VcpuDebug::WriteRegs(Box::new(
+            regs.clone(),
+        )))) {
+            Ok(VcpuDebugStatus::CommandComplete) => Ok(()),
+            Ok(s) => {
+                error!("Unexpected vCPU response for WriteRegs: {:?}", s);
+                Err(NonFatal)
+            }
+            Err(e) => {
+                error!("Failed to request WriteRegs: {}", e);
+                Err(NonFatal)
+            }
+        }
+    }
+
+    fn read_addrs(
+        &mut self,
+        start_addr: <Self::Arch as Arch>::Usize,
+        data: &mut [u8],
+    ) -> TargetResult<(), Self> {
+        match self.vcpu_request(VcpuControl::Debug(VcpuDebug::ReadMem(
+            GuestAddress(start_addr),
+            data.len(),
+        ))) {
+            Ok(VcpuDebugStatus::MemoryRegion(r)) => {
+                for (dst, v) in data.iter_mut().zip(r.iter()) {
+                    *dst = *v;
+                }
+                Ok(())
+            }
+            Ok(s) => {
+                error!("Unexpected vCPU response for ReadMem: {:?}", s);
+                Err(NonFatal)
+            }
+            Err(e) => {
+                error!("Failed to request ReadMem: {}", e);
+                Err(NonFatal)
+            }
+        }
+    }
+
+    fn write_addrs(
+        &mut self,
+        start_addr: <Self::Arch as Arch>::Usize,
+        data: &[u8],
+    ) -> TargetResult<(), Self> {
+        match self.vcpu_request(VcpuControl::Debug(VcpuDebug::WriteMem(
+            GuestAddress(start_addr),
+            data.to_owned(),
+        ))) {
+            Ok(VcpuDebugStatus::CommandComplete) => Ok(()),
+            Ok(s) => {
+                error!("Unexpected vCPU response for WriteMem: {:?}", s);
+                Err(NonFatal)
+            }
+            Err(e) => {
+                error!("Failed to request WriteMem: {}", e);
+                Err(NonFatal)
+            }
+        }
+    }
+}
+
+impl HwBreakpoint for GdbStub {
+    /// Add a new hardware breakpoint.
+    /// Return `Ok(false)` if the operation could not be completed.
+    fn add_hw_breakpoint(&mut self, addr: <Self::Arch as Arch>::Usize) -> TargetResult<bool, Self> {
+        // If we already have 4 breakpoints, we cannot set a new one.
+        if self.hw_breakpoints.len() >= 4 {
+            error!("Not allowed to set more than 4 HW breakpoints");
+            return Err(NonFatal);
+        }
+        self.hw_breakpoints.push(GuestAddress(addr));
+
+        match self.vcpu_request(VcpuControl::Debug(VcpuDebug::SetHwBreakPoint(
+            self.hw_breakpoints.clone(),
+        ))) {
+            Ok(VcpuDebugStatus::CommandComplete) => Ok(true),
+            Ok(s) => {
+                error!("Unexpected vCPU response for SetHwBreakPoint: {:?}", s);
+                Err(NonFatal)
+            }
+            Err(e) => {
+                error!("Failed to request SetHwBreakPoint: {}", e);
+                Err(NonFatal)
+            }
+        }
+    }
+
+    /// Remove an existing hardware breakpoint.
+    /// Return `Ok(false)` if the operation could not be completed.
+    fn remove_hw_breakpoint(
+        &mut self,
+        addr: <Self::Arch as Arch>::Usize,
+    ) -> TargetResult<bool, Self> {
+        self.hw_breakpoints.retain(|&b| b.0 != addr);
+
+        match self.vcpu_request(VcpuControl::Debug(VcpuDebug::SetHwBreakPoint(
+            self.hw_breakpoints.clone(),
+        ))) {
+            Ok(VcpuDebugStatus::CommandComplete) => Ok(true),
+            Ok(s) => {
+                error!("Unexpected vCPU response for SetHwBreakPoint: {:?}", s);
+                Err(NonFatal)
+            }
+            Err(e) => {
+                error!("Failed to request SetHwBreakPoint: {}", e);
+                Err(NonFatal)
+            }
+        }
+    }
+}
diff --git a/src/linux.rs b/src/linux.rs
index 8e5647b..8cfeef7 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -17,12 +17,13 @@
 #[cfg(feature = "gpu")]
 use std::num::NonZeroU8;
 use std::num::ParseIntError;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::os::unix::io::FromRawFd;
 use std::os::unix::net::UnixStream;
 use std::path::{Path, PathBuf};
 use std::ptr;
 use std::str;
-use std::sync::{Arc, Barrier};
+use std::sync::{mpsc, Arc, Barrier};
+
 use std::thread;
 use std::thread::JoinHandle;
 use std::time::Duration;
@@ -38,8 +39,8 @@
 #[cfg(feature = "audio")]
 use devices::Ac97Dev;
 use devices::{
-    self, HostBackendDeviceProvider, KvmKernelIrqChip, PciDevice, VfioContainer, VfioDevice,
-    VfioPciDevice, VirtioPciDevice, XhciController,
+    self, HostBackendDeviceProvider, IrqChip, IrqEventIndex, KvmKernelIrqChip, PciDevice,
+    VcpuRunState, VfioContainer, VfioDevice, VfioPciDevice, VirtioPciDevice, XhciController,
 };
 use hypervisor::kvm::{Kvm, KvmVcpu, KvmVm};
 use hypervisor::{HypervisorCap, Vcpu, VcpuExit, VcpuRunHandle, Vm, VmCap};
@@ -48,25 +49,30 @@
 use net_util::{Error as NetError, MacAddress, Tap};
 use remain::sorted;
 use resources::{Alloc, MmioType, SystemAllocator};
-use sync::{Condvar, Mutex};
+use sync::Mutex;
 
 use base::{
     self, block_signal, clear_signal, drop_capabilities, error, flock, get_blocked_signals,
     get_group_id, get_user_id, getegid, geteuid, info, register_rt_signal_handler,
-    set_cpu_affinity, set_rt_prio_limit, set_rt_round_robin, signal, validate_raw_fd, warn, Event,
-    ExternalMapping, FlockOperation, Killable, MemoryMappingArena, PollContext, PollToken,
-    Protection, ScopedEvent, SignalFd, Terminal, Timer, WatchingEvents, SIGRTMIN,
+    set_cpu_affinity, set_rt_prio_limit, set_rt_round_robin, signal, validate_raw_descriptor, warn,
+    AsRawDescriptor, Event, EventType, ExternalMapping, FlockOperation, FromRawDescriptor,
+    Killable, MemoryMappingArena, PollToken, Protection, RawDescriptor, ScopedEvent, SignalFd,
+    Terminal, Timer, WaitContext, SIGRTMIN,
 };
 use vm_control::{
     BalloonControlCommand, BalloonControlRequestSocket, BalloonControlResponseSocket,
     BalloonControlResult, DiskControlCommand, DiskControlRequestSocket, DiskControlResponseSocket,
-    DiskControlResult, IrqSetup, UsbControlSocket, VmControlResponseSocket, VmIrqRequest,
-    VmIrqRequestSocket, VmIrqResponse, VmIrqResponseSocket, VmMemoryControlRequestSocket,
-    VmMemoryControlResponseSocket, VmMemoryRequest, VmMemoryResponse, VmMsyncRequest,
-    VmMsyncRequestSocket, VmMsyncResponse, VmMsyncResponseSocket, VmRunMode,
+    DiskControlResult, IrqSetup, UsbControlSocket, VcpuControl, VmControlResponseSocket,
+    VmIrqRequest, VmIrqRequestSocket, VmIrqResponse, VmIrqResponseSocket,
+    VmMemoryControlRequestSocket, VmMemoryControlResponseSocket, VmMemoryRequest, VmMemoryResponse,
+    VmMsyncRequest, VmMsyncRequestSocket, VmMsyncResponse, VmMsyncResponseSocket, VmRunMode,
 };
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+use vm_control::{VcpuDebug, VcpuDebugStatus, VcpuDebugStatusMessage, VmRequest, VmResponse};
 use vm_memory::{GuestAddress, GuestMemory};
 
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+use crate::gdb::{gdb_thread, GdbStub};
 use crate::{Config, DiskOption, Executable, SharedDir, SharedDirKind, TouchDeviceOption};
 use arch::{
     self, LinuxArch, RunnableLinuxVm, SerialHardware, SerialParameters, VcpuAffinity,
@@ -76,13 +82,13 @@
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 use {
     aarch64::AArch64 as Arch,
-    devices::{IrqChip, IrqChipAArch64 as IrqChipArch},
+    devices::IrqChipAArch64 as IrqChipArch,
     hypervisor::{VcpuAArch64 as VcpuArch, VmAArch64 as VmArch},
 };
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
 use {
-    devices::{IrqChipX86_64, IrqChipX86_64 as IrqChipArch, KvmSplitIrqChip},
-    hypervisor::{VcpuX86_64, VcpuX86_64 as VcpuArch, VmX86_64 as VmArch},
+    devices::{IrqChipX86_64 as IrqChipArch, KvmSplitIrqChip},
+    hypervisor::{VcpuX86_64 as VcpuArch, VmX86_64 as VmArch},
     x86_64::X8664arch as Arch,
 };
 
@@ -107,7 +113,6 @@
     CreateConsole(arch::serial::Error),
     CreateDiskError(disk::Error),
     CreateEvent(base::Error),
-    CreatePollContext(base::Error),
     CreateSignalFd(base::SignalFdError),
     CreateSocket(io::Error),
     CreateTapDevice(NetError),
@@ -116,6 +121,7 @@
     CreateUsbProvider(devices::usb::host_backend::error::Error),
     CreateVcpu(base::Error),
     CreateVfioDevice(devices::vfio::VfioError),
+    CreateWaitContext(base::Error),
     DeviceJail(minijail::Error),
     DevicePivotRoot(minijail::Error),
     Disk(PathBuf, io::Error),
@@ -124,6 +130,8 @@
     FsDeviceNew(virtio::fs::Error),
     GetMaxOpenFiles(io::Error),
     GetSignalMask(signal::Error),
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    HandleDebugCommand(<Arch as LinuxArch>::Error),
     InputDeviceNew(virtio::InputError),
     InputEventsOpen(std::io::Error),
     InvalidFdPath,
@@ -143,8 +151,6 @@
     PivotRootDoesntExist(&'static str),
     PmemDeviceImageTooBig,
     PmemDeviceNew(base::Error),
-    PollContextAdd(base::Error),
-    PollContextDelete(base::Error),
     ReadMemAvailable(io::Error),
     RegisterBalloon(arch::DeviceRegistrationError),
     RegisterBlock(arch::DeviceRegistrationError),
@@ -160,17 +166,23 @@
     ResetTimer(base::Error),
     RngDeviceNew(virtio::RngError),
     RunnableVcpu(base::Error),
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    SendDebugStatus(Box<mpsc::SendError<VcpuDebugStatusMessage>>),
     SettingGidMap(minijail::Error),
     SettingMaxOpenFiles(minijail::Error),
     SettingSignalMask(base::Error),
     SettingUidMap(minijail::Error),
     SignalFd(base::SignalFdError),
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    SpawnGdbServer(io::Error),
     SpawnVcpu(io::Error),
     Timer(base::Error),
-    ValidateRawFd(base::Error),
+    ValidateRawDescriptor(base::Error),
     VhostNetDeviceNew(virtio::vhost::Error),
     VhostVsockDeviceNew(virtio::vhost::Error),
     VirtioPciDev(base::Error),
+    WaitContextAdd(base::Error),
+    WaitContextDelete(base::Error),
     WaylandDeviceNew(base::Error),
 }
 
@@ -201,7 +213,6 @@
             CreateConsole(e) => write!(f, "failed to create console device: {}", e),
             CreateDiskError(e) => write!(f, "failed to create virtual disk: {}", e),
             CreateEvent(e) => write!(f, "failed to create event: {}", e),
-            CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
             CreateSignalFd(e) => write!(f, "failed to create signalfd: {}", e),
             CreateSocket(e) => write!(f, "failed to create socket: {}", e),
             CreateTapDevice(e) => write!(f, "failed to create tap device: {}", e),
@@ -212,6 +223,7 @@
             CreateUsbProvider(e) => write!(f, "failed to create usb provider: {}", e),
             CreateVcpu(e) => write!(f, "failed to create vcpu: {}", e),
             CreateVfioDevice(e) => write!(f, "Failed to create vfio device {}", e),
+            CreateWaitContext(e) => write!(f, "failed to create wait context: {}", e),
             DeviceJail(e) => write!(f, "failed to jail device: {}", e),
             DevicePivotRoot(e) => write!(f, "failed to pivot root device: {}", e),
             Disk(p, e) => write!(f, "failed to load disk image {}: {}", p.display(), e),
@@ -220,6 +232,8 @@
             FsDeviceNew(e) => write!(f, "failed to create fs device: {}", e),
             GetMaxOpenFiles(e) => write!(f, "failed to get max number of open files: {}", e),
             GetSignalMask(e) => write!(f, "failed to retrieve signal mask for vcpu: {}", e),
+            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+            HandleDebugCommand(e) => write!(f, "failed to handle a gdb command: {}", e),
             InputDeviceNew(e) => write!(f, "failed to set up input device: {}", e),
             InputEventsOpen(e) => write!(f, "failed to open event device: {}", e),
             InvalidFdPath => write!(f, "failed parsing a /proc/self/fd/*"),
@@ -246,8 +260,6 @@
                 write!(f, "failed to create pmem device: pmem device image too big")
             }
             PmemDeviceNew(e) => write!(f, "failed to create pmem device: {}", e),
-            PollContextAdd(e) => write!(f, "failed to add fd to poll context: {}", e),
-            PollContextDelete(e) => write!(f, "failed to remove fd from poll context: {}", e),
             ReadMemAvailable(e) => write!(f, "failed to read /proc/meminfo: {}", e),
             RegisterBalloon(e) => write!(f, "error registering balloon device: {}", e),
             RegisterBlock(e) => write!(f, "error registering block device: {}", e),
@@ -263,17 +275,25 @@
             ResetTimer(e) => write!(f, "failed to reset Timer: {}", e),
             RngDeviceNew(e) => write!(f, "failed to set up rng: {}", e),
             RunnableVcpu(e) => write!(f, "failed to set thread id for vcpu: {}", e),
+            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+            SendDebugStatus(e) => write!(f, "failed to send a debug status to GDB thread: {}", e),
             SettingGidMap(e) => write!(f, "error setting GID map: {}", e),
             SettingMaxOpenFiles(e) => write!(f, "error setting max open files: {}", e),
             SettingSignalMask(e) => write!(f, "failed to set the signal mask for vcpu: {}", e),
             SettingUidMap(e) => write!(f, "error setting UID map: {}", e),
             SignalFd(e) => write!(f, "failed to read signal fd: {}", e),
+            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+            SpawnGdbServer(e) => write!(f, "failed to spawn GDB thread: {}", e),
             SpawnVcpu(e) => write!(f, "failed to spawn VCPU thread: {}", e),
             Timer(e) => write!(f, "failed to read timer fd: {}", e),
-            ValidateRawFd(e) => write!(f, "failed to validate raw fd: {}", e),
+            ValidateRawDescriptor(e) => write!(f, "failed to validate raw descriptor: {}", e),
             VhostNetDeviceNew(e) => write!(f, "failed to set up vhost networking: {}", e),
             VhostVsockDeviceNew(e) => write!(f, "failed to set up virtual socket device: {}", e),
             VirtioPciDev(e) => write!(f, "failed to create virtio pci dev: {}", e),
+            WaitContextAdd(e) => write!(f, "failed to add descriptor to wait context: {}", e),
+            WaitContextDelete(e) => {
+                write!(f, "failed to remove descriptor from wait context: {}", e)
+            }
             WaylandDeviceNew(e) => write!(f, "failed to create wayland device: {}", e),
         }
     }
@@ -308,9 +328,9 @@
     }
 }
 
-impl AsRawFd for TaggedControlSocket {
-    fn as_raw_fd(&self) -> RawFd {
-        self.as_ref().as_raw_fd()
+impl AsRawDescriptor for TaggedControlSocket {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.as_ref().as_raw_descriptor()
     }
 }
 
@@ -442,7 +462,7 @@
     // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
     let raw_image: File = if disk.path.parent() == Some(Path::new("/proc/self/fd")) {
         // Safe because we will validate |raw_fd|.
-        unsafe { File::from_raw_fd(raw_fd_from_path(&disk.path)?) }
+        unsafe { File::from_raw_descriptor(raw_descriptor_from_path(&disk.path)?) }
     } else {
         OpenOptions::new()
             .read(true)
@@ -476,10 +496,8 @@
 }
 
 fn create_rng_device(cfg: &Config) -> DeviceResult {
-    let dev = virtio::Rng::new(
-        virtio::base_features(cfg.protected_vm)
-    )
-    .map_err(Error::RngDeviceNew)?;
+    let dev =
+        virtio::Rng::new(virtio::base_features(cfg.protected_vm)).map_err(Error::RngDeviceNew)?;
 
     Ok(VirtioDeviceStub {
         dev: Box::new(dev),
@@ -636,11 +654,13 @@
     })
 }
 
-fn create_tap_net_device(cfg: &Config, tap_fd: RawFd) -> DeviceResult {
+fn create_tap_net_device(cfg: &Config, tap_fd: RawDescriptor) -> DeviceResult {
     // Safe because we ensure that we get a unique handle to the fd.
     let tap = unsafe {
-        Tap::from_raw_fd(validate_raw_fd(tap_fd).map_err(Error::ValidateRawFd)?)
-            .map_err(Error::CreateTapDevice)?
+        Tap::from_raw_descriptor(
+            validate_raw_descriptor(tap_fd).map_err(Error::ValidateRawDescriptor)?,
+        )
+        .map_err(Error::CreateTapDevice)?
     };
 
     let mut vq_pairs = cfg.net_vq_pairs.unwrap_or(1);
@@ -1024,6 +1044,7 @@
     gid_map: &str,
     src: &Path,
     tag: &str,
+    mut p9_cfg: p9::Config,
 ) -> DeviceResult {
     let max_open_files = get_max_open_files()?;
     let (jail, root) = if cfg.sandbox {
@@ -1051,7 +1072,8 @@
     };
 
     let features = virtio::base_features(cfg.protected_vm);
-    let dev = virtio::P9::new(features, root, tag).map_err(Error::P9DeviceNew)?;
+    p9_cfg.root = root.into();
+    let dev = virtio::P9::new(features, tag, p9_cfg).map_err(Error::P9DeviceNew)?;
 
     Ok(VirtioDeviceStub {
         dev: Box::new(dev),
@@ -1149,10 +1171,10 @@
 }
 
 fn create_console_device(cfg: &Config, param: &SerialParameters) -> DeviceResult {
-    let mut keep_fds = Vec::new();
+    let mut keep_rds = Vec::new();
     let evt = Event::new().map_err(Error::CreateEvent)?;
     let dev = param
-        .create_serial_device::<Console>(cfg.protected_vm, &evt, &mut keep_fds)
+        .create_serial_device::<Console>(cfg.protected_vm, &evt, &mut keep_rds)
         .map_err(Error::CreateConsole)?;
 
     let jail = match simple_jail(&cfg, "serial")? {
@@ -1380,12 +1402,13 @@
             kind,
             uid_map,
             gid_map,
-            cfg: fs_cfg,
+            fs_cfg,
+            p9_cfg,
         } = shared_dir;
 
         let dev = match kind {
             SharedDirKind::FS => create_fs_device(cfg, uid_map, gid_map, src, tag, fs_cfg.clone())?,
-            SharedDirKind::P9 => create_9p_device(cfg, uid_map, gid_map, src, tag)?,
+            SharedDirKind::P9 => create_9p_device(cfg, uid_map, gid_map, src, tag, p9_cfg.clone())?,
         };
         devs.push(dev);
     }
@@ -1521,16 +1544,16 @@
     })
 }
 
-fn raw_fd_from_path(path: &Path) -> Result<RawFd> {
+fn raw_descriptor_from_path(path: &Path) -> Result<RawDescriptor> {
     if !path.is_file() {
         return Err(Error::InvalidFdPath);
     }
-    let raw_fd = path
+    let raw_descriptor = path
         .file_name()
         .and_then(|fd_osstr| fd_osstr.to_str())
         .and_then(|fd_str| fd_str.parse::<c_int>().ok())
         .ok_or(Error::InvalidFdPath)?;
-    validate_raw_fd(raw_fd).map_err(Error::ValidateRawFd)
+    validate_raw_descriptor(raw_descriptor).map_err(Error::ValidateRawDescriptor)
 }
 
 trait IntoUnixStream {
@@ -1541,7 +1564,7 @@
     fn into_unix_stream(self) -> Result<UnixStream> {
         if self.parent() == Some(Path::new("/proc/self/fd")) {
             // Safe because we will validate |raw_fd|.
-            unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(self)?)) }
+            unsafe { Ok(UnixStream::from_raw_fd(raw_descriptor_from_path(self)?)) }
         } else {
             UnixStream::connect(self).map_err(Error::InputEventsOpen)
         }
@@ -1580,19 +1603,6 @@
     Ok(())
 }
 
-#[derive(Default)]
-struct VcpuRunMode {
-    mtx: Mutex<VmRunMode>,
-    cvar: Condvar,
-}
-
-impl VcpuRunMode {
-    fn set_and_notify(&self, new_mode: VmRunMode) {
-        *self.mtx.lock() = new_mode;
-        self.cvar.notify_all();
-    }
-}
-
 // Sets up a vcpu and converts it into a runnable vcpu.
 fn runnable_vcpu<V>(
     cpu_id: usize,
@@ -1674,37 +1684,82 @@
     Ok((vcpu, vcpu_run_handle))
 }
 
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-fn inject_interrupt(irq_chip: &mut dyn IrqChipX86_64, vcpu: &dyn VcpuX86_64, vcpu_id: usize) {
-    if !irq_chip.interrupt_requested(vcpu_id) || !vcpu.ready_for_interrupt() {
-        return;
-    }
-
-    let vector = irq_chip
-        .get_external_interrupt(vcpu_id)
-        .unwrap_or_else(|e| {
-            error!("get_external_interrupt failed on vcpu {}: {}", vcpu_id, e);
-            None
-        });
-    if let Some(vector) = vector {
-        if let Err(e) = vcpu.interrupt(vector as u32) {
-            error!(
-                "Failed to inject interrupt {} to vcpu {}: {}",
-                vector, vcpu_id, e
-            );
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+fn handle_debug_msg<V>(
+    cpu_id: usize,
+    vcpu: &V,
+    guest_mem: &GuestMemory,
+    d: VcpuDebug,
+    reply_channel: &mpsc::Sender<VcpuDebugStatusMessage>,
+) -> Result<()>
+where
+    V: VcpuArch + 'static,
+{
+    match d {
+        VcpuDebug::ReadRegs => {
+            let msg = VcpuDebugStatusMessage {
+                cpu: cpu_id as usize,
+                msg: VcpuDebugStatus::RegValues(
+                    Arch::debug_read_registers(vcpu as &V).map_err(Error::HandleDebugCommand)?,
+                ),
+            };
+            reply_channel
+                .send(msg)
+                .map_err(|e| Error::SendDebugStatus(Box::new(e)))
+        }
+        VcpuDebug::WriteRegs(regs) => {
+            Arch::debug_write_registers(vcpu as &V, &regs).map_err(Error::HandleDebugCommand)?;
+            reply_channel
+                .send(VcpuDebugStatusMessage {
+                    cpu: cpu_id as usize,
+                    msg: VcpuDebugStatus::CommandComplete,
+                })
+                .map_err(|e| Error::SendDebugStatus(Box::new(e)))
+        }
+        VcpuDebug::ReadMem(vaddr, len) => {
+            let msg = VcpuDebugStatusMessage {
+                cpu: cpu_id as usize,
+                msg: VcpuDebugStatus::MemoryRegion(
+                    Arch::debug_read_memory(vcpu as &V, guest_mem, vaddr, len)
+                        .unwrap_or(Vec::new()),
+                ),
+            };
+            reply_channel
+                .send(msg)
+                .map_err(|e| Error::SendDebugStatus(Box::new(e)))
+        }
+        VcpuDebug::WriteMem(vaddr, buf) => {
+            Arch::debug_write_memory(vcpu as &V, guest_mem, vaddr, &buf)
+                .map_err(Error::HandleDebugCommand)?;
+            reply_channel
+                .send(VcpuDebugStatusMessage {
+                    cpu: cpu_id as usize,
+                    msg: VcpuDebugStatus::CommandComplete,
+                })
+                .map_err(|e| Error::SendDebugStatus(Box::new(e)))
+        }
+        VcpuDebug::EnableSinglestep => {
+            Arch::debug_enable_singlestep(vcpu as &V).map_err(Error::HandleDebugCommand)?;
+            reply_channel
+                .send(VcpuDebugStatusMessage {
+                    cpu: cpu_id as usize,
+                    msg: VcpuDebugStatus::CommandComplete,
+                })
+                .map_err(|e| Error::SendDebugStatus(Box::new(e)))
+        }
+        VcpuDebug::SetHwBreakPoint(addrs) => {
+            Arch::debug_set_hw_breakpoints(vcpu as &V, &addrs)
+                .map_err(Error::HandleDebugCommand)?;
+            reply_channel
+                .send(VcpuDebugStatusMessage {
+                    cpu: cpu_id as usize,
+                    msg: VcpuDebugStatus::CommandComplete,
+                })
+                .map_err(|e| Error::SendDebugStatus(Box::new(e)))
         }
     }
-
-    // The second interrupt request should be handled immediately, so ask vCPU to exit as soon as
-    // possible.
-    if irq_chip.interrupt_requested(vcpu_id) {
-        vcpu.request_interrupt_window();
-    }
 }
 
-#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
-fn inject_interrupt(_irq_chip: &mut dyn IrqChip, _vcpu: &dyn Vcpu, _vcpu_id: usize) {}
-
 fn run_vcpu<V>(
     cpu_id: usize,
     vcpu: Option<V>,
@@ -1720,8 +1775,11 @@
     mmio_bus: devices::Bus,
     exit_evt: Event,
     requires_pvclock_ctrl: bool,
-    run_mode_arc: Arc<VcpuRunMode>,
+    from_main_channel: mpsc::Receiver<VcpuControl>,
     use_hypervisor_signals: bool,
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))] to_gdb_channel: Option<
+        mpsc::Sender<VcpuDebugStatusMessage>,
+    >,
 ) -> Result<JoinHandle<()>>
 where
     V: VcpuArch + 'static,
@@ -1733,6 +1791,8 @@
             // implementation accomplishes that.
             let _scoped_exit_evt = ScopedEvent::from(exit_evt);
 
+            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+            let guest_mem = vm.get_memory().clone();
             let runnable_vcpu = runnable_vcpu(
                 cpu_id,
                 vcpu,
@@ -1756,70 +1816,193 @@
                 }
             };
 
-            loop {
-                let mut interrupted_by_signal = false;
-                match vcpu.run(&vcpu_run_handle) {
-                    Ok(VcpuExit::IoIn { port, mut size }) => {
-                        let mut data = [0; 8];
-                        if size > data.len() {
-                            error!("unsupported IoIn size of {} bytes", size);
-                            size = data.len();
-                        }
-                        io_bus.read(port as u64, &mut data[..size]);
-                        if let Err(e) = vcpu.set_data(&data[..size]) {
-                            error!("failed to set return data for IoIn: {}", e);
+            let mut run_mode = VmRunMode::Running;
+            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+            if to_gdb_channel.is_some() {
+                // Wait until a GDB client attaches
+                run_mode = VmRunMode::Breakpoint;
+            }
+
+            let mut interrupted_by_signal = false;
+
+            'vcpu_loop: loop {
+                // Start by checking for messages to process and the run state of the CPU.
+                // An extra check here for Running so there isn't a need to call recv unless a
+                // message is likely to be ready because a signal was sent.
+                if interrupted_by_signal || run_mode != VmRunMode::Running {
+                    'state_loop: loop {
+                        // Tries to get a pending message without blocking first.
+                        let msg = match from_main_channel.try_recv() {
+                            Ok(m) => m,
+                            Err(mpsc::TryRecvError::Empty) if run_mode == VmRunMode::Running => {
+                                // If the VM is running and no message is pending, the state won't
+                                // change.
+                                break 'state_loop;
+                            }
+                            Err(mpsc::TryRecvError::Empty) => {
+                                // If the VM is not running, wait until a message is ready.
+                                match from_main_channel.recv() {
+                                    Ok(m) => m,
+                                    Err(mpsc::RecvError) => {
+                                        error!("Failed to read from main channel in vcpu");
+                                        break 'vcpu_loop;
+                                    }
+                                }
+                            }
+                            Err(mpsc::TryRecvError::Disconnected) => {
+                                error!("Failed to read from main channel in vcpu");
+                                break 'vcpu_loop;
+                            }
+                        };
+
+                        // Collect all pending messages.
+                        let mut messages = vec![msg];
+                        messages.append(&mut from_main_channel.try_iter().collect());
+
+                        for msg in messages {
+                            match msg {
+                                VcpuControl::RunState(new_mode) => {
+                                    run_mode = new_mode;
+                                    match run_mode {
+                                        VmRunMode::Running => break 'state_loop,
+                                        VmRunMode::Suspending => {
+                                            // On KVM implementations that use a paravirtualized
+                                            // clock (e.g. x86), a flag must be set to indicate to
+                                            // the guest kernel that a vCPU was suspended. The guest
+                                            // kernel will use this flag to prevent the soft lockup
+                                            // detection from triggering when this vCPU resumes,
+                                            // which could happen days later in realtime.
+                                            if requires_pvclock_ctrl {
+                                                if let Err(e) = vcpu.pvclock_ctrl() {
+                                                    error!(
+                                                        "failed to tell hypervisor vcpu {} is suspending: {}",
+                                                        cpu_id, e
+                                                    );
+                                                }
+                                            }
+                                        }
+                                        VmRunMode::Breakpoint => {}
+                                        VmRunMode::Exiting => break 'vcpu_loop,
+                                    }
+                                }
+                                #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+                                VcpuControl::Debug(d) => {
+                                    match &to_gdb_channel {
+                                        Some(ref ch) => {
+                                            if let Err(e) = handle_debug_msg(
+                                                cpu_id, &vcpu, &guest_mem, d, &ch,
+                                            ) {
+                                                error!("Failed to handle gdb message: {}", e);
+                                            }
+                                        },
+                                        None => {
+                                            error!("VcpuControl::Debug received while GDB feature is disabled: {:?}", d);
+                                        }
+                                    }
+                                }
+                            }
                         }
                     }
-                    Ok(VcpuExit::IoOut {
-                        port,
-                        mut size,
-                        data,
-                    }) => {
-                        if size > data.len() {
-                            error!("unsupported IoOut size of {} bytes", size);
-                            size = data.len();
+                }
+
+                interrupted_by_signal = false;
+
+                // Vcpus may have run a HLT instruction, which puts them into a state other than
+                // VcpuRunState::Runnable. In that case, this call to wait_until_runnable blocks
+                // until either the irqchip receives an interrupt for this vcpu, or until the main
+                // thread kicks this vcpu as a result of some VmControl operation. In most IrqChip
+                // implementations HLT instructions do not make it to crosvm, and thus this is a
+                // no-op that always returns VcpuRunState::Runnable.
+                match irq_chip.wait_until_runnable(&vcpu) {
+                    Ok(VcpuRunState::Runnable) => {}
+                    Ok(VcpuRunState::Interrupted) => interrupted_by_signal = true,
+                    Err(e) => error!(
+                        "error waiting for vcpu {} to become runnable: {}",
+                        cpu_id, e
+                    ),
+                }
+
+                if !interrupted_by_signal {
+                    match vcpu.run(&vcpu_run_handle) {
+                        Ok(VcpuExit::IoIn { port, mut size }) => {
+                            let mut data = [0; 8];
+                            if size > data.len() {
+                                error!("unsupported IoIn size of {} bytes", size);
+                                size = data.len();
+                            }
+                            io_bus.read(port as u64, &mut data[..size]);
+                            if let Err(e) = vcpu.set_data(&data[..size]) {
+                                error!("failed to set return data for IoIn: {}", e);
+                            }
                         }
-                        io_bus.write(port as u64, &data[..size]);
-                    }
-                    Ok(VcpuExit::MmioRead { address, size }) => {
-                        let mut data = [0; 8];
-                        mmio_bus.read(address, &mut data[..size]);
-                        // Setting data for mmio can not fail.
-                        let _ = vcpu.set_data(&data[..size]);
-                    }
-                    Ok(VcpuExit::MmioWrite {
-                        address,
-                        size,
-                        data,
-                    }) => {
-                        mmio_bus.write(address, &data[..size]);
-                    }
-                    Ok(VcpuExit::IoapicEoi { vector }) => {
-                        if let Err(e) = irq_chip.broadcast_eoi(vector) {
-                            error!(
-                                "failed to broadcast eoi {} on vcpu {}: {}",
-                                vector, cpu_id, e
-                            );
+                        Ok(VcpuExit::IoOut {
+                            port,
+                            mut size,
+                            data,
+                        }) => {
+                            if size > data.len() {
+                                error!("unsupported IoOut size of {} bytes", size);
+                                size = data.len();
+                            }
+                            io_bus.write(port as u64, &data[..size]);
                         }
-                    }
-                    Ok(VcpuExit::Hlt) => break,
-                    Ok(VcpuExit::Shutdown) => break,
-                    Ok(VcpuExit::FailEntry {
-                        hardware_entry_failure_reason,
-                    }) => {
-                        error!("vcpu hw run failure: {:#x}", hardware_entry_failure_reason);
-                        break;
-                    }
-                    Ok(VcpuExit::SystemEvent(_, _)) => break,
-                    Ok(r) => warn!("unexpected vcpu exit: {:?}", r),
-                    Err(e) => match e.errno() {
-                        libc::EINTR => interrupted_by_signal = true,
-                        libc::EAGAIN => {}
-                        _ => {
-                            error!("vcpu hit unknown error: {}", e);
+                        Ok(VcpuExit::MmioRead { address, size }) => {
+                            let mut data = [0; 8];
+                            mmio_bus.read(address, &mut data[..size]);
+                            // Setting data for mmio can not fail.
+                            let _ = vcpu.set_data(&data[..size]);
+                        }
+                        Ok(VcpuExit::MmioWrite {
+                            address,
+                            size,
+                            data,
+                        }) => {
+                            mmio_bus.write(address, &data[..size]);
+                        }
+                        Ok(VcpuExit::IoapicEoi { vector }) => {
+                            if let Err(e) = irq_chip.broadcast_eoi(vector) {
+                                error!(
+                                    "failed to broadcast eoi {} on vcpu {}: {}",
+                                    vector, cpu_id, e
+                                );
+                            }
+                        }
+                        Ok(VcpuExit::IrqWindowOpen) => {}
+                        Ok(VcpuExit::Hlt) => irq_chip.halted(cpu_id),
+                        Ok(VcpuExit::Shutdown) => break,
+                        Ok(VcpuExit::FailEntry {
+                            hardware_entry_failure_reason,
+                        }) => {
+                            error!("vcpu hw run failure: {:#x}", hardware_entry_failure_reason);
                             break;
                         }
-                    },
+                        Ok(VcpuExit::SystemEvent(_, _)) => break,
+                        Ok(VcpuExit::Debug { .. }) => {
+                            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+                            {
+                                let msg = VcpuDebugStatusMessage {
+                                    cpu: cpu_id as usize,
+                                    msg: VcpuDebugStatus::HitBreakPoint,
+                                };
+                                if let Some(ref ch) = to_gdb_channel {
+                                    if let Err(e) = ch.send(msg) {
+                                        error!("failed to notify breakpoint to GDB thread: {}", e);
+                                        break;
+                                    }
+                                }
+                                run_mode = VmRunMode::Breakpoint;
+                            }
+                        }
+                        Ok(r) => warn!("unexpected vcpu exit: {:?}", r),
+                        Err(e) => match e.errno() {
+                            libc::EINTR => interrupted_by_signal = true,
+                            libc::EAGAIN => {}
+                            _ => {
+                                error!("vcpu hit unknown error: {}", e);
+                                break;
+                            }
+                        },
+                    }
                 }
 
                 if interrupted_by_signal {
@@ -1833,35 +2016,11 @@
                     } else {
                         vcpu.set_immediate_exit(false);
                     }
-                    let mut run_mode_lock = run_mode_arc.mtx.lock();
-                    loop {
-                        match *run_mode_lock {
-                            VmRunMode::Running => break,
-                            VmRunMode::Suspending => {
-                                // On KVM implementations that use a paravirtualized clock (e.g.
-                                // x86), a flag must be set to indicate to the guest kernel that a
-                                // VCPU was suspended. The guest kernel will use this flag to
-                                // prevent the soft lockup detection from triggering when this VCPU
-                                // resumes, which could happen days later in realtime.
-                                if requires_pvclock_ctrl {
-                                    if let Err(e) = vcpu.pvclock_ctrl() {
-                                        error!(
-                                            "failed to tell hypervisor vcpu {} is suspending: {}",
-                                            cpu_id, e
-                                        );
-                                    }
-                                }
-                            }
-                            VmRunMode::Exiting => return,
-                        }
-                        // Give ownership of our exclusive lock to the condition variable that will
-                        // block. When the condition variable is notified, `wait` will unblock and
-                        // return a new exclusive lock.
-                        run_mode_lock = run_mode_arc.cvar.wait(run_mode_lock);
-                    }
                 }
 
-                inject_interrupt(&mut irq_chip, &vcpu, cpu_id);
+                if let Err(e) = irq_chip.inject_interrupts(&vcpu) {
+                    error!("failed to inject interrupts for vcpu {}: {}", cpu_id, e);
+                }
             }
         })
         .map_err(Error::SpawnVcpu)
@@ -1979,6 +2138,18 @@
         _ => panic!("Did not receive a bios or kernel, should be impossible."),
     };
 
+    let mut control_sockets = Vec::new();
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    let gdb_socket = if let Some(port) = cfg.gdb {
+        // GDB needs a control socket to interrupt vcpus.
+        let (gdb_host_socket, gdb_control_socket) =
+            msg_socket::pair::<VmResponse, VmRequest>().map_err(Error::CreateSocket)?;
+        control_sockets.push(TaggedControlSocket::Vm(gdb_host_socket));
+        Some((port, gdb_control_socket))
+    } else {
+        None
+    };
+
     let components = VmComponents {
         memory_size: cfg
             .memory
@@ -2005,6 +2176,8 @@
             .collect::<Result<Vec<SDT>>>()?,
         rt_cpus: cfg.rt_cpus.clone(),
         protected_vm: cfg.protected_vm,
+        #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+        gdb: gdb_socket,
     };
 
     let control_server_socket = match &cfg.socket_path {
@@ -2014,7 +2187,6 @@
         None => None,
     };
 
-    let mut control_sockets = Vec::new();
     let (wayland_host_socket, wayland_device_socket) =
         msg_socket::pair::<VmMemoryResponse, VmMemoryRequest>().map_err(Error::CreateSocket)?;
     control_sockets.push(TaggedControlSocket::VmMemory(wayland_host_socket));
@@ -2052,12 +2224,19 @@
         msg_socket::pair::<VmIrqResponse, VmIrqRequest>().map_err(Error::CreateSocket)?;
     control_sockets.push(TaggedControlSocket::VmIrq(ioapic_host_socket));
 
+    let battery = if cfg.battery_type.is_some() {
+        (&cfg.battery_type, simple_jail(&cfg, "battery")?)
+    } else {
+        (&cfg.battery_type, None)
+    };
+
     let map_request: Arc<Mutex<Option<ExternalMapping>>> = Arc::new(Mutex::new(None));
 
     let linux: RunnableLinuxVm<_, Vcpu, _> = Arch::build_vm(
         components,
         &cfg.serial_parameters,
         simple_jail(&cfg, "serial")?,
+        battery,
         |mem, vm, sys_allocator, exit_evt| {
             create_devices(
                 &cfg,
@@ -2093,6 +2272,24 @@
     )
 }
 
+/// Signals all running VCPUs to vmexit, sends VmRunMode message to each VCPU channel, and tells
+/// `irq_chip` to stop blocking halted VCPUs. The channel message is set first because both the
+/// signal and the irq_chip kick could cause the VCPU thread to continue through the VCPU run
+/// loop.
+fn kick_all_vcpus(
+    vcpu_handles: &[(JoinHandle<()>, mpsc::Sender<vm_control::VcpuControl>)],
+    irq_chip: &impl IrqChip,
+    run_mode: &VmRunMode,
+) {
+    for (handle, channel) in vcpu_handles {
+        if let Err(e) = channel.send(VcpuControl::RunState(run_mode.clone())) {
+            error!("failed to send VmRunMode: {}", e);
+        }
+        let _ = handle.kill(SIGRTMIN() + 0);
+    }
+    irq_chip.kick_halted_vcpus();
+}
+
 fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static, I: IrqChipArch + 'static>(
     mut linux: RunnableLinuxVm<V, Vcpu, I>,
     control_server_socket: Option<UnlinkUnixSeqpacketListener>,
@@ -2111,7 +2308,7 @@
         Exit,
         Suspend,
         ChildSignal,
-        IrqFd { gsi: usize },
+        IrqFd { index: IrqEventIndex },
         BalanceMemory,
         BalloonResult,
         VmControlServer,
@@ -2122,42 +2319,42 @@
         .set_raw_mode()
         .expect("failed to set terminal raw mode");
 
-    let poll_ctx = PollContext::build_with(&[
+    let wait_ctx = WaitContext::build_with(&[
         (&linux.exit_evt, Token::Exit),
         (&linux.suspend_evt, Token::Suspend),
         (&sigchld_fd, Token::ChildSignal),
     ])
-    .map_err(Error::PollContextAdd)?;
+    .map_err(Error::WaitContextAdd)?;
 
     if let Some(socket_server) = &control_server_socket {
-        poll_ctx
+        wait_ctx
             .add(socket_server, Token::VmControlServer)
-            .map_err(Error::PollContextAdd)?;
+            .map_err(Error::WaitContextAdd)?;
     }
     for (index, socket) in control_sockets.iter().enumerate() {
-        poll_ctx
+        wait_ctx
             .add(socket.as_ref(), Token::VmControl { index })
-            .map_err(Error::PollContextAdd)?;
+            .map_err(Error::WaitContextAdd)?;
     }
 
     let events = linux
         .irq_chip
         .irq_event_tokens()
-        .map_err(Error::PollContextAdd)?;
+        .map_err(Error::WaitContextAdd)?;
 
-    for (gsi, evt) in events {
-        poll_ctx
-            .add(&evt, Token::IrqFd { gsi: gsi as usize })
-            .map_err(Error::PollContextAdd)?;
+    for (index, _gsi, evt) in events {
+        wait_ctx
+            .add(&evt, Token::IrqFd { index })
+            .map_err(Error::WaitContextAdd)?;
     }
 
     // Balance available memory between guest and host every second.
     let mut balancemem_timer = Timer::new().map_err(Error::CreateTimer)?;
     if Path::new(LOWMEM_AVAILABLE).exists() {
         // Create timer request balloon stats every 1s.
-        poll_ctx
+        wait_ctx
             .add(&balancemem_timer, Token::BalanceMemory)
-            .map_err(Error::PollContextAdd)?;
+            .map_err(Error::WaitContextAdd)?;
         let balancemem_dur = Duration::from_secs(1);
         let balancemem_int = Duration::from_secs(1);
         balancemem_timer
@@ -2165,9 +2362,9 @@
             .map_err(Error::ResetTimer)?;
 
         // Listen for balloon statistics from the guest so we can balance.
-        poll_ctx
+        wait_ctx
             .add(&balloon_host_socket, Token::BalloonResult)
-            .map_err(Error::PollContextAdd)?;
+            .map_err(Error::WaitContextAdd)?;
     } else {
         warn!("Unable to open low mem available, maybe not a chrome os kernel");
     }
@@ -2177,9 +2374,17 @@
         drop_capabilities().map_err(Error::DropCapabilities)?;
     }
 
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    // Create a channel for GDB thread.
+    let (to_gdb_channel, from_vcpu_channel) = if linux.gdb.is_some() {
+        let (s, r) = mpsc::channel();
+        (Some(s), Some(r))
+    } else {
+        (None, None)
+    };
+
     let mut vcpu_handles = Vec::with_capacity(linux.vcpu_count);
     let vcpu_thread_barrier = Arc::new(Barrier::new(linux.vcpu_count + 1));
-    let run_mode_arc = Arc::new(VcpuRunMode::default());
     let use_hypervisor_signals = !linux
         .vm
         .get_hypervisor()
@@ -2191,6 +2396,7 @@
         None => iter::repeat_with(|| None).take(linux.vcpu_count).collect(),
     };
     for (cpu_id, vcpu) in vcpus.into_iter().enumerate() {
+        let (to_vcpu_channel, from_main_channel) = mpsc::channel();
         let vcpu_affinity = match linux.vcpu_affinity.clone() {
             Some(VcpuAffinity::Global(v)) => v,
             Some(VcpuAffinity::PerVcpu(mut m)) => m.remove(&cpu_id).unwrap_or_default(),
@@ -2211,17 +2417,37 @@
             linux.mmio_bus.clone(),
             linux.exit_evt.try_clone().map_err(Error::CloneEvent)?,
             linux.vm.check_capability(VmCap::PvClockSuspend),
-            run_mode_arc.clone(),
+            from_main_channel,
             use_hypervisor_signals,
+            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+            to_gdb_channel.clone(),
         )?;
-        vcpu_handles.push(handle);
+        vcpu_handles.push((handle, to_vcpu_channel));
     }
 
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    // Spawn GDB thread.
+    if let Some((gdb_port_num, gdb_control_socket)) = linux.gdb.take() {
+        let to_vcpu_channels = vcpu_handles
+            .iter()
+            .map(|(_handle, channel)| channel.clone())
+            .collect();
+        let target = GdbStub::new(
+            gdb_control_socket,
+            to_vcpu_channels,
+            from_vcpu_channel.unwrap(), // Must succeed to unwrap()
+        );
+        thread::Builder::new()
+            .name("gdb".to_owned())
+            .spawn(move || gdb_thread(target, gdb_port_num))
+            .map_err(Error::SpawnGdbServer)?;
+    };
+
     vcpu_thread_barrier.wait();
 
-    'poll: loop {
+    'wait: loop {
         let events = {
-            match poll_ctx.wait() {
+            match wait_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed to poll: {}", e);
@@ -2235,19 +2461,16 @@
         }
 
         let mut vm_control_indices_to_remove = Vec::new();
-        for event in events.iter_readable() {
-            match event.token() {
+        for event in events.iter().filter(|e| e.is_readable) {
+            match event.token {
                 Token::Exit => {
                     info!("vcpu requested shutdown");
-                    break 'poll;
+                    break 'wait;
                 }
                 Token::Suspend => {
                     info!("VM requested suspend");
                     linux.suspend_evt.read().unwrap();
-                    run_mode_arc.set_and_notify(VmRunMode::Suspending);
-                    for handle in &vcpu_handles {
-                        let _ = handle.kill(SIGRTMIN() + 0);
-                    }
+                    kick_all_vcpus(&vcpu_handles, &linux.irq_chip, &VmRunMode::Suspending);
                 }
                 Token::ChildSignal => {
                     // Print all available siginfo structs, then exit the loop.
@@ -2262,11 +2485,11 @@
                             pid_label, siginfo.ssi_signo, siginfo.ssi_status, siginfo.ssi_code
                         );
                     }
-                    break 'poll;
+                    break 'wait;
                 }
-                Token::IrqFd { gsi } => {
-                    if let Err(e) = linux.irq_chip.service_irq_event(gsi as u32) {
-                        error!("failed to signal irq {}: {}", gsi, e);
+                Token::IrqFd { index } => {
+                    if let Err(e) = linux.irq_chip.service_irq_event(index) {
+                        error!("failed to signal irq {}: {}", index, e);
                     }
                 }
                 Token::BalanceMemory => {
@@ -2355,14 +2578,14 @@
                     if let Some(socket_server) = &control_server_socket {
                         match socket_server.accept() {
                             Ok(socket) => {
-                                poll_ctx
+                                wait_ctx
                                     .add(
                                         &socket,
                                         Token::VmControl {
                                             index: control_sockets.len(),
                                         },
                                     )
-                                    .map_err(Error::PollContextAdd)?;
+                                    .map_err(Error::WaitContextAdd)?;
                                 control_sockets
                                     .push(TaggedControlSocket::Vm(MsgSocket::new(socket)));
                             }
@@ -2381,6 +2604,7 @@
                                         &balloon_host_socket,
                                         disk_host_sockets,
                                         &usb_control_socket,
+                                        &mut linux.bat_control,
                                     );
                                     if let Err(e) = socket.send(&response) {
                                         error!("failed to send VmResponse: {}", e);
@@ -2389,24 +2613,17 @@
                                         info!("control socket changed run mode to {}", run_mode);
                                         match run_mode {
                                             VmRunMode::Exiting => {
-                                                break 'poll;
-                                            }
-                                            VmRunMode::Running => {
-                                                if let VmRunMode::Suspending =
-                                                    *run_mode_arc.mtx.lock()
-                                                {
-                                                    linux.io_bus.notify_resume();
-                                                }
-                                                run_mode_arc.set_and_notify(VmRunMode::Running);
-                                                for handle in &vcpu_handles {
-                                                    let _ = handle.kill(SIGRTMIN() + 0);
-                                                }
+                                                break 'wait;
                                             }
                                             other => {
-                                                run_mode_arc.set_and_notify(other);
-                                                for handle in &vcpu_handles {
-                                                    let _ = handle.kill(SIGRTMIN() + 0);
+                                                if other == VmRunMode::Running {
+                                                    linux.io_bus.notify_resume();
                                                 }
+                                                kick_all_vcpus(
+                                                    &vcpu_handles,
+                                                    &linux.irq_chip,
+                                                    &other,
+                                                );
                                             }
                                         }
                                     }
@@ -2445,7 +2662,26 @@
                                         request.execute(
                                             |setup| match setup {
                                                 IrqSetup::Event(irq, ev) => {
-                                                    irq_chip.register_irq_event(irq, ev, None)
+                                                    if let Some(event_index) = irq_chip
+                                                        .register_irq_event(irq, ev, None)?
+                                                    {
+                                                        match wait_ctx.add(
+                                                            ev,
+                                                            Token::IrqFd {
+                                                                index: event_index
+                                                            },
+                                                        ) {
+                                                            Err(e) => {
+                                                                warn!("failed to add IrqFd to poll context: {}", e);
+                                                                Err(e)
+                                                            },
+                                                            Ok(_) => {
+                                                                Ok(())
+                                                            }
+                                                        }
+                                                    } else {
+                                                        Ok(())
+                                                    }
                                                 }
                                                 IrqSetup::Route(route) => irq_chip.route_irq(route),
                                             },
@@ -2485,12 +2721,12 @@
             }
         }
 
-        for event in events.iter_hungup() {
-            match event.token() {
+        for event in events.iter().filter(|e| e.is_hungup) {
+            match event.token {
                 Token::Exit => {}
                 Token::Suspend => {}
                 Token::ChildSignal => {}
-                Token::IrqFd { gsi: _ } => {}
+                Token::IrqFd { index: _ } => {}
                 Token::BalanceMemory => {}
                 Token::BalloonResult => {}
                 Token::VmControlServer => {}
@@ -2515,45 +2751,35 @@
         vm_control_indices_to_remove.sort_unstable_by_key(|&k| Reverse(k));
         vm_control_indices_to_remove.dedup();
         for index in vm_control_indices_to_remove {
-            // Delete the socket from the `poll_ctx` synchronously. Otherwise, the kernel will do
-            // this automatically when the FD inserted into the `poll_ctx` is closed after this
+            // Delete the socket from the `wait_ctx` synchronously. Otherwise, the kernel will do
+            // this automatically when the FD inserted into the `wait_ctx` is closed after this
             // if-block, but this removal can be deferred unpredictably. In some instances where the
-            // system is under heavy load, we can even get events returned by `poll_ctx` for an FD
+            // system is under heavy load, we can even get events returned by `wait_ctx` for an FD
             // that has already been closed. Because the token associated with that spurious event
             // now belongs to a different socket, the control loop will start to interact with
             // sockets that might not be ready to use. This can cause incorrect hangup detection or
             // blocking on a socket that will never be ready. See also: crbug.com/1019986
             if let Some(socket) = control_sockets.get(index) {
-                poll_ctx.delete(socket).map_err(Error::PollContextDelete)?;
+                wait_ctx.delete(socket).map_err(Error::WaitContextDelete)?;
             }
 
             // This line implicitly drops the socket at `index` when it gets returned by
             // `swap_remove`. After this line, the socket at `index` is not the one from
             // `vm_control_indices_to_remove`. Because of this socket's change in index, we need to
-            // use `poll_ctx.modify` to change the associated index in its `Token::VmControl`.
+            // use `wait_ctx.modify` to change the associated index in its `Token::VmControl`.
             control_sockets.swap_remove(index);
             if let Some(socket) = control_sockets.get(index) {
-                poll_ctx
-                    .modify(
-                        socket,
-                        WatchingEvents::empty().set_read(),
-                        Token::VmControl { index },
-                    )
-                    .map_err(Error::PollContextAdd)?;
+                wait_ctx
+                    .modify(socket, EventType::Read, Token::VmControl { index })
+                    .map_err(Error::WaitContextAdd)?;
             }
         }
     }
 
-    // VCPU threads MUST see the VmRunMode flag, otherwise they may re-enter the VM.
-    run_mode_arc.set_and_notify(VmRunMode::Exiting);
-    for handle in vcpu_handles {
-        match handle.kill(SIGRTMIN() + 0) {
-            Ok(_) => {
-                if let Err(e) = handle.join() {
-                    error!("failed to join vcpu thread: {:?}", e);
-                }
-            }
-            Err(e) => error!("failed to kill vcpu thread: {}", e),
+    kick_all_vcpus(&vcpu_handles, &linux.irq_chip, &VmRunMode::Exiting);
+    for (handle, _) in vcpu_handles {
+        if let Err(e) = handle.join() {
+            error!("failed to join vcpu thread: {:?}", e);
         }
     }
 
diff --git a/src/main.rs b/src/main.rs
index bf09f0f..4aab39a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,6 @@
 use std::fs::{File, OpenOptions};
 use std::io::{BufRead, BufReader};
 use std::num::ParseIntError;
-use std::os::unix::io::{FromRawFd, RawFd};
 use std::path::{Path, PathBuf};
 use std::string::String;
 use std::thread::sleep;
@@ -24,11 +23,12 @@
 };
 use base::{
     debug, error, getpid, info, kill_process_group, net::UnixSeqpacket, reap_child, syslog,
-    validate_raw_fd, warn,
+    validate_raw_descriptor, warn, FromRawDescriptor, IntoRawDescriptor, RawDescriptor,
+    SafeDescriptor,
 };
 use crosvm::{
     argument::{self, print_help, set_arguments, Argument},
-    linux, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
+    platform, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
 };
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::{GpuMode, GpuParameters};
@@ -37,15 +37,13 @@
 use disk::QcowFile;
 use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
 use vm_control::{
-    BalloonControlCommand, DiskControlCommand, MaybeOwnedFd, UsbControlCommand, UsbControlResult,
-    VmControlRequestSocket, VmRequest, VmResponse, USB_CONTROL_MAX_PORTS,
+    BalloonControlCommand, BatControlCommand, BatControlResult, BatteryType, DiskControlCommand,
+    MaybeOwnedDescriptor, UsbControlCommand, UsbControlResult, VmControlRequestSocket, VmRequest,
+    VmResponse, USB_CONTROL_MAX_PORTS,
 };
 
 fn executable_is_plugin(executable: &Option<Executable>) -> bool {
-    match executable {
-        Some(Executable::Plugin(_)) => true,
-        _ => false,
-    }
+    matches!(executable, Some(Executable::Plugin(_)))
 }
 
 // Wait for all children to exit. Return true if they have all exited, false
@@ -162,6 +160,8 @@
     let mut vulkan_specified = false;
     #[cfg(feature = "gfxstream")]
     let mut syncfd_specified = false;
+    #[cfg(feature = "gfxstream")]
+    let mut angle_specified = false;
 
     if let Some(s) = s {
         let opts = s
@@ -283,6 +283,24 @@
                     }
                 }
                 #[cfg(feature = "gfxstream")]
+                "angle" => {
+                    angle_specified = true;
+                    match v {
+                        "true" | "" => {
+                            gpu_params.gfxstream_use_guest_angle = true;
+                        }
+                        "false" => {
+                            gpu_params.gfxstream_use_guest_angle = false;
+                        }
+                        _ => {
+                            return Err(argument::Error::InvalidValue {
+                                value: v.to_string(),
+                                expected: String::from("gpu parameter 'angle' should be a boolean"),
+                            });
+                        }
+                    }
+                }
+                #[cfg(feature = "gfxstream")]
                 "vulkan" => {
                     vulkan_specified = true;
                     match v {
@@ -337,7 +355,7 @@
 
     #[cfg(feature = "gfxstream")]
     {
-        if vulkan_specified || syncfd_specified {
+        if vulkan_specified || syncfd_specified || angle_specified {
             match gpu_params.mode {
                 GpuMode::ModeGfxStream => {}
                 _ => {
@@ -574,6 +592,40 @@
     })
 }
 
+fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
+    let mut battery_type: BatteryType = Default::default();
+
+    if let Some(s) = s {
+        let opts = s
+            .split(",")
+            .map(|frag| frag.split("="))
+            .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
+
+        for (k, v) in opts {
+            match k {
+                "type" => match v.parse::<BatteryType>() {
+                    Ok(type_) => battery_type = type_,
+                    Err(e) => {
+                        return Err(argument::Error::InvalidValue {
+                            value: v.to_string(),
+                            expected: e.to_string(),
+                        });
+                    }
+                },
+                "" => {}
+                _ => {
+                    return Err(argument::Error::UnknownArgument(format!(
+                        "battery parameter {}",
+                        k
+                    )));
+                }
+            }
+        }
+    }
+
+    Ok(battery_type)
+}
+
 fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
     match name {
         "" => {
@@ -1109,8 +1161,8 @@
                         })?;
 
                         let dur = Duration::from_secs(seconds);
-                        shared_dir.cfg.entry_timeout = dur.clone();
-                        shared_dir.cfg.attr_timeout = dur;
+                        shared_dir.fs_cfg.entry_timeout = dur.clone();
+                        shared_dir.fs_cfg.attr_timeout = dur;
                     }
                     "cache" => {
                         let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
@@ -1119,7 +1171,7 @@
                                 "`cache` must be one of `never`, `always`, or `auto`",
                             ),
                         })?;
-                        shared_dir.cfg.cache_policy = policy;
+                        shared_dir.fs_cfg.cache_policy = policy;
                     }
                     "writeback" => {
                         let writeback =
@@ -1127,7 +1179,7 @@
                                 value: value.to_owned(),
                                 expected: String::from("`writeback` must be a boolean"),
                             })?;
-                        shared_dir.cfg.writeback = writeback;
+                        shared_dir.fs_cfg.writeback = writeback;
                     }
                     "rewrite-security-xattrs" => {
                         let rewrite_security_xattrs =
@@ -1137,7 +1189,7 @@
                                     "`rewrite-security-xattrs` must be a boolean",
                                 ),
                             })?;
-                        shared_dir.cfg.rewrite_security_xattrs = rewrite_security_xattrs;
+                        shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
                     }
                     "ascii_casefold" => {
                         let ascii_casefold =
@@ -1145,7 +1197,8 @@
                                 value: value.to_owned(),
                                 expected: String::from("`ascii_casefold` must be a boolean"),
                             })?;
-                        shared_dir.cfg.ascii_casefold = ascii_casefold;
+                        shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
+                        shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
                     }
                     _ => {
                         return Err(argument::Error::InvalidValue {
@@ -1381,7 +1434,21 @@
             cfg.protected_vm = true;
             cfg.params.push("swiotlb=force".to_string());
         }
-
+        "battery" => {
+            let params = parse_battery_options(value)?;
+            cfg.battery_type = Some(params);
+        }
+        #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+        "gdb" => {
+            let port = value
+                .unwrap()
+                .parse()
+                .map_err(|_| argument::Error::InvalidValue {
+                    value: value.unwrap().to_owned(),
+                    expected: String::from("expected a valid port number"),
+                })?;
+            cfg.gdb = Some(port);
+        }
         "help" => return Err(argument::Error::PrintHelp),
         _ => unreachable!(),
     }
@@ -1423,6 +1490,14 @@
             }
         }
     }
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    if cfg.gdb.is_some() {
+        if cfg.vcpu_count.unwrap_or(1) != 1 {
+            return Err(argument::Error::ExpectedArgument(
+                "`gdb` requires the number of vCPU to be 1".to_owned(),
+            ));
+        }
+    }
     set_default_serial_parameters(&mut cfg.serial_parameters);
     Ok(())
 }
@@ -1543,6 +1618,7 @@
                                   egl[=true|=false] - If the virtio-gpu backend should use a EGL context for rendering.
                                   glx[=true|=false] - If the virtio-gpu backend should use a GLX context for rendering.
                                   surfaceless[=true|=false] - If the virtio-gpu backend should use a surfaceless context for rendering.
+                                  angle[=true|=false] - If the guest is using ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
                                   syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
                                   vulkan[=true|=false] - If the gfxstream backend should support vulkan
                                   "),
@@ -1563,6 +1639,13 @@
           Argument::flag("video-encoder", "(EXPERIMENTAL) enable virtio-video encoder device"),
           Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
           Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
+          Argument::flag_or_value("battery",
+                                  "[type=TYPE]",
+                                  "Comma separated key=value pairs for setting up battery device
+                                  Possible key values:
+                                  type=goldfish - type of battery emulation, defaults to goldfish
+                                  "),
+          Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
           Argument::short_flag('h', "help", "Print help message.")];
 
     let mut cfg = Config::default();
@@ -1585,7 +1668,7 @@
                 }
             }
         }
-        Ok(()) => match linux::run_config(cfg) {
+        Ok(()) => match platform::run_config(cfg) {
             Ok(_) => {
                 info!("crosvm has exited normally");
                 Ok(())
@@ -1834,7 +1917,7 @@
     ArgMissing(&'static str),
     ArgParse(&'static str, String),
     ArgParseInt(&'static str, String, ParseIntError),
-    FailedFdValidate(base::Error),
+    FailedDescriptorValidate(base::Error),
     PathDoesNotExist(PathBuf),
     SocketFailed,
     UnexpectedResponse(VmResponse),
@@ -1856,7 +1939,7 @@
                 "failed to parse integer argument {} value `{}`: {}",
                 name, value, e
             ),
-            FailedFdValidate(e) => write!(f, "failed to validate file descriptor: {}", e),
+            FailedDescriptorValidate(e) => write!(f, "failed to validate file descriptor: {}", e),
             PathDoesNotExist(p) => write!(f, "path `{}` does not exist", p.display()),
             SocketFailed => write!(f, "socket failed"),
             UnexpectedResponse(r) => write!(f, "unexpected response: {}", r),
@@ -1892,11 +1975,11 @@
     }
 }
 
-fn raw_fd_from_path(path: &Path) -> ModifyUsbResult<RawFd> {
+fn raw_descriptor_from_path(path: &Path) -> ModifyUsbResult<RawDescriptor> {
     if !path.exists() {
         return Err(ModifyUsbError::PathDoesNotExist(path.to_owned()));
     }
-    let raw_fd = path
+    let raw_descriptor = path
         .file_name()
         .and_then(|fd_osstr| fd_osstr.to_str())
         .map_or(
@@ -1910,7 +1993,7 @@
                 })
             },
         )?;
-    validate_raw_fd(raw_fd).map_err(ModifyUsbError::FailedFdValidate)
+    validate_raw_descriptor(raw_descriptor).map_err(ModifyUsbError::FailedDescriptorValidate)
 }
 
 fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
@@ -1927,7 +2010,7 @@
     } else if dev_path.parent() == Some(Path::new("/proc/self/fd")) {
         // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
         // Safe because we will validate |raw_fd|.
-        Some(unsafe { File::from_raw_fd(raw_fd_from_path(&dev_path)?) })
+        Some(unsafe { File::from_raw_descriptor(raw_descriptor_from_path(&dev_path)?) })
     } else {
         Some(
             OpenOptions::new()
@@ -1943,7 +2026,12 @@
         addr,
         vid,
         pid,
-        fd: usb_file.map(MaybeOwnedFd::Owned),
+        // Safe because we are transferring ownership to the rawdescriptor
+        descriptor: usb_file.map(|file| {
+            MaybeOwnedDescriptor::Owned(unsafe {
+                SafeDescriptor::from_raw_descriptor(file.into_raw_descriptor())
+            })
+        }),
     });
     let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
     match response {
@@ -2030,6 +2118,55 @@
     Ok(())
 }
 
+enum ModifyBatError {
+    BatControlErr(BatControlResult),
+}
+
+impl fmt::Display for ModifyBatError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::ModifyBatError::*;
+
+        match self {
+            BatControlErr(e) => write!(f, "{}", e),
+        }
+    }
+}
+
+fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
+    if args.len() < 4 {
+        print_help("crosvm battery BATTERY_TYPE ",
+                   "[status STATUS | present PRESENT | health HEALTH | capacity CAPACITY | aconline ACONLINE ] VM_SOCKET...", &[]);
+        return Err(());
+    }
+
+    // This unwrap will not panic because of the above length check.
+    let battery_type = args.next().unwrap();
+    let property = args.next().unwrap();
+    let target = args.next().unwrap();
+
+    let response = match battery_type.parse::<BatteryType>() {
+        Ok(type_) => match BatControlCommand::new(property, target) {
+            Ok(cmd) => {
+                let request = VmRequest::BatCommand(type_, cmd);
+                Ok(handle_request(&request, args)?)
+            }
+            Err(e) => Err(ModifyBatError::BatControlErr(e)),
+        },
+        Err(e) => Err(ModifyBatError::BatControlErr(e)),
+    };
+
+    match response {
+        Ok(response) => {
+            println!("{}", response);
+            Ok(())
+        }
+        Err(e) => {
+            println!("error {}", e);
+            Err(())
+        }
+    }
+}
+
 fn crosvm_main() -> std::result::Result<(), ()> {
     if let Err(e) = syslog::init() {
         println!("failed to initialize syslog: {}", e);
@@ -2060,6 +2197,7 @@
         Some("disk") => disk_cmd(args),
         Some("usb") => modify_usb(args),
         Some("version") => pkg_version(),
+        Some("battery") => modify_battery(args),
         Some(c) => {
             println!("invalid subcommand: {:?}", c);
             print_usage();
@@ -2526,4 +2664,24 @@
         assert!(parse_gpu_options(Some("backend=3d,syncfd=true")).is_err());
         assert!(parse_gpu_options(Some("syncfd=true,backend=3d")).is_err());
     }
+
+    #[test]
+    fn parse_battery_vaild() {
+        parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
+    }
+
+    #[test]
+    fn parse_battery_vaild_no_type() {
+        parse_battery_options(None).expect("parse should have succeded");
+    }
+
+    #[test]
+    fn parse_battery_invaild_parameter() {
+        parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
+    }
+
+    #[test]
+    fn parse_battery_invaild_type_value() {
+        parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
+    }
 }
diff --git a/src/panic_hook.rs b/src/panic_hook.rs
index d5cd6a2..f6625ef 100644
--- a/src/panic_hook.rs
+++ b/src/panic_hook.rs
@@ -5,12 +5,11 @@
 use std::env;
 use std::fs::File;
 use std::io::{stderr, Read};
-use std::os::unix::io::{FromRawFd, IntoRawFd};
 use std::panic::{self, PanicInfo};
 use std::process::abort;
 use std::string::String;
 
-use base::error;
+use base::{error, FromRawDescriptor, IntoRawDescriptor};
 use libc::{close, dup, dup2, pipe2, O_NONBLOCK, STDERR_FILENO};
 
 // Opens a pipe and puts the write end into the stderr FD slot. On success, returns the read end of
@@ -38,16 +37,20 @@
         // The write end is no longer needed.
         close(fds[1]);
         // Safe because each of the fds was the result of a successful FD creation syscall.
-        Some((File::from_raw_fd(fds[0]), File::from_raw_fd(old_stderr)))
+        Some((
+            File::from_raw_descriptor(fds[0]),
+            File::from_raw_descriptor(old_stderr),
+        ))
     }
 }
 
 // Sets stderr to the given file. Returns true on success.
 fn restore_stderr(stderr: File) -> bool {
-    let fd = stderr.into_raw_fd();
+    let descriptor = stderr.into_raw_descriptor();
 
-    // Safe because fd is guaranteed to be valid and replacing stderr should be an atomic operation.
-    unsafe { dup2(fd, STDERR_FILENO) != -1 }
+    // Safe because descriptor is guaranteed to be valid and replacing stderr
+    // should be an atomic operation.
+    unsafe { dup2(descriptor, STDERR_FILENO) != -1 }
 }
 
 // Sends as much information about the panic as possible to syslog.
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index 9c171fd..f900bf5 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -8,7 +8,6 @@
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::io;
-use std::os::unix::io::{AsRawFd, FromRawFd};
 use std::os::unix::net::UnixDatagram;
 use std::path::Path;
 use std::result;
@@ -28,8 +27,9 @@
 
 use base::{
     block_signal, clear_signal, drop_capabilities, error, getegid, geteuid, info, pipe,
-    register_rt_signal_handler, validate_raw_fd, warn, Error as SysError, Event, Killable,
-    MmapError, PollContext, PollToken, Result as SysResult, SignalFd, SignalFdError, SIGRTMIN,
+    register_rt_signal_handler, validate_raw_descriptor, warn, AsRawDescriptor, Error as SysError,
+    Event, FromRawDescriptor, Killable, MmapError, PollToken, Result as SysResult, SignalFd,
+    SignalFdError, WaitContext, SIGRTMIN,
 };
 use kvm::{Cap, Datamatch, IoeventAddress, Kvm, Vcpu, VcpuExit, Vm};
 use minijail::{self, Minijail};
@@ -54,13 +54,13 @@
     CreateKvm(SysError),
     CreateMainSocket(SysError),
     CreatePIT(SysError),
-    CreatePollContext(SysError),
     CreateSignalFd(SignalFdError),
     CreateSocketPair(io::Error),
     CreateTapFd(TapError),
     CreateVcpu(SysError),
     CreateVcpuSocket(SysError),
     CreateVm(SysError),
+    CreateWaitContext(SysError),
     DecodeRequest(ProtobufError),
     DropCapabilities(SysError),
     EncodeResponse(ProtobufError),
@@ -87,7 +87,6 @@
     PluginTimeout,
     PluginWait(SysError),
     Poll(SysError),
-    PollContextAdd(SysError),
     RootNotAbsolute,
     RootNotDir,
     SetGidMap(minijail::Error),
@@ -106,6 +105,7 @@
     TapSetMacAddress(TapError),
     TapSetNetmask(TapError),
     ValidateTapFd(SysError),
+    WaitContextAdd(SysError),
 }
 
 impl Display for Error {
@@ -123,13 +123,13 @@
             CreateKvm(e) => write!(f, "error creating Kvm: {}", e),
             CreateMainSocket(e) => write!(f, "error creating main request socket: {}", e),
             CreatePIT(e) => write!(f, "failed to create kvm PIT: {}", e),
-            CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
             CreateSignalFd(e) => write!(f, "failed to create signalfd: {}", e),
             CreateSocketPair(e) => write!(f, "failed to create socket pair: {}", e),
             CreateTapFd(e) => write!(f, "failed to create tap device from raw fd: {}", e),
             CreateVcpu(e) => write!(f, "error creating vcpu: {}", e),
             CreateVcpuSocket(e) => write!(f, "error creating vcpu request socket: {}", e),
             CreateVm(e) => write!(f, "error creating vm: {}", e),
+            CreateWaitContext(e) => write!(f, "failed to create wait context: {}", e),
             DecodeRequest(e) => write!(f, "failed to decode plugin request: {}", e),
             DropCapabilities(e) => write!(f, "failed to drop process capabilities: {}", e),
             EncodeResponse(e) => write!(f, "failed to encode plugin response: {}", e),
@@ -152,7 +152,6 @@
             PluginTimeout => write!(f, "plugin did not exit within timeout"),
             PluginWait(e) => write!(f, "error waiting for plugin to exit: {}", e),
             Poll(e) => write!(f, "failed to poll all FDs: {}", e),
-            PollContextAdd(e) => write!(f, "failed to add fd to poll context: {}", e),
             RootNotAbsolute => write!(f, "path to the root directory must be absolute"),
             RootNotDir => write!(f, "specified root directory is not a directory"),
             SetGidMap(e) => write!(f, "failed to set gidmap for jail: {}", e),
@@ -175,6 +174,7 @@
             TapSetMacAddress(e) => write!(f, "error setting tap mac address: {}", e),
             TapSetNetmask(e) => write!(f, "error setting tap netmask: {}", e),
             ValidateTapFd(e) => write!(f, "failed to validate raw tap fd: {}", e),
+            WaitContextAdd(e) => write!(f, "failed to add descriptor to wait context: {}", e),
         }
     }
 }
@@ -188,8 +188,8 @@
         if ret == 0 {
             ioctl(fds[0], FIOCLEX);
             Ok((
-                UnixDatagram::from_raw_fd(fds[0]),
-                UnixDatagram::from_raw_fd(fds[1]),
+                UnixDatagram::from_raw_descriptor(fds[0]),
+                UnixDatagram::from_raw_descriptor(fds[1]),
             ))
         } else {
             Err(SysError::last())
@@ -212,7 +212,7 @@
     // though it's not necessary a hard requirement for things to work.
     let flags = unsafe {
         fcntl(
-            to_crosvm.0.as_raw_fd(),
+            to_crosvm.0.as_raw_descriptor(),
             F_SETPIPE_SZ,
             MAX_VCPU_DATAGRAM_SIZE as c_int,
         )
@@ -226,7 +226,7 @@
     }
     let flags = unsafe {
         fcntl(
-            to_plugin.0.as_raw_fd(),
+            to_plugin.0.as_raw_descriptor(),
             F_SETPIPE_SZ,
             MAX_VCPU_DATAGRAM_SIZE as c_int,
         )
@@ -677,7 +677,7 @@
     for tap_fd in cfg.tap_fd {
         // Safe because we ensure that we get a unique handle to the fd.
         let tap = unsafe {
-            Tap::from_raw_fd(validate_raw_fd(tap_fd).map_err(Error::ValidateTapFd)?)
+            Tap::from_raw_descriptor(validate_raw_descriptor(tap_fd).map_err(Error::ValidateTapFd)?)
                 .map_err(Error::CreateTapFd)?
         };
         tap_interfaces.push(tap);
@@ -710,17 +710,17 @@
     let kill_signaled = Arc::new(AtomicBool::new(false));
     let mut vcpu_handles = Vec::with_capacity(vcpu_count as usize);
 
-    let poll_ctx =
-        PollContext::build_with(&[(&exit_evt, Token::Exit), (&sigchld_fd, Token::ChildSignal)])
-            .map_err(Error::PollContextAdd)?;
+    let wait_ctx =
+        WaitContext::build_with(&[(&exit_evt, Token::Exit), (&sigchld_fd, Token::ChildSignal)])
+            .map_err(Error::WaitContextAdd)?;
 
     let mut sockets_to_drop = Vec::new();
-    let mut redo_poll_ctx_sockets = true;
+    let mut redo_wait_ctx_sockets = true;
     // In this loop, make every attempt to not return early. If an error is encountered, set `res`
     // to the error, set `dying_instant` to now, and signal the plugin that it will be killed soon.
     // If the plugin cannot be signaled because it is dead of `signal_kill` failed, simply break
     // from the poll loop so that the VCPU threads can be cleaned up.
-    'poll: loop {
+    'wait: loop {
         // After we have waited long enough, it's time to give up and exit.
         if dying_instant
             .map(|i| i.elapsed() >= duration_to_die)
@@ -729,19 +729,19 @@
             break;
         }
 
-        if redo_poll_ctx_sockets {
+        if redo_wait_ctx_sockets {
             for (index, socket) in plugin.sockets().iter().enumerate() {
-                poll_ctx
+                wait_ctx
                     .add(socket, Token::Plugin { index })
-                    .map_err(Error::PollContextAdd)?;
+                    .map_err(Error::WaitContextAdd)?;
             }
         }
 
         let plugin_socket_count = plugin.sockets().len();
         let events = {
             let poll_res = match dying_instant {
-                Some(inst) => poll_ctx.wait_timeout(duration_to_die - inst.elapsed()),
-                None => poll_ctx.wait(),
+                Some(inst) => wait_ctx.wait_timeout(duration_to_die - inst.elapsed()),
+                None => wait_ctx.wait(),
             };
             match poll_res {
                 Ok(v) => v,
@@ -754,11 +754,11 @@
                 }
             }
         };
-        for event in events.iter_readable() {
-            match event.token() {
+        for event in events.iter().filter(|e| e.is_readable) {
+            match event.token {
                 Token::Exit => {
                     // No need to check the exit event if we are already doing cleanup.
-                    let _ = poll_ctx.delete(&exit_evt);
+                    let _ = wait_ctx.delete(&exit_evt);
                     dying_instant.get_or_insert(Instant::now());
                     let sig_res = plugin.signal_kill();
                     if res.is_ok() && sig_res.is_err() {
@@ -773,7 +773,7 @@
                                 // If the plugin process has ended, there is no need to continue
                                 // processing plugin connections, so we break early.
                                 if siginfo.ssi_pid == plugin.pid() as u32 {
-                                    break 'poll;
+                                    break 'wait;
                                 }
                                 // Because SIGCHLD is not expected from anything other than the
                                 // plugin process, report it as an error.
@@ -840,7 +840,7 @@
             }
         }
 
-        redo_poll_ctx_sockets =
+        redo_wait_ctx_sockets =
             !sockets_to_drop.is_empty() || plugin.sockets().len() != plugin_socket_count;
 
         // Cleanup all of the sockets that we have determined were disconnected or suffered some
@@ -848,9 +848,9 @@
         plugin.drop_sockets(&mut sockets_to_drop);
         sockets_to_drop.clear();
 
-        if redo_poll_ctx_sockets {
+        if redo_wait_ctx_sockets {
             for socket in plugin.sockets() {
-                let _ = poll_ctx.delete(socket);
+                let _ = wait_ctx.delete(socket);
             }
         }
     }
diff --git a/src/plugin/process.rs b/src/plugin/process.rs
index cb9bc21..9146256 100644
--- a/src/plugin/process.rs
+++ b/src/plugin/process.rs
@@ -7,7 +7,6 @@
 use std::fs::File;
 use std::io::{IoSlice, Write};
 use std::mem::transmute;
-use std::os::unix::io::{IntoRawFd, RawFd};
 use std::os::unix::net::UnixDatagram;
 use std::path::Path;
 use std::process::Command;
@@ -21,8 +20,9 @@
 use protobuf::Message;
 
 use base::{
-    error, Error as SysError, Event, Killable, MemoryMappingBuilder, Result as SysResult,
-    ScmSocket, SharedMemory, SharedMemoryUnix, SIGRTMIN,
+    error, AsRawDescriptor, Error as SysError, Event, IntoRawDescriptor, Killable,
+    MemoryMappingBuilder, RawDescriptor, Result as SysResult, ScmSocket, SharedMemory,
+    SharedMemoryUnix, SIGRTMIN,
 };
 use kvm::{dirty_log_bitmap_size, Datamatch, IoeventAddress, IrqRoute, IrqSource, PicId, Vm};
 use kvm_sys::{kvm_clock_data, kvm_ioapic_state, kvm_pic_state, kvm_pit_state2};
@@ -162,13 +162,19 @@
 
         let plugin_pid = match jail {
             Some(jail) => {
-                set_var("CROSVM_SOCKET", child_socket.as_raw_fd().to_string());
-                jail.run(cmd, &[0, 1, 2, child_socket.as_raw_fd()], args)
+                set_var(
+                    "CROSVM_SOCKET",
+                    child_socket.as_raw_descriptor().to_string(),
+                );
+                jail.run(cmd, &[0, 1, 2, child_socket.as_raw_descriptor()], args)
                     .map_err(Error::PluginRunJail)?
             }
             None => Command::new(cmd)
                 .args(args)
-                .env("CROSVM_SOCKET", child_socket.as_raw_fd().to_string())
+                .env(
+                    "CROSVM_SOCKET",
+                    child_socket.as_raw_descriptor().to_string(),
+                )
                 .spawn()
                 .map_err(Error::PluginSpawn)?
                 .id() as pid_t,
@@ -306,7 +312,7 @@
         entry: VacantEntry<u32, PluginObject>,
         vm: &mut Vm,
         io_event: &MainRequest_Create_IoEvent,
-    ) -> SysResult<RawFd> {
+    ) -> SysResult<RawDescriptor> {
         let evt = Event::new()?;
         let addr = match io_event.space {
             AddressSpace::IOPORT => IoeventAddress::Pio(io_event.address),
@@ -327,7 +333,7 @@
             _ => return Err(SysError::new(EINVAL)),
         };
 
-        let fd = evt.as_raw_fd();
+        let fd = evt.as_raw_descriptor();
         entry.insert(PluginObject::IoEvent {
             evt,
             addr,
@@ -522,7 +528,7 @@
 
         /// Use this to make it easier to stuff various kinds of File-like objects into the
         /// `boxed_fds` list.
-        fn box_owned_fd<F: IntoRawFd + 'static>(f: F) -> Box<dyn IntoRawFd> {
+        fn box_owned_fd<F: IntoRawDescriptor + 'static>(f: F) -> Box<dyn IntoRawDescriptor> {
             Box::new(f)
         }
 
@@ -567,8 +573,8 @@
                                 irq_event.irq_id,
                             ) {
                                 Ok(()) => {
-                                    response_fds.push(evt.as_raw_fd());
-                                    response_fds.push(resample_evt.as_raw_fd());
+                                    response_fds.push(evt.as_raw_descriptor());
+                                    response_fds.push(resample_evt.as_raw_descriptor());
                                     boxed_fds.push(box_owned_fd(resample_evt));
                                     entry.insert(PluginObject::IrqEvent {
                                         irq_id: irq_event.irq_id,
@@ -597,7 +603,7 @@
             match new_seqpacket_pair() {
                 Ok((request_socket, child_socket)) => {
                     self.request_sockets.push(request_socket);
-                    response_fds.push(child_socket.as_raw_fd());
+                    response_fds.push(child_socket.as_raw_descriptor());
                     boxed_fds.push(box_owned_fd(child_socket));
                     Ok(())
                 }
@@ -605,7 +611,7 @@
             }
         } else if request.has_get_shutdown_eventfd() {
             response.mut_get_shutdown_eventfd();
-            response_fds.push(self.kill_evt.as_raw_fd());
+            response_fds.push(self.kill_evt.as_raw_descriptor());
             Ok(())
         } else if request.has_check_extension() {
             // Safe because the Cap enum is not read by the check_extension method. In that method,
@@ -650,8 +656,8 @@
         } else if request.has_get_vcpus() {
             response.mut_get_vcpus();
             for pipe in self.vcpu_pipes.iter() {
-                response_fds.push(pipe.plugin_write.as_raw_fd());
-                response_fds.push(pipe.plugin_read.as_raw_fd());
+                response_fds.push(pipe.plugin_write.as_raw_descriptor());
+                response_fds.push(pipe.plugin_read.as_raw_descriptor());
             }
             Ok(())
         } else if request.has_start() {
@@ -667,7 +673,7 @@
                 Some(tap) => {
                     match Self::handle_get_net_config(tap, response.mut_get_net_config()) {
                         Ok(_) => {
-                            response_fds.push(tap.as_raw_fd());
+                            response_fds.push(tap.as_raw_descriptor());
                             Ok(())
                         }
                         Err(e) => Err(e),
diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs
index 022d990..9438bb2 100644
--- a/src/plugin/vcpu.rs
+++ b/src/plugin/vcpu.rs
@@ -322,10 +322,7 @@
 
 impl<'a> VcpuRunData<'a> {
     fn is_write(&self) -> bool {
-        match self {
-            VcpuRunData::Write(_) => true,
-            _ => false,
-        }
+        matches!(self, VcpuRunData::Write(_))
     }
 
     fn as_slice(&self) -> &[u8] {
diff --git a/sys_util/Android.bp b/sys_util/Android.bp
index a1499f2..5023be5 100644
--- a/sys_util/Android.bp
+++ b/sys_util/Android.bp
@@ -80,5 +80,5 @@
 //   libc-0.2.80 "default,std"
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   syn-1.0.53 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
 //   unicode-xid-0.2.1 "default"
diff --git a/sys_util/poll_token_derive/Android.bp b/sys_util/poll_token_derive/Android.bp
index f4efe20..4577dc1 100644
--- a/sys_util/poll_token_derive/Android.bp
+++ b/sys_util/poll_token_derive/Android.bp
@@ -31,5 +31,5 @@
 // dependent_library ["feature_list"]
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   syn-1.0.53 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
 //   unicode-xid-0.2.1 "default"
diff --git a/sys_util/src/descriptor.rs b/sys_util/src/descriptor.rs
index 0eff3d0..5ee075a 100644
--- a/sys_util/src/descriptor.rs
+++ b/sys_util/src/descriptor.rs
@@ -3,11 +3,15 @@
 // found in the LICENSE file.
 
 use std::fs::File;
+use std::io::{Stderr, Stdin, Stdout};
 use std::mem;
+use std::net::UdpSocket;
 use std::ops::Drop;
 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::unix::net::{UnixDatagram, UnixStream};
 
-use crate::{errno_result, Result};
+use crate::net::UnlinkUnixSeqpacketListener;
+use crate::{errno_result, PollToken, Result};
 
 pub type RawDescriptor = RawFd;
 
@@ -129,6 +133,17 @@
     }
 }
 
+/// Implement token for implementations that wish to use this struct as such
+impl PollToken for Descriptor {
+    fn as_raw_token(&self) -> u64 {
+        self.0 as u64
+    }
+
+    fn from_raw_token(data: u64) -> Self {
+        Descriptor(data as RawDescriptor)
+    }
+}
+
 macro_rules! AsRawDescriptor {
     ($name:ident) => {
         impl AsRawDescriptor for $name {
@@ -164,8 +179,42 @@
 // descriptor container. That should go to either SafeDescriptor or another more
 // relevant container type.
 AsRawDescriptor!(File);
+AsRawDescriptor!(UnlinkUnixSeqpacketListener);
 FromRawDescriptor!(File);
 IntoRawDescriptor!(File);
+AsRawDescriptor!(Stdin);
+AsRawDescriptor!(Stdout);
+AsRawDescriptor!(Stderr);
+
+impl AsRawDescriptor for UdpSocket {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.as_raw_fd()
+    }
+}
+
+impl AsRawDescriptor for UnixStream {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.as_raw_fd()
+    }
+}
+
+impl AsRawDescriptor for UnixDatagram {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.as_raw_fd()
+    }
+}
+
+impl FromRawDescriptor for UnixDatagram {
+    unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
+        Self::from_raw_fd(descriptor)
+    }
+}
+
+impl IntoRawDescriptor for UnixDatagram {
+    fn into_raw_descriptor(self) -> RawDescriptor {
+        self.into_raw_fd()
+    }
+}
 
 #[test]
 fn clone_equality() {
diff --git a/sys_util/src/net.rs b/sys_util/src/net.rs
index 71ab3ee..5cc87f5 100644
--- a/sys_util/src/net.rs
+++ b/sys_util/src/net.rs
@@ -19,6 +19,7 @@
 use libc::{recvfrom, MSG_PEEK, MSG_TRUNC};
 
 use crate::sock_ctrl_msg::{ScmSocket, SCM_SOCKET_MAX_FD_COUNT};
+use crate::{AsRawDescriptor, RawDescriptor};
 
 // Offset of sun_path in structure sockaddr_un.
 fn sun_path_offset() -> usize {
@@ -338,6 +339,18 @@
     }
 }
 
+impl AsRawFd for &UnixSeqpacket {
+    fn as_raw_fd(&self) -> RawFd {
+        self.fd
+    }
+}
+
+impl AsRawDescriptor for UnixSeqpacket {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.fd
+    }
+}
+
 /// Like a `UnixListener` but for accepting `UnixSeqpacket` type sockets.
 pub struct UnixSeqpacketListener {
     fd: RawFd,
diff --git a/sys_util/src/poll.rs b/sys_util/src/poll.rs
index 1b92c96..50af029 100644
--- a/sys_util/src/poll.rs
+++ b/sys_util/src/poll.rs
@@ -132,6 +132,11 @@
         self.event.events & (EPOLLIN as u32) != 0
     }
 
+    /// True if the `fd` associated with this token in `PollContext::add` is writable.
+    pub fn writable(&self) -> bool {
+        self.event.events & (EPOLLOUT as u32) != 0
+    }
+
     /// True if the `fd` associated with this token in `PollContext::add` has been hungup on.
     pub fn hungup(&self) -> bool {
         self.event.events & (EPOLLHUP as u32) != 0
diff --git a/sys_util/src/signalfd.rs b/sys_util/src/signalfd.rs
index 0b50ea4..bdb94dd 100644
--- a/sys_util/src/signalfd.rs
+++ b/sys_util/src/signalfd.rs
@@ -12,8 +12,7 @@
 use libc::{c_void, read, signalfd, signalfd_siginfo};
 use libc::{EAGAIN, SFD_CLOEXEC, SFD_NONBLOCK};
 
-use crate::errno;
-use crate::signal;
+use crate::{errno, signal, AsRawDescriptor, RawDescriptor};
 
 #[derive(Debug)]
 pub enum Error {
@@ -135,6 +134,12 @@
     }
 }
 
+impl AsRawDescriptor for SignalFd {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.signalfd.as_raw_descriptor()
+    }
+}
+
 impl Drop for SignalFd {
     fn drop(&mut self) {
         // This is thread-safe and safe in the sense that we're doing what
diff --git a/sys_util/src/syslog.rs b/sys_util/src/syslog.rs
index 8a80acc..3cb551a 100644
--- a/sys_util/src/syslog.rs
+++ b/sys_util/src/syslog.rs
@@ -21,6 +21,7 @@
 //! ```
 
 use crate::target_os::syslog::PlatformSyslog;
+use crate::RawDescriptor;
 use std::env;
 use std::ffi::{OsStr, OsString};
 use std::fmt::{self, Display};
@@ -308,6 +309,11 @@
     fds.extend(state.file.iter().map(|f| f.as_raw_fd()));
 }
 
+/// Does the same as push_fds, but using the RawDescriptorType
+pub fn push_descriptors(descriptors: &mut Vec<RawDescriptor>) {
+    push_fds(descriptors)
+}
+
 /// Records a log message with the given details.
 ///
 /// Note that this will fail silently if syslog was not initialized.
diff --git a/tests/boot.rs b/tests/boot.rs
index 4277072..5eab683 100644
--- a/tests/boot.rs
+++ b/tests/boot.rs
@@ -15,7 +15,7 @@
 
 use arch::{set_default_serial_parameters, SerialHardware, SerialParameters, SerialType};
 use base::syslog;
-use crosvm::{linux, Config, Executable};
+use crosvm::{platform, Config, Executable};
 
 const CHROOT_KERNEL_PATH: &str = "/mnt/host/source/src/third_party/kernel/v4.19/";
 const CONTAINER_VM_DEFCONFIG: &str = "arch/x86/configs/chromiumos-container-vm-x86_64_defconfig";
@@ -243,6 +243,6 @@
     set_default_serial_parameters(&mut c.serial_parameters);
     c.executable_path = Some(Executable::Kernel(kernel_path));
 
-    let r = linux::run_config(c);
+    let r = platform::run_config(c);
     r.expect("failed to run linux");
 }
diff --git a/tests/plugins.rs b/tests/plugins.rs
index 054ce6a..e7c872e 100644
--- a/tests/plugins.rs
+++ b/tests/plugins.rs
@@ -8,7 +8,6 @@
 use std::ffi::OsString;
 use std::fs::remove_file;
 use std::io::{Read, Write};
-use std::os::unix::io::AsRawFd;
 use std::path::{Path, PathBuf};
 use std::process::{Command, Stdio};
 use std::thread::sleep;
@@ -127,7 +126,7 @@
     run_plugin(&bin_path.0, true);
 }
 
-fn keep_fd_on_exec<F: AsRawFd>(f: &F) {
+fn keep_fd_on_exec<F: AsRawDescriptor>(f: &F) {
     unsafe {
         ioctl(f, 0x5450 /* FIONCLEX */);
     }
diff --git a/usb_sys/Android.bp b/usb_sys/Android.bp
index 960736b..2583e50 100644
--- a/usb_sys/Android.bp
+++ b/usb_sys/Android.bp
@@ -38,14 +38,31 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/usb_util/Android.bp b/usb_util/Android.bp
index a9b4f04..4b9afc5 100644
--- a/usb_util/Android.bp
+++ b/usb_util/Android.bp
@@ -48,16 +48,33 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
 //   ../usb_sys/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   remain-0.2.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/vfio_sys/Android.bp b/vfio_sys/Android.bp
index f3d0701..17c4a1a 100644
--- a/vfio_sys/Android.bp
+++ b/vfio_sys/Android.bp
@@ -38,14 +38,31 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/vhost/Android.bp b/vhost/Android.bp
index 6da545d..9c393eb 100644
--- a/vhost/Android.bp
+++ b/vhost/Android.bp
@@ -76,7 +76,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/vhost/src/lib.rs b/vhost/src/lib.rs
index bc750d6..1af9af6 100644
--- a/vhost/src/lib.rs
+++ b/vhost/src/lib.rs
@@ -13,12 +13,11 @@
 use std::fmt::{self, Display};
 use std::io::Error as IoError;
 use std::mem;
-use std::os::unix::io::AsRawFd;
 use std::ptr::null;
 
 use assertions::const_assert;
 use base::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};
-use base::{Event, LayoutAllocation};
+use base::{AsRawDescriptor, Event, LayoutAllocation};
 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
 
 #[derive(Debug)]
@@ -64,7 +63,7 @@
 /// from regular virtio devices because the host kernel takes care of handling all the data
 /// transfer.  The device itself only needs to deal with setting up the kernel driver and
 /// managing the control channel.
-pub trait Vhost: AsRawFd + std::marker::Sized {
+pub trait Vhost: AsRawDescriptor + std::marker::Sized {
     /// Get the guest memory mapping.
     fn mem(&self) -> &GuestMemory;
 
@@ -298,11 +297,11 @@
     ///
     /// # Arguments
     /// * `queue_index` - Index of the queue to modify.
-    /// * `fd` - Event to trigger.
-    fn set_vring_call(&self, queue_index: usize, fd: &Event) -> Result<()> {
+    /// * `event` - Event to trigger.
+    fn set_vring_call(&self, queue_index: usize, event: &Event) -> Result<()> {
         let vring_file = virtio_sys::vhost_vring_file {
             index: queue_index as u32,
-            fd: fd.as_raw_fd(),
+            event: event.as_raw_descriptor(),
         };
 
         // This ioctl is called on a valid vhost_net fd and has its
@@ -320,10 +319,10 @@
     /// # Arguments
     /// * `queue_index` - Index of the queue to modify.
     /// * `fd` - Event that will be signaled from guest.
-    fn set_vring_kick(&self, queue_index: usize, fd: &Event) -> Result<()> {
+    fn set_vring_kick(&self, queue_index: usize, event: &Event) -> Result<()> {
         let vring_file = virtio_sys::vhost_vring_file {
             index: queue_index as u32,
-            fd: fd.as_raw_fd(),
+            event: event.as_raw_descriptor(),
         };
 
         // This ioctl is called on a valid vhost_net fd and has its
diff --git a/vhost/src/net.rs b/vhost/src/net.rs
index 3263efb..df6de95 100644
--- a/vhost/src/net.rs
+++ b/vhost/src/net.rs
@@ -6,9 +6,8 @@
 use std::fs::{File, OpenOptions};
 use std::marker::PhantomData;
 use std::os::unix::fs::OpenOptionsExt;
-use std::os::unix::io::{AsRawFd, RawFd};
 
-use base::ioctl_with_ref;
+use base::{ioctl_with_ref, AsRawDescriptor, RawDescriptor};
 use vm_memory::GuestMemory;
 
 use super::{ioctl_result, Error, Result, Vhost};
@@ -20,14 +19,14 @@
 /// This provides a simple wrapper around a VHOST_NET file descriptor and
 /// methods that safely run ioctls on that file descriptor.
 pub struct Net<T> {
-    // fd must be dropped first, which will stop and tear down the
+    // descriptor must be dropped first, which will stop and tear down the
     // vhost-net worker before GuestMemory can potentially be unmapped.
-    fd: File,
+    descriptor: File,
     mem: GuestMemory,
     phantom: PhantomData<T>,
 }
 
-pub trait NetT<T: TapT>: Vhost + AsRawFd + Send + Sized {
+pub trait NetT<T: TapT>: Vhost + AsRawDescriptor + Send + Sized {
     /// Create a new NetT instance
     fn new(mem: &GuestMemory) -> Result<Self>;
 
@@ -36,8 +35,8 @@
     ///
     /// # Arguments
     /// * `queue_index` - Index of the queue to modify.
-    /// * `fd` - Tap interface that will be used as the backend.
-    fn set_backend(&self, queue_index: usize, fd: Option<&T>) -> Result<()>;
+    /// * `descriptor` - Tap interface that will be used as the backend.
+    fn set_backend(&self, queue_index: usize, descriptor: Option<&T>) -> Result<()>;
 }
 
 impl<T> NetT<T> for Net<T>
@@ -50,7 +49,7 @@
     /// * `mem` - Guest memory mapping.
     fn new(mem: &GuestMemory) -> Result<Net<T>> {
         Ok(Net::<T> {
-            fd: OpenOptions::new()
+            descriptor: OpenOptions::new()
                 .read(true)
                 .write(true)
                 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
@@ -61,16 +60,21 @@
         })
     }
 
-    fn set_backend(&self, queue_index: usize, fd: Option<&T>) -> Result<()> {
+    fn set_backend(&self, queue_index: usize, event: Option<&T>) -> Result<()> {
         let vring_file = virtio_sys::vhost_vring_file {
             index: queue_index as u32,
-            fd: fd.map_or(-1, |fd| fd.as_raw_fd()),
+            event: event.map_or(-1, |event| event.as_raw_descriptor()),
         };
 
-        // This ioctl is called on a valid vhost_net fd and has its
+        // This ioctl is called on a valid vhost_net descriptor and has its
         // return value checked.
-        let ret =
-            unsafe { ioctl_with_ref(&self.fd, virtio_sys::VHOST_NET_SET_BACKEND(), &vring_file) };
+        let ret = unsafe {
+            ioctl_with_ref(
+                &self.descriptor,
+                virtio_sys::VHOST_NET_SET_BACKEND(),
+                &vring_file,
+            )
+        };
         if ret < 0 {
             return ioctl_result();
         }
@@ -84,9 +88,9 @@
     }
 }
 
-impl<T> AsRawFd for Net<T> {
-    fn as_raw_fd(&self) -> RawFd {
-        self.fd.as_raw_fd()
+impl<T> AsRawDescriptor for Net<T> {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.descriptor.as_raw_descriptor()
     }
 }
 
@@ -98,7 +102,7 @@
     const TMP_FILE: &str = "/tmp/crosvm_vhost_test_file";
 
     pub struct FakeNet<T> {
-        fd: File,
+        descriptor: File,
         mem: GuestMemory,
         phantom: PhantomData<T>,
     }
@@ -115,7 +119,7 @@
     {
         fn new(mem: &GuestMemory) -> Result<FakeNet<T>> {
             Ok(FakeNet::<T> {
-                fd: OpenOptions::new()
+                descriptor: OpenOptions::new()
                     .read(true)
                     .append(true)
                     .create(true)
@@ -137,9 +141,9 @@
         }
     }
 
-    impl<T> AsRawFd for FakeNet<T> {
-        fn as_raw_fd(&self) -> RawFd {
-            self.fd.as_raw_fd()
+    impl<T> AsRawDescriptor for FakeNet<T> {
+        fn as_raw_descriptor(&self) -> RawDescriptor {
+            self.descriptor.as_raw_descriptor()
         }
     }
 }
diff --git a/vhost/src/vsock.rs b/vhost/src/vsock.rs
index e4b24fe..95fbb72 100644
--- a/vhost/src/vsock.rs
+++ b/vhost/src/vsock.rs
@@ -4,9 +4,8 @@
 
 use std::fs::{File, OpenOptions};
 use std::os::unix::fs::OpenOptionsExt;
-use std::os::unix::io::{AsRawFd, RawFd};
 
-use base::ioctl_with_ref;
+use base::{ioctl_with_ref, AsRawDescriptor, RawDescriptor};
 use virtio_sys::{VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING};
 use vm_memory::GuestMemory;
 
@@ -16,7 +15,7 @@
 
 /// Handle for running VHOST_VSOCK ioctls.
 pub struct Vsock {
-    fd: File,
+    descriptor: File,
     mem: GuestMemory,
 }
 
@@ -24,7 +23,7 @@
     /// Open a handle to a new VHOST_VSOCK instance.
     pub fn new(mem: &GuestMemory) -> Result<Vsock> {
         Ok(Vsock {
-            fd: OpenOptions::new()
+            descriptor: OpenOptions::new()
                 .read(true)
                 .write(true)
                 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
@@ -41,7 +40,7 @@
     /// # Arguments
     /// * `cid` - CID to assign to the guest
     pub fn set_cid(&self, cid: u64) -> Result<()> {
-        let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_GUEST_CID(), &cid) };
+        let ret = unsafe { ioctl_with_ref(&self.descriptor, VHOST_VSOCK_SET_GUEST_CID(), &cid) };
         if ret < 0 {
             return ioctl_result();
         }
@@ -60,7 +59,7 @@
 
     fn set_running(&self, running: bool) -> Result<()> {
         let on: ::std::os::raw::c_int = if running { 1 } else { 0 };
-        let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_RUNNING(), &on) };
+        let ret = unsafe { ioctl_with_ref(&self.descriptor, VHOST_VSOCK_SET_RUNNING(), &on) };
 
         if ret < 0 {
             return ioctl_result();
@@ -75,8 +74,8 @@
     }
 }
 
-impl AsRawFd for Vsock {
-    fn as_raw_fd(&self) -> RawFd {
-        self.fd.as_raw_fd()
+impl AsRawDescriptor for Vsock {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        self.descriptor.as_raw_descriptor()
     }
 }
diff --git a/virtio_sys/Android.bp b/virtio_sys/Android.bp
index 97899e4..2b96727 100644
--- a/virtio_sys/Android.bp
+++ b/virtio_sys/Android.bp
@@ -38,14 +38,31 @@
 // dependent_library ["feature_list"]
 //   ../assertions/src/lib.rs
 //   ../base/src/lib.rs
+//   ../cros_async/src/lib.rs
 //   ../data_model/src/lib.rs
+//   ../io_uring/src/lib.rs
 //   ../sync/src/lib.rs
 //   ../sys_util/poll_token_derive/poll_token_derive.rs
 //   ../sys_util/src/lib.rs
 //   ../syscall_defines/src/lib.rs
 //   ../tempfile/src/lib.rs
+//   async-trait-0.1.42
+//   futures-0.3.8 "alloc"
+//   futures-channel-0.3.8 "alloc,futures-sink,sink"
+//   futures-core-0.3.8 "alloc"
+//   futures-io-0.3.8
+//   futures-sink-0.3.8 "alloc"
+//   futures-task-0.3.8 "alloc"
+//   futures-util-0.3.8 "alloc,futures-sink,sink"
 //   libc-0.2.80 "default,std"
+//   paste-1.0.3
+//   pin-project-1.0.2
+//   pin-project-internal-1.0.2
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.50 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   slab-0.4.2
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   thiserror-1.0.22
+//   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/virtio_sys/src/vhost.rs b/virtio_sys/src/vhost.rs
index 70d2db6..020d43c 100644
--- a/virtio_sys/src/vhost.rs
+++ b/virtio_sys/src/vhost.rs
@@ -574,7 +574,7 @@
 #[derive(Debug, Default, Copy)]
 pub struct vhost_vring_file {
     pub index: ::std::os::raw::c_uint,
-    pub fd: ::std::os::raw::c_int,
+    pub event: ::std::os::raw::c_int,
 }
 #[test]
 fn bindgen_test_layout_vhost_vring_file() {
@@ -599,7 +599,7 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(0 as *const vhost_vring_file)).fd as *const _ as usize },
+        unsafe { &(*(0 as *const vhost_vring_file)).event as *const _ as usize },
         4usize,
         concat!(
             "Alignment of field: ",
diff --git a/vm_control/Android.bp b/vm_control/Android.bp
index cf0ad82..032beb0 100644
--- a/vm_control/Android.bp
+++ b/vm_control/Android.bp
@@ -71,6 +71,7 @@
 //   ../tempfile/src/lib.rs
 //   ../vm_memory/src/lib.rs
 //   async-trait-0.1.42
+//   base-0.1.0
 //   downcast-rs-1.2.0 "default,std"
 //   futures-0.3.8 "alloc,async-await,default,executor,futures-executor,std"
 //   futures-channel-0.3.8 "alloc,futures-sink,sink,std"
@@ -93,7 +94,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/vm_control/Cargo.toml b/vm_control/Cargo.toml
index e383679..2382059 100644
--- a/vm_control/Cargo.toml
+++ b/vm_control/Cargo.toml
@@ -4,8 +4,12 @@
 authors = ["The Chromium OS Authors"]
 edition = "2018"
 
+[features]
+gdb = ["gdbstub"]
+
 [dependencies]
 data_model = { path = "../data_model" }
+gdbstub = { version = "0.4.0", optional = true }
 hypervisor = { path = "../hypervisor" }
 libc = "*"
 msg_socket = { path = "../msg_socket" }
diff --git a/vm_control/src/gdb.rs b/vm_control/src/gdb.rs
new file mode 100644
index 0000000..cf1cf6d
--- /dev/null
+++ b/vm_control/src/gdb.rs
@@ -0,0 +1,35 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[cfg(target_arch = "x86_64")]
+use gdbstub::arch::x86::reg::X86_64CoreRegs as CoreRegs;
+use vm_memory::GuestAddress;
+
+/// Messages that can be sent to a vCPU to set/get its state from the debugger.
+#[derive(Debug)]
+pub enum VcpuDebug {
+    ReadMem(GuestAddress, usize),
+    ReadRegs,
+    WriteRegs(Box<CoreRegs>),
+    WriteMem(GuestAddress, Vec<u8>),
+    EnableSinglestep,
+    SetHwBreakPoint(Vec<GuestAddress>),
+}
+
+/// Messages that can be sent from a vCPU to update the state to the debugger.
+#[derive(Debug)]
+pub enum VcpuDebugStatus {
+    RegValues(CoreRegs),
+    MemoryRegion(Vec<u8>),
+    CommandComplete,
+    HitBreakPoint,
+}
+
+/// Pair of a vCPU ID and messages that can be sent from the vCPU to update the state to the
+/// debugger.
+#[derive(Debug)]
+pub struct VcpuDebugStatusMessage {
+    pub cpu: usize,
+    pub msg: VcpuDebugStatus,
+}
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
index 92bf35a..311ad7e 100644
--- a/vm_control/src/lib.rs
+++ b/vm_control/src/lib.rs
@@ -10,18 +10,23 @@
 //! The wire message format is a little-endian C-struct of fixed size, along with a file descriptor
 //! if the request type expects one.
 
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+pub mod gdb;
+
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::io::{Seek, SeekFrom};
 use std::mem::ManuallyDrop;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::result::Result as StdResult;
+use std::str::FromStr;
 use std::sync::Arc;
 
 use libc::{EINVAL, EIO, ENODEV};
 
 use base::{
-    error, AsRawDescriptor, Error as SysError, Event, ExternalMapping, MappedRegion,
-    MemoryMappingBuilder, MmapError, RawDescriptor, Result,
+    error, AsRawDescriptor, Error as SysError, Event, ExternalMapping, FromRawDescriptor,
+    IntoRawDescriptor, MappedRegion, MemoryMappingBuilder, MmapError, RawDescriptor, Result,
+    SafeDescriptor,
 };
 use hypervisor::{IrqRoute, IrqSource, Vm};
 use msg_socket::{MsgError, MsgOnSocket, MsgReceiver, MsgResult, MsgSender, MsgSocket};
@@ -29,60 +34,80 @@
 use sync::Mutex;
 use vm_memory::GuestAddress;
 
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+pub use crate::gdb::*;
 pub use hypervisor::MemSlot;
 
+/// Control the state of a particular VM CPU.
+#[derive(Debug)]
+pub enum VcpuControl {
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    Debug(VcpuDebug),
+    RunState(VmRunMode),
+}
+
 /// A file descriptor either borrowed or owned by this.
 #[derive(Debug)]
-pub enum MaybeOwnedFd {
+pub enum MaybeOwnedDescriptor {
     /// Owned by this enum variant, and will be destructed automatically if not moved out.
-    Owned(File),
+    Owned(SafeDescriptor),
     /// A file descriptor borrwed by this enum.
     Borrowed(RawDescriptor),
 }
 
-impl AsRawDescriptor for MaybeOwnedFd {
+impl AsRawDescriptor for MaybeOwnedDescriptor {
     fn as_raw_descriptor(&self) -> RawDescriptor {
         match self {
-            MaybeOwnedFd::Owned(f) => f.as_raw_descriptor(),
-            MaybeOwnedFd::Borrowed(descriptor) => *descriptor,
+            MaybeOwnedDescriptor::Owned(f) => f.as_raw_descriptor(),
+            MaybeOwnedDescriptor::Borrowed(descriptor) => *descriptor,
         }
     }
 }
 
-// TODO(mikehoyle): Remove this in favor of just AsRawDescriptor
-impl AsRawFd for MaybeOwnedFd {
-    fn as_raw_fd(&self) -> RawFd {
-        self.as_raw_descriptor()
+impl AsRawDescriptor for &MaybeOwnedDescriptor {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
+        match self {
+            MaybeOwnedDescriptor::Owned(f) => f.as_raw_descriptor(),
+            MaybeOwnedDescriptor::Borrowed(descriptor) => *descriptor,
+        }
     }
 }
 
 // When sent, it could be owned or borrowed. On the receiver end, it always owned.
-impl MsgOnSocket for MaybeOwnedFd {
-    fn uses_fd() -> bool {
+impl MsgOnSocket for MaybeOwnedDescriptor {
+    fn uses_descriptor() -> bool {
         true
     }
     fn fixed_size() -> Option<usize> {
         Some(0)
     }
-    fn fd_count(&self) -> usize {
+    fn descriptor_count(&self) -> usize {
         1usize
     }
-    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
-        let (file, size) = File::read_from_buffer(buffer, fds)?;
-        Ok((MaybeOwnedFd::Owned(file), size))
+    unsafe fn read_from_buffer(
+        buffer: &[u8],
+        descriptors: &[RawDescriptor],
+    ) -> MsgResult<(Self, usize)> {
+        let (file, size) = File::read_from_buffer(buffer, descriptors)?;
+        let safe_descriptor = SafeDescriptor::from_raw_descriptor(file.into_raw_descriptor());
+        Ok((MaybeOwnedDescriptor::Owned(safe_descriptor), size))
     }
-    fn write_to_buffer(&self, _buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
-        if fds.is_empty() {
-            return Err(MsgError::WrongFdBufferSize);
+    fn write_to_buffer(
+        &self,
+        _buffer: &mut [u8],
+        descriptors: &mut [RawDescriptor],
+    ) -> MsgResult<usize> {
+        if descriptors.is_empty() {
+            return Err(MsgError::WrongDescriptorBufferSize);
         }
 
-        fds[0] = self.as_raw_fd();
+        descriptors[0] = self.as_raw_descriptor();
         Ok(1)
     }
 }
 
 /// Mode of execution for the VM.
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq)]
 pub enum VmRunMode {
     /// The default run mode indicating the VCPUs are running.
     Running,
@@ -90,6 +115,8 @@
     Suspending,
     /// Indicates that the VM is exiting all processes.
     Exiting,
+    /// Indicates that the VM is in a breakpoint waiting for the debugger to do continue.
+    Breakpoint,
 }
 
 impl Display for VmRunMode {
@@ -100,6 +127,7 @@
             Running => write!(f, "running"),
             Suspending => write!(f, "suspending"),
             Exiting => write!(f, "exiting"),
+            Breakpoint => write!(f, "breakpoint"),
         }
     }
 }
@@ -215,7 +243,7 @@
         addr: u8,
         vid: u16,
         pid: u16,
-        fd: Option<MaybeOwnedFd>,
+        descriptor: Option<MaybeOwnedDescriptor>,
     },
     DetachDevice {
         port: u8,
@@ -271,12 +299,12 @@
 
 #[derive(MsgOnSocket, Debug)]
 pub enum VmMemoryRequest {
-    /// Register shared memory represented by the given fd into guest address space. The response
-    /// variant is `VmResponse::RegisterMemory`.
-    RegisterMemory(MaybeOwnedFd, usize),
+    /// Register shared memory represented by the given descriptor into guest address space.
+    /// The response variant is `VmResponse::RegisterMemory`.
+    RegisterMemory(MaybeOwnedDescriptor, usize),
     /// Similiar to `VmMemoryRequest::RegisterMemory`, but doesn't allocate new address space.
     /// Useful for cases where the address space is already allocated (PCI regions).
-    RegisterFdAtPciBarOffset(Alloc, MaybeOwnedFd, usize, u64),
+    RegisterFdAtPciBarOffset(Alloc, MaybeOwnedDescriptor, usize, u64),
     /// Similar to RegisterFdAtPciBarOffset, but is for buffers in the current address space.
     RegisterHostPointerAtPciBarOffset(Alloc, u64),
     /// Unregister the given memory slot that was previously registered with `RegisterMemory*`.
@@ -290,7 +318,7 @@
     },
     /// Register mmaped memory into the hypervisor's EPT.
     RegisterMmapMemory {
-        fd: MaybeOwnedFd,
+        descriptor: MaybeOwnedDescriptor,
         size: usize,
         offset: u64,
         gpa: u64,
@@ -315,14 +343,14 @@
     ) -> VmMemoryResponse {
         use self::VmMemoryRequest::*;
         match *self {
-            RegisterMemory(ref fd, size) => {
-                match register_memory(vm, sys_allocator, fd, size, None) {
+            RegisterMemory(ref descriptor, size) => {
+                match register_memory(vm, sys_allocator, descriptor, size, None) {
                     Ok((pfn, slot)) => VmMemoryResponse::RegisterMemory { pfn, slot },
                     Err(e) => VmMemoryResponse::Err(e),
                 }
             }
-            RegisterFdAtPciBarOffset(alloc, ref fd, size, offset) => {
-                match register_memory(vm, sys_allocator, fd, size, Some((alloc, offset))) {
+            RegisterFdAtPciBarOffset(alloc, ref descriptor, size, offset) => {
+                match register_memory(vm, sys_allocator, descriptor, size, Some((alloc, offset))) {
                     Ok((pfn, slot)) => VmMemoryResponse::RegisterMemory { pfn, slot },
                     Err(e) => VmMemoryResponse::Err(e),
                 }
@@ -363,7 +391,11 @@
                 };
                 match register_memory(vm, sys_allocator, &fd, size as usize, None) {
                     Ok((pfn, slot)) => VmMemoryResponse::AllocateAndRegisterGpuMemory {
-                        fd: MaybeOwnedFd::Owned(fd),
+                        // Safe because ownership is transferred to SafeDescriptor via
+                        // into_raw_descriptor
+                        descriptor: MaybeOwnedDescriptor::Owned(unsafe {
+                            SafeDescriptor::from_raw_descriptor(fd.into_raw_descriptor())
+                        }),
                         pfn,
                         slot,
                         desc,
@@ -372,13 +404,13 @@
                 }
             }
             RegisterMmapMemory {
-                ref fd,
+                ref descriptor,
                 size,
                 offset,
                 gpa,
             } => {
                 let mmap = match MemoryMappingBuilder::new(size)
-                    .from_descriptor(fd)
+                    .from_descriptor(descriptor)
                     .offset(offset as u64)
                     .build()
                 {
@@ -405,7 +437,7 @@
     /// The request to allocate and register GPU memory into guest address space was successfully
     /// done at page frame number `pfn` and memory slot number `slot` for buffer with `desc`.
     AllocateAndRegisterGpuMemory {
-        fd: MaybeOwnedFd,
+        descriptor: MaybeOwnedDescriptor,
         pfn: u64,
         slot: MemSlot,
         desc: GpuMemoryDesc,
@@ -417,7 +449,7 @@
 #[derive(MsgOnSocket, Debug)]
 pub enum VmIrqRequest {
     /// Allocate one gsi, and associate gsi to irqfd with register_irqfd()
-    AllocateOneMsi { irqfd: MaybeOwnedFd },
+    AllocateOneMsi { irqfd: MaybeOwnedDescriptor },
     /// Add one msi route entry into the IRQ chip.
     AddMsiRoute {
         gsi: u32,
@@ -451,15 +483,17 @@
         match *self {
             AllocateOneMsi { ref irqfd } => {
                 if let Some(irq_num) = sys_allocator.allocate_irq() {
-                    // Beacuse of the limitation of `MaybeOwnedFd` not fitting into `register_irqfd`
-                    // which expects an `&Event`, we use the unsafe `from_raw_fd` to assume that
-                    // the fd given is an `Event`, and we ignore the ownership question using
-                    // `ManuallyDrop`. This is safe because `ManuallyDrop` prevents any Drop
-                    // implementation from triggering on `irqfd` which already has an owner, and the
-                    // `Event` methods are never called. The underlying fd is merely passed to the
-                    // kernel which doesn't care about ownership and deals with incorrect FDs, in
-                    // the case of bugs on our part.
-                    let evt = unsafe { ManuallyDrop::new(Event::from_raw_fd(irqfd.as_raw_fd())) };
+                    // Because of the limitation of `MaybeOwnedDescriptor` not fitting into
+                    // `register_irqfd` which expects an `&Event`, we use the unsafe `from_raw_fd`
+                    // to assume that the descriptor given is an `Event`, and we ignore the
+                    // ownership question using `ManuallyDrop`. This is safe because `ManuallyDrop`
+                    // prevents any Drop implementation from triggering on `irqfd` which already has
+                    // an owner, and the `Event` methods are never called. The underlying descriptor
+                    // is merely passed to the kernel which doesn't care about ownership and deals
+                    // with incorrect FDs, in the case of bugs on our part.
+                    let evt = unsafe {
+                        ManuallyDrop::new(Event::from_raw_descriptor(irqfd.as_raw_descriptor()))
+                    };
 
                     match set_up_irq(IrqSetup::Event(irq_num, &evt)) {
                         Ok(_) => VmIrqResponse::AllocateOneMsi { gsi: irq_num },
@@ -536,9 +570,208 @@
     }
 }
 
+#[derive(MsgOnSocket, Debug)]
+pub enum BatControlResult {
+    Ok,
+    NoBatDevice,
+    NoSuchHealth,
+    NoSuchProperty,
+    NoSuchStatus,
+    NoSuchBatType,
+    StringParseIntErr,
+}
+
+impl Display for BatControlResult {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::BatControlResult::*;
+
+        match self {
+            Ok => write!(f, "Setting battery property successfully"),
+            NoBatDevice => write!(f, "No battery device created"),
+            NoSuchHealth => write!(f, "Invalid Battery health setting. Only support: unknown/good/overheat/dead/overvoltage/unexpectedfailure/cold/watchdogtimerexpire/safetytimerexpire/overcurrent"),
+            NoSuchProperty => write!(f, "Battery doesn't have such property. Only support: status/health/present/capacity/aconline"),
+            NoSuchStatus => write!(f, "Invalid Battery status setting. Only support: unknown/charging/discharging/notcharging/full"),
+            NoSuchBatType => write!(f, "Invalid Battery type setting. Only support: goldfish"),
+            StringParseIntErr => write!(f, "Battery property target ParseInt error"),
+        }
+    }
+}
+
+#[derive(MsgOnSocket, Copy, Clone, Debug, PartialEq)]
+pub enum BatteryType {
+    Goldfish,
+}
+
+impl Default for BatteryType {
+    fn default() -> Self {
+        BatteryType::Goldfish
+    }
+}
+
+impl FromStr for BatteryType {
+    type Err = BatControlResult;
+
+    fn from_str(s: &str) -> StdResult<Self, Self::Err> {
+        match s {
+            "goldfish" => Ok(BatteryType::Goldfish),
+            _ => Err(BatControlResult::NoSuchBatType),
+        }
+    }
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum BatProperty {
+    Status,
+    Health,
+    Present,
+    Capacity,
+    ACOnline,
+}
+
+impl FromStr for BatProperty {
+    type Err = BatControlResult;
+
+    fn from_str(s: &str) -> StdResult<Self, Self::Err> {
+        match s {
+            "status" => Ok(BatProperty::Status),
+            "health" => Ok(BatProperty::Health),
+            "present" => Ok(BatProperty::Present),
+            "capacity" => Ok(BatProperty::Capacity),
+            "aconline" => Ok(BatProperty::ACOnline),
+            _ => Err(BatControlResult::NoSuchProperty),
+        }
+    }
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum BatStatus {
+    Unknown,
+    Charging,
+    DisCharging,
+    NotCharging,
+    Full,
+}
+
+impl BatStatus {
+    pub fn new(status: String) -> std::result::Result<Self, BatControlResult> {
+        match status.as_str() {
+            "unknown" => Ok(BatStatus::Unknown),
+            "charging" => Ok(BatStatus::Charging),
+            "discharging" => Ok(BatStatus::DisCharging),
+            "notcharging" => Ok(BatStatus::NotCharging),
+            "full" => Ok(BatStatus::Full),
+            _ => Err(BatControlResult::NoSuchStatus),
+        }
+    }
+}
+
+impl FromStr for BatStatus {
+    type Err = BatControlResult;
+
+    fn from_str(s: &str) -> StdResult<Self, Self::Err> {
+        match s {
+            "unknown" => Ok(BatStatus::Unknown),
+            "charging" => Ok(BatStatus::Charging),
+            "discharging" => Ok(BatStatus::DisCharging),
+            "notcharging" => Ok(BatStatus::NotCharging),
+            "full" => Ok(BatStatus::Full),
+            _ => Err(BatControlResult::NoSuchStatus),
+        }
+    }
+}
+
+impl From<BatStatus> for u32 {
+    fn from(status: BatStatus) -> Self {
+        status as u32
+    }
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum BatHealth {
+    Unknown,
+    Good,
+    Overheat,
+    Dead,
+    OverVoltage,
+    UnexpectedFailure,
+    Cold,
+    WatchdogTimerExpire,
+    SafetyTimerExpire,
+    OverCurrent,
+}
+
+impl FromStr for BatHealth {
+    type Err = BatControlResult;
+
+    fn from_str(s: &str) -> StdResult<Self, Self::Err> {
+        match s {
+            "unknown" => Ok(BatHealth::Unknown),
+            "good" => Ok(BatHealth::Good),
+            "overheat" => Ok(BatHealth::Overheat),
+            "dead" => Ok(BatHealth::Dead),
+            "overvoltage" => Ok(BatHealth::OverVoltage),
+            "unexpectedfailure" => Ok(BatHealth::UnexpectedFailure),
+            "cold" => Ok(BatHealth::Cold),
+            "watchdogtimerexpire" => Ok(BatHealth::WatchdogTimerExpire),
+            "safetytimerexpire" => Ok(BatHealth::SafetyTimerExpire),
+            "overcurrent" => Ok(BatHealth::OverCurrent),
+            _ => Err(BatControlResult::NoSuchHealth),
+        }
+    }
+}
+
+impl From<BatHealth> for u32 {
+    fn from(status: BatHealth) -> Self {
+        status as u32
+    }
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum BatControlCommand {
+    SetStatus(BatStatus),
+    SetHealth(BatHealth),
+    SetPresent(u32),
+    SetCapacity(u32),
+    SetACOnline(u32),
+}
+
+impl BatControlCommand {
+    pub fn new(property: String, target: String) -> std::result::Result<Self, BatControlResult> {
+        let cmd = property.parse::<BatProperty>()?;
+        match cmd {
+            BatProperty::Status => Ok(BatControlCommand::SetStatus(target.parse::<BatStatus>()?)),
+            BatProperty::Health => Ok(BatControlCommand::SetHealth(target.parse::<BatHealth>()?)),
+            BatProperty::Present => Ok(BatControlCommand::SetPresent(
+                target
+                    .parse::<u32>()
+                    .map_err(|_| BatControlResult::StringParseIntErr)?,
+            )),
+            BatProperty::Capacity => Ok(BatControlCommand::SetCapacity(
+                target
+                    .parse::<u32>()
+                    .map_err(|_| BatControlResult::StringParseIntErr)?,
+            )),
+            BatProperty::ACOnline => Ok(BatControlCommand::SetACOnline(
+                target
+                    .parse::<u32>()
+                    .map_err(|_| BatControlResult::StringParseIntErr)?,
+            )),
+        }
+    }
+}
+
+/// Used for VM to control battery properties.
+pub struct BatControl {
+    pub type_: BatteryType,
+    pub control_socket: BatControlRequestSocket,
+}
+
 pub type BalloonControlRequestSocket = MsgSocket<BalloonControlCommand, BalloonControlResult>;
 pub type BalloonControlResponseSocket = MsgSocket<BalloonControlResult, BalloonControlCommand>;
 
+pub type BatControlRequestSocket = MsgSocket<BatControlCommand, BatControlResult>;
+pub type BatControlResponseSocket = MsgSocket<BatControlResult, BatControlCommand>;
+
 pub type DiskControlRequestSocket = MsgSocket<DiskControlCommand, DiskControlResult>;
 pub type DiskControlResponseSocket = MsgSocket<DiskControlResult, DiskControlCommand>;
 
@@ -577,16 +810,21 @@
     },
     /// Command to use controller.
     UsbCommand(UsbControlCommand),
+    /// Command to set battery.
+    BatCommand(BatteryType, BatControlCommand),
 }
 
 fn register_memory(
     vm: &mut impl Vm,
     allocator: &mut SystemAllocator,
-    fd: &dyn AsRawDescriptor,
+    descriptor: &dyn AsRawDescriptor,
     size: usize,
     pci_allocation: Option<(Alloc, u64)>,
 ) -> Result<(u64, MemSlot)> {
-    let mmap = match MemoryMappingBuilder::new(size).from_descriptor(fd).build() {
+    let mmap = match MemoryMappingBuilder::new(size)
+        .from_descriptor(descriptor)
+        .build()
+    {
         Ok(v) => v,
         Err(MmapError::SystemCallFailed(e)) => return Err(e),
         _ => return Err(SysError::new(EINVAL)),
@@ -638,6 +876,7 @@
         balloon_host_socket: &BalloonControlRequestSocket,
         disk_host_sockets: &[DiskControlRequestSocket],
         usb_control_socket: &UsbControlSocket,
+        bat_control: &mut Option<BatControl>,
     ) -> VmResponse {
         match *self {
             VmRequest::Exit => {
@@ -713,6 +952,31 @@
                     }
                 }
             }
+            VmRequest::BatCommand(type_, ref cmd) => {
+                match bat_control {
+                    Some(battery) => {
+                        if battery.type_ != type_ {
+                            error!("ignored battery command due to battery type: expected {:?}, got {:?}", battery.type_, type_);
+                            return VmResponse::Err(SysError::new(EINVAL));
+                        }
+
+                        let res = battery.control_socket.send(cmd);
+                        if let Err(e) = res {
+                            error!("fail to send command to bat control socket: {}", e);
+                            return VmResponse::Err(SysError::new(EIO));
+                        }
+
+                        match battery.control_socket.recv() {
+                            Ok(response) => VmResponse::BatResponse(response),
+                            Err(e) => {
+                                error!("fail to recv command from bat control socket: {}", e);
+                                VmResponse::Err(SysError::new(EIO))
+                            }
+                        }
+                    }
+                    None => VmResponse::BatResponse(BatControlResult::NoBatDevice),
+                }
+            }
         }
     }
 }
@@ -732,7 +996,7 @@
     /// The request to allocate and register GPU memory into guest address space was successfully
     /// done at page frame number `pfn` and memory slot number `slot` for buffer with `desc`.
     AllocateAndRegisterGpuMemory {
-        fd: MaybeOwnedFd,
+        descriptor: MaybeOwnedDescriptor,
         pfn: u64,
         slot: u32,
         desc: GpuMemoryDesc,
@@ -744,6 +1008,8 @@
     },
     /// Results of usb control commands.
     UsbResponse(UsbControlResult),
+    /// Results of battery control commands.
+    BatResponse(BatControlResult),
 }
 
 impl Display for VmResponse {
@@ -772,6 +1038,7 @@
                 balloon_actual, stats
             ),
             UsbResponse(result) => write!(f, "usb control request get result {:?}", result),
+            BatResponse(result) => write!(f, "{}", result),
         }
     }
 }
diff --git a/vm_memory/Android.bp b/vm_memory/Android.bp
index a9463bb..5d35372 100644
--- a/vm_memory/Android.bp
+++ b/vm_memory/Android.bp
@@ -70,7 +70,7 @@
 //   proc-macro2-1.0.24 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
 //   unicode-xid-0.2.1 "default"
diff --git a/vm_memory/src/guest_memory.rs b/vm_memory/src/guest_memory.rs
index 8a1640b..a7b9d53 100644
--- a/vm_memory/src/guest_memory.rs
+++ b/vm_memory/src/guest_memory.rs
@@ -8,7 +8,6 @@
 use std::convert::TryFrom;
 use std::fmt::{self, Display};
 use std::mem::size_of;
-use std::os::unix::io::{AsRawFd, RawFd};
 use std::result;
 use std::sync::Arc;
 
@@ -16,7 +15,7 @@
 use base::{pagesize, Error as SysError};
 use base::{
     AsRawDescriptor, MappedRegion, MemfdSeals, MemoryMapping, MemoryMappingBuilder, MmapError,
-    SharedMemory, SharedMemoryUnix,
+    RawDescriptor, SharedMemory, SharedMemoryUnix,
 };
 use cros_async::{
     uring_mem::{self, BorrowedIoVec},
@@ -115,8 +114,8 @@
     memfd: Arc<SharedMemory>,
 }
 
-impl AsRawFd for GuestMemory {
-    fn as_raw_fd(&self) -> RawFd {
+impl AsRawDescriptor for GuestMemory {
+    fn as_raw_descriptor(&self) -> RawDescriptor {
         self.memfd.as_raw_descriptor()
     }
 }
diff --git a/x86_64/Android.bp b/x86_64/Android.bp
index 8677080..d78feaa 100644
--- a/x86_64/Android.bp
+++ b/x86_64/Android.bp
@@ -26,6 +26,7 @@
         "libvm_memory",
     ],
     proc_macros: ["libremain"],
+    // Exclude arm family manually
     arch: {
         arm: {
             enabled: false,
@@ -62,6 +63,7 @@
         "libvm_memory",
     ],
     proc_macros: ["libremain"],
+    // Exclude arm family manually
     arch: {
         arm: {
             enabled: false,
@@ -88,6 +90,7 @@
 //   ../../adhd/cras/client/libcras/src/libcras.rs
 //   ../../minijail/rust/minijail-sys/lib.rs
 //   ../../minijail/rust/minijail/src/lib.rs
+//   ../../rust/crates/libchromeos-rs/src/lib.rs
 //   ../../vm_tools/p9/src/lib.rs
 //   ../../vm_tools/p9/wire_format_derive/wire_format_derive.rs
 //   ../acpi_tables/src/lib.rs
@@ -101,6 +104,7 @@
 //   ../devices/src/lib.rs
 //   ../disk/src/disk.rs
 //   ../enumn/src/lib.rs
+//   ../fuse/src/lib.rs
 //   ../hypervisor/src/lib.rs
 //   ../io_uring/src/lib.rs
 //   ../kernel_cmdline/src/kernel_cmdline.rs
@@ -127,6 +131,8 @@
 //   ../vm_control/src/lib.rs
 //   ../vm_memory/src/lib.rs
 //   async-trait-0.1.42
+//   autocfg-1.0.1
+//   base-0.1.0
 //   bitflags-1.2.1 "default"
 //   cfg-if-0.1.10
 //   downcast-rs-1.2.0 "default,std"
@@ -139,9 +145,13 @@
 //   futures-sink-0.3.8 "alloc,std"
 //   futures-task-0.3.8 "alloc,once_cell,std"
 //   futures-util-0.3.8 "alloc,async-await,async-await-macro,channel,futures-channel,futures-io,futures-macro,futures-sink,io,memchr,proc-macro-hack,proc-macro-nested,sink,slab,std"
+//   getopts-0.2.21
 //   getrandom-0.1.15 "std"
+//   intrusive-collections-0.9.0 "alloc,default"
 //   libc-0.2.80 "default,std"
+//   log-0.4.11
 //   memchr-2.3.4 "default,std"
+//   memoffset-0.5.6 "default"
 //   once_cell-1.5.2 "alloc,std"
 //   paste-1.0.3
 //   pin-project-1.0.2
@@ -152,6 +162,7 @@
 //   proc-macro-hack-0.5.19
 //   proc-macro-nested-0.1.6
 //   proc-macro2-1.0.24 "default,proc-macro"
+//   protobuf-2.18.1
 //   quote-1.0.7 "default,proc-macro"
 //   rand-0.7.3 "alloc,default,getrandom,getrandom_package,libc,std"
 //   rand_chacha-0.2.2 "std"
@@ -159,8 +170,9 @@
 //   remain-0.2.2
 //   remove_dir_all-0.5.3
 //   slab-0.4.2
-//   syn-1.0.50 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+//   syn-1.0.53 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
 //   tempfile-3.1.0
 //   thiserror-1.0.22
 //   thiserror-impl-1.0.22
+//   unicode-width-0.1.8 "default"
 //   unicode-xid-0.2.1 "default"
diff --git a/x86_64/Cargo.toml b/x86_64/Cargo.toml
index 7ed07cf..21e019d 100644
--- a/x86_64/Cargo.toml
+++ b/x86_64/Cargo.toml
@@ -4,16 +4,21 @@
 authors = ["The Chromium OS Authors"]
 edition = "2018"
 
+[features]
+gdb = ["gdbstub", "msg_socket", "arch/gdb"]
+
 [dependencies]
 arch = { path = "../arch" }
 assertions = { path = "../assertions" }
 data_model = { path = "../data_model" }
 devices = { path = "../devices" }
+gdbstub = { version = "0.4.0", optional = true }
 hypervisor = { path = "../hypervisor" }
 kernel_cmdline = { path = "../kernel_cmdline" }
 kernel_loader = { path = "../kernel_loader" }
 libc = "*"
 minijail = { path = "../../minijail/rust/minijail" } # ignored by ebuild
+msg_socket = { path = "../msg_socket", optional = true }
 remain = "*"
 resources = { path = "../resources" }
 sync = { path = "../sync" }
diff --git a/x86_64/src/cpuid.rs b/x86_64/src/cpuid.rs
index 38d2a89..f8cd977 100644
--- a/x86_64/src/cpuid.rs
+++ b/x86_64/src/cpuid.rs
@@ -6,7 +6,8 @@
 use std::fmt::{self, Display};
 use std::result;
 
-use hypervisor::{HypervisorCap, HypervisorX86_64, VcpuX86_64};
+use devices::{IrqChipCap, IrqChipX86_64};
+use hypervisor::{HypervisorX86_64, VcpuX86_64};
 
 #[derive(Debug, PartialEq)]
 pub enum Error {
@@ -34,7 +35,8 @@
 const EBX_CPU_COUNT_SHIFT: u32 = 16; // Index of this CPU.
 const EBX_CPUID_SHIFT: u32 = 24; // Index of this CPU.
 const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
-const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24; // TSC deadline mode of APIC timer
+const ECX_X2APIC_SHIFT: u32 = 21; // APIC supports extended xAPIC (x2APIC) standard.
+const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24; // TSC deadline mode of APIC timer.
 const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
 const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.
 const ECX_TOPO_TYPE_SHIFT: u32 = 8; // Topology Level type.
@@ -45,7 +47,7 @@
     vcpu_id: usize,
     cpu_count: usize,
     cpuid: &mut hypervisor::CpuId,
-    hypervisor: &dyn HypervisorX86_64,
+    irq_chip: &dyn IrqChipX86_64,
     no_smt: bool,
 ) -> Result<()> {
     let entries = &mut cpuid.cpu_id_entries;
@@ -57,7 +59,12 @@
                 if entry.index == 0 {
                     entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
                 }
-                if hypervisor.check_capability(&HypervisorCap::TscDeadlineTimer) {
+                if irq_chip.check_capability(IrqChipCap::X2Apic) {
+                    entry.ecx |= 1 << ECX_X2APIC_SHIFT;
+                } else {
+                    entry.ecx &= !(1 << ECX_X2APIC_SHIFT);
+                }
+                if irq_chip.check_capability(IrqChipCap::TscDeadlineTimer) {
                     entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
                 }
                 entry.ebx = (vcpu_id << EBX_CPUID_SHIFT) as u32
@@ -129,6 +136,7 @@
 /// * `nrcpus` - The number of vcpus being used by this VM.
 pub fn setup_cpuid(
     hypervisor: &dyn HypervisorX86_64,
+    irq_chip: &dyn IrqChipX86_64,
     vcpu: &dyn VcpuX86_64,
     vcpu_id: usize,
     nrcpus: usize,
@@ -138,7 +146,7 @@
         .get_supported_cpuid()
         .map_err(Error::GetSupportedCpusFailed)?;
 
-    filter_cpuid(vcpu_id, nrcpus, &mut cpuid, hypervisor, no_smt)?;
+    filter_cpuid(vcpu_id, nrcpus, &mut cpuid, irq_chip, no_smt)?;
 
     vcpu.set_cpuid(&cpuid)
         .map_err(Error::SetSupportedCpusFailed)
@@ -165,7 +173,11 @@
     #[test]
     fn feature_and_vendor_name() {
         let mut cpuid = hypervisor::CpuId::new(2);
-        let hypervisor = hypervisor::kvm::Kvm::new().unwrap();
+        let guest_mem =
+            vm_memory::GuestMemory::new(&[(vm_memory::GuestAddress(0), 0x10000)]).unwrap();
+        let kvm = hypervisor::kvm::Kvm::new().unwrap();
+        let vm = hypervisor::kvm::KvmVm::new(&kvm, guest_mem).unwrap();
+        let irq_chip = devices::KvmKernelIrqChip::new(vm, 1).unwrap();
 
         let entries = &mut cpuid.cpu_id_entries;
         entries.push(CpuIdEntry {
@@ -178,7 +190,7 @@
             edx: 0,
             ..Default::default()
         });
-        assert_eq!(Ok(()), filter_cpuid(1, 2, &mut cpuid, &hypervisor, false));
+        assert_eq!(Ok(()), filter_cpuid(1, 2, &mut cpuid, &irq_chip, false));
 
         let entries = &mut cpuid.cpu_id_entries;
         assert_eq!(entries[0].function, 0);
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index ef44adc..cc00a79 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -67,7 +67,13 @@
 use remain::sorted;
 use resources::SystemAllocator;
 use sync::Mutex;
+use vm_control::{BatControl, BatteryType};
 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+use {
+    gdbstub::arch::x86::reg::X86_64CoreRegs,
+    hypervisor::x86_64::{Regs, Sregs},
+};
 
 #[sorted]
 #[derive(Debug)]
@@ -77,6 +83,7 @@
     CloneEvent(base::Error),
     Cmdline(kernel_cmdline::Error),
     ConfigureSystem,
+    CreateBatDevices(arch::DeviceRegistrationError),
     CreateDevices(Box<dyn StdError>),
     CreateEvent(base::Error),
     CreateFdt(arch::fdt::Error),
@@ -90,6 +97,7 @@
     CreateVcpu(base::Error),
     CreateVm(Box<dyn StdError>),
     E820Configuration,
+    EnableSinglestep(base::Error),
     EnableSplitIrqchip(base::Error),
     GetSerialCmdline(GetSerialCmdlineError),
     KernelOffsetPastEnd,
@@ -98,9 +106,13 @@
     LoadCmdline(kernel_loader::Error),
     LoadInitrd(arch::LoadImageError),
     LoadKernel(kernel_loader::Error),
+    PageNotPresent,
     Pstore(arch::pstore::Error),
+    ReadingGuestMemory(vm_memory::GuestMemoryError),
+    ReadRegs(base::Error),
     RegisterIrqfd(base::Error),
     RegisterVsock(arch::DeviceRegistrationError),
+    SetHwBreakpoint(base::Error),
     SetLint(interrupts::Error),
     SetTssAddr(base::Error),
     SetupCpuid(cpuid::Error),
@@ -111,6 +123,9 @@
     SetupRegs(regs::Error),
     SetupSmbios(smbios::Error),
     SetupSregs(regs::Error),
+    TranslatingVirtAddr,
+    WriteRegs(base::Error),
+    WritingGuestMemory(GuestMemoryError),
     ZeroPagePastRamEnd,
     ZeroPageSetup,
 }
@@ -127,6 +142,7 @@
             CloneEvent(e) => write!(f, "unable to clone an Event: {}", e),
             Cmdline(e) => write!(f, "the given kernel command line was invalid: {}", e),
             ConfigureSystem => write!(f, "error configuring the system"),
+            CreateBatDevices(e) => write!(f, "unable to create battery devices: {}", e),
             CreateDevices(e) => write!(f, "error creating devices: {}", e),
             CreateEvent(e) => write!(f, "unable to make an Event: {}", e),
             CreateFdt(e) => write!(f, "failed to create fdt: {}", e),
@@ -140,6 +156,7 @@
             CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
             CreateVm(e) => write!(f, "failed to create VM: {}", e),
             E820Configuration => write!(f, "invalid e820 setup params"),
+            EnableSinglestep(e) => write!(f, "failed to enable singlestep execution: {}", e),
             EnableSplitIrqchip(e) => write!(f, "failed to enable split irqchip: {}", e),
             GetSerialCmdline(e) => write!(f, "failed to get serial cmdline: {}", e),
             KernelOffsetPastEnd => write!(f, "the kernel extends past the end of RAM"),
@@ -148,9 +165,13 @@
             LoadCmdline(e) => write!(f, "error loading command line: {}", e),
             LoadInitrd(e) => write!(f, "error loading initrd: {}", e),
             LoadKernel(e) => write!(f, "error loading Kernel: {}", e),
+            PageNotPresent => write!(f, "error translating address: Page not present"),
             Pstore(e) => write!(f, "failed to allocate pstore region: {}", e),
+            ReadingGuestMemory(e) => write!(f, "error reading guest memory {}", e),
+            ReadRegs(e) => write!(f, "error reading CPU registers {}", e),
             RegisterIrqfd(e) => write!(f, "error registering an IrqFd: {}", e),
             RegisterVsock(e) => write!(f, "error registering virtual socket device: {}", e),
+            SetHwBreakpoint(e) => write!(f, "failed to set a hardware breakpoint: {}", e),
             SetLint(e) => write!(f, "failed to set interrupts: {}", e),
             SetTssAddr(e) => write!(f, "failed to set tss addr: {}", e),
             SetupCpuid(e) => write!(f, "failed to set up cpuid: {}", e),
@@ -161,6 +182,9 @@
             SetupRegs(e) => write!(f, "failed to set up registers: {}", e),
             SetupSmbios(e) => write!(f, "failed to set up SMBIOS: {}", e),
             SetupSregs(e) => write!(f, "failed to set up sregs: {}", e),
+            TranslatingVirtAddr => write!(f, "failed to translate virtual address"),
+            WriteRegs(e) => write!(f, "error writing CPU registers {}", e),
+            WritingGuestMemory(e) => write!(f, "error writing guest memory {}", e),
             ZeroPagePastRamEnd => write!(f, "the zero page extends past the end of guest_mem"),
             ZeroPageSetup => write!(f, "error writing the zero page of guest memory"),
         }
@@ -325,6 +349,7 @@
         mut components: VmComponents,
         serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
         serial_jail: Option<Minijail>,
+        battery: (&Option<BatteryType>, Option<Minijail>),
         create_devices: FD,
         create_vm: FV,
         create_irq_chip: FI,
@@ -345,10 +370,7 @@
         E2: StdError + 'static,
         E3: StdError + 'static,
     {
-        let has_bios = match components.vm_image {
-            VmImage::Bios(_) => true,
-            _ => false,
-        };
+        let has_bios = matches!(components.vm_image, VmImage::Bios(_));
         let mem = Self::setup_memory(components.memory_size, has_bios)?;
         let mut resources = Self::get_resource_allocator(&mem, components.wayland_dmabuf);
 
@@ -395,11 +417,15 @@
             serial_jail,
         )?;
 
-        let acpi_dev_resource = Self::setup_acpi_devices(
+        let (acpi_dev_resource, bat_control) = Self::setup_acpi_devices(
             &mut io_bus,
             &mut resources,
             suspend_evt.try_clone().map_err(Error::CloneEvent)?,
+            exit_evt.try_clone().map_err(Error::CloneEvent)?,
             components.acpi_sdts,
+            &mut irq_chip,
+            battery,
+            &mut mmio_bus,
         )?;
 
         let ramoops_region = match components.pstore {
@@ -489,6 +515,9 @@
             pid_debug_label_map,
             suspend_evt,
             rt_cpus: components.rt_cpus,
+            bat_control,
+            #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+            gdb: components.gdb,
         })
     }
 
@@ -502,7 +531,7 @@
         has_bios: bool,
         no_smt: bool,
     ) -> Result<()> {
-        cpuid::setup_cpuid(hypervisor, vcpu, vcpu_id, num_cpus, no_smt)
+        cpuid::setup_cpuid(hypervisor, irq_chip, vcpu, vcpu_id, num_cpus, no_smt)
             .map_err(Error::SetupCpuid)?;
 
         if has_bios {
@@ -527,6 +556,234 @@
 
         Ok(())
     }
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    fn debug_read_registers<T: VcpuX86_64>(vcpu: &T) -> Result<X86_64CoreRegs> {
+        // General registers: RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15
+        let gregs = vcpu.get_regs().map_err(Error::ReadRegs)?;
+        let regs = [
+            gregs.rax, gregs.rbx, gregs.rcx, gregs.rdx, gregs.rsi, gregs.rdi, gregs.rbp, gregs.rsp,
+            gregs.r8, gregs.r9, gregs.r10, gregs.r11, gregs.r12, gregs.r13, gregs.r14, gregs.r15,
+        ];
+
+        // GDB exposes 32-bit eflags instead of 64-bit rflags.
+        // https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml
+        let eflags = gregs.rflags as u32;
+        let rip = gregs.rip;
+
+        // Segment registers: CS, SS, DS, ES, FS, GS
+        let sregs = vcpu.get_sregs().map_err(Error::ReadRegs)?;
+        let sgs = [sregs.cs, sregs.ss, sregs.ds, sregs.es, sregs.fs, sregs.gs];
+        let mut segments = [0u32; 6];
+        // GDB uses only the selectors.
+        for i in 0..sgs.len() {
+            segments[i] = sgs[i].selector as u32;
+        }
+
+        // TODO(keiichiw): Other registers such as FPU, xmm and mxcsr.
+
+        Ok(X86_64CoreRegs {
+            regs,
+            eflags,
+            rip,
+            segments,
+            ..Default::default()
+        })
+    }
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    fn debug_write_registers<T: VcpuX86_64>(vcpu: &T, regs: &X86_64CoreRegs) -> Result<()> {
+        // General purpose registers (RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15) + RIP + rflags
+        let orig_gregs = vcpu.get_regs().map_err(Error::ReadRegs)?;
+        let gregs = Regs {
+            rax: regs.regs[0],
+            rbx: regs.regs[1],
+            rcx: regs.regs[2],
+            rdx: regs.regs[3],
+            rsi: regs.regs[4],
+            rdi: regs.regs[5],
+            rbp: regs.regs[6],
+            rsp: regs.regs[7],
+            r8: regs.regs[8],
+            r9: regs.regs[9],
+            r10: regs.regs[10],
+            r11: regs.regs[11],
+            r12: regs.regs[12],
+            r13: regs.regs[13],
+            r14: regs.regs[14],
+            r15: regs.regs[15],
+            rip: regs.rip,
+            // Update the lower 32 bits of rflags.
+            rflags: (orig_gregs.rflags & !(u32::MAX as u64)) | (regs.eflags as u64),
+        };
+        vcpu.set_regs(&gregs).map_err(Error::WriteRegs)?;
+
+        // Segment registers: CS, SS, DS, ES, FS, GS
+        // Since GDB care only selectors, we call get_sregs() first.
+        let mut sregs = vcpu.get_sregs().map_err(Error::ReadRegs)?;
+        sregs.cs.selector = regs.segments[0] as u16;
+        sregs.ss.selector = regs.segments[1] as u16;
+        sregs.ds.selector = regs.segments[2] as u16;
+        sregs.es.selector = regs.segments[3] as u16;
+        sregs.fs.selector = regs.segments[4] as u16;
+        sregs.gs.selector = regs.segments[5] as u16;
+
+        vcpu.set_sregs(&sregs).map_err(Error::WriteRegs)?;
+
+        // TODO(keiichiw): Other registers such as FPU, xmm and mxcsr.
+
+        Ok(())
+    }
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    fn debug_read_memory<T: VcpuX86_64>(
+        vcpu: &T,
+        guest_mem: &GuestMemory,
+        vaddr: GuestAddress,
+        len: usize,
+    ) -> Result<Vec<u8>> {
+        let sregs = vcpu.get_sregs().map_err(Error::ReadRegs)?;
+        let mut buf = vec![0; len];
+        let mut total_read = 0u64;
+        // Handle reads across page boundaries.
+
+        while total_read < len as u64 {
+            let (paddr, psize) = phys_addr(guest_mem, vaddr.0 + total_read, &sregs)?;
+            let read_len = std::cmp::min(len as u64 - total_read, psize - (paddr & (psize - 1)));
+            guest_mem
+                .get_slice_at_addr(GuestAddress(paddr), read_len as usize)
+                .map_err(Error::ReadingGuestMemory)?
+                .copy_to(&mut buf[total_read as usize..]);
+            total_read += read_len;
+        }
+        Ok(buf)
+    }
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    fn debug_write_memory<T: VcpuX86_64>(
+        vcpu: &T,
+        guest_mem: &GuestMemory,
+        vaddr: GuestAddress,
+        buf: &[u8],
+    ) -> Result<()> {
+        let sregs = vcpu.get_sregs().map_err(Error::ReadRegs)?;
+        let mut total_written = 0u64;
+        // Handle writes across page boundaries.
+        while total_written < buf.len() as u64 {
+            let (paddr, psize) = phys_addr(guest_mem, vaddr.0 + total_written, &sregs)?;
+            let write_len = std::cmp::min(
+                buf.len() as u64 - total_written,
+                psize - (paddr & (psize - 1)),
+            );
+
+            guest_mem
+                .write_all_at_addr(
+                    &buf[total_written as usize..(total_written as usize + write_len as usize)],
+                    GuestAddress(paddr),
+                )
+                .map_err(Error::WritingGuestMemory)?;
+            total_written += write_len;
+        }
+        Ok(())
+    }
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    fn debug_enable_singlestep<T: VcpuX86_64>(vcpu: &T) -> Result<()> {
+        vcpu.set_guest_debug(&[], true /* enable_singlestep */)
+            .map_err(Error::EnableSinglestep)
+    }
+
+    #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+    fn debug_set_hw_breakpoints<T: VcpuX86_64>(
+        vcpu: &T,
+        breakpoints: &[GuestAddress],
+    ) -> Result<()> {
+        vcpu.set_guest_debug(&breakpoints, false /* enable_singlestep */)
+            .map_err(Error::SetHwBreakpoint)
+    }
+}
+
+#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+// return the translated address and the size of the page it resides in.
+fn phys_addr(mem: &GuestMemory, vaddr: u64, sregs: &Sregs) -> Result<(u64, u64)> {
+    const CR0_PG_MASK: u64 = 1 << 31;
+    const CR4_PAE_MASK: u64 = 1 << 5;
+    const CR4_LA57_MASK: u64 = 1 << 12;
+    const MSR_EFER_LMA: u64 = 1 << 10;
+    // bits 12 through 51 are the address in a PTE.
+    const PTE_ADDR_MASK: u64 = ((1 << 52) - 1) & !0x0fff;
+    const PAGE_PRESENT: u64 = 0x1;
+    const PAGE_PSE_MASK: u64 = 0x1 << 7;
+
+    const PAGE_SIZE_4K: u64 = 4 * 1024;
+    const PAGE_SIZE_2M: u64 = 2 * 1024 * 1024;
+    const PAGE_SIZE_1G: u64 = 1024 * 1024 * 1024;
+
+    fn next_pte(mem: &GuestMemory, curr_table_addr: u64, vaddr: u64, level: usize) -> Result<u64> {
+        let ent: u64 = mem
+            .read_obj_from_addr(GuestAddress(
+                (curr_table_addr & PTE_ADDR_MASK) + page_table_offset(vaddr, level),
+            ))
+            .map_err(|_| Error::TranslatingVirtAddr)?;
+        /* TODO - convert to a trace
+        println!(
+            "level {} vaddr {:x} table-addr {:x} mask {:x} ent {:x} offset {:x}",
+            level,
+            vaddr,
+            curr_table_addr,
+            PTE_ADDR_MASK,
+            ent,
+            page_table_offset(vaddr, level)
+        );
+        */
+        if ent & PAGE_PRESENT == 0 {
+            return Err(Error::PageNotPresent);
+        }
+        Ok(ent)
+    }
+
+    // Get the offset in to the page of `vaddr`.
+    fn page_offset(vaddr: u64, page_size: u64) -> u64 {
+        vaddr & (page_size - 1)
+    }
+
+    // Get the offset in to the page table of the given `level` specified by the virtual `address`.
+    // `level` is 1 through 5 in x86_64 to handle the five levels of paging.
+    fn page_table_offset(addr: u64, level: usize) -> u64 {
+        let offset = (level - 1) * 9 + 12;
+        ((addr >> offset) & 0x1ff) << 3
+    }
+
+    if sregs.cr0 & CR0_PG_MASK == 0 {
+        return Ok((vaddr, PAGE_SIZE_4K));
+    }
+
+    if sregs.cr4 & CR4_PAE_MASK == 0 {
+        return Err(Error::TranslatingVirtAddr);
+    }
+
+    if sregs.efer & MSR_EFER_LMA != 0 {
+        // TODO - check LA57
+        if sregs.cr4 & CR4_LA57_MASK != 0 {}
+        let p4_ent = next_pte(mem, sregs.cr3, vaddr, 4)?;
+        let p3_ent = next_pte(mem, p4_ent, vaddr, 3)?;
+        // TODO check if it's a 1G page with the PSE bit in p2_ent
+        if p3_ent & PAGE_PSE_MASK != 0 {
+            // It's a 1G page with the PSE bit in p3_ent
+            let paddr = p3_ent & PTE_ADDR_MASK | page_offset(vaddr, PAGE_SIZE_1G);
+            return Ok((paddr, PAGE_SIZE_1G));
+        }
+        let p2_ent = next_pte(mem, p3_ent, vaddr, 2)?;
+        if p2_ent & PAGE_PSE_MASK != 0 {
+            // It's a 2M page with the PSE bit in p2_ent
+            let paddr = p2_ent & PTE_ADDR_MASK | page_offset(vaddr, PAGE_SIZE_2M);
+            return Ok((paddr, PAGE_SIZE_2M));
+        }
+        let p1_ent = next_pte(mem, p2_ent, vaddr, 1)?;
+        let paddr = p1_ent & PTE_ADDR_MASK | page_offset(vaddr, PAGE_SIZE_4K);
+        return Ok((paddr, PAGE_SIZE_4K));
+    }
+    Err(Error::TranslatingVirtAddr)
 }
 
 impl X8664arch {
@@ -736,7 +993,6 @@
                 Arc::new(Mutex::new(devices::Cmos::new(mem_below_4g, mem_above_4g))),
                 0x70,
                 0x2,
-                false,
             )
             .unwrap();
 
@@ -746,25 +1002,19 @@
         )));
 
         if pit_uses_speaker_port {
-            io_bus.insert(i8042, 0x062, 0x3, true).unwrap();
+            io_bus.insert(i8042, 0x062, 0x3).unwrap();
         } else {
-            io_bus.insert(i8042, 0x061, 0x4, true).unwrap();
+            io_bus.insert(i8042, 0x061, 0x4).unwrap();
         }
 
-        io_bus
-            .insert(nul_device.clone(), 0x0ed, 0x1, false)
-            .unwrap(); // most likely this one does nothing
-        io_bus
-            .insert(nul_device.clone(), 0x0f0, 0x2, false)
-            .unwrap(); // ignore fpu
+        io_bus.insert(nul_device.clone(), 0x0ed, 0x1).unwrap(); // most likely this one does nothing
+        io_bus.insert(nul_device.clone(), 0x0f0, 0x2).unwrap(); // ignore fpu
 
         if let Some(pci_root) = pci {
-            io_bus.insert(pci_root, 0xcf8, 0x8, false).unwrap();
+            io_bus.insert(pci_root, 0xcf8, 0x8).unwrap();
         } else {
             // ignore pci.
-            io_bus
-                .insert(nul_device.clone(), 0xcf8, 0x8, false)
-                .unwrap();
+            io_bus.insert(nul_device.clone(), 0xcf8, 0x8).unwrap();
         }
 
         Ok(io_bus)
@@ -778,13 +1028,21 @@
     /// * - `io_bus` the I/O bus to add the devices to
     /// * - `resources` the SystemAllocator to allocate IO and MMIO for acpi
     ///                devices.
-    /// * - `suspend_evt` - the event object which used to suspend the vm
+    /// * - `suspend_evt` the event object which used to suspend the vm
+    /// * - `sdts` ACPI system description tables
+    /// * - `irq_chip` the IrqChip object for registering irq events
+    /// * - `battery` indicate whether to create the battery
+    /// * - `mmio_bus` the MMIO bus to add the devices to
     fn setup_acpi_devices(
         io_bus: &mut devices::Bus,
         resources: &mut SystemAllocator,
         suspend_evt: Event,
+        exit_evt: Event,
         sdts: Vec<SDT>,
-    ) -> Result<acpi::ACPIDevResource> {
+        irq_chip: &mut impl IrqChip,
+        battery: (&Option<BatteryType>, Option<Minijail>),
+        mmio_bus: &mut devices::Bus,
+    ) -> Result<(acpi::ACPIDevResource, Option<BatControl>)> {
         // The AML data for the acpi devices
         let mut amls = Vec::new();
 
@@ -801,7 +1059,7 @@
             None => 0x600,
         };
 
-        let pmresource = devices::ACPIPMResource::new(suspend_evt);
+        let pmresource = devices::ACPIPMResource::new(suspend_evt, exit_evt);
         Aml::to_aml_bytes(&pmresource, &mut amls);
         let pm = Arc::new(Mutex::new(pmresource));
         io_bus
@@ -809,16 +1067,40 @@
                 pm.clone(),
                 pm_iobase as u64,
                 devices::acpi::ACPIPM_RESOURCE_LEN as u64,
-                false,
             )
             .unwrap();
         io_bus.notify_on_resume(pm);
 
-        Ok(acpi::ACPIDevResource {
-            amls,
-            pm_iobase,
-            sdts,
-        })
+        let bat_control = if let Some(battery_type) = battery.0 {
+            match battery_type {
+                BatteryType::Goldfish => {
+                    let control_socket = arch::add_goldfish_battery(
+                        &mut amls,
+                        battery.1,
+                        mmio_bus,
+                        irq_chip,
+                        X86_64_SCI_IRQ,
+                        resources,
+                    )
+                    .map_err(Error::CreateBatDevices)?;
+                    Some(BatControl {
+                        type_: BatteryType::Goldfish,
+                        control_socket,
+                    })
+                }
+            }
+        } else {
+            None
+        };
+
+        Ok((
+            acpi::ACPIDevResource {
+                amls,
+                pm_iobase,
+                sdts,
+            },
+            bat_control,
+        ))
     }
 
     /// Sets up the serial devices for this platform. Returns the serial port number and serial
diff --git a/x86_64/src/mptable.rs b/x86_64/src/mptable.rs
index 0354b4f..548ea33 100644
--- a/x86_64/src/mptable.rs
+++ b/x86_64/src/mptable.rs
@@ -268,7 +268,7 @@
         base_mp = base_mp.unchecked_add(size as u64);
         checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
     }
-    let pci_irq_base = super::X86_64_IRQ_BASE as u8;
+
     // Insert PCI interrupts after platform IRQs.
     for (address, irq_num, irq_pin) in pci_irqs.iter() {
         let size = mem::size_of::<mpc_intsrc>();
@@ -285,8 +285,14 @@
         base_mp = base_mp.unchecked_add(size as u64);
         checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
     }
+
+    let starting_isa_irq_num = pci_irqs
+        .into_iter()
+        .map(|(_, irq_num, _)| irq_num + 1)
+        .fold(super::X86_64_IRQ_BASE, u32::max) as u8;
+
     // Finally insert ISA interrupts.
-    for i in pci_irq_base + pci_irqs.len() as u8..16 {
+    for i in starting_isa_irq_num..16 {
         let size = mem::size_of::<mpc_intsrc>();
         let mut mpc_intsrc = mpc_intsrc::default();
         mpc_intsrc.type_ = MP_INTSRC as u8;