Merge remote-tracking branch 'aosp/upstream-master' into mergeupstream
Change-Id: If9ce6bf5a59f5fd502241197f53421f9419bf30b
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..8d9ed33
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+docker
+*/*/target
+target
+.git
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6ead6aa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+target/
+**/*.rs.bk
+**/*.sw[po]
+**/*.orig
+**/Cargo.lock
+!/Cargo.lock
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..f04998d
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "tpm2-sys/libtpm2"]
+ path = tpm2-sys/libtpm2
+ url = https://chromium.googlesource.com/chromiumos/third_party/tpm2
diff --git a/.rustfmt.toml b/.rustfmt.toml
new file mode 100644
index 0000000..a2db301
--- /dev/null
+++ b/.rustfmt.toml
@@ -0,0 +1,2 @@
+use_field_init_shorthand = true
+use_try_shorthand = true
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..8446532
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,604 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "aarch64"
+version = "0.1.0"
+dependencies = [
+ "arch 0.1.0",
+ "data_model 0.1.0",
+ "devices 0.1.0",
+ "io_jail 0.1.0",
+ "kernel_cmdline 0.1.0",
+ "kvm 0.1.0",
+ "kvm_sys 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "resources 0.1.0",
+ "sync 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "arch"
+version = "0.1.0"
+dependencies = [
+ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "devices 0.1.0",
+ "io_jail 0.1.0",
+ "kernel_cmdline 0.1.0",
+ "kvm 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "resources 0.1.0",
+ "sync 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "assertions"
+version = "0.1.0"
+
+[[package]]
+name = "audio_streams"
+version = "0.1.0"
+
+[[package]]
+name = "bit_field"
+version = "0.1.0"
+dependencies = [
+ "bit_field_derive 0.1.0",
+]
+
+[[package]]
+name = "bit_field_derive"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cc"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cras-sys"
+version = "0.1.0"
+dependencies = [
+ "data_model 0.1.0",
+]
+
+[[package]]
+name = "crosvm"
+version = "0.1.0"
+dependencies = [
+ "aarch64 0.1.0",
+ "arch 0.1.0",
+ "assertions 0.1.0",
+ "audio_streams 0.1.0",
+ "bit_field 0.1.0",
+ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crosvm_plugin 0.17.0",
+ "data_model 0.1.0",
+ "devices 0.1.0",
+ "enumn 0.1.0",
+ "gpu_buffer 0.1.0",
+ "io_jail 0.1.0",
+ "kernel_cmdline 0.1.0",
+ "kernel_loader 0.1.0",
+ "kvm 0.1.0",
+ "kvm_sys 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libcras 0.1.0",
+ "msg_socket 0.1.0",
+ "net_util 0.1.0",
+ "p9 0.1.0",
+ "protobuf 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "protos 0.1.0",
+ "qcow 0.1.0",
+ "rand_ish 0.1.0",
+ "remain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "render_node_forward 0.1.0",
+ "resources 0.1.0",
+ "sync 0.1.0",
+ "sys_util 0.1.0",
+ "vhost 0.1.0",
+ "vm_control 0.1.0",
+ "x86_64 0.1.0",
+]
+
+[[package]]
+name = "crosvm_plugin"
+version = "0.17.0"
+dependencies = [
+ "kvm 0.1.0",
+ "kvm_sys 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "protobuf 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "protos 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "data_model"
+version = "0.1.0"
+dependencies = [
+ "assertions 0.1.0",
+]
+
+[[package]]
+name = "devices"
+version = "0.1.0"
+dependencies = [
+ "audio_streams 0.1.0",
+ "bit_field 0.1.0",
+ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "data_model 0.1.0",
+ "enumn 0.1.0",
+ "gpu_buffer 0.1.0",
+ "gpu_display 0.1.0",
+ "gpu_renderer 0.1.0",
+ "io_jail 0.1.0",
+ "kvm 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg_on_socket_derive 0.1.0",
+ "msg_socket 0.1.0",
+ "net_sys 0.1.0",
+ "net_util 0.1.0",
+ "p9 0.1.0",
+ "protos 0.1.0",
+ "remain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "resources 0.1.0",
+ "sync 0.1.0",
+ "sys_util 0.1.0",
+ "tpm2 0.1.0",
+ "usb_util 0.1.0",
+ "vhost 0.1.0",
+ "virtio_sys 0.1.0",
+ "vm_control 0.1.0",
+]
+
+[[package]]
+name = "enumn"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.26 (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"
+dependencies = [
+ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gpu_buffer"
+version = "0.1.0"
+dependencies = [
+ "data_model 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "gpu_display"
+version = "0.1.0"
+dependencies = [
+ "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "data_model 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "gpu_renderer"
+version = "0.1.0"
+dependencies = [
+ "data_model 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "io_jail"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "kernel_cmdline"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "kernel_loader"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "kvm"
+version = "0.1.0"
+dependencies = [
+ "kvm_sys 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg_socket 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "kvm_sys"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libcras"
+version = "0.1.0"
+dependencies = [
+ "audio_streams 0.1.0",
+ "cras-sys 0.1.0",
+ "data_model 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "log"
+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)",
+]
+
+[[package]]
+name = "msg_on_socket_derive"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "msg_socket"
+version = "0.1.0"
+dependencies = [
+ "data_model 0.1.0",
+ "msg_on_socket_derive 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "net_sys"
+version = "0.1.0"
+dependencies = [
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "net_util"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net_sys 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "p9"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wire_format_derive 0.1.0",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "poll_token_derive"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "protobuf"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "protobuf-codegen"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "protobuf 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "protoc"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "protoc-rust"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "protobuf 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "protobuf-codegen 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "protoc 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempfile 3.0.7",
+]
+
+[[package]]
+name = "protos"
+version = "0.1.0"
+dependencies = [
+ "kvm_sys 0.1.0",
+ "protobuf 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "protoc-rust 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "qcow"
+version = "0.1.0"
+dependencies = [
+ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "data_model 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "qcow_utils"
+version = "0.1.0"
+dependencies = [
+ "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "qcow 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "quote"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_ish"
+version = "0.1.0"
+
+[[package]]
+name = "remain"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "render_node_forward"
+version = "0.1.0"
+dependencies = [
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "resources"
+version = "0.1.0"
+dependencies = [
+ "gpu_buffer 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg_socket 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "syn"
+version = "0.15.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "sync"
+version = "0.1.0"
+
+[[package]]
+name = "sys_util"
+version = "0.1.0"
+dependencies = [
+ "data_model 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "poll_token_derive 0.1.0",
+ "sync 0.1.0",
+ "syscall_defines 0.1.0",
+]
+
+[[package]]
+name = "syscall_defines"
+version = "0.1.0"
+
+[[package]]
+name = "tempfile"
+version = "3.0.7"
+dependencies = [
+ "rand_ish 0.1.0",
+]
+
+[[package]]
+name = "tpm2"
+version = "0.1.0"
+dependencies = [
+ "tpm2-sys 0.1.0",
+]
+
+[[package]]
+name = "tpm2-sys"
+version = "0.1.0"
+dependencies = [
+ "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "usb_util"
+version = "0.1.0"
+dependencies = [
+ "assertions 0.1.0",
+ "data_model 0.1.0",
+ "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sync 0.1.0",
+]
+
+[[package]]
+name = "vhost"
+version = "0.1.0"
+dependencies = [
+ "assertions 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net_util 0.1.0",
+ "sys_util 0.1.0",
+ "virtio_sys 0.1.0",
+]
+
+[[package]]
+name = "virtio_sys"
+version = "0.1.0"
+dependencies = [
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "vm_control"
+version = "0.1.0"
+dependencies = [
+ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "data_model 0.1.0",
+ "kvm 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg_socket 0.1.0",
+ "resources 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[[package]]
+name = "wire_format_derive"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "x86_64"
+version = "0.1.0"
+dependencies = [
+ "arch 0.1.0",
+ "assertions 0.1.0",
+ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "data_model 0.1.0",
+ "devices 0.1.0",
+ "io_jail 0.1.0",
+ "kernel_cmdline 0.1.0",
+ "kernel_loader 0.1.0",
+ "kvm 0.1.0",
+ "kvm_sys 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "resources 0.1.0",
+ "sync 0.1.0",
+ "sys_util 0.1.0",
+]
+
+[metadata]
+"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"
+"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 getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
+"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
+"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
+"checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238"
+"checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f"
+"checksum proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)" = "ab2fc21ba78ac73e4ff6b3818ece00be4e175ffbef4d0a717d978b48b24150c4"
+"checksum protobuf 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "524d165d95627ddebba768db728216c4429bbb62882f7e6ab1a6c3c54a7ed830"
+"checksum protobuf-codegen 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e142c5972a0736674d647714ac7a454f20aef31b09902d330583b8d8a96401a1"
+"checksum protoc 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "82ac4c59bf852f415c62a1d30da3348f977322dc66bdb283c92b3df9bee2073a"
+"checksum protoc-rust 2.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9dc0547688715431c954528a3dabe7559b4d53b3161426981e19419ea7b1f0"
+"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
+"checksum remain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3bec2543b50be4539fdc27fde082e218cf4c3895358ca77f5c52fe930589e209"
+"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
+"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
+"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..048681f
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,83 @@
+[package]
+name = "crosvm"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[profile.release]
+panic = 'abort'
+overflow-checks = true
+
+[workspace]
+members = ["qcow_utils"]
+exclude = [
+ "assertions",
+ "data_model",
+ "rand_ish",
+ "sync",
+ "sys_util",
+ "syscall_defines",
+ "tempfile",
+]
+
+[features]
+default-no-sandbox = []
+gpu = ["devices/gpu"]
+gpu-forward = ["render_node_forward"]
+plugin = ["protos/plugin", "crosvm_plugin", "protobuf"]
+sandboxed-libusb = ["devices/sandboxed-libusb", "vm_control/sandboxed-libusb"]
+tpm = ["devices/tpm"]
+wl-dmabuf = ["devices/wl-dmabuf", "gpu_buffer", "resources/wl-dmabuf"]
+
+[dependencies]
+arch = { path = "arch" }
+assertions = { path = "assertions" }
+audio_streams = "*"
+bit_field = { path = "bit_field" }
+byteorder = "=1.1.0"
+crosvm_plugin = { path = "crosvm_plugin", optional = true }
+data_model = "*"
+devices = { path = "devices" }
+enumn = { path = "enumn" }
+gpu_buffer = { path = "gpu_buffer", optional = true }
+io_jail = { path = "io_jail" }
+kernel_cmdline = { path = "kernel_cmdline" }
+kernel_loader = { path = "kernel_loader" }
+kvm = { path = "kvm" }
+kvm_sys = { path = "kvm_sys" }
+libc = "=0.2.44"
+libcras = "*"
+msg_socket = { path = "msg_socket" }
+net_util = { path = "net_util" }
+p9 = { path = "p9" }
+protobuf = { version = "2.3", optional = true }
+protos = { path = "protos", optional = true }
+qcow = { path = "qcow" }
+rand_ish = { path = "rand_ish" }
+remain = "*"
+render_node_forward = { path = "render_node_forward", optional = true }
+resources = { path = "resources" }
+sync = { path = "sync" }
+sys_util = "*"
+vhost = { path = "vhost" }
+vm_control = { path = "vm_control" }
+
+[target.'cfg(target_arch = "x86_64")'.dependencies]
+x86_64 = { path = "x86_64" }
+
+[target.'cfg(any(target_arch = "aarch64", target_arch = "arm"))'.dependencies]
+aarch64 = { path = "aarch64" }
+
+[dev-dependencies]
+sys_util = "*"
+
+[patch.crates-io]
+assertions = { path = "assertions" }
+audio_streams = { path = "../../third_party/adhd/audio_streams" } # ignored by ebuild
+data_model = { path = "data_model" }
+libcras = { path = "../../third_party/adhd/cras/client/libcras" } # ignored by ebuild
+poll_token_derive = { path = "sys_util/poll_token_derive" }
+sync = { path = "sync" }
+sys_util = { path = "sys_util" }
+syscall_defines = { path = "syscall_defines" }
+tempfile = { path = "tempfile" }
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8bafca3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..f4d5b78
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,5 @@
+chirantan@chromium.org
+dgreid@chromium.org
+dverkamp@chromium.org
+smbarber@chromium.org
+zachr@chromium.org
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d287af8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,189 @@
+# crosvm - The Chrome OS Virtual Machine Monitor
+
+This component, known as crosvm, runs untrusted operating systems along with
+virtualized devices. No actual hardware is emulated. This only runs VMs
+through the Linux's KVM interface. What makes crosvm unique is a focus on
+safety within the programming language and a sandbox around the virtual
+devices to protect the kernel from attack in case of an exploit in the
+devices.
+
+## Building with Docker
+
+See the [README](docker/README.md) from the `docker` subdirectory to learn how
+to build crosvm in enviroments outside of the Chrome OS chroot.
+
+## Usage
+
+To see the usage information for your version of crosvm, run `crosvm` or `crosvm
+run --help`.
+
+### Boot a Kernel
+
+To run a very basic VM with just a kernel and default devices:
+
+```bash
+$ crosvm run "${KERNEL_PATH}"
+```
+
+The uncompressed kernel image, also known as vmlinux, can be found in your kernel
+build directory in the case of x86 at `arch/x86/boot/compressed/vmlinux`.
+
+### Rootfs
+
+In most cases, you will want to give the VM a virtual block device to use as a
+root file system:
+
+```bash
+$ crosvm run -r "${ROOT_IMAGE}" "${KERNEL_PATH}"
+```
+
+The root image must be a path to a disk image formatted in a way that the kernel
+can read. Typically this is a squashfs image made with `mksquashfs` or an ext4
+image made with `mkfs.ext4`. By using the `-r` argument, the kernel is
+automatically told to use that image as the root, and therefore can only be
+given once. More disks can be given with `-d` or `--rwdisk` if a writable disk
+is desired.
+
+To run crosvm with a writable rootfs:
+
+>**WARNING:** Writable disks are at risk of corruption by a malicious or
+malfunctioning guest OS.
+```bash
+crosvm run --rwdisk "${ROOT_IMAGE}" -p "root=/dev/vda" vmlinux
+```
+>**NOTE:** If more disks arguments are added prior to the desired rootfs image,
+the `root=/dev/vda` must be adjusted to the appropriate letter.
+
+### Control Socket
+
+If the control socket was enabled with `-s`, the main process can be controlled
+while crosvm is running. To tell crosvm to stop and exit, for example:
+
+>**NOTE:** If the socket path given is for a directory, a socket name underneath
+that path will be generated based on crosvm's PID.
+```bash
+$ crosvm run -s /run/crosvm.sock ${USUAL_CROSVM_ARGS}
+ <in another shell>
+$ crosvm stop /run/crosvm.sock
+```
+>**WARNING:** The guest OS will not be notified or gracefully shutdown.
+
+This will cause the original crosvm process to exit in an orderly fashion,
+allowing it to clean up any OS resources that might have stuck around if crosvm
+were terminated early.
+
+### Multiprocess Mode
+
+By default crosvm runs in multiprocess mode. Each device that supports running
+inside of a sandbox will run in a jailed child process of crosvm. The
+appropriate minijail seccomp policy files must be present either in
+`/usr/share/policy/crosvm` or in the path specified by the
+`--seccomp-policy-dir` argument. The sandbox can be disabled for testing with
+the `--disable-sandbox` option.
+
+### Virtio Wayland
+
+Virtio Wayland support requires special support on the part of the guest and as
+such is unlikely to work out of the box unless you are using a Chrome OS kernel
+along with a `termina` rootfs.
+
+To use it, ensure that the `XDG_RUNTIME_DIR` enviroment variable is set and that
+the path `$XDG_RUNTIME_DIR/wayland-0` points to the socket of the Wayland
+compositor you would like the guest to use.
+
+## Defaults
+
+The following are crosvm's default arguments and how to override them.
+
+* 256MB of memory (set with `-m`)
+* 1 virtual CPU (set with `-c`)
+* no block devices (set with `-r`, `-d`, or `--rwdisk`)
+* no network (set with `--host_ip`, `--netmask`, and `--mac`)
+* virtio wayland support if `XDG_RUNTIME_DIR` enviroment variable is set (disable with `--no-wl`)
+* only the kernel arguments necessary to run with the supported devices (add more with `-p`)
+* run in multiprocess mode (run in single process mode with `--disable-sandbox`)
+* no control socket (set with `-s`)
+
+## System Requirements
+
+A Linux kernel with KVM support (check for `/dev/kvm`) is required to run
+crosvm. In order to run certain devices, there are additional system
+requirements:
+
+* `virtio-wayland` - The `memfd_create` syscall, introduced in Linux 3.17, and a Wayland compositor.
+* `vsock` - Host Linux kernel with vhost-vsock support, introduced in Linux 4.8.
+* `multiprocess` - Host Linux kernel with seccomp-bpf and Linux namespacing support.
+* `virtio-net` - Host Linux kernel with TUN/TAP support (check for `/dev/net/tun`) and running with `CAP_NET_ADMIN` privileges.
+
+## Emulated Devices
+
+| Device | Description |
+|------------------|------------------------------------------------------------------------------------|
+| `CMOS/RTC` | Used to get the current calendar time. |
+| `i8042` | Used by the guest kernel to exit crosvm. |
+| `serial` | x86 I/O port driven serial devices that print to stdout and take input from stdin. |
+| `virtio-block` | Basic read/write block device. |
+| `virtio-net` | Device to interface the host and guest networks. |
+| `virtio-rng` | Entropy source used to seed guest OS's entropy pool. |
+| `virtio-vsock` | Enabled VSOCKs for the guests. |
+| `virtio-wayland` | Allowed guest to use host Wayland socket. |
+
+## Contributing
+
+### Code Health
+
+#### `build_test`
+
+There are no automated tests run before code is committed to crosvm. In order to
+maintain sanity, please execute `build_test` before submitting code for review.
+All tests should be passing or ignored and there should be no compiler warnings
+or errors. All supported architectures are built, but only tests for x86_64 are
+run. In order to build everything without failures, sysroots must be supplied
+for each architecture. See `build_test -h` for more information.
+
+#### `rustfmt`
+
+All code should be formatted with `rustfmt`. We have a script that applies
+rustfmt to all Rust code in the crosvm repo: please run `bin/fmt` before
+checking in a change. This is different from `cargo fmt --all` which formats
+multiple crates but a single workspace only; crosvm consists of multiple
+workspaces.
+
+#### Dependencies
+
+With a few exceptions, external dependencies inside of the `Cargo.toml` files
+are not allowed. The reason being that community made crates tend to explode the
+binary size by including dozens of transitive dependencies. All these
+dependencies also must be reviewed to ensure their suitability to the crosvm
+project. Currently allowed crates are:
+
+* `byteorder` - A very small library used for endian swaps.
+* `cc` - Build time dependency needed to build C source code used in crosvm.
+* `libc` - Required to use the standard library, this crate is a simple wrapper around `libc`'s symbols.
+
+### Code Overview
+
+The crosvm source code is written in Rust and C. To build, crosvm generally
+requires the most recent stable version of rustc.
+
+Source code is organized into crates, each with their own unit tests. These
+crates are:
+
+* `crosvm` - The top-level binary front-end for using crosvm.
+* `devices` - Virtual devices exposed to the guest OS.
+* `io_jail` - Creates jailed process using `libminijail`.
+* `kernel_loader` - Loads elf64 kernel files to a slice of memory.
+* `kvm_sys` - Low-level (mostly) auto-generated structures and constants for using KVM.
+* `kvm` - Unsafe, low-level wrapper code for using `kvm_sys`.
+* `net_sys` - Low-level (mostly) auto-generated structures and constants for creating TUN/TAP devices.
+* `net_util` - Wrapper for creating TUN/TAP devices.
+* `sys_util` - Mostly safe wrappers for small system facilities such as `eventfd` or `syslog`.
+* `syscall_defines` - Lists of syscall numbers in each architecture used to make syscalls not supported in `libc`.
+* `vhost` - Wrappers for creating vhost based devices.
+* `virtio_sys` - Low-level (mostly) auto-generated structures and constants for interfacing with kernel vhost support.
+* `vm_control` - IPC for the VM.
+* `x86_64` - Support code specific to 64 bit intel machines.
+
+The `seccomp` folder contains minijail seccomp policy files for each sandboxed
+device. Because some syscalls vary by architecture, the seccomp policies are
+split by architecture.
diff --git a/aarch64/Cargo.toml b/aarch64/Cargo.toml
new file mode 100644
index 0000000..8c754d1
--- /dev/null
+++ b/aarch64/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "aarch64"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+arch = { path = "../arch" }
+data_model = { path = "../data_model" }
+devices = { path = "../devices" }
+io_jail = { path = "../io_jail" }
+kernel_cmdline = { path = "../kernel_cmdline" }
+kvm = { path = "../kvm" }
+kvm_sys = { path = "../kvm_sys" }
+libc = "*"
+remain = "*"
+resources = { path = "../resources" }
+sync = { path = "../sync" }
+sys_util = { path = "../sys_util" }
diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs
new file mode 100644
index 0000000..753ca0f
--- /dev/null
+++ b/aarch64/src/fdt.rs
@@ -0,0 +1,369 @@
+// Copyright 2018 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::CStr;
+use std::fs::File;
+
+use arch::fdt::{
+ begin_node, end_node, finish_fdt, generate_prop32, generate_prop64, property, property_cstring,
+ property_null, property_string, property_u32, property_u64, start_fdt, Error, Result,
+};
+use devices::{PciInterruptPin, SERIAL_ADDR};
+use sys_util::{GuestAddress, GuestMemory};
+
+// This is the start of DRAM in the physical address space.
+use crate::AARCH64_PHYS_MEM_START;
+
+// These are GIC address-space location constants.
+use crate::AARCH64_GIC_CPUI_BASE;
+use crate::AARCH64_GIC_CPUI_SIZE;
+use crate::AARCH64_GIC_DIST_BASE;
+use crate::AARCH64_GIC_DIST_SIZE;
+
+// These are RTC related constants
+use crate::AARCH64_RTC_ADDR;
+use crate::AARCH64_RTC_IRQ;
+use crate::AARCH64_RTC_SIZE;
+use devices::pl030::PL030_AMBA_ID;
+
+// These are serial device related constants.
+use crate::AARCH64_SERIAL_1_3_IRQ;
+use crate::AARCH64_SERIAL_2_4_IRQ;
+use crate::AARCH64_SERIAL_SIZE;
+use crate::AARCH64_SERIAL_SPEED;
+
+// These are related to guest virtio devices.
+use crate::AARCH64_IRQ_BASE;
+use crate::AARCH64_MMIO_BASE;
+use crate::AARCH64_MMIO_SIZE;
+use crate::AARCH64_PCI_CFG_BASE;
+use crate::AARCH64_PCI_CFG_SIZE;
+
+// This is an arbitrary number to specify the node for the GIC.
+// If we had a more complex interrupt architecture, then we'd need an enum for
+// these.
+const PHANDLE_GIC: u32 = 1;
+
+// These are specified by the Linux GIC bindings
+const GIC_FDT_IRQ_NUM_CELLS: u32 = 3;
+const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
+const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
+const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8;
+const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = (0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT);
+const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001;
+const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004;
+const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008;
+
+fn create_memory_node(fdt: &mut Vec<u8>, guest_mem: &GuestMemory) -> Result<()> {
+ let mem_size = guest_mem.memory_size();
+ let mem_reg_prop = generate_prop64(&[AARCH64_PHYS_MEM_START, mem_size]);
+
+ begin_node(fdt, "memory")?;
+ property_string(fdt, "device_type", "memory")?;
+ property(fdt, "reg", &mem_reg_prop)?;
+ end_node(fdt)?;
+ Ok(())
+}
+
+fn create_cpu_nodes(fdt: &mut Vec<u8>, num_cpus: u32) -> Result<()> {
+ begin_node(fdt, "cpus")?;
+ property_u32(fdt, "#address-cells", 0x1)?;
+ property_u32(fdt, "#size-cells", 0x0)?;
+
+ for cpu_id in 0..num_cpus {
+ let cpu_name = format!("cpu@{:x}", cpu_id);
+ begin_node(fdt, &cpu_name)?;
+ property_string(fdt, "device_type", "cpu")?;
+ property_string(fdt, "compatible", "arm,arm-v8")?;
+ if num_cpus > 1 {
+ property_string(fdt, "enable-method", "psci")?;
+ }
+ property_u32(fdt, "reg", cpu_id)?;
+ end_node(fdt)?;
+ }
+ end_node(fdt)?;
+ Ok(())
+}
+
+fn create_gic_node(fdt: &mut Vec<u8>) -> Result<()> {
+ let gic_reg_prop = generate_prop64(&[
+ AARCH64_GIC_DIST_BASE,
+ AARCH64_GIC_DIST_SIZE,
+ AARCH64_GIC_CPUI_BASE,
+ AARCH64_GIC_CPUI_SIZE,
+ ]);
+
+ begin_node(fdt, "intc")?;
+ property_string(fdt, "compatible", "arm,cortex-a15-gic")?;
+ property_u32(fdt, "#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)?;
+ property_null(fdt, "interrupt-controller")?;
+ property(fdt, "reg", &gic_reg_prop)?;
+ property_u32(fdt, "phandle", PHANDLE_GIC)?;
+ property_u32(fdt, "#address-cells", 2)?;
+ property_u32(fdt, "#size-cells", 2)?;
+ end_node(fdt)?;
+
+ Ok(())
+}
+
+fn create_timer_node(fdt: &mut Vec<u8>, num_cpus: u32) -> Result<()> {
+ // These are fixed interrupt numbers for the timer device.
+ let irqs = [13, 14, 11, 10];
+ let compatible = "arm,armv8-timer";
+ let cpu_mask: u32 =
+ (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
+
+ let mut timer_reg_cells = Vec::new();
+ for &irq in &irqs {
+ timer_reg_cells.push(GIC_FDT_IRQ_TYPE_PPI);
+ timer_reg_cells.push(irq);
+ timer_reg_cells.push(cpu_mask | IRQ_TYPE_LEVEL_LOW);
+ }
+ let timer_reg_prop = generate_prop32(timer_reg_cells.as_slice());
+
+ begin_node(fdt, "timer")?;
+ property_string(fdt, "compatible", compatible)?;
+ property(fdt, "interrupts", &timer_reg_prop)?;
+ property_null(fdt, "always-on")?;
+ end_node(fdt)?;
+
+ Ok(())
+}
+
+fn create_serial_node(fdt: &mut Vec<u8>, addr: u64, irq: u32) -> Result<()> {
+ let serial_reg_prop = generate_prop64(&[addr, AARCH64_SERIAL_SIZE]);
+ let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING]);
+
+ begin_node(fdt, &format!("U6_16550A@{:x}", addr))?;
+ property_string(fdt, "compatible", "ns16550a")?;
+ property(fdt, "reg", &serial_reg_prop)?;
+ property_u32(fdt, "clock-frequency", AARCH64_SERIAL_SPEED)?;
+ property(fdt, "interrupts", &irq)?;
+ end_node(fdt)?;
+
+ Ok(())
+}
+
+fn create_serial_nodes(fdt: &mut Vec<u8>) -> Result<()> {
+ // Note that SERIAL_ADDR contains the I/O port addresses conventionally used
+ // for serial ports on x86. This uses the same addresses (but on the MMIO bus)
+ // to simplify the shared serial code.
+ create_serial_node(fdt, SERIAL_ADDR[0], AARCH64_SERIAL_1_3_IRQ)?;
+ create_serial_node(fdt, SERIAL_ADDR[1], AARCH64_SERIAL_2_4_IRQ)?;
+ create_serial_node(fdt, SERIAL_ADDR[2], AARCH64_SERIAL_1_3_IRQ)?;
+ create_serial_node(fdt, SERIAL_ADDR[3], AARCH64_SERIAL_2_4_IRQ)?;
+
+ 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";
+ begin_node(fdt, "psci")?;
+ 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(())
+}
+
+fn create_chosen_node(
+ fdt: &mut Vec<u8>,
+ cmdline: &CStr,
+ initrd: Option<(GuestAddress, usize)>,
+) -> Result<()> {
+ begin_node(fdt, "chosen")?;
+ property_u32(fdt, "linux,pci-probe-only", 1)?;
+ property_cstring(fdt, "bootargs", cmdline)?;
+ property_u64(fdt, "kaslr", 0)?;
+ if let Some((initrd_addr, initrd_size)) = initrd {
+ let initrd_start = initrd_addr.offset() as u32;
+ let initrd_end = initrd_start + initrd_size as u32;
+ property_u32(fdt, "linux,initrd-start", initrd_start)?;
+ property_u32(fdt, "linux,initrd-end", initrd_end)?;
+ }
+ end_node(fdt)?;
+
+ Ok(())
+}
+
+fn create_pci_nodes(
+ fdt: &mut Vec<u8>,
+ pci_irqs: Vec<(u32, PciInterruptPin)>,
+ pci_device_base: u64,
+ pci_device_size: u64,
+) -> Result<()> {
+ // Add devicetree nodes describing a PCI generic host controller.
+ // See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
+ // and "PCI Bus Binding to IEEE Std 1275-1994".
+ let ranges = generate_prop32(&[
+ // mmio addresses
+ 0x3000000, // (ss = 11: 64-bit memory space)
+ (AARCH64_MMIO_BASE >> 32) as u32, // PCI address
+ AARCH64_MMIO_BASE as u32,
+ (AARCH64_MMIO_BASE >> 32) as u32, // CPU address
+ AARCH64_MMIO_BASE as u32,
+ (AARCH64_MMIO_SIZE >> 32) as u32, // size
+ AARCH64_MMIO_SIZE as u32,
+ // device addresses
+ 0x3000000, // (ss = 11: 64-bit memory space)
+ (pci_device_base >> 32) as u32, // PCI address
+ pci_device_base as u32,
+ (pci_device_base >> 32) as u32, // CPU address
+ pci_device_base as u32,
+ (pci_device_size >> 32) as u32, // size
+ pci_device_size as u32,
+ ]);
+ let bus_range = generate_prop32(&[0, 0]); // Only bus 0
+ let reg = generate_prop64(&[AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE]);
+
+ let mut interrupts: Vec<u32> = Vec::new();
+ let mut masks: Vec<u32> = Vec::new();
+
+ for (i, pci_irq) in pci_irqs.iter().enumerate() {
+ // PCI_DEVICE(3)
+ interrupts.push((pci_irq.0 + 1) << 11);
+ interrupts.push(0);
+ interrupts.push(0);
+
+ // INT#(1)
+ interrupts.push(pci_irq.1.to_mask() + 1);
+
+ // CONTROLLER(PHANDLE)
+ interrupts.push(PHANDLE_GIC);
+ interrupts.push(0);
+ interrupts.push(0);
+
+ // CONTROLLER_DATA(3)
+ interrupts.push(GIC_FDT_IRQ_TYPE_SPI);
+ interrupts.push(AARCH64_IRQ_BASE + i as u32);
+ interrupts.push(IRQ_TYPE_LEVEL_HIGH);
+
+ // PCI_DEVICE(3)
+ masks.push(0xf800); // bits 11..15 (device)
+ masks.push(0);
+ masks.push(0);
+
+ // INT#(1)
+ masks.push(0x7); // allow INTA#-INTD# (1 | 2 | 3 | 4)
+ }
+
+ let interrupt_map = generate_prop32(&interrupts);
+ let interrupt_map_mask = generate_prop32(&masks);
+
+ begin_node(fdt, "pci")?;
+ property_string(fdt, "compatible", "pci-host-cam-generic")?;
+ property_string(fdt, "device_type", "pci")?;
+ property(fdt, "ranges", &ranges)?;
+ property(fdt, "bus-range", &bus_range)?;
+ property_u32(fdt, "#address-cells", 3)?;
+ property_u32(fdt, "#size-cells", 2)?;
+ property(fdt, "reg", ®)?;
+ property_u32(fdt, "#interrupt-cells", 1)?;
+ property(fdt, "interrupt-map", &interrupt_map)?;
+ property(fdt, "interrupt-map-mask", &interrupt_map_mask)?;
+ property_null(fdt, "dma-coherent")?;
+ end_node(fdt)?;
+
+ Ok(())
+}
+
+fn create_rtc_node(fdt: &mut Vec<u8>) -> Result<()> {
+ // the kernel driver for pl030 really really wants a clock node
+ // associated with an AMBA device or it will fail to probe, so we
+ // need to make up a clock node to associate with the pl030 rtc
+ // node and an associated handle with a unique phandle value.
+ const CLK_PHANDLE: u32 = 24;
+ begin_node(fdt, "pclk@3M")?;
+ property_u32(fdt, "#clock-cells", 0)?;
+ property_string(fdt, "compatible", "fixed-clock")?;
+ property_u32(fdt, "clock-frequency", 3141592)?;
+ property_u32(fdt, "phandle", CLK_PHANDLE)?;
+ end_node(fdt)?;
+
+ let rtc_name = format!("rtc@{:x}", AARCH64_RTC_ADDR);
+ let reg = generate_prop64(&[AARCH64_RTC_ADDR, AARCH64_RTC_SIZE]);
+ let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, AARCH64_RTC_IRQ, IRQ_TYPE_LEVEL_HIGH]);
+
+ begin_node(fdt, &rtc_name)?;
+ property_string(fdt, "compatible", "arm,primecell")?;
+ property_u32(fdt, "arm,primecell-periphid", PL030_AMBA_ID)?;
+ property(fdt, "reg", ®)?;
+ property(fdt, "interrupts", &irq)?;
+ property_u32(fdt, "clocks", CLK_PHANDLE)?;
+ property_string(fdt, "clock-names", "apb_pclk")?;
+ end_node(fdt)?;
+ Ok(())
+}
+
+/// Creates a flattened device tree containing all of the parameters for the
+/// kernel and loads it into the guest memory at the specified offset.
+///
+/// # Arguments
+///
+/// * `fdt_max_size` - The amount of space reserved for the device tree
+/// * `guest_mem` - The guest memory object
+/// * `pci_irqs` - List of PCI device number to PCI interrupt pin mappings
+/// * `num_cpus` - Number of virtual CPUs the guest will have
+/// * `fdt_load_offset` - The offset into physical memory for the device tree
+/// * `pci_device_base` - The offset into physical memory for PCI device memory
+/// * `pci_device_size` - The size of PCI device memory
+/// * `cmdline` - The kernel commandline
+/// * `initrd` - An optional tuple of initrd guest physical address and size
+/// * `android_fstab` - An optional file holding Android fstab entries
+pub fn create_fdt(
+ fdt_max_size: usize,
+ guest_mem: &GuestMemory,
+ pci_irqs: Vec<(u32, PciInterruptPin)>,
+ num_cpus: u32,
+ fdt_load_offset: u64,
+ pci_device_base: u64,
+ pci_device_size: u64,
+ cmdline: &CStr,
+ initrd: Option<(GuestAddress, usize)>,
+ android_fstab: Option<File>,
+) -> Result<()> {
+ let mut fdt = vec![0; fdt_max_size];
+ start_fdt(&mut fdt, fdt_max_size)?;
+
+ // The whole thing is put into one giant node with some top level properties
+ begin_node(&mut fdt, "")?;
+ property_u32(&mut fdt, "interrupt-parent", PHANDLE_GIC)?;
+ property_string(&mut fdt, "compatible", "linux,dummy-virt")?;
+ property_u32(&mut fdt, "#address-cells", 0x2)?;
+ property_u32(&mut fdt, "#size-cells", 0x2)?;
+ if let Some(android_fstab) = android_fstab {
+ arch::android::create_android_fdt(&mut fdt, android_fstab)?;
+ }
+ create_chosen_node(&mut fdt, cmdline, initrd)?;
+ create_memory_node(&mut fdt, guest_mem)?;
+ create_cpu_nodes(&mut fdt, num_cpus)?;
+ create_gic_node(&mut fdt)?;
+ create_timer_node(&mut fdt, num_cpus)?;
+ create_serial_nodes(&mut fdt)?;
+ create_psci_node(&mut fdt)?;
+ create_pci_nodes(&mut fdt, pci_irqs, pci_device_base, pci_device_size)?;
+ create_rtc_node(&mut fdt)?;
+ // End giant node
+ end_node(&mut fdt)?;
+
+ // Allocate another buffer so we can format and then write fdt to guest
+ let mut fdt_final = vec![0; fdt_max_size];
+ finish_fdt(&mut fdt, &mut fdt_final, fdt_max_size)?;
+
+ let fdt_address = GuestAddress(AARCH64_PHYS_MEM_START + fdt_load_offset);
+ let written = guest_mem
+ .write_at_addr(fdt_final.as_slice(), fdt_address)
+ .map_err(|_| Error::FdtGuestMemoryWriteError)?;
+ if written < fdt_max_size {
+ return Err(Error::FdtGuestMemoryWriteError);
+ }
+ Ok(())
+}
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
new file mode 100644
index 0000000..d21d70d
--- /dev/null
+++ b/aarch64/src/lib.rs
@@ -0,0 +1,548 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::collections::BTreeMap;
+use std::error::Error as StdError;
+use std::ffi::{CStr, CString};
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io;
+use std::os::unix::io::FromRawFd;
+use std::sync::Arc;
+
+use arch::{RunnableLinuxVm, VmComponents, VmImage};
+use devices::{
+ get_serial_tty_string, Bus, BusError, PciConfigMmio, PciDevice, PciInterruptPin,
+ SerialParameters,
+};
+use io_jail::Minijail;
+use remain::sorted;
+use resources::SystemAllocator;
+use sync::Mutex;
+use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError};
+
+use kvm::*;
+use kvm_sys::kvm_device_attr;
+
+mod fdt;
+
+// We place the kernel at offset 8MB
+const AARCH64_KERNEL_OFFSET: u64 = 0x80000;
+const AARCH64_FDT_MAX_SIZE: u64 = 0x200000;
+const AARCH64_INITRD_ALIGN: u64 = 0x1000000;
+
+// These constants indicate the address space used by the ARM vGIC.
+const AARCH64_GIC_DIST_SIZE: u64 = 0x10000;
+const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000;
+
+// This indicates the start of DRAM inside the physical address space.
+const AARCH64_PHYS_MEM_START: u64 = 0x80000000;
+const AARCH64_AXI_BASE: u64 = 0x40000000;
+
+// These constants indicate the placement of the GIC registers in the physical
+// address space.
+const AARCH64_GIC_DIST_BASE: u64 = AARCH64_AXI_BASE - AARCH64_GIC_DIST_SIZE;
+const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE;
+
+// This is the minimum number of SPI interrupts aligned to 32 + 32 for the
+// PPI (16) and GSI (16).
+const AARCH64_GIC_NR_IRQS: u32 = 64;
+
+// PSR (Processor State Register) bits
+const PSR_MODE_EL1H: u64 = 0x00000005;
+const PSR_F_BIT: u64 = 0x00000040;
+const PSR_I_BIT: u64 = 0x00000080;
+const PSR_A_BIT: u64 = 0x00000100;
+const PSR_D_BIT: u64 = 0x00000200;
+
+macro_rules! offset__of {
+ ($str:ty, $($field:ident).+ $([$idx:expr])*) => {
+ unsafe { &(*(0 as *const $str))$(.$field)* $([$idx])* as *const _ as usize }
+ }
+}
+
+const KVM_REG_ARM64: u64 = 0x6000000000000000;
+const KVM_REG_SIZE_U64: u64 = 0x0030000000000000;
+const KVM_REG_ARM_COPROC_SHIFT: u64 = 16;
+const KVM_REG_ARM_CORE: u64 = 0x0010 << KVM_REG_ARM_COPROC_SHIFT;
+
+macro_rules! arm64_core_reg {
+ ($reg: tt) => {
+ KVM_REG_ARM64
+ | KVM_REG_SIZE_U64
+ | KVM_REG_ARM_CORE
+ | ((offset__of!(kvm_sys::user_pt_regs, $reg) / 4) as u64)
+ };
+}
+
+fn get_kernel_addr() -> GuestAddress {
+ GuestAddress(AARCH64_PHYS_MEM_START + AARCH64_KERNEL_OFFSET)
+}
+
+// Serial device requires 8 bytes of registers;
+const AARCH64_SERIAL_SIZE: u64 = 0x8;
+// This was the speed kvmtool used, not sure if it matters.
+const AARCH64_SERIAL_SPEED: u32 = 1843200;
+// The serial device gets the first interrupt line
+// Which gets mapped to the first SPI interrupt (physical 32).
+const AARCH64_SERIAL_1_3_IRQ: u32 = 0;
+const AARCH64_SERIAL_2_4_IRQ: u32 = 2;
+
+// Place the RTC device at page 2
+const AARCH64_RTC_ADDR: u64 = 0x2000;
+// The RTC device gets one 4k page
+const AARCH64_RTC_SIZE: u64 = 0x1000;
+// The RTC device gets the second interrupt line
+const AARCH64_RTC_IRQ: u32 = 1;
+
+// PCI MMIO configuration region base address.
+const AARCH64_PCI_CFG_BASE: u64 = 0x10000;
+// PCI MMIO configuration region size.
+const AARCH64_PCI_CFG_SIZE: u64 = 0x1000000;
+// This is the base address of MMIO devices.
+const AARCH64_MMIO_BASE: u64 = 0x1010000;
+// Size of the whole MMIO region.
+const AARCH64_MMIO_SIZE: u64 = 0x100000;
+// Virtio devices start at SPI interrupt number 3
+const AARCH64_IRQ_BASE: u32 = 3;
+
+#[sorted]
+#[derive(Debug)]
+pub enum Error {
+ CloneEventFd(sys_util::Error),
+ Cmdline(kernel_cmdline::Error),
+ CreateDevices(Box<dyn StdError>),
+ CreateEventFd(sys_util::Error),
+ CreateFdt(arch::fdt::Error),
+ CreateGICFailure(sys_util::Error),
+ CreateKvm(sys_util::Error),
+ CreatePciRoot(arch::DeviceRegistrationError),
+ CreateSerialDevices(arch::DeviceRegistrationError),
+ CreateSocket(io::Error),
+ CreateVcpu(sys_util::Error),
+ CreateVm(sys_util::Error),
+ InitrdLoadFailure(arch::LoadImageError),
+ KernelLoadFailure(arch::LoadImageError),
+ KernelMissing,
+ ReadPreferredTarget(sys_util::Error),
+ RegisterIrqfd(sys_util::Error),
+ RegisterPci(BusError),
+ RegisterVsock(arch::DeviceRegistrationError),
+ SetDeviceAttr(sys_util::Error),
+ SetReg(sys_util::Error),
+ SetupGuestMemory(GuestMemoryError),
+ VcpuInit(sys_util::Error),
+}
+
+impl Display for Error {
+ #[remain::check]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ #[sorted]
+ match self {
+ CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
+ Cmdline(e) => write!(f, "the given kernel command line was invalid: {}", e),
+ CreateDevices(e) => write!(f, "error creating devices: {}", e),
+ CreateEventFd(e) => write!(f, "unable to make an EventFd: {}", e),
+ CreateFdt(e) => write!(f, "FDT could not be created: {}", e),
+ CreateGICFailure(e) => write!(f, "failed to create GIC: {}", e),
+ CreateKvm(e) => write!(f, "failed to open /dev/kvm: {}", e),
+ CreatePciRoot(e) => write!(f, "failed to create a PCI root hub: {}", e),
+ CreateSerialDevices(e) => write!(f, "unable to create serial devices: {}", e),
+ CreateSocket(e) => write!(f, "failed to create socket: {}", e),
+ CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
+ CreateVm(e) => write!(f, "failed to create vm: {}", e),
+ InitrdLoadFailure(e) => write!(f, "initrd cound not be loaded: {}", e),
+ KernelLoadFailure(e) => write!(f, "kernel cound not be loaded: {}", e),
+ KernelMissing => write!(f, "aarch64 requires a kernel"),
+ ReadPreferredTarget(e) => write!(f, "failed to read preferred target: {}", e),
+ RegisterIrqfd(e) => write!(f, "failed to register irq fd: {}", e),
+ RegisterPci(e) => write!(f, "error registering PCI bus: {}", e),
+ RegisterVsock(e) => write!(f, "error registering virtual socket device: {}", e),
+ SetDeviceAttr(e) => write!(f, "failed to set device attr: {}", e),
+ SetReg(e) => write!(f, "failed to set register: {}", e),
+ SetupGuestMemory(e) => write!(f, "failed to set up guest memory: {}", e),
+ VcpuInit(e) => write!(f, "failed to initialize VCPU: {}", e),
+ }
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+/// Returns a Vec of the valid memory addresses.
+/// These should be used to configure the GuestMemory structure for the platfrom.
+pub fn arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)> {
+ vec![(GuestAddress(AARCH64_PHYS_MEM_START), size)]
+}
+
+fn fdt_offset(mem_size: u64) -> u64 {
+ // Put fdt up near the top of memory
+ // TODO(sonnyrao): will have to handle this differently if there's
+ // > 4GB memory
+ mem_size - AARCH64_FDT_MAX_SIZE - 0x10000
+}
+
+pub struct AArch64;
+
+impl arch::LinuxArch for AArch64 {
+ type Error = Error;
+
+ fn build_vm<F, E>(
+ mut components: VmComponents,
+ _split_irqchip: bool,
+ serial_parameters: &BTreeMap<u8, SerialParameters>,
+ create_devices: F,
+ ) -> Result<RunnableLinuxVm>
+ where
+ F: FnOnce(
+ &GuestMemory,
+ &mut Vm,
+ &mut SystemAllocator,
+ &EventFd,
+ ) -> std::result::Result<Vec<(Box<dyn PciDevice>, Option<Minijail>)>, E>,
+ E: StdError + 'static,
+ {
+ let mut resources =
+ Self::get_resource_allocator(components.memory_size, components.wayland_dmabuf);
+ let mem = Self::setup_memory(components.memory_size)?;
+ let kvm = Kvm::new().map_err(Error::CreateKvm)?;
+ let mut vm = Vm::new(&kvm, mem.clone()).map_err(Error::CreateVm)?;
+
+ let vcpu_count = components.vcpu_count;
+ let mut vcpus = Vec::with_capacity(vcpu_count as usize);
+ for cpu_id in 0..vcpu_count {
+ let vcpu = Vcpu::new(cpu_id as libc::c_ulong, &kvm, &vm).map_err(Error::CreateVcpu)?;
+ Self::configure_vcpu(
+ vm.get_memory(),
+ &kvm,
+ &vm,
+ &vcpu,
+ cpu_id as u64,
+ vcpu_count as u64,
+ )?;
+ vcpus.push(vcpu);
+ }
+
+ let vcpu_affinity = components.vcpu_affinity;
+
+ let irq_chip = Self::create_irq_chip(&vm)?;
+
+ let mut mmio_bus = devices::Bus::new();
+
+ let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
+
+ let pci_devices = create_devices(&mem, &mut vm, &mut resources, &exit_evt)
+ .map_err(|e| Error::CreateDevices(Box::new(e)))?;
+ let (pci, pci_irqs, pid_debug_label_map) =
+ arch::generate_pci_root(pci_devices, &mut mmio_bus, &mut resources, &mut vm)
+ .map_err(Error::CreatePciRoot)?;
+ let pci_bus = Arc::new(Mutex::new(PciConfigMmio::new(pci)));
+
+ // ARM doesn't really use the io bus like x86, so just create an empty bus.
+ let io_bus = devices::Bus::new();
+
+ Self::add_arch_devs(&mut vm, &mut mmio_bus)?;
+
+ let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
+ let com_evt_2_4 = EventFd::new().map_err(Error::CreateEventFd)?;
+ let (stdio_serial_num, stdio_serial) = arch::add_serial_devices(
+ &mut mmio_bus,
+ &com_evt_1_3,
+ &com_evt_2_4,
+ &serial_parameters,
+ )
+ .map_err(Error::CreateSerialDevices)?;
+
+ vm.register_irqfd(&com_evt_1_3, AARCH64_SERIAL_1_3_IRQ)
+ .map_err(Error::RegisterIrqfd)?;
+ vm.register_irqfd(&com_evt_2_4, AARCH64_SERIAL_2_4_IRQ)
+ .map_err(Error::RegisterIrqfd)?;
+
+ mmio_bus
+ .insert(
+ pci_bus.clone(),
+ AARCH64_PCI_CFG_BASE,
+ AARCH64_PCI_CFG_SIZE,
+ false,
+ )
+ .map_err(Error::RegisterPci)?;
+
+ let mut cmdline = Self::get_base_linux_cmdline(stdio_serial_num);
+ for param in components.extra_kernel_params {
+ cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
+ }
+
+ let kernel_image = if let VmImage::Kernel(ref mut img) = components.vm_image {
+ img
+ } else {
+ return Err(Error::KernelMissing);
+ };
+
+ // separate out kernel loading from other setup to get a specific error for
+ // kernel loading
+ 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;
+ Self::setup_system_memory(
+ &mem,
+ components.memory_size,
+ vcpu_count,
+ &CString::new(cmdline).unwrap(),
+ components.initrd_image,
+ pci_irqs,
+ components.android_fstab,
+ kernel_end,
+ )?;
+
+ Ok(RunnableLinuxVm {
+ vm,
+ kvm,
+ resources,
+ stdio_serial,
+ exit_evt,
+ vcpus,
+ vcpu_affinity,
+ irq_chip,
+ io_bus,
+ mmio_bus,
+ pid_debug_label_map,
+ })
+ }
+}
+
+impl AArch64 {
+ fn setup_system_memory(
+ mem: &GuestMemory,
+ mem_size: u64,
+ vcpu_count: u32,
+ cmdline: &CStr,
+ initrd_file: Option<File>,
+ pci_irqs: Vec<(u32, PciInterruptPin)>,
+ android_fstab: Option<File>,
+ kernel_end: u64,
+ ) -> Result<()> {
+ let initrd = match initrd_file {
+ Some(initrd_file) => {
+ let mut initrd_file = initrd_file;
+ let initrd_addr =
+ (kernel_end + (AARCH64_INITRD_ALIGN - 1)) & !(AARCH64_INITRD_ALIGN - 1);
+ let initrd_max_size = mem_size - (initrd_addr - AARCH64_PHYS_MEM_START);
+ let initrd_addr = GuestAddress(initrd_addr);
+ let initrd_size =
+ arch::load_image(mem, &mut initrd_file, initrd_addr, initrd_max_size)
+ .map_err(Error::InitrdLoadFailure)?;
+ Some((initrd_addr, initrd_size))
+ }
+ None => None,
+ };
+ let (pci_device_base, pci_device_size) = Self::get_device_addr_base_size(mem_size);
+ fdt::create_fdt(
+ AARCH64_FDT_MAX_SIZE as usize,
+ mem,
+ pci_irqs,
+ vcpu_count,
+ fdt_offset(mem_size),
+ pci_device_base,
+ pci_device_size,
+ cmdline,
+ initrd,
+ android_fstab,
+ )
+ .map_err(Error::CreateFdt)?;
+ Ok(())
+ }
+
+ fn setup_memory(mem_size: u64) -> Result<GuestMemory> {
+ let arch_mem_regions = arch_memory_regions(mem_size);
+ let mem = GuestMemory::new(&arch_mem_regions).map_err(Error::SetupGuestMemory)?;
+ Ok(mem)
+ }
+
+ fn get_device_addr_base_size(mem_size: u64) -> (u64, u64) {
+ let base = AARCH64_PHYS_MEM_START + mem_size;
+ let size = u64::max_value() - base;
+ (base, size)
+ }
+
+ /// This returns a base part of the kernel command for this architecture
+ fn get_base_linux_cmdline(stdio_serial_num: Option<u8>) -> kernel_cmdline::Cmdline {
+ let mut cmdline = kernel_cmdline::Cmdline::new(sys_util::pagesize());
+ if stdio_serial_num.is_some() {
+ let tty_string = get_serial_tty_string(stdio_serial_num.unwrap());
+ cmdline.insert("console", &tty_string).unwrap();
+ }
+ cmdline.insert_str("panic=-1").unwrap();
+ cmdline
+ }
+
+ /// Returns a system resource allocator.
+ fn get_resource_allocator(mem_size: u64, gpu_allocation: bool) -> SystemAllocator {
+ let (device_addr_base, device_addr_size) = Self::get_device_addr_base_size(mem_size);
+ SystemAllocator::builder()
+ .add_device_addresses(device_addr_base, device_addr_size)
+ .add_mmio_addresses(AARCH64_MMIO_BASE, AARCH64_MMIO_SIZE)
+ .create_allocator(AARCH64_IRQ_BASE, gpu_allocation)
+ .unwrap()
+ }
+
+ /// This adds any early platform devices for this architecture.
+ ///
+ /// # Arguments
+ ///
+ /// * `vm` - The vm to add irqs to.
+ /// * `bus` - The bus to add devices to.
+ fn add_arch_devs(vm: &mut Vm, bus: &mut Bus) -> Result<()> {
+ let rtc_evt = EventFd::new().map_err(Error::CreateEventFd)?;
+ vm.register_irqfd(&rtc_evt, AARCH64_RTC_IRQ)
+ .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)
+ .expect("failed to add rtc device");
+
+ Ok(())
+ }
+
+ /// The creates the interrupt controller device and optionally returns the fd for it.
+ /// Some architectures may not have a separate descriptor for the interrupt
+ /// controller, so they would return None even on success.
+ ///
+ /// # Arguments
+ ///
+ /// * `vm` - the vm object
+ fn create_irq_chip(vm: &Vm) -> Result<Option<File>> {
+ let cpu_if_addr: u64 = AARCH64_GIC_CPUI_BASE;
+ let dist_if_addr: u64 = AARCH64_GIC_DIST_BASE;
+ let raw_cpu_if_addr = &cpu_if_addr as *const u64;
+ let raw_dist_if_addr = &dist_if_addr as *const u64;
+
+ let cpu_if_attr = kvm_device_attr {
+ group: kvm_sys::KVM_DEV_ARM_VGIC_GRP_ADDR,
+ attr: kvm_sys::KVM_VGIC_V2_ADDR_TYPE_CPU as u64,
+ addr: raw_cpu_if_addr as u64,
+ flags: 0,
+ };
+ let dist_attr = kvm_device_attr {
+ group: kvm_sys::KVM_DEV_ARM_VGIC_GRP_ADDR,
+ attr: kvm_sys::KVM_VGIC_V2_ADDR_TYPE_DIST as u64,
+ addr: raw_dist_if_addr as u64,
+ flags: 0,
+ };
+ let mut kcd = kvm_sys::kvm_create_device {
+ type_: kvm_sys::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2,
+ fd: 0,
+ flags: 0,
+ };
+ vm.create_device(&mut kcd)
+ .map_err(|e| Error::CreateGICFailure(e))?;
+
+ // Safe because the kernel is passing us an FD back inside
+ // the struct after we successfully did the create_device ioctl
+ let vgic_fd = unsafe { File::from_raw_fd(kcd.fd as i32) };
+
+ // Safe because we allocated the struct that's being passed in
+ let ret = unsafe {
+ sys_util::ioctl_with_ref(&vgic_fd, kvm_sys::KVM_SET_DEVICE_ATTR(), &cpu_if_attr)
+ };
+ if ret != 0 {
+ return Err(Error::CreateGICFailure(sys_util::Error::new(ret)));
+ }
+
+ // Safe because we allocated the struct that's being passed in
+ let ret = unsafe {
+ sys_util::ioctl_with_ref(&vgic_fd, kvm_sys::KVM_SET_DEVICE_ATTR(), &dist_attr)
+ };
+ if ret != 0 {
+ return Err(Error::CreateGICFailure(sys_util::Error::new(ret)));
+ }
+
+ // We need to tell the kernel how many irqs to support with this vgic
+ let nr_irqs: u32 = AARCH64_GIC_NR_IRQS;
+ let nr_irqs_ptr = &nr_irqs as *const u32;
+ let nr_irqs_attr = kvm_device_attr {
+ group: kvm_sys::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
+ attr: 0,
+ addr: nr_irqs_ptr as u64,
+ flags: 0,
+ };
+ // Safe because we allocated the struct that's being passed in
+ let ret = unsafe {
+ sys_util::ioctl_with_ref(&vgic_fd, kvm_sys::KVM_SET_DEVICE_ATTR(), &nr_irqs_attr)
+ };
+ if ret != 0 {
+ return Err(Error::CreateGICFailure(sys_util::Error::new(ret)));
+ }
+
+ // Finalize the GIC
+ let init_gic_attr = kvm_device_attr {
+ group: kvm_sys::KVM_DEV_ARM_VGIC_GRP_CTRL,
+ attr: kvm_sys::KVM_DEV_ARM_VGIC_CTRL_INIT as u64,
+ addr: 0,
+ flags: 0,
+ };
+
+ // Safe because we allocated the struct that's being passed in
+ let ret = unsafe {
+ sys_util::ioctl_with_ref(&vgic_fd, kvm_sys::KVM_SET_DEVICE_ATTR(), &init_gic_attr)
+ };
+ if ret != 0 {
+ return Err(Error::SetDeviceAttr(sys_util::Error::new(ret)));
+ }
+ Ok(Some(vgic_fd))
+ }
+
+ fn configure_vcpu(
+ guest_mem: &GuestMemory,
+ _kvm: &Kvm,
+ vm: &Vm,
+ vcpu: &Vcpu,
+ cpu_id: u64,
+ _num_cpus: u64,
+ ) -> Result<()> {
+ let mut kvi = kvm_sys::kvm_vcpu_init {
+ target: kvm_sys::KVM_ARM_TARGET_GENERIC_V8,
+ features: [0; 7],
+ };
+
+ // This reads back the kernel's preferred target type.
+ vm.arm_preferred_target(&mut kvi)
+ .map_err(Error::ReadPreferredTarget)?;
+
+ // TODO(sonnyrao): need to verify this feature is supported by host kernel
+ kvi.features[0] |= 1 << kvm_sys::KVM_ARM_VCPU_PSCI_0_2;
+
+ // Non-boot cpus are powered off initially
+ if cpu_id > 0 {
+ kvi.features[0] |= 1 << kvm_sys::KVM_ARM_VCPU_POWER_OFF;
+ }
+ vcpu.arm_vcpu_init(&kvi).map_err(Error::VcpuInit)?;
+
+ // set up registers
+ let mut data: u64;
+ let mut reg_id: u64;
+
+ // All interrupts masked
+ data = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1H;
+ reg_id = arm64_core_reg!(pstate);
+ vcpu.set_one_reg(reg_id, data).map_err(Error::SetReg)?;
+
+ // Other cpus are powered off initially
+ if cpu_id == 0 {
+ data = AARCH64_PHYS_MEM_START + AARCH64_KERNEL_OFFSET;
+ reg_id = arm64_core_reg!(pc);
+ vcpu.set_one_reg(reg_id, data).map_err(Error::SetReg)?;
+
+ /* X0 -- fdt address */
+ let mem_size = guest_mem.memory_size();
+ data = (AARCH64_PHYS_MEM_START + fdt_offset(mem_size)) as u64;
+ // hack -- can't get this to do offsetof(regs[0]) but luckily it's at offset 0
+ reg_id = arm64_core_reg!(regs);
+ vcpu.set_one_reg(reg_id, data).map_err(Error::SetReg)?;
+ }
+ Ok(())
+ }
+}
diff --git a/arch/Cargo.toml b/arch/Cargo.toml
new file mode 100644
index 0000000..f562fca
--- /dev/null
+++ b/arch/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "arch"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+byteorder = "*"
+devices = { path = "../devices" }
+io_jail = { path = "../io_jail" }
+kernel_cmdline = { path = "../kernel_cmdline" }
+kvm = { path = "../kvm" }
+libc = "*"
+resources = { path = "../resources" }
+sync = { path = "../sync" }
+sys_util = { path = "../sys_util" }
diff --git a/arch/src/android.rs b/arch/src/android.rs
new file mode 100644
index 0000000..3d459d4
--- /dev/null
+++ b/arch/src/android.rs
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::fs::File;
+use std::io::BufRead;
+use std::io::BufReader;
+
+use crate::fdt::{begin_node, end_node, property_string, Error, Result};
+
+fn parse_fstab_line(line: &str) -> Result<Vec<String>> {
+ let vec: Vec<&str> = line.split_whitespace().collect();
+ if vec.len() != 5 {
+ return Err(Error::FdtFileParseError);
+ }
+ Ok(vec.iter().map(|s| s.to_string()).collect())
+}
+
+/// Creates a flattened device tree containing all of the parameters used
+/// by Android.
+///
+/// # Arguments
+///
+/// * `fdt` - The DTB to modify. The top-most node should be open.
+/// * `android-fstab` - A text file of Android fstab entries to add to the DTB
+pub fn create_android_fdt(fdt: &mut Vec<u8>, fstab: File) -> Result<()> {
+ let vecs = BufReader::new(fstab)
+ .lines()
+ .map(|l| parse_fstab_line(&l.map_err(Error::FdtIoError)?))
+ .collect::<Result<Vec<Vec<String>>>>()?;
+ begin_node(fdt, "firmware")?;
+ begin_node(fdt, "android")?;
+ property_string(fdt, "compatible", "android,firmware")?;
+ begin_node(fdt, "fstab")?;
+ property_string(fdt, "compatible", "android,fstab")?;
+ for vec in vecs {
+ let partition = &vec[1][1..];
+ begin_node(fdt, partition)?;
+ property_string(fdt, "compatible", &("android,".to_owned() + partition))?;
+ property_string(fdt, "dev", &vec[0])?;
+ property_string(fdt, "type", &vec[2])?;
+ property_string(fdt, "mnt_flags", &vec[3])?;
+ property_string(fdt, "fsmgr_flags", &vec[4])?;
+ end_node(fdt)?;
+ }
+ end_node(fdt)?; // fstab
+ end_node(fdt)?; // android
+ end_node(fdt)?; // firmware
+ Ok(())
+}
diff --git a/arch/src/fdt.rs b/arch/src/fdt.rs
new file mode 100644
index 0000000..71c791d
--- /dev/null
+++ b/arch/src/fdt.rs
@@ -0,0 +1,228 @@
+// Copyright 2018 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 byteorder::{BigEndian, ByteOrder};
+use libc::{c_char, c_int, c_void};
+use std::ffi::{CStr, CString};
+use std::fmt::{self, Display};
+use std::io;
+use std::ptr::null;
+
+// This links to libfdt which handles the creation of the binary blob
+// flattened device tree (fdt) that is passed to the kernel and indicates
+// the hardware configuration of the machine.
+#[link(name = "fdt")]
+extern "C" {
+ fn fdt_create(buf: *mut c_void, bufsize: c_int) -> c_int;
+ fn fdt_finish_reservemap(fdt: *mut c_void) -> c_int;
+ fn fdt_begin_node(fdt: *mut c_void, name: *const c_char) -> c_int;
+ fn fdt_property(fdt: *mut c_void, name: *const c_char, val: *const c_void, len: c_int)
+ -> c_int;
+ fn fdt_end_node(fdt: *mut c_void) -> c_int;
+ fn fdt_open_into(fdt: *const c_void, buf: *mut c_void, bufsize: c_int) -> c_int;
+ fn fdt_finish(fdt: *const c_void) -> c_int;
+ fn fdt_pack(fdt: *mut c_void) -> c_int;
+}
+
+#[derive(Debug)]
+pub enum Error {
+ FdtCreateError(c_int),
+ FdtFinishReservemapError(c_int),
+ FdtBeginNodeError(c_int),
+ FdtPropertyError(c_int),
+ FdtEndNodeError(c_int),
+ FdtOpenIntoError(c_int),
+ FdtFinishError(c_int),
+ FdtPackError(c_int),
+ FdtGuestMemoryWriteError,
+ FdtFileParseError,
+ FdtIoError(io::Error),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ write!(f, "libfdt: ")?;
+
+ match self {
+ FdtCreateError(ret) => write!(f, "error creating FDT, code={}", ret),
+ FdtFinishReservemapError(ret) => write!(f, "error finishing reserve map, code={}", ret),
+ FdtBeginNodeError(ret) => write!(f, "error beginning FDT node, code={}", ret),
+ FdtPropertyError(ret) => write!(f, "error adding FDT property, code={}", ret),
+ FdtEndNodeError(ret) => write!(f, "error ending FDT node, code={}", ret),
+ FdtOpenIntoError(ret) => write!(f, "error copying FDT to Guest, code={}", ret),
+ FdtFinishError(ret) => write!(f, "error performing FDT finish, code={}", ret),
+ FdtPackError(ret) => write!(f, "error packing FDT, code={}", ret),
+ FdtGuestMemoryWriteError => write!(f, "error writing FDT to guest memory"),
+ FdtFileParseError => write!(f, "parse error reading FDT parameters"),
+ FdtIoError(ret) => write!(f, "I/O error reading FDT parameters code={}", ret),
+ }
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+pub fn begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
+ let cstr_name = CString::new(name).unwrap();
+
+ // Safe because we allocated fdt and converted name to a CString
+ let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) };
+ if fdt_ret != 0 {
+ return Err(Error::FdtBeginNodeError(fdt_ret));
+ }
+ Ok(())
+}
+
+pub fn end_node(fdt: &mut Vec<u8>) -> Result<()> {
+ // Safe because we allocated fdt
+ let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) };
+ if fdt_ret != 0 {
+ return Err(Error::FdtEndNodeError(fdt_ret));
+ }
+ Ok(())
+}
+
+pub fn property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<()> {
+ let cstr_name = CString::new(name).unwrap();
+ let val_ptr = val.as_ptr() as *const c_void;
+
+ // Safe because we allocated fdt and converted name to a CString
+ let fdt_ret = unsafe {
+ fdt_property(
+ fdt.as_mut_ptr() as *mut c_void,
+ cstr_name.as_ptr(),
+ val_ptr,
+ val.len() as i32,
+ )
+ };
+ if fdt_ret != 0 {
+ return Err(Error::FdtPropertyError(fdt_ret));
+ }
+ Ok(())
+}
+
+fn cpu_to_fdt32(input: u32) -> [u8; 4] {
+ let mut buf = [0; 4];
+ BigEndian::write_u32(&mut buf, input);
+ buf
+}
+
+fn cpu_to_fdt64(input: u64) -> [u8; 8] {
+ let mut buf = [0; 8];
+ BigEndian::write_u64(&mut buf, input);
+ buf
+}
+
+pub fn property_u32(fdt: &mut Vec<u8>, name: &str, val: u32) -> Result<()> {
+ property(fdt, name, &cpu_to_fdt32(val))
+}
+
+pub fn property_u64(fdt: &mut Vec<u8>, name: &str, val: u64) -> Result<()> {
+ property(fdt, name, &cpu_to_fdt64(val))
+}
+
+// Helper to generate a properly formatted byte vector using 32-bit cells
+pub fn generate_prop32(cells: &[u32]) -> Vec<u8> {
+ let mut ret: Vec<u8> = Vec::new();
+ for &e in cells {
+ ret.extend(&cpu_to_fdt32(e));
+ }
+ ret
+}
+
+// Helper to generate a properly formatted byte vector using 64-bit cells
+pub fn generate_prop64(cells: &[u64]) -> Vec<u8> {
+ let mut ret: Vec<u8> = Vec::new();
+ for &e in cells {
+ ret.extend(&cpu_to_fdt64(e));
+ }
+ ret
+}
+
+pub fn property_null(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
+ let cstr_name = CString::new(name).unwrap();
+
+ // Safe because we allocated fdt, converted name to a CString
+ let fdt_ret = unsafe {
+ fdt_property(
+ fdt.as_mut_ptr() as *mut c_void,
+ cstr_name.as_ptr(),
+ null(),
+ 0,
+ )
+ };
+ if fdt_ret != 0 {
+ return Err(Error::FdtPropertyError(fdt_ret));
+ }
+ Ok(())
+}
+
+pub fn property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) -> Result<()> {
+ let value_bytes = cstr_value.to_bytes_with_nul();
+ let cstr_name = CString::new(name).unwrap();
+
+ // Safe because we allocated fdt, converted name and value to CStrings
+ let fdt_ret = unsafe {
+ fdt_property(
+ fdt.as_mut_ptr() as *mut c_void,
+ cstr_name.as_ptr(),
+ value_bytes.as_ptr() as *mut c_void,
+ value_bytes.len() as i32,
+ )
+ };
+ if fdt_ret != 0 {
+ return Err(Error::FdtPropertyError(fdt_ret));
+ }
+ Ok(())
+}
+
+pub fn property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<()> {
+ let cstr_value = CString::new(value).unwrap();
+ property_cstring(fdt, name, &cstr_value)
+}
+
+pub fn start_fdt(fdt: &mut Vec<u8>, fdt_max_size: usize) -> Result<()> {
+ // Safe since we allocated this array with fdt_max_size
+ let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, fdt_max_size as c_int) };
+
+ if fdt_ret != 0 {
+ return Err(Error::FdtCreateError(fdt_ret));
+ }
+ // Safe since we allocated this array
+ fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) };
+ if fdt_ret != 0 {
+ return Err(Error::FdtFinishReservemapError(fdt_ret));
+ }
+ Ok(())
+}
+
+pub fn finish_fdt(fdt: &mut Vec<u8>, fdt_final: &mut Vec<u8>, fdt_max_size: usize) -> Result<()> {
+ // Safe since we allocated fdt_final and previously passed in it's size
+ let mut fdt_ret = unsafe { fdt_finish(fdt.as_mut_ptr() as *mut c_void) };
+ if fdt_ret != 0 {
+ return Err(Error::FdtFinishError(fdt_ret));
+ }
+
+ // Safe because we allocated both arrays with the correct size
+ fdt_ret = unsafe {
+ fdt_open_into(
+ fdt.as_mut_ptr() as *mut c_void,
+ fdt_final.as_mut_ptr() as *mut c_void,
+ fdt_max_size as i32,
+ )
+ };
+ if fdt_ret != 0 {
+ return Err(Error::FdtOpenIntoError(fdt_ret));
+ }
+
+ // Safe since we allocated fdt_final
+ fdt_ret = unsafe { fdt_pack(fdt_final.as_mut_ptr() as *mut c_void) };
+ if fdt_ret != 0 {
+ return Err(Error::FdtPackError(fdt_ret));
+ }
+ Ok(())
+}
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
new file mode 100644
index 0000000..71e4327
--- /dev/null
+++ b/arch/src/lib.rs
@@ -0,0 +1,391 @@
+// Copyright 2018 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.
+
+pub mod android;
+pub mod fdt;
+
+use std::collections::BTreeMap;
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::{self, Read, Seek, SeekFrom};
+use std::os::unix::io::AsRawFd;
+use std::sync::Arc;
+
+use devices::virtio::VirtioDevice;
+use devices::{
+ Bus, BusDevice, BusError, PciDevice, PciDeviceError, PciInterruptPin, PciRoot, ProxyDevice,
+ Serial, SerialParameters, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR,
+};
+use io_jail::Minijail;
+use kvm::{IoeventAddress, Kvm, Vcpu, Vm};
+use resources::SystemAllocator;
+use sync::Mutex;
+use sys_util::{syslog, EventFd, GuestAddress, GuestMemory, GuestMemoryError};
+
+pub enum VmImage {
+ Kernel(File),
+ Bios(File),
+}
+
+/// Holds the pieces needed to build a VM. Passed to `build_vm` in the `LinuxArch` trait below to
+/// create a `RunnableLinuxVm`.
+pub struct VmComponents {
+ pub memory_size: u64,
+ pub vcpu_count: u32,
+ pub vcpu_affinity: Vec<usize>,
+ pub vm_image: VmImage,
+ pub android_fstab: Option<File>,
+ pub initrd_image: Option<File>,
+ pub extra_kernel_params: Vec<String>,
+ pub wayland_dmabuf: bool,
+}
+
+/// Holds the elements needed to run a Linux VM. Created by `build_vm`.
+pub struct RunnableLinuxVm {
+ pub vm: Vm,
+ pub kvm: Kvm,
+ pub resources: SystemAllocator,
+ pub stdio_serial: Option<Arc<Mutex<Serial>>>,
+ pub exit_evt: EventFd,
+ pub vcpus: Vec<Vcpu>,
+ pub vcpu_affinity: Vec<usize>,
+ pub irq_chip: Option<File>,
+ pub io_bus: Bus,
+ pub mmio_bus: Bus,
+ pub pid_debug_label_map: BTreeMap<u32, String>,
+}
+
+/// The device and optional jail.
+pub struct VirtioDeviceStub {
+ pub dev: Box<dyn VirtioDevice>,
+ pub jail: Option<Minijail>,
+}
+
+/// Trait which is implemented for each Linux Architecture in order to
+/// set up the memory, cpus, and system devices and to boot the kernel.
+pub trait LinuxArch {
+ type Error: StdError;
+
+ /// Takes `VmComponents` and generates a `RunnableLinuxVm`.
+ ///
+ /// # Arguments
+ ///
+ /// * `components` - Parts to use to build the VM.
+ /// * `split_irqchip` - whether to use a split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
+ /// * `serial_parameters` - definitions for how the serial devices should be configured.
+ /// * `create_devices` - Function to generate a list of devices.
+ fn build_vm<F, E>(
+ components: VmComponents,
+ split_irqchip: bool,
+ serial_parameters: &BTreeMap<u8, SerialParameters>,
+ create_devices: F,
+ ) -> Result<RunnableLinuxVm, Self::Error>
+ where
+ F: FnOnce(
+ &GuestMemory,
+ &mut Vm,
+ &mut SystemAllocator,
+ &EventFd,
+ ) -> Result<Vec<(Box<dyn PciDevice>, Option<Minijail>)>, E>,
+ E: StdError + 'static;
+}
+
+/// Errors for device manager.
+#[derive(Debug)]
+pub enum DeviceRegistrationError {
+ /// Could not allocate IO space for the device.
+ AllocateIoAddrs(PciDeviceError),
+ /// Could not allocate device address space for the device.
+ AllocateDeviceAddrs(PciDeviceError),
+ /// Could not allocate an IRQ number.
+ AllocateIrq,
+ /// Could not create the mmio device to wrap a VirtioDevice.
+ CreateMmioDevice(sys_util::Error),
+ // Unable to create serial device from serial parameters
+ CreateSerialDevice(devices::SerialError),
+ /// Could not create an event fd.
+ EventFdCreate(sys_util::Error),
+ /// Could not add a device to the mmio bus.
+ MmioInsert(BusError),
+ /// Failed to register ioevent with VM.
+ RegisterIoevent(sys_util::Error),
+ /// Failed to register irq eventfd with VM.
+ RegisterIrqfd(sys_util::Error),
+ /// Failed to initialize proxy device for jailed device.
+ ProxyDeviceCreation(devices::ProxyError),
+ /// Appending to kernel command line failed.
+ Cmdline(kernel_cmdline::Error),
+ /// No more IRQs are available.
+ IrqsExhausted,
+ /// No more MMIO space available.
+ AddrsExhausted,
+ /// Could not register PCI device capabilities.
+ RegisterDeviceCapabilities(PciDeviceError),
+}
+
+impl Display for DeviceRegistrationError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::DeviceRegistrationError::*;
+
+ match self {
+ AllocateIoAddrs(e) => write!(f, "Allocating IO addresses: {}", e),
+ AllocateDeviceAddrs(e) => write!(f, "Allocating device addresses: {}", e),
+ AllocateIrq => write!(f, "Allocating IRQ number"),
+ CreateMmioDevice(e) => write!(f, "failed to create mmio device: {}", e),
+ CreateSerialDevice(e) => write!(f, "failed to create serial device: {}", e),
+ Cmdline(e) => write!(f, "unable to add device to kernel command line: {}", e),
+ EventFdCreate(e) => write!(f, "failed to create eventfd: {}", e),
+ MmioInsert(e) => write!(f, "failed to add to mmio bus: {}", e),
+ RegisterIoevent(e) => write!(f, "failed to register ioevent to VM: {}", e),
+ RegisterIrqfd(e) => write!(f, "failed to register irq eventfd to VM: {}", e),
+ ProxyDeviceCreation(e) => write!(f, "failed to create proxy device: {}", e),
+ IrqsExhausted => write!(f, "no more IRQs are available"),
+ AddrsExhausted => write!(f, "no more addresses are available"),
+ RegisterDeviceCapabilities(e) => {
+ write!(f, "could not register PCI device capabilities: {}", e)
+ }
+ }
+ }
+}
+
+/// Creates a root PCI device for use by this Vm.
+pub fn generate_pci_root(
+ devices: Vec<(Box<dyn PciDevice>, Option<Minijail>)>,
+ mmio_bus: &mut Bus,
+ resources: &mut SystemAllocator,
+ vm: &mut Vm,
+) -> Result<(PciRoot, Vec<(u32, PciInterruptPin)>, BTreeMap<u32, String>), DeviceRegistrationError>
+{
+ let mut root = PciRoot::new();
+ let mut pci_irqs = Vec::new();
+ let mut pid_labels = BTreeMap::new();
+ for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() {
+ // Only support one bus.
+ device.assign_bus_dev(0, dev_idx as u8);
+
+ let mut keep_fds = device.keep_fds();
+ syslog::push_fds(&mut keep_fds);
+
+ let irqfd = EventFd::new().map_err(DeviceRegistrationError::EventFdCreate)?;
+ let irq_resample_fd = EventFd::new().map_err(DeviceRegistrationError::EventFdCreate)?;
+ let irq_num = resources
+ .allocate_irq()
+ .ok_or(DeviceRegistrationError::AllocateIrq)? as u32;
+ let pci_irq_pin = match dev_idx % 4 {
+ 0 => PciInterruptPin::IntA,
+ 1 => PciInterruptPin::IntB,
+ 2 => PciInterruptPin::IntC,
+ 3 => PciInterruptPin::IntD,
+ _ => panic!(""), // Obviously not possible, but the compiler is not smart enough.
+ };
+ vm.register_irqfd_resample(&irqfd, &irq_resample_fd, irq_num)
+ .map_err(DeviceRegistrationError::RegisterIrqfd)?;
+ keep_fds.push(irqfd.as_raw_fd());
+ keep_fds.push(irq_resample_fd.as_raw_fd());
+ device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin);
+ pci_irqs.push((dev_idx as u32, pci_irq_pin));
+
+ let ranges = device
+ .allocate_io_bars(resources)
+ .map_err(DeviceRegistrationError::AllocateIoAddrs)?;
+ let device_ranges = device
+ .allocate_device_bars(resources)
+ .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
+ device
+ .register_device_capabilities()
+ .map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?;
+ for (event, addr, datamatch) in device.ioeventfds() {
+ let io_addr = IoeventAddress::Mmio(addr);
+ vm.register_ioevent(&event, io_addr, datamatch)
+ .map_err(DeviceRegistrationError::RegisterIoevent)?;
+ keep_fds.push(event.as_raw_fd());
+ }
+ let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
+ let proxy = ProxyDevice::new(device, &jail, keep_fds)
+ .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
+ pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
+ Arc::new(Mutex::new(proxy))
+ } else {
+ device.on_sandboxed();
+ Arc::new(Mutex::new(device))
+ };
+ root.add_device(arced_dev.clone());
+ for range in &ranges {
+ mmio_bus
+ .insert(arced_dev.clone(), range.0, range.1, true)
+ .map_err(DeviceRegistrationError::MmioInsert)?;
+ }
+
+ for range in &device_ranges {
+ mmio_bus
+ .insert(arced_dev.clone(), range.0, range.1, true)
+ .map_err(DeviceRegistrationError::MmioInsert)?;
+ }
+ }
+ Ok((root, pci_irqs, pid_labels))
+}
+
+/// Adds serial devices to the provided bus based on the serial parameters given. Returns the serial
+/// port number and serial device to be used for stdout if defined.
+///
+/// # Arguments
+///
+/// * `io_bus` - Bus to add the devices to
+/// * `com_evt_1_3` - eventfd for com1 and com3
+/// * `com_evt_1_4` - eventfd for com2 and com4
+/// * `io_bus` - Bus to add the devices to
+/// * `serial_parameters` - definitions of serial parameter configuationis. If a setting is not
+/// provided for a port, then it will use the default configuation.
+pub fn add_serial_devices(
+ io_bus: &mut Bus,
+ com_evt_1_3: &EventFd,
+ com_evt_2_4: &EventFd,
+ serial_parameters: &BTreeMap<u8, SerialParameters>,
+) -> Result<(Option<u8>, Option<Arc<Mutex<Serial>>>), DeviceRegistrationError> {
+ let mut stdio_serial_num = None;
+ let mut stdio_serial = None;
+
+ for x in 0..=3 {
+ let com_evt = match x {
+ 0 => com_evt_1_3,
+ 1 => com_evt_2_4,
+ 2 => com_evt_1_3,
+ 3 => com_evt_2_4,
+ _ => com_evt_1_3,
+ };
+
+ let param = serial_parameters
+ .get(&(x + 1))
+ .unwrap_or(&DEFAULT_SERIAL_PARAMS[x as usize]);
+
+ let com = Arc::new(Mutex::new(
+ param
+ .create_serial_device(&com_evt)
+ .map_err(DeviceRegistrationError::CreateSerialDevice)?,
+ ));
+ io_bus
+ .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
+ .unwrap();
+
+ if param.console {
+ stdio_serial_num = Some(x + 1);
+ stdio_serial = Some(com.clone());
+ }
+ }
+
+ Ok((stdio_serial_num, stdio_serial))
+}
+
+/// Errors for image loading.
+#[derive(Debug)]
+pub enum LoadImageError {
+ BadAlignment(u64),
+ Seek(io::Error),
+ ImageSizeTooLarge(u64),
+ ReadToMemory(GuestMemoryError),
+}
+
+impl Display for LoadImageError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::LoadImageError::*;
+
+ match self {
+ BadAlignment(a) => write!(f, "Alignment not a power of two: {}", a),
+ Seek(e) => write!(f, "Seek failed: {}", e),
+ ImageSizeTooLarge(size) => write!(f, "Image size too large: {}", size),
+ ReadToMemory(e) => write!(f, "Reading image into memory failed: {}", e),
+ }
+ }
+}
+
+/// Load an image from a file into guest memory.
+///
+/// # Arguments
+///
+/// * `guest_mem` - The memory to be used by the guest.
+/// * `guest_addr` - The starting address to load the image in the guest memory.
+/// * `max_size` - The amount of space in bytes available in the guest memory for the image.
+/// * `image` - The file containing the image to be loaded.
+///
+/// The size in bytes of the loaded image is returned.
+pub fn load_image<F>(
+ guest_mem: &GuestMemory,
+ image: &mut F,
+ guest_addr: GuestAddress,
+ max_size: u64,
+) -> Result<usize, LoadImageError>
+where
+ F: Read + Seek + AsRawFd,
+{
+ let size = image.seek(SeekFrom::End(0)).map_err(LoadImageError::Seek)?;
+
+ if size > usize::max_value() as u64 || size > max_size {
+ return Err(LoadImageError::ImageSizeTooLarge(size));
+ }
+
+ // This is safe due to the bounds check above.
+ let size = size as usize;
+
+ image
+ .seek(SeekFrom::Start(0))
+ .map_err(LoadImageError::Seek)?;
+
+ guest_mem
+ .read_to_memory(guest_addr, image, size)
+ .map_err(LoadImageError::ReadToMemory)?;
+
+ Ok(size)
+}
+
+/// Load an image from a file into guest memory at the highest possible address.
+///
+/// # Arguments
+///
+/// * `guest_mem` - The memory to be used by the guest.
+/// * `image` - The file containing the image to be loaded.
+/// * `min_guest_addr` - The minimum address of the start of the image.
+/// * `max_guest_addr` - The address to load the last byte of the image.
+/// * `align` - The minimum alignment of the start address of the image in bytes
+/// (must be a power of two).
+///
+/// The guest address and size in bytes of the loaded image are returned.
+pub fn load_image_high<F>(
+ guest_mem: &GuestMemory,
+ image: &mut F,
+ min_guest_addr: GuestAddress,
+ max_guest_addr: GuestAddress,
+ align: u64,
+) -> Result<(GuestAddress, usize), LoadImageError>
+where
+ F: Read + Seek + AsRawFd,
+{
+ if !align.is_power_of_two() {
+ return Err(LoadImageError::BadAlignment(align));
+ }
+
+ let max_size = max_guest_addr.offset_from(min_guest_addr) & !(align - 1);
+ let size = image.seek(SeekFrom::End(0)).map_err(LoadImageError::Seek)?;
+
+ if size > usize::max_value() as u64 || size > max_size {
+ return Err(LoadImageError::ImageSizeTooLarge(size));
+ }
+
+ image
+ .seek(SeekFrom::Start(0))
+ .map_err(LoadImageError::Seek)?;
+
+ // Load image at the maximum aligned address allowed.
+ // The subtraction cannot underflow because of the size checks above.
+ let guest_addr = GuestAddress((max_guest_addr.offset() - size) & !(align - 1));
+
+ // This is safe due to the bounds check above.
+ let size = size as usize;
+
+ guest_mem
+ .read_to_memory(guest_addr, image, size)
+ .map_err(LoadImageError::ReadToMemory)?;
+
+ Ok((guest_addr, size))
+}
diff --git a/assertions/Cargo.toml b/assertions/Cargo.toml
new file mode 100644
index 0000000..60b1c5e
--- /dev/null
+++ b/assertions/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "assertions"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+include = ["src/**/*", "Cargo.toml"]
+
+[workspace]
diff --git a/assertions/src/lib.rs b/assertions/src/lib.rs
new file mode 100644
index 0000000..2e9d188
--- /dev/null
+++ b/assertions/src/lib.rs
@@ -0,0 +1,45 @@
+// Copyright 2018 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.
+
+//! Macros that assert properties of code at compile time.
+//!
+//! A static assertion is particularly appropriate when unsafe code relies on
+//! two types to have the same size, or on some type to have a particular size.
+
+#[doc(hidden)]
+pub mod mechanism;
+
+// Re-export so that these types appear with a more concise name in error
+// messages.
+#[doc(hidden)]
+pub use crate::mechanism::*;
+
+/// Macro that fails to compile if a given const expression is not true.
+///
+/// # Example
+///
+/// ```rust
+/// use assertions::const_assert;
+///
+/// fn main() {
+/// const_assert!(std::mem::size_of::<String>() == 24);
+/// }
+/// ```
+///
+/// # Example that fails to compile
+///
+/// ```rust,compile_fail
+/// use assertions::const_assert;
+///
+/// fn main() {
+/// // fails to compile:
+/// const_assert!(std::mem::size_of::<String>() == 8);
+/// }
+/// ```
+#[macro_export]
+macro_rules! const_assert {
+ ($e:expr) => {
+ let _: $crate::Assert<[(); $e as bool as usize]>;
+ };
+}
diff --git a/assertions/src/mechanism.rs b/assertions/src/mechanism.rs
new file mode 100644
index 0000000..e14b91a
--- /dev/null
+++ b/assertions/src/mechanism.rs
@@ -0,0 +1,34 @@
+// Copyright 2018 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::marker::PhantomData;
+
+pub struct True;
+pub struct False;
+
+pub trait Expr {
+ type Value;
+}
+
+impl Expr for [(); 0] {
+ type Value = False;
+}
+
+impl Expr for [(); 1] {
+ type Value = True;
+}
+
+// If the macro instantiates this with `T = [(); 1]` then it compiles successfully.
+//
+// On the other hand if `T = [(); 0]` the user receives an error like the following:
+//
+// error[E0271]: type mismatch resolving `<[(); 0] as assertions::Expr>::Value == assertions::True`
+// --> src/main.rs:5:5
+// |
+// 5 | const_assert!(std::mem::size_of::<String>() == 8);
+// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `assertions::True`, found struct `assertions::False`
+//
+pub struct Assert<T: Expr<Value = True>> {
+ marker: PhantomData<T>,
+}
diff --git a/bin/clippy b/bin/clippy
new file mode 100755
index 0000000..9f31816
--- /dev/null
+++ b/bin/clippy
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Run `cargo clippy` on all Rust code in crosvm with a mindful set of lints
+# suppressed.
+
+set -euo pipefail
+
+# Change into directory of script, which is crosvm/bin.
+cd "$(dirname "${BASH_SOURCE[0]}")"
+
+# Jump up to root directory of crosvm repo.
+cd ..
+
+SUPPRESS=(
+ # To be resolved.
+ let_unit_value
+ question_mark
+ range_plus_one
+ unit_arg
+
+ # We don't care about these lints. Okay to remain suppressed globally.
+ blacklisted_name
+ cast_lossless
+ cognitive_complexity
+ enum_variant_names
+ identity_op
+ len_without_is_empty
+ len_zero
+ match_bool
+ match_wild_err_arm
+ module_inception
+ needless_bool
+ new_without_default
+ or_fun_call
+ should_implement_trait
+ single_char_pattern
+ too_many_arguments
+ transmute_ptr_to_ptr
+ trivially_copy_pass_by_ref
+ type_complexity
+ unreadable_literal
+ useless_let_if_seq
+ useless_transmute
+)
+
+# Needed or else clippy won't re-run on code that has already compiled.
+cargo clean
+
+cargo clippy --all-features -- ${SUPPRESS[@]/#/-Aclippy::} "$@"
diff --git a/bin/fmt b/bin/fmt
new file mode 100755
index 0000000..39eac2c
--- /dev/null
+++ b/bin/fmt
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Run `cargo fmt` on all Rust code contained in crosvm. This is different from
+# `cargo fmt --all` which formats multiple crates but a single workspace only.
+# Crosvm consists of multiple workspaces.
+#
+# Usage:
+#
+# $ bin/fmt
+#
+# To print a diff and exit 1 if code is not formatted, but without changing any
+# files, use:
+#
+# $ bin/fmt --check
+#
+
+set -euo pipefail
+
+# Change into directory of script, which is crosvm/bin.
+cd "$(dirname "${BASH_SOURCE[0]}")"
+
+# Jump up to root directory of crosvm repo.
+cd ..
+
+# Keep track of whether any cargo fmt invocation exited with error.
+EXIT=0
+
+FIND_CARGO_TOMLS="$(find "$PWD" -name Cargo.toml)"
+
+while read path_to_cargo_toml; do
+ cd "$(dirname "$path_to_cargo_toml")"
+
+ if grep --quiet '\[workspace\]' Cargo.toml; then
+ if ! cargo fmt --all -- "$@"; then
+ EXIT=1
+ fi
+ fi
+done <<< "$FIND_CARGO_TOMLS"
+
+exit $EXIT
diff --git a/bin/smoke_test b/bin/smoke_test
new file mode 100755
index 0000000..e488e0e
--- /dev/null
+++ b/bin/smoke_test
@@ -0,0 +1,19 @@
+#!/bin/bash
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -ex
+
+cd "$(dirname "${BASH_SOURCE[0]}")"
+cd ../
+
+rustup default "$(cat rust-toolchain)"
+rustup component add rustfmt-preview
+cargo --version && rustc --version && rustfmt --version
+echo "Running cargo test"
+cargo test --no-fail-fast --features plugin,default-no-sandbox,wl-dmabuf,gpu,tpm,gpu-forward \
+ --all --exclude aarch64 $TEST_FLAGS -- \
+ --test-threads=1 $TEST_RUNNER_FLAGS
+echo "Running cargo fmt"
+bin/fmt --check
diff --git a/bit_field/Cargo.toml b/bit_field/Cargo.toml
new file mode 100644
index 0000000..ac8846c
--- /dev/null
+++ b/bit_field/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "bit_field"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+bit_field_derive = { path = "bit_field_derive" }
diff --git a/bit_field/bit_field_derive/Cargo.toml b/bit_field/bit_field_derive/Cargo.toml
new file mode 100644
index 0000000..df84a2a
--- /dev/null
+++ b/bit_field/bit_field_derive/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "bit_field_derive"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+proc-macro2 = "=0.4"
+quote = "=0.6"
+syn = "=0.15"
+
+[lib]
+proc-macro = true
+path = "bit_field_derive.rs"
diff --git a/bit_field/bit_field_derive/bit_field_derive.rs b/bit_field/bit_field_derive/bit_field_derive.rs
new file mode 100644
index 0000000..7084e22
--- /dev/null
+++ b/bit_field/bit_field_derive/bit_field_derive.rs
@@ -0,0 +1,799 @@
+// Copyright 2018 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.
+
+#![recursion_limit = "256"]
+
+extern crate proc_macro;
+
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, quote_spanned};
+use syn::parse::{Error, Result};
+use syn::{
+ parse_macro_input, Attribute, Data, DataEnum, DeriveInput, Fields, FieldsNamed, FieldsUnnamed,
+ Ident, Lit, LitInt, Meta, Type, Visibility,
+};
+
+/// The function that derives the actual implementation.
+#[proc_macro_attribute]
+pub fn bitfield(
+ _args: proc_macro::TokenStream,
+ input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ let derive_input = parse_macro_input!(input as DeriveInput);
+
+ let expanded = bitfield_impl(&derive_input).unwrap_or_else(|err| {
+ let compile_error = err.to_compile_error();
+ quote! {
+ #compile_error
+
+ // Include the original input to avoid "use of undeclared type"
+ // errors elsewhere.
+ #derive_input
+ }
+ });
+
+ expanded.into()
+}
+
+fn bitfield_impl(ast: &DeriveInput) -> Result<TokenStream> {
+ if !ast.generics.params.is_empty() {
+ return Err(Error::new(
+ Span::call_site(),
+ "#[bitfield] does not support generic parameters",
+ ));
+ }
+
+ match &ast.data {
+ Data::Struct(data_struct) => match &data_struct.fields {
+ Fields::Named(fields_named) => bitfield_struct_impl(ast, fields_named),
+ Fields::Unnamed(fields_unnamed) => bitfield_tuple_struct_impl(ast, fields_unnamed),
+ Fields::Unit => Err(Error::new(
+ Span::call_site(),
+ "#[bitfield] does not work with unit struct",
+ )),
+ },
+ Data::Enum(data_enum) => bitfield_enum_impl(ast, data_enum),
+ Data::Union(_) => Err(Error::new(
+ Span::call_site(),
+ "#[bitfield] does not support unions",
+ )),
+ }
+}
+
+fn bitfield_tuple_struct_impl(ast: &DeriveInput, fields: &FieldsUnnamed) -> Result<TokenStream> {
+ let mut ast = ast.clone();
+ let width = match parse_remove_bits_attr(&mut ast)? {
+ Some(w) => w,
+ None => {
+ return Err(Error::new(
+ Span::call_site(),
+ "tuple struct field must have bits attribute",
+ ));
+ }
+ };
+
+ let ident = &ast.ident;
+
+ if width.value() > 64 {
+ return Err(Error::new(
+ Span::call_site(),
+ "max width of bitfield field is 64",
+ ));
+ }
+
+ let bits = width.value() as u8;
+
+ if fields.unnamed.len() != 1 {
+ return Err(Error::new(
+ Span::call_site(),
+ "tuple struct field must have exactly 1 field",
+ ));
+ }
+
+ let field_type = match &fields.unnamed.first().unwrap().value().ty {
+ Type::Path(t) => t,
+ _ => {
+ return Err(Error::new(
+ Span::call_site(),
+ "tuple struct field must have primitive field",
+ ));
+ }
+ };
+ let span = field_type
+ .path
+ .segments
+ .first()
+ .unwrap()
+ .value()
+ .ident
+ .span();
+
+ let from_u64 = quote_spanned! {
+ span => val as #field_type
+ };
+
+ let into_u64 = quote_spanned! {
+ span => val.0 as u64
+ };
+
+ let expanded = quote! {
+ #ast
+
+ impl bit_field::BitFieldSpecifier for #ident {
+ const FIELD_WIDTH: u8 = #bits;
+ type SetterType = Self;
+ type GetterType = Self;
+
+ #[inline]
+ fn from_u64(val: u64) -> Self::GetterType {
+ Self(#from_u64)
+ }
+
+ #[inline]
+ fn into_u64(val: Self::SetterType) -> u64 {
+ #into_u64
+ }
+ }
+ };
+
+ Ok(expanded)
+}
+
+fn bitfield_enum_impl(ast: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
+ let mut ast = ast.clone();
+ let width = parse_remove_bits_attr(&mut ast)?;
+ match width {
+ None => bitfield_enum_without_width_impl(&ast, data),
+ Some(width) => bitfield_enum_with_width_impl(&ast, data, &width),
+ }
+}
+
+fn bitfield_enum_with_width_impl(
+ ast: &DeriveInput,
+ data: &DataEnum,
+ width: &LitInt,
+) -> Result<TokenStream> {
+ if width.value() > 64 {
+ return Err(Error::new(
+ Span::call_site(),
+ "max width of bitfield enum is 64",
+ ));
+ }
+ let bits = width.value() as u8;
+ let declare_discriminants = get_declare_discriminants_for_enum(bits, ast, data);
+
+ let ident = &ast.ident;
+ let type_name = ident.to_string();
+ let variants = &data.variants;
+ let match_discriminants = variants.iter().map(|variant| {
+ let variant = &variant.ident;
+ quote! {
+ discriminant::#variant => Ok(#ident::#variant),
+ }
+ });
+
+ let expanded = quote! {
+ #ast
+
+ impl bit_field::BitFieldSpecifier for #ident {
+ const FIELD_WIDTH: u8 = #bits;
+ type SetterType = Self;
+ type GetterType = std::result::Result<Self, bit_field::Error>;
+
+ #[inline]
+ fn from_u64(val: u64) -> Self::GetterType {
+ struct discriminant;
+ impl discriminant {
+ #(#declare_discriminants)*
+ }
+ match val {
+ #(#match_discriminants)*
+ v => Err(bit_field::Error::new(#type_name, v)),
+ }
+ }
+
+ #[inline]
+ fn into_u64(val: Self::SetterType) -> u64 {
+ val as u64
+ }
+ }
+ };
+
+ Ok(expanded)
+}
+// Expand to an impl of BitFieldSpecifier for an enum like:
+//
+// #[bitfield]
+// #[derive(Debug, PartialEq)]
+// enum TwoBits {
+// Zero = 0b00,
+// One = 0b01,
+// Two = 0b10,
+// Three = 0b11,
+// }
+//
+// Such enums may be used as a field of a bitfield struct.
+//
+// #[bitfield]
+// struct Struct {
+// prefix: BitField1,
+// two_bits: TwoBits,
+// suffix: BitField5,
+// }
+//
+fn bitfield_enum_without_width_impl(ast: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
+ let ident = &ast.ident;
+ let variants = &data.variants;
+ let len = variants.len();
+ if len.count_ones() != 1 {
+ return Err(Error::new(
+ Span::call_site(),
+ "#[bitfield] expected a number of variants which is a power of 2 when bits is not \
+ specified for the enum",
+ ));
+ }
+
+ let bits = len.trailing_zeros() as u8;
+ let declare_discriminants = get_declare_discriminants_for_enum(bits, ast, data);
+
+ let match_discriminants = variants.iter().map(|variant| {
+ let variant = &variant.ident;
+ quote! {
+ discriminant::#variant => #ident::#variant,
+ }
+ });
+
+ let expanded = quote! {
+ #ast
+
+ impl bit_field::BitFieldSpecifier for #ident {
+ const FIELD_WIDTH: u8 = #bits;
+ type SetterType = Self;
+ type GetterType = Self;
+
+ #[inline]
+ fn from_u64(val: u64) -> Self::GetterType {
+ struct discriminant;
+ impl discriminant {
+ #(#declare_discriminants)*
+ }
+ match val {
+ #(#match_discriminants)*
+ _ => unreachable!(),
+ }
+ }
+
+ #[inline]
+ fn into_u64(val: Self::SetterType) -> u64 {
+ val as u64
+ }
+ }
+ };
+
+ Ok(expanded)
+}
+
+fn get_declare_discriminants_for_enum(
+ bits: u8,
+ ast: &DeriveInput,
+ data: &DataEnum,
+) -> Vec<TokenStream> {
+ let variants = &data.variants;
+ let upper_bound = 2u64.pow(bits as u32);
+ let ident = &ast.ident;
+
+ variants
+ .iter()
+ .map(|variant| {
+ let variant = &variant.ident;
+ let span = variant.span();
+
+ let assertion = quote_spanned! {span=>
+ // If IS_IN_BOUNDS is true, this evaluates to 0.
+ //
+ // If IS_IN_BOUNDS is false, this evaluates to `0 - 1` which
+ // triggers a compile error on underflow when referenced below. The
+ // error is not beautiful but does carry the span of the problematic
+ // enum variant so at least it points to the right line.
+ //
+ // error: any use of this value will cause an error
+ // --> bit_field/test.rs:10:5
+ // |
+ // 10 | OutOfBounds = 0b111111,
+ // | ^^^^^^^^^^^ attempt to subtract with overflow
+ // |
+ //
+ // error[E0080]: erroneous constant used
+ // --> bit_field/test.rs:5:1
+ // |
+ // 5 | #[bitfield]
+ // | ^^^^^^^^^^^ referenced constant has errors
+ //
+ const ASSERT: u64 = 0 - !IS_IN_BOUNDS as u64;
+ };
+
+ quote! {
+ #[allow(non_upper_case_globals)]
+ const #variant: u64 = {
+ const IS_IN_BOUNDS: bool = (#ident::#variant as u64) < #upper_bound;
+
+ #assertion
+
+ #ident::#variant as u64 + ASSERT
+ };
+ }
+ })
+ .collect()
+}
+
+fn bitfield_struct_impl(ast: &DeriveInput, fields: &FieldsNamed) -> Result<TokenStream> {
+ let name = &ast.ident;
+ let vis = &ast.vis;
+ let attrs = &ast.attrs;
+ let fields = get_struct_fields(fields)?;
+ let struct_def = get_struct_def(vis, &name, &fields);
+ let bits_impl = get_bits_impl(&name);
+ let fields_impl = get_fields_impl(&fields);
+ let debug_fmt_impl = get_debug_fmt_impl(&name, &fields);
+
+ let expanded = quote! {
+ #(#attrs)*
+ #struct_def
+ #bits_impl
+ impl #name {
+ #(#fields_impl)*
+ }
+ #debug_fmt_impl
+ };
+
+ Ok(expanded)
+}
+
+struct FieldSpec<'a> {
+ ident: &'a Ident,
+ ty: &'a Type,
+ expected_bits: Option<LitInt>,
+}
+
+// Unwrap ast to get the named fields. We only care about field names and types:
+// "myfield : BitField3" -> ("myfield", Token(BitField3))
+fn get_struct_fields(fields: &FieldsNamed) -> Result<Vec<FieldSpec>> {
+ let mut vec = Vec::new();
+
+ for field in &fields.named {
+ let ident = field
+ .ident
+ .as_ref()
+ .expect("Fields::Named has named fields");
+ let ty = &field.ty;
+ let expected_bits = parse_bits_attr(&field.attrs)?;
+ vec.push(FieldSpec {
+ ident,
+ ty,
+ expected_bits,
+ });
+ }
+
+ Ok(vec)
+}
+
+// For example: #[bits = 1]
+fn parse_bits_attr(attrs: &[Attribute]) -> Result<Option<LitInt>> {
+ let mut expected_bits = None;
+
+ for attr in attrs {
+ if attr.path.is_ident("doc") {
+ continue;
+ }
+ if let Some(v) = try_parse_bits_attr(attr)? {
+ expected_bits = Some(v);
+ continue;
+ }
+
+ return Err(Error::new_spanned(attr, "unrecognized attribute"));
+ }
+
+ Ok(expected_bits)
+}
+
+// This function will return None if the attribute is not #[bits = *].
+fn try_parse_bits_attr(attr: &Attribute) -> Result<Option<LitInt>> {
+ if attr.path.is_ident("bits") {
+ if let Meta::NameValue(name_value) = attr.parse_meta()? {
+ if let Lit::Int(int) = name_value.lit {
+ return Ok(Some(int));
+ }
+ }
+ }
+ Ok(None)
+}
+
+fn parse_remove_bits_attr(ast: &mut DeriveInput) -> Result<Option<LitInt>> {
+ let mut width = None;
+ let mut bits_idx = 0;
+
+ for (i, attr) in ast.attrs.iter().enumerate() {
+ if let Some(w) = try_parse_bits_attr(attr)? {
+ bits_idx = i;
+ width = Some(w);
+ }
+ }
+
+ if width.is_some() {
+ ast.attrs.remove(bits_idx);
+ }
+
+ Ok(width)
+}
+
+fn get_struct_def(vis: &Visibility, name: &Ident, fields: &[FieldSpec]) -> TokenStream {
+ let mut field_types = Vec::new();
+ for spec in fields {
+ field_types.push(spec.ty);
+ }
+
+ // `(BitField1::FIELD_WIDTH + BitField3::FIELD_WIDTH + ...)`
+ let data_size_in_bits = quote! {
+ (
+ #(
+ <#field_types as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ )+*
+ )
+ };
+
+ quote! {
+ #[repr(C)]
+ #vis struct #name {
+ data: [u8; #data_size_in_bits / 8],
+ }
+
+ impl #name {
+ pub fn new() -> #name {
+ let _: ::bit_field::Check<[u8; #data_size_in_bits % 8]>;
+
+ #name {
+ data: [0; #data_size_in_bits / 8],
+ }
+ }
+ }
+ }
+}
+
+// Implement setter and getter for all fields.
+fn get_fields_impl(fields: &[FieldSpec]) -> Vec<TokenStream> {
+ let mut impls = Vec::new();
+ // This vec keeps track of types before this field, used to generate the offset.
+ let current_types = &mut vec![quote!(::bit_field::BitField0)];
+
+ for spec in fields {
+ let ty = spec.ty;
+ let getter_ident = Ident::new(format!("get_{}", spec.ident).as_str(), Span::call_site());
+ let setter_ident = Ident::new(format!("set_{}", spec.ident).as_str(), Span::call_site());
+
+ // Optional #[bits = N] attribute to provide compile-time checked
+ // documentation of how many bits some field covers.
+ let check_expected_bits = spec.expected_bits.as_ref().map(|expected_bits| {
+ // If expected_bits does not match the actual number of bits in the
+ // bit field specifier, this will fail to compile with an error
+ // pointing into the #[bits = N] attribute.
+ let span = expected_bits.span();
+ quote_spanned! {span=>
+ #[allow(dead_code)]
+ const EXPECTED_BITS: [(); #expected_bits as usize] =
+ [(); <#ty as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize];
+ }
+ });
+
+ impls.push(quote! {
+ pub fn #getter_ident(&self) -> <#ty as ::bit_field::BitFieldSpecifier>::GetterType {
+ #check_expected_bits
+ let offset = #(<#current_types as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)+*;
+ let val = self.get(offset, <#ty as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH);
+ <#ty as ::bit_field::BitFieldSpecifier>::from_u64(val)
+ }
+
+ pub fn #setter_ident(&mut self, val: <#ty as ::bit_field::BitFieldSpecifier>::SetterType) {
+ let val = <#ty as ::bit_field::BitFieldSpecifier>::into_u64(val);
+ debug_assert!(val <= ::bit_field::max::<#ty>());
+ let offset = #(<#current_types as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)+*;
+ self.set(offset, <#ty as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH, val)
+ }
+ });
+
+ current_types.push(quote!(#ty));
+ }
+
+ impls
+}
+
+// Implement setter and getter for all fields.
+fn get_debug_fmt_impl(name: &Ident, fields: &[FieldSpec]) -> TokenStream {
+ // print fields:
+ let mut impls = Vec::new();
+ for spec in fields {
+ let field_name = spec.ident.to_string();
+ let getter_ident = Ident::new(&format!("get_{}", spec.ident), Span::call_site());
+ impls.push(quote! {
+ .field(#field_name, &self.#getter_ident())
+ });
+ }
+
+ let name_str = format!("{}", name);
+ quote! {
+ impl std::fmt::Debug for #name {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ f.debug_struct(#name_str)
+ #(#impls)*
+ .finish()
+ }
+ }
+ }
+}
+
+fn get_bits_impl(name: &Ident) -> TokenStream {
+ quote! {
+ impl #name {
+ #[inline]
+ fn check_access(&self, offset: usize, width: u8) {
+ debug_assert!(width <= 64);
+ debug_assert!(offset / 8 < self.data.len());
+ debug_assert!((offset + (width as usize)) <= (self.data.len() * 8));
+ }
+
+ #[inline]
+ pub fn get_bit(&self, offset: usize) -> bool {
+ self.check_access(offset, 1);
+
+ let byte_index = offset / 8;
+ let bit_offset = offset % 8;
+
+ let byte = self.data[byte_index];
+ let mask = 1 << bit_offset;
+
+ byte & mask == mask
+ }
+
+ #[inline]
+ pub fn set_bit(&mut self, offset: usize, val: bool) {
+ self.check_access(offset, 1);
+
+ let byte_index = offset / 8;
+ let bit_offset = offset % 8;
+
+ let byte = &mut self.data[byte_index];
+ let mask = 1 << bit_offset;
+
+ if val {
+ *byte |= mask;
+ } else {
+ *byte &= !mask;
+ }
+ }
+
+ #[inline]
+ pub fn get(&self, offset: usize, width: u8) -> u64 {
+ self.check_access(offset, width);
+ let mut val = 0;
+
+ for i in 0..(width as usize) {
+ if self.get_bit(i + offset) {
+ val |= 1 << i;
+ }
+ }
+
+ val
+ }
+
+ #[inline]
+ pub fn set(&mut self, offset: usize, width: u8, val: u64) {
+ self.check_access(offset, width);
+
+ for i in 0..(width as usize) {
+ let mask = 1 << i;
+ let val_bit_is_set = val & mask == mask;
+ self.set_bit(i + offset, val_bit_is_set);
+ }
+ }
+ }
+ }
+}
+
+// Only intended to be used from the bit_field crate. This macro emits the
+// marker types bit_field::BitField0 through bit_field::BitField64.
+#[proc_macro]
+#[doc(hidden)]
+pub fn define_bit_field_specifiers(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let mut code = TokenStream::new();
+
+ for width in 0u8..=64 {
+ let span = Span::call_site();
+ let long_name = Ident::new(&format!("BitField{}", width), span);
+ let short_name = Ident::new(&format!("B{}", width), span);
+
+ let default_field_type = if width <= 8 {
+ quote!(u8)
+ } else if width <= 16 {
+ quote!(u16)
+ } else if width <= 32 {
+ quote!(u32)
+ } else {
+ quote!(u64)
+ };
+
+ code.extend(quote! {
+ pub struct #long_name;
+ pub use self::#long_name as #short_name;
+
+ impl BitFieldSpecifier for #long_name {
+ const FIELD_WIDTH: u8 = #width;
+ type SetterType = #default_field_type;
+ type GetterType = #default_field_type;
+
+ #[inline]
+ fn from_u64(val: u64) -> Self::GetterType {
+ val as Self::GetterType
+ }
+
+ #[inline]
+ fn into_u64(val: Self::SetterType) -> u64 {
+ val as u64
+ }
+ }
+ });
+ }
+
+ code.into()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use syn::parse_quote;
+
+ #[test]
+ fn end_to_end() {
+ let input: DeriveInput = parse_quote! {
+ #[derive(Clone)]
+ struct MyBitField {
+ a: BitField1,
+ b: BitField2,
+ c: BitField5,
+ }
+ };
+
+ let expected = quote! {
+ #[derive(Clone)]
+ #[repr(C)]
+ struct MyBitField {
+ data: [u8; (<BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)
+ / 8],
+ }
+ impl MyBitField {
+ pub fn new() -> MyBitField {
+ let _: ::bit_field::Check<[
+ u8;
+ (<BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)
+ % 8
+ ]>;
+
+ MyBitField {
+ data: [0; (<BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)
+ / 8],
+ }
+ }
+ }
+ impl MyBitField {
+ #[inline]
+ fn check_access(&self, offset: usize, width: u8) {
+ debug_assert!(width <= 64);
+ debug_assert!(offset / 8 < self.data.len());
+ debug_assert!((offset + (width as usize)) <= (self.data.len() * 8));
+ }
+ #[inline]
+ pub fn get_bit(&self, offset: usize) -> bool {
+ self.check_access(offset, 1);
+ let byte_index = offset / 8;
+ let bit_offset = offset % 8;
+ let byte = self.data[byte_index];
+ let mask = 1 << bit_offset;
+ byte & mask == mask
+ }
+ #[inline]
+ pub fn set_bit(&mut self, offset: usize, val: bool) {
+ self.check_access(offset, 1);
+ let byte_index = offset / 8;
+ let bit_offset = offset % 8;
+ let byte = &mut self.data[byte_index];
+ let mask = 1 << bit_offset;
+ if val {
+ *byte |= mask;
+ } else {
+ *byte &= !mask;
+ }
+ }
+ #[inline]
+ pub fn get(&self, offset: usize, width: u8) -> u64 {
+ self.check_access(offset, width);
+ let mut val = 0;
+ for i in 0..(width as usize) {
+ if self.get_bit(i + offset) {
+ val |= 1 << i;
+ }
+ }
+ val
+ }
+ #[inline]
+ pub fn set(&mut self, offset: usize, width: u8, val: u64) {
+ self.check_access(offset, width);
+ for i in 0..(width as usize) {
+ let mask = 1 << i;
+ let val_bit_is_set = val & mask == mask;
+ self.set_bit(i + offset, val_bit_is_set);
+ }
+ }
+ }
+ impl MyBitField {
+ pub fn get_a(&self) -> <BitField1 as ::bit_field::BitFieldSpecifier>::GetterType {
+ let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+ let val = self.get(offset, <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH);
+ <BitField1 as ::bit_field::BitFieldSpecifier>::from_u64(val)
+ }
+ pub fn set_a(&mut self, val: <BitField1 as ::bit_field::BitFieldSpecifier>::SetterType) {
+ let val = <BitField1 as ::bit_field::BitFieldSpecifier>::into_u64(val);
+ debug_assert!(val <= ::bit_field::max::<BitField1>());
+ let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+ self.set(offset, <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH, val)
+ }
+ pub fn get_b(&self) -> <BitField2 as ::bit_field::BitFieldSpecifier>::GetterType {
+ let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+ let val = self.get(offset, <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH);
+ <BitField2 as ::bit_field::BitFieldSpecifier>::from_u64(val)
+ }
+ pub fn set_b(&mut self, val: <BitField2 as ::bit_field::BitFieldSpecifier>::SetterType) {
+ let val = <BitField2 as ::bit_field::BitFieldSpecifier>::into_u64(val);
+ debug_assert!(val <= ::bit_field::max::<BitField2>());
+ let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+ self.set(offset, <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH, val)
+ }
+ pub fn get_c(&self) -> <BitField5 as ::bit_field::BitFieldSpecifier>::GetterType {
+ let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+ let val = self.get(offset, <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH);
+ <BitField5 as ::bit_field::BitFieldSpecifier>::from_u64(val)
+ }
+ pub fn set_c(&mut self, val: <BitField5 as ::bit_field::BitFieldSpecifier>::SetterType) {
+ let val = <BitField5 as ::bit_field::BitFieldSpecifier>::into_u64(val);
+ debug_assert!(val <= ::bit_field::max::<BitField5>());
+ let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+ + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+ self.set(offset, <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH, val)
+ }
+ }
+ impl std::fmt::Debug for MyBitField {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ f.debug_struct("MyBitField")
+ .field("a", &self.get_a())
+ .field("b", &self.get_b())
+ .field("c", &self.get_c())
+ .finish()
+ }
+ }
+ };
+
+ assert_eq!(
+ bitfield_impl(&input).unwrap().to_string(),
+ expected.to_string()
+ );
+ }
+}
diff --git a/bit_field/src/lib.rs b/bit_field/src/lib.rs
new file mode 100644
index 0000000..1455430
--- /dev/null
+++ b/bit_field/src/lib.rs
@@ -0,0 +1,361 @@
+// Copyright 2018 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 crate provides a `#[bitfield]` attribute macro for defining structs in
+//! a packed binary representation that supports access to ranges of bits.
+//!
+//! We conceptualize one of these structs as a sequence of bits 0..N. The bits
+//! are grouped into fields in the order specified by a struct written by the
+//! caller. The `#[bitfield]` attribute rewrites the caller's struct into a
+//! private byte array representation with public getter and setter methods for
+//! each field.
+//!
+//! Byte order: note that we consider the bit `i` to be the `i % 8`'th least
+//! significant bit in the `i / 8`'th byte of the struct.
+//!
+//! The total number of bits N is required to be a multiple of 8 (this is
+//! checked at compile time).
+//!
+//! # Examples
+//!
+//! The following invocation builds a struct with a total size of 32 bits or 4
+//! bytes. It places field `a` in the least significant bit of the first byte,
+//! field `b` in the next three least significant bits, field `c` in the
+//! remaining four most significant bits of the first byte, and field `d`
+//! spanning the next three bytes. The least significant byte of `d` will be
+//! held in the second byte of our struct, adjacent to the byte holding the
+//! first three fields.
+//!
+//! ```
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! pub struct MyFourBytes {
+//! a: B1,
+//! b: B3,
+//! c: B4,
+//! d: B24,
+//! }
+//! ```
+//!
+//! ```text
+//! less significant
+//! / more significant
+//! / /
+//! (first byte) (second byte) / (third) / (fourth byte)
+//! 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+//! | \ / \_ _/ \_______________________ _______________________/
+//! a b c less significant d more significant
+//! ```
+//!
+//! The code emitted by the `#[bitfield]` macro for this struct is as follows.
+//! Note that the field getters and setters use whichever of `u8`, `u16`, `u32`,
+//! `u64` is the smallest while being at least as large as the number of bits in
+//! the field.
+//!
+//! ```ignore
+//! impl MyFourBytes {
+//! // Initializes all fields to 0.
+//! pub fn new() -> Self;
+//!
+//! // Field getters and setters:
+//! pub fn get_a(&self) -> u8;
+//! pub fn set_a(&mut self, val: u8);
+//! pub fn get_b(&self) -> u8;
+//! pub fn set_b(&mut self, val: u8);
+//! pub fn get_c(&self) -> u8;
+//! pub fn set_c(&mut self, val: u8);
+//! pub fn get_d(&self) -> u32;
+//! pub fn set_d(&mut self, val: u32);
+//!
+//! // Bit-level accessors:
+//! pub fn get_bit(&self, offset: usize) -> bool;
+//! pub fn set_bit(&mut self, offset: usize, val: bool);
+//! pub fn get(&self, offset: usize, width: u8) -> u64;
+//! pub fn set(&mut self, offset: usize, width: u8, val: u64);
+//! }
+//! ```
+//!
+//! # Bit field specifier types
+//!
+//! Field types may be specified as B1 through B64, or alternatively as
+//! BitField1 through BitField64 in code that benefits from the clarification.
+//!
+//! Fields may also be specified as `bool`, which is laid out equivalently to
+//! `B1` but with accessors that use `bool` rather than `u8`.
+//!
+//! ```
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! pub struct MyFourBytes {
+//! a: bool,
+//! b: B3,
+//! c: B4,
+//! d: B24,
+//! }
+//! ```
+//!
+//! Fields may be user-defined single element tuple struct with primitive types. Use must specify
+//! the width with `#[bits = N]`. This should be used to improve type safety.
+//!
+//! ```
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! #[bits = 60]
+//! struct AddressField(u64);
+//!
+//! impl AddressField {
+//! pub fn new(addr: u64) -> AddressField {
+//! AddressField(addr >> 4)
+//! }
+//!
+//! pub fn get_addr(&self) -> u64 {
+//! self.0 << 4
+//! }
+//! }
+//!
+//! ```
+//!
+//! Finally, fields may be of user-defined enum types. The enum must satisfy one of the following
+//! requirements.
+//!
+//! The enum has `#[bits = N]` attributes with it. `N` will be the width of the field. The getter
+//! function of this enum field will return `Result<EnumType, u64>`. Raw value that does not match
+//! any variant will result in an `Err(u64)`.
+//!
+//! ```
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! #[bits = 2]
+//! #[derive(Debug, PartialEq)]
+//! enum TwoBits {
+//! Zero = 0b00,
+//! One = 0b01,
+//! Three = 0b11,
+//! }
+//!
+//! #[bitfield]
+//! struct Struct {
+//! prefix: BitField1,
+//! two_bits: TwoBits,
+//! suffix: BitField5,
+//! }
+//! ```
+//!
+//! The enum has a number of variants which is a power of 2 and the discriminant values
+//! (explicit or implicit) are 0 through (2^n)-1. In this case the generated
+//! getter and setter are defined in terms of the given enum type.
+//!
+//! ```
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! #[derive(Debug, PartialEq)]
+//! enum TwoBits {
+//! Zero = 0b00,
+//! One = 0b01,
+//! Two = 0b10,
+//! Three = 0b11,
+//! }
+//!
+//! #[bitfield]
+//! struct Struct {
+//! prefix: BitField1,
+//! two_bits: TwoBits,
+//! suffix: BitField5,
+//! }
+//! ```
+//!
+//! An optional `#[bits = N]` attribute may be used to document the number of
+//! bits in any field. This is intended for fields of enum type whose name does
+//! not clearly indicate the number of bits. The attribute is optional but helps
+//! make it possible to read off the field sizes directly from the definition of
+//! a bitfield struct.
+//!
+//! ```
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! #[derive(Debug, PartialEq)]
+//! enum WhoKnows {
+//! Zero = 0b00,
+//! One = 0b01,
+//! Two = 0b10,
+//! Three = 0b11,
+//! }
+//!
+//! #[bitfield]
+//! struct Struct {
+//! prefix: BitField1,
+//! #[bits = 2]
+//! two_bits: WhoKnows,
+//! suffix: BitField5,
+//! }
+//! ```
+//!
+//! # Derives
+//!
+//! Derives may be specified and are applied to the data structure post
+//! rewriting by the macro.
+//!
+//! ```
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! #[derive(Copy, Clone)]
+//! pub struct ExampleWithDerives {
+//! car: B4,
+//! cdr: B4,
+//! }
+//! ```
+//!
+//! # Compile time checks
+//!
+//! If the total size is not a multiple of 8 bits, you will receive an error
+//! message at compile time mentioning:
+//!
+//! > the trait `bit_field::checks::TotalSizeIsMultipleOfEightBits` is not implemented
+//!
+//! ```compile_fail
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! pub struct Broken {
+//! field_a: B1,
+//! field_b: B3,
+//! field_c: B6,
+//! }
+//! ```
+//!
+//! If a bitfield enum has discriminants that are outside the range 0 through
+//! (2^n)-1, it will be caught at compile time.
+//!
+//! ```compile_fail
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! enum Broken {
+//! Zero = 0b00,
+//! One = 0b01,
+//! Two = 0b10,
+//! Nine = 0b1001, // error
+//! }
+//! ```
+//!
+//! If the value provided in a #[bits = N] attribute does not match the real
+//! number of bits in that field, it will be caught.
+//!
+//! ```compile_fail
+//! use bit_field::*;
+//!
+//! #[bitfield]
+//! #[derive(Debug, PartialEq)]
+//! enum OneBit {
+//! No = 0,
+//! Yes = 1,
+//! }
+//!
+//! #[bitfield]
+//! struct Struct {
+//! #[bits = 4] // error
+//! two_bits: OneBit,
+//! padding: BitField7,
+//! }
+//! ```
+
+use std::fmt::{self, Display};
+
+pub use bit_field_derive::bitfield;
+
+/// Error type for bit field get.
+#[derive(Debug)]
+pub struct Error {
+ type_name: &'static str,
+ val: u64,
+}
+
+impl Error {
+ pub fn new(type_name: &'static str, val: u64) -> Error {
+ Error { type_name, val }
+ }
+
+ pub fn raw_val(&self) -> u64 {
+ self.val
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "enum field type {} has a bad value {}",
+ self.type_name, self.val
+ )
+ }
+}
+
+impl std::error::Error for Error {}
+
+#[doc(hidden)]
+pub trait BitFieldSpecifier {
+ // Width of this field in bits.
+ const FIELD_WIDTH: u8;
+ // Date type for setter of this field.
+ // For any field, we use the closest u* type. e.g. FIELD_WIDTH <= 8 will
+ // have defulat type of u8.
+ // It's possible to write a custom specifier and use i8.
+ type SetterType;
+ // Data type for getter of this field. For enums, it will be Result<EnumType, SetterType>.
+ // For others, it will be the same as SetterType.
+ type GetterType;
+
+ fn from_u64(val: u64) -> Self::GetterType;
+ fn into_u64(val: Self::SetterType) -> u64;
+}
+
+// Largest u64 representable by this bit field specifier. Used by generated code
+// in bit_field_derive.
+#[doc(hidden)]
+#[inline]
+pub fn max<T: BitFieldSpecifier>() -> u64 {
+ if T::FIELD_WIDTH < 64 {
+ (1 << T::FIELD_WIDTH) - 1
+ } else {
+ u64::max_value()
+ }
+}
+
+// Defines bit_field::BitField0 through bit_field::BitField64.
+bit_field_derive::define_bit_field_specifiers!();
+
+impl BitFieldSpecifier for bool {
+ const FIELD_WIDTH: u8 = 1;
+ type SetterType = bool;
+ type GetterType = bool;
+
+ #[inline]
+ fn from_u64(val: u64) -> Self::GetterType {
+ val > 0
+ }
+
+ #[inline]
+ fn into_u64(val: Self::SetterType) -> u64 {
+ val as u64
+ }
+}
+
+// Instantiated by the generated code to prove that the total size of fields is
+// a multiple of 8 bits.
+#[doc(hidden)]
+pub struct Check<T: checks::TotalSizeIsMultipleOfEightBits> {
+ marker: std::marker::PhantomData<T>,
+}
+
+mod checks {
+ pub trait TotalSizeIsMultipleOfEightBits {}
+ impl TotalSizeIsMultipleOfEightBits for [u8; 0] {}
+}
diff --git a/bit_field/tests/test_enum.rs b/bit_field/tests/test_enum.rs
new file mode 100644
index 0000000..3839f21
--- /dev/null
+++ b/bit_field/tests/test_enum.rs
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use bit_field::*;
+
+#[bitfield]
+#[derive(Debug, PartialEq)]
+enum TwoBits {
+ Zero = 0b00,
+ One = 0b01,
+ Two = 0b10,
+ Three = 0b11,
+}
+
+#[bitfield]
+#[bits = 3]
+#[derive(Debug, PartialEq)]
+enum ThreeBits {
+ Zero = 0b00,
+ One = 0b01,
+ Two = 0b10,
+ Three = 0b111,
+}
+
+#[bitfield]
+struct Struct {
+ prefix: BitField1,
+ two_bits: TwoBits,
+ three_bits: ThreeBits,
+ suffix: BitField2,
+}
+
+#[test]
+fn test_enum() {
+ let mut s = Struct::new();
+ assert_eq!(s.get(0, 8), 0b_0000_0000);
+ assert_eq!(s.get_two_bits(), TwoBits::Zero);
+
+ s.set_two_bits(TwoBits::Three);
+ assert_eq!(s.get(0, 8), 0b_0000_0110);
+ assert_eq!(s.get_two_bits(), TwoBits::Three);
+
+ s.set(0, 8, 0b_1010_1010);
+ // ^^ TwoBits
+ // ^^_^ Three Bits.
+ assert_eq!(s.get_two_bits(), TwoBits::One);
+ assert_eq!(s.get_three_bits().unwrap_err().raw_val(), 0b101);
+
+ s.set_three_bits(ThreeBits::Two);
+ assert_eq!(s.get(0, 8), 0b_1001_0010);
+}
diff --git a/bit_field/tests/test_tuple_struct.rs b/bit_field/tests/test_tuple_struct.rs
new file mode 100644
index 0000000..13566a0
--- /dev/null
+++ b/bit_field/tests/test_tuple_struct.rs
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use bit_field::*;
+
+#[bitfield]
+#[bits = 5]
+#[derive(Debug, PartialEq)]
+pub struct FiveBits(u8);
+
+#[bitfield]
+struct Struct {
+ prefix: BitField1,
+ five_bits: FiveBits,
+ suffix: BitField2,
+}
+
+#[test]
+fn test_enum() {
+ let mut s = Struct::new();
+ assert_eq!(s.get(0, 8), 0b_0000_0000);
+
+ s.set_five_bits(FiveBits(0b10101));
+ assert_eq!(s.get(0, 8), 0b_0010_1010);
+ assert_eq!(s.get_five_bits(), FiveBits(0b10101));
+}
diff --git a/build_test b/build_test
new file mode 120000
index 0000000..a864fcf
--- /dev/null
+++ b/build_test
@@ -0,0 +1 @@
+build_test.py
\ No newline at end of file
diff --git a/build_test.py b/build_test.py
new file mode 100755
index 0000000..21d87df
--- /dev/null
+++ b/build_test.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python3
+# Copyright 2017 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.
+
+"""Builds crosvm in debug/release mode on all supported target architectures.
+
+A sysroot for each target architectures is required. The defaults are all
+generic boards' sysroots, but they can be changed with the command line
+arguments.
+
+To test changes more quickly, set the --noclean option. This prevents the
+target directories from being removed before building and testing.
+"""
+
+from __future__ import print_function
+import argparse
+import multiprocessing.pool
+import os
+import shutil
+import subprocess
+import sys
+
+ARM_TRIPLE = os.getenv('ARM_TRIPLE', 'armv7a-cros-linux-gnueabihf')
+AARCH64_TRIPLE = os.getenv('AARCH64_TRIPLE', 'aarch64-cros-linux-gnu')
+X86_64_TRIPLE = os.getenv('X86_64_TRIPLE', 'x86_64-cros-linux-gnu')
+
+TEST_MODULES_PARALLEL = [
+ 'crosvm',
+ 'data_model',
+ 'kernel_loader',
+ 'kvm',
+ 'kvm_sys',
+ 'net_sys',
+ 'net_util',
+ 'syscall_defines',
+ 'vhost',
+ 'virtio_sys',
+ 'x86_64',
+]
+
+TEST_MODULES_SERIAL = [
+ 'io_jail',
+ 'sys_util',
+]
+
+# Bright green.
+PASS_COLOR = '\033[1;32m'
+# Bright red.
+FAIL_COLOR = '\033[1;31m'
+# Default color.
+END_COLOR = '\033[0m'
+
+
+def get_target_path(triple, kind, test_it):
+ """Constructs a target path based on the configuration parameters.
+
+ Args:
+ triple: Target triple. Example: 'x86_64-unknown-linux-gnu'.
+ kind: 'debug' or 'release'.
+ test_it: If this target is tested.
+ """
+ target_path = '/tmp/%s_%s' % (triple, kind)
+ if test_it:
+ target_path += '_test'
+ return target_path
+
+
+def build_target(triple, is_release, env):
+ """Does a cargo build for the triple in release or debug mode.
+
+ Args:
+ triple: Target triple. Example: 'x86_64-unknown-linux-gnu'.
+ is_release: True to build a release version.
+ env: Enviroment variables to run cargo with.
+ """
+ args = ['cargo', 'build', '--target=%s' % triple]
+
+ if is_release:
+ args.append('--release')
+
+ return subprocess.Popen(args, env=env).wait() == 0
+
+
+def test_target_modules(triple, is_release, env, modules, parallel):
+ """Does a cargo test on given modules for the triple and configuration.
+
+ Args:
+ triple: Target triple. Example: 'x86_64-unknown-linux-gnu'.
+ is_release: True to build a release version.
+ env: Enviroment variables to run cargo with.
+ modules: List of module strings to test.
+ parallel: True to run the tests in parallel threads.
+ """
+ args = ['cargo', 'test', '--target=%s' % triple]
+
+ if is_release:
+ args.append('--release')
+
+ for mod in modules:
+ args.append('-p')
+ args.append(mod)
+
+ if not parallel:
+ args.append('--')
+ args.append('--test-threads=1')
+
+ return subprocess.Popen(args, env=env).wait() == 0
+
+
+def test_target(triple, is_release, env):
+ """Does a cargo test for the given triple and configuration.
+
+ Args:
+ triple: Target triple. Example: 'x86_64-unknown-linux-gnu'.
+ is_release: True to build a release version.
+ env: Enviroment variables to run cargo with.
+ """
+
+ parallel_result = test_target_modules(
+ triple, is_release, env, TEST_MODULES_PARALLEL, True)
+
+ serial_result = test_target_modules(
+ triple, is_release, env, TEST_MODULES_SERIAL, False)
+
+ return parallel_result and serial_result
+
+
+def check_build(sysroot, triple, kind, test_it, clean):
+ """Runs relavent builds/tests for the given triple and configuration
+
+ Args:
+ sysroot: path to the target's sysroot directory.
+ triple: Target triple. Example: 'x86_64-unknown-linux-gnu'.
+ kind: 'debug' or 'release'.
+ test_it: True to test this triple and kind.
+ clean: True to skip cleaning the target path.
+ """
+ if not os.path.isdir(sysroot):
+ return 'sysroot missing'
+
+ target_path = get_target_path(triple, kind, test_it)
+
+ if clean:
+ shutil.rmtree(target_path, True)
+
+ is_release = kind == 'release'
+
+ env = os.environ.copy()
+ env['TARGET_CC'] = '%s-clang'%triple
+ env['SYSROOT'] = sysroot
+ env['CARGO_TARGET_DIR'] = target_path
+
+ if test_it:
+ if not test_target(triple, is_release, env):
+ return 'test error'
+ else:
+ if not build_target(triple, is_release, env):
+ return 'build error'
+
+ return 'pass'
+
+
+def get_stripped_size(triple):
+ """Returns the formatted size of the given triple's release binary.
+
+ Args:
+ triple: Target triple. Example: 'x86_64-unknown-linux-gnu'.
+ """
+ target_path = get_target_path(triple, 'release', False)
+ bin_path = os.path.join(target_path, triple, 'release', 'crosvm')
+ proc = subprocess.Popen(['%s-strip' % triple, bin_path])
+
+ if proc.wait() != 0:
+ return 'failed'
+
+ return '%dKiB' % (os.path.getsize(bin_path) / 1024)
+
+
+def get_parser():
+ """Gets the argument parser"""
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--arm-sysroot',
+ default='/build/arm-generic',
+ help='ARM sysroot directory (default=%(default)s)')
+ parser.add_argument('--aarch64-sysroot',
+ default='/build/arm64-generic',
+ help='AARCH64 sysroot directory (default=%(default)s)')
+ parser.add_argument('--x86_64-sysroot',
+ default='/build/amd64-generic',
+ help='x86_64 sysroot directory (default=%(default)s)')
+ parser.add_argument('--noclean', dest='clean', default=True,
+ action='store_false',
+ help='Keep the tempororary build directories.')
+ return parser
+
+
+def main(argv):
+ opts = get_parser().parse_args(argv)
+ build_test_cases = (
+ #(sysroot path, target triple, debug/release, should test?)
+ (opts.arm_sysroot, ARM_TRIPLE, "debug", False, opts.clean),
+ (opts.arm_sysroot, ARM_TRIPLE, "release", False, opts.clean),
+ (opts.aarch64_sysroot, AARCH64_TRIPLE, "debug", False, opts.clean),
+ (opts.aarch64_sysroot, AARCH64_TRIPLE, "release", False, opts.clean),
+ (opts.x86_64_sysroot, X86_64_TRIPLE, "debug", False, opts.clean),
+ (opts.x86_64_sysroot, X86_64_TRIPLE, "release", False, opts.clean),
+ (opts.x86_64_sysroot, X86_64_TRIPLE, "debug", True, opts.clean),
+ (opts.x86_64_sysroot, X86_64_TRIPLE, "release", True, opts.clean),
+ )
+
+ os.chdir(os.path.dirname(sys.argv[0]))
+ pool = multiprocessing.pool.Pool(len(build_test_cases))
+ results = pool.starmap(check_build, build_test_cases, 1)
+
+ print('---')
+ print('build test summary:')
+ for test_case, result in zip(build_test_cases, results):
+ _, triple, kind, test_it, _ = test_case
+ title = '%s_%s' % (triple.split('-')[0], kind)
+ if test_it:
+ title += "_test"
+
+ result_color = FAIL_COLOR
+ if result == 'pass':
+ result_color = PASS_COLOR
+
+ display_size = ''
+ if result == 'pass' and kind == 'release' and not test_it:
+ display_size = get_stripped_size(triple) + ' stripped binary'
+
+ print('%20s: %s%15s%s %s' %
+ (title, result_color, result, END_COLOR, display_size))
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/crosvm_plugin/Cargo.toml b/crosvm_plugin/Cargo.toml
new file mode 100644
index 0000000..6805898
--- /dev/null
+++ b/crosvm_plugin/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "crosvm_plugin"
+version = "0.17.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+kvm = { path = "../kvm" }
+kvm_sys = { path = "../kvm_sys" }
+libc = "*"
+protobuf = "2.3"
+protos = { path = "../protos", features = ["plugin"] }
+sys_util = { path = "../sys_util" }
diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h
new file mode 100644
index 0000000..d7a036c
--- /dev/null
+++ b/crosvm_plugin/crosvm.h
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#ifndef __CROSVM_H__
+#define __CROSVM_H__
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <linux/kvm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This module is used to implement a plugin for crosvm.
+ *
+ * A plugin for crosvm interfaces with the virtual machine using the `struct
+ * crosvm` object and its child objects. A typical plugin is expected to call
+ * `crosvm_connect`, perform some amount of setup with the functions defined
+ * here, get a handle to every vcpu using `struct crosvm_vcpu` and then call
+ * `crosvm_start`. Each vcpu will then be waited on with `crosvm_vcpu_wait`,
+ * each event will be responded to by the plugin, and then the vcpu is resumed
+ * with `crosvm_vcpu_resume`. The vcpu state can only be examined and modified
+ * between the `crosvm_vcpu_wait` and `crosvm_vcpu_resume` calls. The crosvm
+ * connection can be used to modify global virtual machine state at any time,
+ * with some structural restrictions after `crosvm_start` is called.
+ *
+ * In general, functions that return an `int` return 0 on success or a non-
+ * negative file descriptor if one is expected. A negative return value is an
+ * errno and indicates error. Functions that take a pointer-to-pointer to an
+ * opaque structure either return a structure or delete and nullify that
+ * structure pointer.
+ */
+
+/*
+ * We use Semantic Versioning (http://semver.org/) here, which means that as
+ * long as MAJOR is 0, breaking changes can occur, but once MAJOR is non-zero, a
+ * breaking change requires a MAJOR version bump. The MINOR number increases as
+ * backward compatible functionality is added. The PATCH number increases bug
+ * fixes are done. The version numbers indicate here are for the plugin API and
+ * do not indicate anything about what version of crosvm is running.
+ */
+#define CROSVM_API_MAJOR 0
+#define CROSVM_API_MINOR 17
+#define CROSVM_API_PATCH 0
+
+enum crosvm_address_space {
+ /* I/O port */
+ CROSVM_ADDRESS_SPACE_IOPORT = 0,
+ /* physical memory space */
+ CROSVM_ADDRESS_SPACE_MMIO,
+};
+
+/* Handle to the parent crosvm process. */
+struct crosvm;
+
+/* Handle to a register ioeventfd. */
+struct crosvm_io;
+
+/* Handle to a registered range of shared memory. */
+struct crosvm_memory;
+
+/* Handle to a registered irqfd. */
+struct crosvm_irq;
+
+/* Handle to one of the VM's VCPUs. */
+struct crosvm_vcpu;
+
+/*
+ * Connects to the parent crosvm process and returns a new `struct crosvm`
+ * interface object.
+ *
+ * This is the entry point for interfacing with crosvm as a plugin. This should
+ * be called before any other function. The returned object is not-thread safe.
+ */
+int crosvm_connect(struct crosvm**);
+
+/*
+ * Creates another connection for interfacing with crosvm concurrently.
+ *
+ * The new connection behaves exactly like the original `struct crosvm` but can
+ * be used concurrently on a different thread than the original. Actual
+ * execution order of the requests to crosvm is unspecified but every request is
+ * completed when the `crosvm_*` call returns.
+ *
+ * It is invalid to call this after `crosvm_start` is called on any `struct
+ * crosvm`.
+ */
+int crosvm_new_connection(struct crosvm*, struct crosvm**);
+
+/*
+ * Destroys this connection and tells the parent crosvm process to stop
+ * listening for messages from it.
+ */
+int crosvm_destroy_connection(struct crosvm**);
+
+/*
+ * Gets an eventfd that is triggered when this plugin should exit.
+ *
+ * The returned eventfd is owned by the caller but the underlying event is
+ * shared and will therefore only trigger once.
+ */
+int crosvm_get_shutdown_eventfd(struct crosvm*);
+
+/*
+ * Gets a bool indicating if a KVM_CAP_* enum is supported on this VM
+ */
+int crosvm_check_extension(struct crosvm*, uint32_t __extension,
+ bool *has_extension);
+
+/*
+ * Queries x86 cpuid features which are supported by the hardware and
+ * kvm.
+ */
+int crosvm_get_supported_cpuid(struct crosvm*, uint32_t __entry_count,
+ struct kvm_cpuid_entry2 *__cpuid_entries,
+ uint32_t *__out_count);
+
+/*
+ * Queries x86 cpuid features which are emulated by kvm.
+ */
+int crosvm_get_emulated_cpuid(struct crosvm*, uint32_t __entry_count,
+ struct kvm_cpuid_entry2 *__cpuid_entries,
+ uint32_t *__out_count);
+
+/*
+ * Queries kvm for list of supported MSRs.
+ */
+int crosvm_get_msr_index_list(struct crosvm*, uint32_t __entry_count,
+ uint32_t *__msr_indices,
+ uint32_t *__out_count);
+
+/*
+ * The network configuration for a crosvm instance.
+ */
+struct crosvm_net_config {
+ /*
+ * The tap device fd. This fd is owned by the caller, and should be closed
+ * by the caller when it is no longer in use.
+ */
+ int tap_fd;
+ /* The IPv4 address of the tap interface, in network (big-endian) format. */
+ uint32_t host_ip;
+ /* The netmask of the tap interface subnet, in network (big-endian) format. */
+ uint32_t netmask;
+ /* The mac address of the host side of the tap interface. */
+ uint8_t host_mac_address[6];
+ uint8_t _padding[2];
+};
+
+#ifdef static_assert
+static_assert(sizeof(struct crosvm_net_config) == 20,
+ "extra padding in struct crosvm_net_config");
+#endif
+
+/*
+ * Gets the network configuration.
+ */
+int crosvm_net_get_config(struct crosvm*, struct crosvm_net_config*);
+
+/*
+ * Registers a range in the given address space that, when accessed, will block
+ * and wait for a crosvm_vcpu_resume call.
+ *
+ * To unreserve a range previously reserved by this function, pass the |__space|
+ * and |__start| of the old reservation with a 0 |__length|.
+ */
+int crosvm_reserve_range(struct crosvm*, uint32_t __space, uint64_t __start,
+ uint64_t __length);
+
+/*
+ * Sets the state of the given irq pin.
+ */
+int crosvm_set_irq(struct crosvm*, uint32_t __irq_id, bool __active);
+
+enum crosvm_irq_route_kind {
+ /* IRQ pin to GSI route */
+ CROSVM_IRQ_ROUTE_IRQCHIP = 0,
+ /* MSI address and data to GSI route */
+ CROSVM_IRQ_ROUTE_MSI,
+};
+
+/* One entry in the array of irq routing table */
+struct crosvm_irq_route {
+ /* The IRQ number to trigger. */
+ uint32_t irq_id;
+ /* A `crosvm_irq_route_kind` indicating which union member to use */
+ uint32_t kind;
+ union {
+ struct {
+ /*
+ * One of KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE, or
+ * KVM_IRQCHIP_IOAPIC indicating which irqchip the indicated pin is on.
+ */
+ uint32_t irqchip;
+ /* The pin on the irqchip used to trigger the IRQ. */
+ uint32_t pin;
+ } irqchip;
+
+ struct {
+ /* Address that triggers the irq. */
+ uint64_t address;
+ /* Data written to `address` that triggers the irq */
+ uint32_t data;
+
+ uint8_t _reserved[4];
+ } msi;
+
+ uint8_t _reserved[16];
+ };
+};
+
+#ifdef static_assert
+static_assert(sizeof(struct crosvm_irq_route) == 24,
+ "extra padding in struct crosvm_irq_route");
+#endif
+
+/*
+ * Sets all the gsi routing entries to those indicated by `routes`.
+ *
+ * To remove all routing entries, pass NULL for `routes` and 0 to route_count.
+ */
+int crosvm_set_irq_routing(struct crosvm*, uint32_t __route_count,
+ const struct crosvm_irq_route* __routes);
+
+/* Gets the state of interrupt controller in a VM. */
+int crosvm_get_pic_state(struct crosvm *, bool __primary,
+ struct kvm_pic_state *__pic_state);
+
+/* Sets the state of interrupt controller in a VM. */
+int crosvm_set_pic_state(struct crosvm *, bool __primary,
+ const struct kvm_pic_state *__pic_state);
+
+/* Gets the state of IOAPIC in a VM. */
+int crosvm_get_ioapic_state(struct crosvm *,
+ struct kvm_ioapic_state *__ioapic_state);
+
+/* Sets the state of IOAPIC in a VM. */
+int crosvm_set_ioapic_state(struct crosvm *,
+ const struct kvm_ioapic_state *__ioapic_state);
+
+/* Gets the state of interrupt controller in a VM. */
+int crosvm_get_pit_state(struct crosvm *, struct kvm_pit_state2 *__pit_state);
+
+/* Sets the state of interrupt controller in a VM. */
+int crosvm_set_pit_state(struct crosvm *,
+ const struct kvm_pit_state2 *__pit_state);
+
+/* Gets the current timestamp of kvmclock as seen by the VM. */
+int crosvm_get_clock(struct crosvm *, struct kvm_clock_data *__clock_data);
+
+/* Sets the current timestamp of kvmclock for the VM. */
+int crosvm_set_clock(struct crosvm *,
+ const struct kvm_clock_data *__clock_data);
+
+/* Sets the identity map address as in the KVM_SET_IDENTITY_MAP_ADDR ioctl. */
+int crosvm_set_identity_map_addr(struct crosvm*, uint32_t __addr);
+
+/*
+ * Triggers a CROSVM_VCPU_EVENT_KIND_PAUSED event on each vcpu identified
+ * |__cpu_mask|.
+ *
+ * The `user` pointer will be given as the `user` pointer in the `struct
+ * crosvm_vcpu_event` returned by crosvm_vcpu_wait.
+ */
+int crosvm_pause_vcpus(struct crosvm*, uint64_t __cpu_mask, void* __user);
+
+/*
+ * Call once initialization is done. This indicates that crosvm should proceed
+ * with running the VM.
+ *
+ * After this call, this function is no longer valid to call.
+ */
+int crosvm_start(struct crosvm*);
+
+/*
+ * Allocates an eventfd that is triggered asynchronously on write in |__space|
+ * at the given |__addr|.
+ *
+ * If |__datamatch| is non-NULL, it must be contain |__length| bytes that will
+ * be compared to the bytes being written by the vcpu which will only trigger
+ * the eventfd if equal. If datamatch is NULL all writes to the address will
+ * trigger the eventfd.
+ *
+ * On successful allocation, returns a crosvm_io. Obtain the actual fd
+ * by passing this result to crosvm_io_event_fd().
+ */
+int crosvm_create_io_event(struct crosvm*, uint32_t __space, uint64_t __addr,
+ uint32_t __len, const uint8_t* __datamatch,
+ struct crosvm_io**);
+
+/*
+ * Destroys the given io event and unregisters it from the VM.
+ */
+int crosvm_destroy_io_event(struct crosvm*, struct crosvm_io**);
+
+/*
+ * Gets the eventfd triggered by the given io event.
+ *
+ * The returned fd is owned by the given `struct crosvm_io` and has a lifetime
+ * equal to that handle.
+ */
+int crosvm_io_event_fd(struct crosvm_io*);
+
+/*
+ * Creates a shared memory segment backed by a memfd.
+ *
+ * Inserts non-overlapping memory pages in the guest physical address range
+ * specified by |__start| address and |__length| bytes. The memory pages are
+ * backed by the memfd |__fd| and are taken starting at |__offset| bytes from
+ * the beginning of the memfd.
+ *
+ * The `memfd_create` syscall |__fd| must be used to create |__fd| and a shrink
+ * seal must have been added to |__fd|. The memfd must be at least
+ * `__length+__offset` bytes long.
+ *
+ * If |read_only| is true, attempts by the guest to write to this memory region
+ * will trigger an IO access exit.
+ *
+ * To use the `crosvm_memory_get_dirty_log` method with the returned object,
+ * |__dirty_log| must be true.
+ */
+int crosvm_create_memory(struct crosvm*, int __fd, uint64_t __offset,
+ uint64_t __length, uint64_t __start,
+ bool __read_only, bool __dirty_log,
+ struct crosvm_memory**);
+
+/*
+ * Destroys the given shared memory and unregisters it from guest physical
+ * address space.
+ */
+int crosvm_destroy_memory(struct crosvm*, struct crosvm_memory**);
+
+/*
+ * For a given memory region returns a bitmap containing any pages
+ * dirtied since the last call to this function.
+ *
+ * The `log` array must have as many bits as the memory segment has pages.
+ */
+int crosvm_memory_get_dirty_log(struct crosvm*, struct crosvm_memory*,
+ uint8_t* __log);
+
+/*
+ * Creates an irq eventfd that can be used to trigger an irq asynchronously.
+ *
+ * The irq that will be triggered is identified as pin |__irq_id|.
+ */
+int crosvm_create_irq_event(struct crosvm*, uint32_t __irq_id,
+ struct crosvm_irq**);
+
+/*
+ * Unregisters and destroys an irq eventfd.
+ */
+int crosvm_destroy_irq_event(struct crosvm*, struct crosvm_irq**);
+
+/*
+ * Gets the eventfd used to trigger the irq
+ *
+ * The returned fd is owned by the given `struct crosvm_irq` and has a lifetime
+ * equal to that handle.
+ */
+int crosvm_irq_event_get_fd(const struct crosvm_irq*);
+
+/*
+ * Gets the resample eventfd associated with the crosvm_irq object.
+ */
+int crosvm_irq_event_get_resample_fd(const struct crosvm_irq*);
+
+enum crosvm_vcpu_event_kind {
+ /*
+ * The first event returned by crosvm_vcpu_wait, indicating the VCPU has been
+ * created but not yet started for the first time.
+ */
+ CROSVM_VCPU_EVENT_KIND_INIT = 0,
+
+ /*
+ * Access to an address in a space previously reserved by
+ * crosvm_reserve_range.
+ */
+ CROSVM_VCPU_EVENT_KIND_IO_ACCESS,
+
+ /*
+ * A pause on this vcpu (and possibly others) was requested by this plugin in
+ * a `crosvm_pause_vcpus` call.
+ */
+ CROSVM_VCPU_EVENT_KIND_PAUSED,
+};
+
+struct crosvm_vcpu_event {
+ /* Indicates the kind of event and which union member is valid. */
+ uint32_t kind;
+
+ uint8_t _padding[4];
+
+ union {
+ /* CROSVM_VCPU_EVENT_KIND_IO_ACCESS */
+ struct {
+ /*
+ * One of `enum crosvm_address_space` indicating which address space the
+ * access occurred in.
+ */
+ uint32_t address_space;
+
+ uint8_t _padding[4];
+
+ /* The address that the access occurred at. */
+ uint64_t address;
+
+ /*
+ * In the case that `is_write` is true, the first `length` bytes are the
+ * data being written by the vcpu.
+ */
+ uint8_t *data;
+
+ /*
+ * Number of bytes in the access. In the case that the access is larger
+ * than 8 bytes, such as by AVX-512 instructions, multiple vcpu access
+ * events are generated serially to cover each 8 byte fragment of the
+ * access.
+ *
+ * Larger I/O accesses are possible. "rep in" can generate I/Os larger
+ * than 8 bytes, though such accesses can also be split into multiple
+ * events. Currently kvm doesn't seem to batch "rep out" I/Os.
+ */
+ uint32_t length;
+
+ /*
+ * True if the vcpu was attempting to write, false in case of an attempt
+ * to read.
+ */
+ uint8_t is_write;
+
+ uint8_t _reserved[3];
+ } io_access;
+
+ /* CROSVM_VCPU_EVENT_KIND_PAUSED */
+ void *user;
+
+ uint8_t _reserved[64];
+ };
+};
+
+#ifdef static_assert
+static_assert(sizeof(struct crosvm_vcpu_event) == 72,
+ "extra padding in struct crosvm_vcpu_event");
+#endif
+
+/*
+ * Gets the vcpu object for the given |__cpu_id|.
+ *
+ *
+ * The `struct crosvm_vcpu` is owned by `struct crosvm`. Each call with the same
+ * `crosvm` and |__cpu_id| will yield the same pointer. The `crosvm_vcpu` does
+ * not need to be destroyed or created explicitly.
+ *
+ * The range of valid |__cpu_id|s is 0 to the number of vcpus - 1. To get every
+ * `crosvm_vcpu`, simply call this function iteratively with increasing
+ * |__cpu_id| until `-ENOENT` is returned.
+ *
+ */
+int crosvm_get_vcpu(struct crosvm*, uint32_t __cpu_id, struct crosvm_vcpu**);
+
+/*
+ * Blocks until a vcpu event happens that requires a response.
+ *
+ * When crosvm_vcpu_wait returns successfully, the event structure is filled
+ * with the description of the event that occurred. The vcpu will suspend
+ * execution until a matching call to `crosvm_vcpu_resume` is made. Until such a
+ * call is made, the vcpu's run structure can be read and written using any
+ * `crosvm_vcpu_get` or `crosvm_vcpu_set` function.
+ */
+int crosvm_vcpu_wait(struct crosvm_vcpu*, struct crosvm_vcpu_event*);
+
+/*
+ * Resumes execution of a vcpu after a call to `crosvm_vcpu_wait` returns.
+ *
+ * In the case that the event was a read operation, `data` indicates what the
+ * result of that read operation should be. If the read operation was larger
+ * than 8 bytes, such as by AVX-512 instructions, this will not actually resume
+ * the vcpu, but instead generate another vcpu access event of the next fragment
+ * of the read, which can be handled by the next `crosvm_vcpu_wait` call.
+ *
+ * Once the vcpu event has been responded to sufficiently enough to resume
+ * execution, `crosvm_vcpu_resume` should be called. After `crosvm_vcpu_resume`
+ * is called, none of the vcpu state operations are valid until the next time
+ * `crosvm_vcpu_wait` returns.
+ */
+int crosvm_vcpu_resume(struct crosvm_vcpu*);
+
+/* Gets the state of the vcpu's registers. */
+int crosvm_vcpu_get_regs(struct crosvm_vcpu*, struct kvm_regs*);
+/* Sets the state of the vcpu's registers. */
+int crosvm_vcpu_set_regs(struct crosvm_vcpu*, const struct kvm_regs*);
+
+/* Gets the state of the vcpu's special registers. */
+int crosvm_vcpu_get_sregs(struct crosvm_vcpu*, struct kvm_sregs*);
+/* Sets the state of the vcpu's special registers. */
+int crosvm_vcpu_set_sregs(struct crosvm_vcpu*, const struct kvm_sregs*);
+
+/* Gets the state of the vcpu's floating point unint. */
+int crosvm_vcpu_get_fpu(struct crosvm_vcpu*, struct kvm_fpu*);
+/* Sets the state of the vcpu's floating point unint. */
+int crosvm_vcpu_set_fpu(struct crosvm_vcpu*, const struct kvm_fpu*);
+
+/* Gets the state of the vcpu's debug registers. */
+int crosvm_vcpu_get_debugregs(struct crosvm_vcpu*, struct kvm_debugregs*);
+/* Sets the state of the vcpu's debug registers */
+int crosvm_vcpu_set_debugregs(struct crosvm_vcpu*, const struct kvm_debugregs*);
+
+/* Gets the state of the vcpu's xcr registers. */
+int crosvm_vcpu_get_xcrs(struct crosvm_vcpu*, struct kvm_xcrs*);
+/* Sets the state of the vcpu's xcr registers. */
+int crosvm_vcpu_set_xcrs(struct crosvm_vcpu*, const struct kvm_xcrs*);
+
+/* Gets the MSRs of the vcpu indicated by the index field of each entry. */
+int crosvm_vcpu_get_msrs(struct crosvm_vcpu*, uint32_t __msr_count,
+ struct kvm_msr_entry *__msr_entries,
+ uint32_t *__out_count);
+/* Sets the MSRs of the vcpu indicated by the index field of each entry. */
+int crosvm_vcpu_set_msrs(struct crosvm_vcpu*, uint32_t __msr_count,
+ const struct kvm_msr_entry *__msr_entries);
+
+/* Sets the responses to the cpuid instructions executed on this vcpu, */
+int crosvm_vcpu_set_cpuid(struct crosvm_vcpu*, uint32_t __cpuid_count,
+ const struct kvm_cpuid_entry2 *__cpuid_entries);
+
+/* Gets state of LAPIC of the VCPU. */
+int crosvm_vcpu_get_lapic_state(struct crosvm_vcpu *,
+ struct kvm_lapic_state *__lapic_state);
+/* Sets state of LAPIC of the VCPU. */
+int crosvm_vcpu_set_lapic_state(struct crosvm_vcpu *,
+ const struct kvm_lapic_state *__lapic_state);
+
+/* Gets the "multiprocessor state" of given VCPU. */
+int crosvm_vcpu_get_mp_state(struct crosvm_vcpu *,
+ struct kvm_mp_state *__mp_state);
+/* Sets the "multiprocessor state" of given VCPU. */
+int crosvm_vcpu_set_mp_state(struct crosvm_vcpu *,
+ const struct kvm_mp_state *__mp_state);
+
+/* Gets currently pending exceptions, interrupts, NMIs, etc for VCPU. */
+int crosvm_vcpu_get_vcpu_events(struct crosvm_vcpu *,
+ struct kvm_vcpu_events *);
+
+/* Sets currently pending exceptions, interrupts, NMIs, etc for VCPU. */
+int crosvm_vcpu_set_vcpu_events(struct crosvm_vcpu *,
+ const struct kvm_vcpu_events *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs
new file mode 100644
index 0000000..a6fd4df
--- /dev/null
+++ b/crosvm_plugin/src/lib.rs
@@ -0,0 +1,1686 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![allow(non_camel_case_types)]
+
+//! This module implements the dynamically loaded client library API used by a crosvm plugin,
+//! defined in `crosvm.h`. It implements the client half of the plugin protocol, which is defined in
+//! the `protos::plugin` module.
+//!
+//! To implement the `crosvm.h` C API, each function and struct definition is repeated here, with
+//! concrete definitions for each struct. Most functions are thin shims to the underlying object
+//! oriented Rust implementation method. Most methods require a request over the crosvm connection,
+//! which is done by creating a `MainRequest` or `VcpuRequest` protobuf and sending it over the
+//! connection's socket. Then, that socket is read for a `MainResponse` or `VcpuResponse`, which is
+//! translated to the appropriate return type for the C API.
+
+use std::env;
+use std::fs::File;
+use std::io::{Read, Write};
+use std::mem::{size_of, swap};
+use std::os::raw::{c_int, c_void};
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::unix::net::UnixDatagram;
+use std::ptr::{self, null_mut};
+use std::result;
+use std::slice;
+use std::slice::{from_raw_parts, from_raw_parts_mut};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::time::Instant;
+
+use libc::{E2BIG, EINVAL, ENOENT, ENOTCONN, EPROTO};
+
+use protobuf::{parse_from_bytes, Message, ProtobufEnum, RepeatedField};
+
+use sys_util::ScmSocket;
+
+use kvm::dirty_log_bitmap_size;
+
+use kvm_sys::{
+ kvm_clock_data, kvm_cpuid_entry2, kvm_debugregs, kvm_fpu, kvm_ioapic_state, kvm_lapic_state,
+ kvm_mp_state, kvm_msr_entry, kvm_pic_state, kvm_pit_state2, kvm_regs, kvm_sregs,
+ kvm_vcpu_events, kvm_xcrs,
+};
+
+use protos::plugin::*;
+
+// Needs to be large enough to receive all the VCPU sockets.
+const MAX_DATAGRAM_FD: usize = 32;
+// Needs to be large enough for a sizable dirty log.
+const MAX_DATAGRAM_SIZE: usize = 0x40000;
+
+const CROSVM_IRQ_ROUTE_IRQCHIP: u32 = 0;
+const CROSVM_IRQ_ROUTE_MSI: u32 = 1;
+
+const CROSVM_VCPU_EVENT_KIND_INIT: u32 = 0;
+const CROSVM_VCPU_EVENT_KIND_IO_ACCESS: u32 = 1;
+const CROSVM_VCPU_EVENT_KIND_PAUSED: u32 = 2;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct crosvm_net_config {
+ tap_fd: c_int,
+ host_ipv4_address: u32,
+ netmask: u32,
+ host_mac_address: [u8; 6],
+ _reserved: [u8; 2],
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct anon_irqchip {
+ irqchip: u32,
+ pin: u32,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct anon_msi {
+ address: u64,
+ data: u32,
+}
+
+#[repr(C)]
+pub union anon_route {
+ irqchip: anon_irqchip,
+ msi: anon_msi,
+ reserved: [u8; 16],
+}
+
+#[repr(C)]
+pub struct crosvm_irq_route {
+ irq_id: u32,
+ kind: u32,
+ route: anon_route,
+}
+
+fn proto_error_to_int(e: protobuf::ProtobufError) -> c_int {
+ match e {
+ protobuf::ProtobufError::IoError(e) => e.raw_os_error().unwrap_or(EINVAL),
+ _ => EINVAL,
+ }
+}
+
+fn fd_cast<F: FromRawFd>(f: File) -> F {
+ // Safe because we are transferring unique ownership.
+ unsafe { F::from_raw_fd(f.into_raw_fd()) }
+}
+
+#[derive(Default)]
+struct IdAllocator(AtomicUsize);
+
+impl IdAllocator {
+ fn alloc(&self) -> u32 {
+ self.0.fetch_add(1, Ordering::Relaxed) as u32
+ }
+
+ fn free(&self, id: u32) {
+ self.0
+ .compare_and_swap(id as usize + 1, id as usize, Ordering::Relaxed);
+ }
+}
+
+#[repr(u8)]
+#[derive(Debug, Clone, Copy)]
+enum Stat {
+ IoEventFd,
+ MemoryGetDirtyLog,
+ IrqEventGetFd,
+ IrqEventGetResampleFd,
+ Connect,
+ DestroyConnection,
+ GetShutdownEventFd,
+ CheckExtentsion,
+ GetSupportedCpuid,
+ GetEmulatedCpuid,
+ GetMsrIndexList,
+ NetGetConfig,
+ ReserveRange,
+ SetIrq,
+ SetIrqRouting,
+ GetPicState,
+ SetPicState,
+ GetIoapicState,
+ SetIoapicState,
+ GetPitState,
+ SetPitState,
+ GetClock,
+ SetClock,
+ SetIdentityMapAddr,
+ PauseVcpus,
+ Start,
+ GetVcpu,
+ VcpuWait,
+ VcpuResume,
+ VcpuGetRegs,
+ VcpuSetRegs,
+ VcpuGetSregs,
+ VcpuSetSregs,
+ GetFpu,
+ SetFpu,
+ GetDebugRegs,
+ SetDebugRegs,
+ GetXCRegs,
+ SetXCRegs,
+ VcpuGetMsrs,
+ VcpuSetMsrs,
+ VcpuSetCpuid,
+ VcpuGetLapicState,
+ VcpuSetLapicState,
+ VcpuGetMpState,
+ VcpuSetMpState,
+ VcpuGetVcpuEvents,
+ VcpuSetVcpuEvents,
+ NewConnection,
+
+ Count,
+}
+
+#[derive(Clone, Copy)]
+struct StatEntry {
+ count: u64,
+ total: u64,
+ max: u64,
+}
+
+struct StatUpdater {
+ idx: usize,
+ start: Instant,
+}
+
+struct GlobalStats {
+ entries: [StatEntry; Stat::Count as usize],
+}
+
+static mut STATS: GlobalStats = GlobalStats {
+ entries: [StatEntry {
+ count: 0,
+ total: 0,
+ max: 0,
+ }; Stat::Count as usize],
+};
+
+impl GlobalStats {
+ // Record latency from this call until the end of block/function
+ // Example:
+ // pub fn foo() {
+ // let _u = STATS.record(Stat::Foo);
+ // // ... some operation ...
+ // }
+ // The added STATS.record will record latency of "some operation" and will
+ // update max and average latencies for it under Stats::Foo. Subsequent
+ // call to STATS.print() will print out max and average latencies for all
+ // operations that were performed.
+ fn record(&mut self, idx: Stat) -> StatUpdater {
+ StatUpdater {
+ idx: idx as usize,
+ start: Instant::now(),
+ }
+ }
+
+ fn print(&self) {
+ for idx in 0..Stat::Count as usize {
+ let e = &self.entries[idx as usize];
+ let stat = unsafe { std::mem::transmute::<u8, Stat>(idx as u8) };
+ if e.count > 0 {
+ println!(
+ "Stat::{:?}: avg {}ns max {}ns",
+ stat,
+ e.total / e.count,
+ e.max
+ );
+ }
+ }
+ }
+
+ fn update(&mut self, idx: usize, elapsed_nanos: u64) {
+ let e = &mut self.entries[idx as usize];
+ e.total += elapsed_nanos;
+ if e.max < elapsed_nanos {
+ e.max = elapsed_nanos;
+ }
+ e.count += 1;
+ }
+}
+
+impl Drop for StatUpdater {
+ fn drop(&mut self) {
+ let elapsed = self.start.elapsed();
+ let elapsed_nanos = elapsed.as_secs() * 1000000000 + elapsed.subsec_nanos() as u64;
+ // Unsafe due to racy access - OK for stats
+ unsafe {
+ STATS.update(self.idx, elapsed_nanos);
+ }
+ }
+}
+
+pub struct crosvm {
+ id_allocator: Arc<IdAllocator>,
+ socket: UnixDatagram,
+ request_buffer: Vec<u8>,
+ response_buffer: Vec<u8>,
+ vcpus: Arc<Vec<crosvm_vcpu>>,
+}
+
+impl crosvm {
+ fn from_connection(socket: UnixDatagram) -> result::Result<crosvm, c_int> {
+ let mut crosvm = crosvm {
+ id_allocator: Default::default(),
+ socket,
+ request_buffer: Vec::new(),
+ response_buffer: vec![0; MAX_DATAGRAM_SIZE],
+ vcpus: Default::default(),
+ };
+ crosvm.load_all_vcpus()?;
+ Ok(crosvm)
+ }
+
+ fn new(
+ id_allocator: Arc<IdAllocator>,
+ socket: UnixDatagram,
+ vcpus: Arc<Vec<crosvm_vcpu>>,
+ ) -> crosvm {
+ crosvm {
+ id_allocator,
+ socket,
+ request_buffer: Vec::new(),
+ response_buffer: vec![0; MAX_DATAGRAM_SIZE],
+ vcpus,
+ }
+ }
+
+ fn get_id_allocator(&self) -> &IdAllocator {
+ &*self.id_allocator
+ }
+
+ fn main_transaction(
+ &mut self,
+ request: &MainRequest,
+ fds: &[RawFd],
+ ) -> result::Result<(MainResponse, Vec<File>), c_int> {
+ self.request_buffer.clear();
+ request
+ .write_to_vec(&mut self.request_buffer)
+ .map_err(proto_error_to_int)?;
+ self.socket
+ .send_with_fds(self.request_buffer.as_slice(), fds)
+ .map_err(|e| -e.errno())?;
+
+ let mut datagram_fds = [0; MAX_DATAGRAM_FD];
+ let (msg_size, fd_count) = self
+ .socket
+ .recv_with_fds(&mut self.response_buffer, &mut datagram_fds)
+ .map_err(|e| -e.errno())?;
+ // Safe because the first fd_count fds from recv_with_fds are owned by us and valid.
+ let datagram_files = datagram_fds[..fd_count]
+ .iter()
+ .map(|&fd| unsafe { File::from_raw_fd(fd) })
+ .collect();
+
+ let response: MainResponse =
+ parse_from_bytes(&self.response_buffer[..msg_size]).map_err(proto_error_to_int)?;
+ if response.errno != 0 {
+ return Err(response.errno);
+ }
+ Ok((response, datagram_files))
+ }
+
+ fn try_clone(&mut self) -> result::Result<crosvm, c_int> {
+ let mut r = MainRequest::new();
+ r.mut_new_connection();
+ let mut files = self.main_transaction(&r, &[])?.1;
+ match files.pop() {
+ Some(new_socket) => Ok(crosvm::new(
+ self.id_allocator.clone(),
+ fd_cast(new_socket),
+ self.vcpus.clone(),
+ )),
+ None => Err(EPROTO),
+ }
+ }
+
+ fn destroy(&mut self, id: u32) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ r.mut_destroy().id = id;
+ self.main_transaction(&r, &[])?;
+ self.get_id_allocator().free(id);
+ // Unsafe due to racy access - OK for stats
+ if std::env::var("CROSVM_STATS").is_ok() {
+ unsafe {
+ STATS.print();
+ }
+ }
+ Ok(())
+ }
+
+ // Only call this at `from_connection` function.
+ fn load_all_vcpus(&mut self) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ r.mut_get_vcpus();
+ let (_, mut files) = self.main_transaction(&r, &[])?;
+ if files.is_empty() || files.len() % 2 != 0 {
+ return Err(EPROTO);
+ }
+
+ let mut vcpus = Vec::with_capacity(files.len() / 2);
+ while files.len() > 1 {
+ let write_pipe = files.remove(0);
+ let read_pipe = files.remove(0);
+ vcpus.push(crosvm_vcpu::new(fd_cast(read_pipe), fd_cast(write_pipe)));
+ }
+ // Only called once by the `from_connection` constructor, which makes a new unique
+ // `self.vcpus`.
+ let self_vcpus = Arc::get_mut(&mut self.vcpus).unwrap();
+ *self_vcpus = vcpus;
+ Ok(())
+ }
+
+ fn get_shutdown_eventfd(&mut self) -> result::Result<File, c_int> {
+ let mut r = MainRequest::new();
+ r.mut_get_shutdown_eventfd();
+ let (_, mut files) = self.main_transaction(&r, &[])?;
+ match files.pop() {
+ Some(f) => Ok(f),
+ None => Err(EPROTO),
+ }
+ }
+
+ fn check_extension(&mut self, extension: u32) -> result::Result<bool, c_int> {
+ let mut r = MainRequest::new();
+ r.mut_check_extension().extension = extension;
+ let (response, _) = self.main_transaction(&r, &[])?;
+ if !response.has_check_extension() {
+ return Err(EPROTO);
+ }
+ Ok(response.get_check_extension().has_extension)
+ }
+
+ fn get_supported_cpuid(
+ &mut self,
+ cpuid_entries: &mut [kvm_cpuid_entry2],
+ cpuid_count: &mut usize,
+ ) -> result::Result<(), c_int> {
+ *cpuid_count = 0;
+
+ let mut r = MainRequest::new();
+ r.mut_get_supported_cpuid();
+
+ let (response, _) = self.main_transaction(&r, &[])?;
+ if !response.has_get_supported_cpuid() {
+ return Err(EPROTO);
+ }
+
+ let supported_cpuids: &MainResponse_CpuidResponse = response.get_get_supported_cpuid();
+
+ *cpuid_count = supported_cpuids.get_entries().len();
+ if *cpuid_count > cpuid_entries.len() {
+ return Err(E2BIG);
+ }
+
+ for (proto_entry, kvm_entry) in supported_cpuids
+ .get_entries()
+ .iter()
+ .zip(cpuid_entries.iter_mut())
+ {
+ *kvm_entry = cpuid_proto_to_kvm(proto_entry);
+ }
+
+ Ok(())
+ }
+
+ fn get_emulated_cpuid(
+ &mut self,
+ cpuid_entries: &mut [kvm_cpuid_entry2],
+ cpuid_count: &mut usize,
+ ) -> result::Result<(), c_int> {
+ *cpuid_count = 0;
+
+ let mut r = MainRequest::new();
+ r.mut_get_emulated_cpuid();
+
+ let (response, _) = self.main_transaction(&r, &[])?;
+ if !response.has_get_emulated_cpuid() {
+ return Err(EPROTO);
+ }
+
+ let emulated_cpuids: &MainResponse_CpuidResponse = response.get_get_emulated_cpuid();
+
+ *cpuid_count = emulated_cpuids.get_entries().len();
+ if *cpuid_count > cpuid_entries.len() {
+ return Err(E2BIG);
+ }
+
+ for (proto_entry, kvm_entry) in emulated_cpuids
+ .get_entries()
+ .iter()
+ .zip(cpuid_entries.iter_mut())
+ {
+ *kvm_entry = cpuid_proto_to_kvm(proto_entry);
+ }
+
+ Ok(())
+ }
+
+ fn get_msr_index_list(
+ &mut self,
+ msr_indices: &mut [u32],
+ msr_count: &mut usize,
+ ) -> result::Result<(), c_int> {
+ *msr_count = 0;
+
+ let mut r = MainRequest::new();
+ r.mut_get_msr_index_list();
+
+ let (response, _) = self.main_transaction(&r, &[])?;
+ if !response.has_get_msr_index_list() {
+ return Err(EPROTO);
+ }
+
+ let msr_list: &MainResponse_MsrListResponse = response.get_get_msr_index_list();
+
+ *msr_count = msr_list.get_indices().len();
+ if *msr_count > msr_indices.len() {
+ return Err(E2BIG);
+ }
+
+ for (proto_entry, kvm_entry) in msr_list.get_indices().iter().zip(msr_indices.iter_mut()) {
+ *kvm_entry = *proto_entry;
+ }
+
+ Ok(())
+ }
+
+ fn reserve_range(&mut self, space: u32, start: u64, length: u64) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ let reserve: &mut MainRequest_ReserveRange = r.mut_reserve_range();
+ reserve.space = AddressSpace::from_i32(space as i32).ok_or(EINVAL)?;
+ reserve.start = start;
+ reserve.length = length;
+
+ self.main_transaction(&r, &[])?;
+ Ok(())
+ }
+
+ fn set_irq(&mut self, irq_id: u32, active: bool) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ let set_irq: &mut MainRequest_SetIrq = r.mut_set_irq();
+ set_irq.irq_id = irq_id;
+ set_irq.active = active;
+
+ self.main_transaction(&r, &[])?;
+ Ok(())
+ }
+
+ fn set_irq_routing(&mut self, routing: &[crosvm_irq_route]) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ let set_irq_routing: &mut RepeatedField<MainRequest_SetIrqRouting_Route> =
+ r.mut_set_irq_routing().mut_routes();
+ for route in routing {
+ let mut entry = MainRequest_SetIrqRouting_Route::new();
+ entry.irq_id = route.irq_id;
+ match route.kind {
+ CROSVM_IRQ_ROUTE_IRQCHIP => {
+ let irqchip: &mut MainRequest_SetIrqRouting_Route_Irqchip;
+ irqchip = entry.mut_irqchip();
+ // Safe because route.kind indicates which union field is valid.
+ irqchip.irqchip = unsafe { route.route.irqchip }.irqchip;
+ irqchip.pin = unsafe { route.route.irqchip }.pin;
+ }
+ CROSVM_IRQ_ROUTE_MSI => {
+ let msi: &mut MainRequest_SetIrqRouting_Route_Msi = entry.mut_msi();
+ // Safe because route.kind indicates which union field is valid.
+ msi.address = unsafe { route.route.msi }.address;
+ msi.data = unsafe { route.route.msi }.data;
+ }
+ _ => return Err(EINVAL),
+ }
+ set_irq_routing.push(entry);
+ }
+
+ self.main_transaction(&r, &[])?;
+ Ok(())
+ }
+
+ fn get_state(
+ &mut self,
+ state_set: MainRequest_StateSet,
+ out: &mut [u8],
+ ) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ r.mut_get_state().set = state_set;
+ let (response, _) = self.main_transaction(&r, &[])?;
+ if !response.has_get_state() {
+ return Err(EPROTO);
+ }
+ let get_state: &MainResponse_GetState = response.get_get_state();
+ if get_state.state.len() != out.len() {
+ return Err(EPROTO);
+ }
+ out.copy_from_slice(&get_state.state);
+ Ok(())
+ }
+
+ fn set_state(
+ &mut self,
+ state_set: MainRequest_StateSet,
+ new_state: &[u8],
+ ) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ let set_state: &mut MainRequest_SetState = r.mut_set_state();
+ set_state.set = state_set;
+ set_state.state = new_state.to_vec();
+
+ self.main_transaction(&r, &[])?;
+ Ok(())
+ }
+
+ fn set_identity_map_addr(&mut self, addr: u32) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ r.mut_set_identity_map_addr().address = addr;
+
+ self.main_transaction(&r, &[])?;
+ Ok(())
+ }
+
+ fn pause_vcpus(&mut self, cpu_mask: u64, user: *mut c_void) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ let pause_vcpus: &mut MainRequest_PauseVcpus = r.mut_pause_vcpus();
+ pause_vcpus.cpu_mask = cpu_mask;
+ pause_vcpus.user = user as u64;
+ self.main_transaction(&r, &[])?;
+ Ok(())
+ }
+
+ fn start(&mut self) -> result::Result<(), c_int> {
+ let mut r = MainRequest::new();
+ r.mut_start();
+ self.main_transaction(&r, &[])?;
+ Ok(())
+ }
+
+ fn get_vcpu(&mut self, cpu_id: u32) -> Result<*mut crosvm_vcpu, c_int> {
+ if let Some(vcpu) = self.vcpus.get(cpu_id as usize) {
+ Ok(vcpu as *const crosvm_vcpu as *mut crosvm_vcpu)
+ } else {
+ Err(ENOENT)
+ }
+ }
+
+ fn get_net_config(&mut self) -> result::Result<crosvm_net_config, c_int> {
+ let mut r = MainRequest::new();
+ r.mut_get_net_config();
+
+ let (response, mut files) = self.main_transaction(&r, &[])?;
+ if !response.has_get_net_config() {
+ return Err(EPROTO);
+ }
+ let config = response.get_get_net_config();
+
+ match files.pop() {
+ Some(f) => {
+ let mut net_config = crosvm_net_config {
+ tap_fd: f.into_raw_fd(),
+ host_ipv4_address: config.host_ipv4_address,
+ netmask: config.netmask,
+ host_mac_address: [0; 6],
+ _reserved: [0; 2],
+ };
+
+ let mac_addr = config.get_host_mac_address();
+ if mac_addr.len() != net_config.host_mac_address.len() {
+ return Err(EPROTO);
+ }
+ net_config.host_mac_address.copy_from_slice(mac_addr);
+
+ Ok(net_config)
+ }
+ None => Err(EPROTO),
+ }
+ }
+}
+
+/// This helper macro implements the C API's constructor/destructor for a given type. Because they
+/// all follow the same pattern and include lots of boilerplate unsafe code, it makes sense to write
+/// it once with this helper macro.
+macro_rules! impl_ctor_dtor {
+ (
+ $t:ident,
+ $ctor:ident ( $( $x:ident: $y:ty ),* ),
+ $dtor:ident,
+ ) => {
+ #[allow(unused_unsafe)]
+ #[no_mangle]
+ pub unsafe extern fn $ctor(self_: *mut crosvm, $($x: $y,)* obj_ptr: *mut *mut $t) -> c_int {
+ let self_ = &mut (*self_);
+ match $t::create(self_, $($x,)*) {
+ Ok(obj) => {
+ *obj_ptr = Box::into_raw(Box::new(obj));
+ 0
+ }
+ Err(e) => -e,
+ }
+ }
+ #[no_mangle]
+ pub unsafe extern fn $dtor(self_: *mut crosvm, obj_ptr: *mut *mut $t) -> c_int {
+ let self_ = &mut (*self_);
+ let obj = Box::from_raw(*obj_ptr);
+ match self_.destroy(obj.id) {
+ Ok(_) => {
+ *obj_ptr = null_mut();
+ 0
+ }
+ Err(e) => {
+ Box::into_raw(obj);
+ -e
+ }
+ }
+ }
+ }
+}
+
+pub struct crosvm_io_event {
+ id: u32,
+ evt: File,
+}
+
+impl crosvm_io_event {
+ // Clippy: we use ptr::read_unaligned to read from pointers that may be
+ // underaligned. Dereferencing such a pointer is always undefined behavior
+ // in Rust.
+ //
+ // Lint can be unsuppressed once Clippy recognizes this pattern as correct.
+ // https://github.com/rust-lang/rust-clippy/issues/2881
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe fn create(
+ crosvm: &mut crosvm,
+ space: u32,
+ addr: u64,
+ length: u32,
+ datamatch: *const u8,
+ ) -> result::Result<crosvm_io_event, c_int> {
+ let datamatch = match length {
+ 0 => 0,
+ 1 => ptr::read_unaligned(datamatch as *const u8) as u64,
+ 2 => ptr::read_unaligned(datamatch as *const u16) as u64,
+ 4 => ptr::read_unaligned(datamatch as *const u32) as u64,
+ 8 => ptr::read_unaligned(datamatch as *const u64),
+ _ => return Err(EINVAL),
+ };
+ Self::safe_create(crosvm, space, addr, length, datamatch)
+ }
+
+ fn safe_create(
+ crosvm: &mut crosvm,
+ space: u32,
+ addr: u64,
+ length: u32,
+ datamatch: u64,
+ ) -> result::Result<crosvm_io_event, c_int> {
+ let id = crosvm.get_id_allocator().alloc();
+
+ let mut r = MainRequest::new();
+ let create: &mut MainRequest_Create = r.mut_create();
+ create.id = id;
+ let io_event: &mut MainRequest_Create_IoEvent = create.mut_io_event();
+ io_event.space = AddressSpace::from_i32(space as i32).ok_or(EINVAL)?;
+ io_event.address = addr;
+ io_event.length = length;
+ io_event.datamatch = datamatch;
+
+ let ret = match crosvm.main_transaction(&r, &[]) {
+ Ok((_, mut files)) => match files.pop() {
+ Some(evt) => return Ok(crosvm_io_event { id, evt }),
+ None => EPROTO,
+ },
+ Err(e) => e,
+ };
+ crosvm.get_id_allocator().free(id);
+ Err(ret)
+ }
+}
+
+impl_ctor_dtor!(
+ crosvm_io_event,
+ crosvm_create_io_event(space: u32, addr: u64, len: u32, datamatch: *const u8),
+ crosvm_destroy_io_event,
+);
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_io_event_fd(this: *mut crosvm_io_event) -> c_int {
+ let _u = STATS.record(Stat::IoEventFd);
+ (*this).evt.as_raw_fd()
+}
+
+pub struct crosvm_memory {
+ id: u32,
+ length: u64,
+}
+
+impl crosvm_memory {
+ fn create(
+ crosvm: &mut crosvm,
+ fd: c_int,
+ offset: u64,
+ length: u64,
+ start: u64,
+ read_only: bool,
+ dirty_log: bool,
+ ) -> result::Result<crosvm_memory, c_int> {
+ const PAGE_MASK: u64 = 0x0fff;
+ if offset & PAGE_MASK != 0 || length & PAGE_MASK != 0 {
+ return Err(EINVAL);
+ }
+ let id = crosvm.get_id_allocator().alloc();
+
+ let mut r = MainRequest::new();
+ let create: &mut MainRequest_Create = r.mut_create();
+ create.id = id;
+ let memory: &mut MainRequest_Create_Memory = create.mut_memory();
+ memory.offset = offset;
+ memory.start = start;
+ memory.length = length;
+ memory.read_only = read_only;
+ memory.dirty_log = dirty_log;
+
+ let ret = match crosvm.main_transaction(&r, &[fd]) {
+ Ok(_) => return Ok(crosvm_memory { id, length }),
+ Err(e) => e,
+ };
+ crosvm.get_id_allocator().free(id);
+ Err(ret)
+ }
+
+ fn get_dirty_log(&mut self, crosvm: &mut crosvm) -> result::Result<Vec<u8>, c_int> {
+ let mut r = MainRequest::new();
+ r.mut_dirty_log().id = self.id;
+ let (mut response, _) = crosvm.main_transaction(&r, &[])?;
+ if !response.has_dirty_log() {
+ return Err(EPROTO);
+ }
+ Ok(response.take_dirty_log().bitmap)
+ }
+}
+
+impl_ctor_dtor!(
+ crosvm_memory,
+ crosvm_create_memory(
+ fd: c_int,
+ offset: u64,
+ length: u64,
+ start: u64,
+ read_only: bool,
+ dirty_log: bool
+ ),
+ crosvm_destroy_memory,
+);
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_memory_get_dirty_log(
+ crosvm: *mut crosvm,
+ this: *mut crosvm_memory,
+ log: *mut u8,
+) -> c_int {
+ let _u = STATS.record(Stat::MemoryGetDirtyLog);
+ let crosvm = &mut *crosvm;
+ let this = &mut *this;
+ let log_slice = slice::from_raw_parts_mut(log, dirty_log_bitmap_size(this.length as usize));
+ match this.get_dirty_log(crosvm) {
+ Ok(bitmap) => {
+ if bitmap.len() == log_slice.len() {
+ log_slice.copy_from_slice(&bitmap);
+ 0
+ } else {
+ -EPROTO
+ }
+ }
+ Err(e) => -e,
+ }
+}
+
+pub struct crosvm_irq_event {
+ id: u32,
+ trigger_evt: File,
+ resample_evt: File,
+}
+
+impl crosvm_irq_event {
+ fn create(crosvm: &mut crosvm, irq_id: u32) -> result::Result<crosvm_irq_event, c_int> {
+ let id = crosvm.get_id_allocator().alloc();
+
+ let mut r = MainRequest::new();
+ let create: &mut MainRequest_Create = r.mut_create();
+ create.id = id;
+ let irq_event: &mut MainRequest_Create_IrqEvent = create.mut_irq_event();
+ irq_event.irq_id = irq_id;
+ irq_event.resample = true;
+
+ let ret = match crosvm.main_transaction(&r, &[]) {
+ Ok((_, mut files)) => {
+ if files.len() >= 2 {
+ let resample_evt = files.pop().unwrap();
+ let trigger_evt = files.pop().unwrap();
+ return Ok(crosvm_irq_event {
+ id,
+ trigger_evt,
+ resample_evt,
+ });
+ }
+ EPROTO
+ }
+ Err(e) => e,
+ };
+ crosvm.get_id_allocator().free(id);
+ Err(ret)
+ }
+}
+
+impl_ctor_dtor!(
+ crosvm_irq_event,
+ crosvm_create_irq_event(irq_id: u32),
+ crosvm_destroy_irq_event,
+);
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_irq_event_get_fd(this: *mut crosvm_irq_event) -> c_int {
+ let _u = STATS.record(Stat::IrqEventGetFd);
+ (*this).trigger_evt.as_raw_fd()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_irq_event_get_resample_fd(this: *mut crosvm_irq_event) -> c_int {
+ let _u = STATS.record(Stat::IrqEventGetResampleFd);
+ (*this).resample_evt.as_raw_fd()
+}
+
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct anon_io_access {
+ address_space: u32,
+ __reserved0: [u8; 4],
+ address: u64,
+ data: *mut u8,
+ length: u32,
+ is_write: u8,
+ __reserved1: u8,
+}
+
+#[repr(C)]
+union anon_vcpu_event {
+ io_access: anon_io_access,
+ user: *mut c_void,
+ #[allow(dead_code)]
+ __reserved: [u8; 64],
+}
+
+#[repr(C)]
+pub struct crosvm_vcpu_event {
+ kind: u32,
+ __reserved: [u8; 4],
+ event: anon_vcpu_event,
+}
+
+pub struct crosvm_vcpu {
+ read_pipe: File,
+ write_pipe: File,
+ send_init: bool,
+ request_buffer: Vec<u8>,
+ response_buffer: Vec<u8>,
+ resume_data: Vec<u8>,
+}
+
+impl crosvm_vcpu {
+ fn new(read_pipe: File, write_pipe: File) -> crosvm_vcpu {
+ crosvm_vcpu {
+ read_pipe,
+ write_pipe,
+ send_init: true,
+ request_buffer: Vec::new(),
+ response_buffer: vec![0; MAX_DATAGRAM_SIZE],
+ resume_data: Vec::new(),
+ }
+ }
+ fn vcpu_send(&mut self, request: &VcpuRequest) -> result::Result<(), c_int> {
+ self.request_buffer.clear();
+ request
+ .write_to_vec(&mut self.request_buffer)
+ .map_err(proto_error_to_int)?;
+ self.write_pipe
+ .write(self.request_buffer.as_slice())
+ .map_err(|e| -e.raw_os_error().unwrap_or(EINVAL))?;
+ Ok(())
+ }
+
+ fn vcpu_recv(&mut self) -> result::Result<VcpuResponse, c_int> {
+ let msg_size = self
+ .read_pipe
+ .read(&mut self.response_buffer)
+ .map_err(|e| -e.raw_os_error().unwrap_or(EINVAL))?;
+
+ let response: VcpuResponse =
+ parse_from_bytes(&self.response_buffer[..msg_size]).map_err(proto_error_to_int)?;
+ if response.errno != 0 {
+ return Err(response.errno);
+ }
+ Ok(response)
+ }
+
+ fn vcpu_transaction(&mut self, request: &VcpuRequest) -> result::Result<VcpuResponse, c_int> {
+ self.vcpu_send(request)?;
+ let response: VcpuResponse = self.vcpu_recv()?;
+ Ok(response)
+ }
+
+ fn wait(&mut self, event: &mut crosvm_vcpu_event) -> result::Result<(), c_int> {
+ if self.send_init {
+ self.send_init = false;
+ let mut r = VcpuRequest::new();
+ r.mut_wait();
+ self.vcpu_send(&r)?;
+ }
+ let mut response: VcpuResponse = self.vcpu_recv()?;
+ if !response.has_wait() {
+ return Err(EPROTO);
+ }
+ let wait: &mut VcpuResponse_Wait = response.mut_wait();
+ if wait.has_init() {
+ event.kind = CROSVM_VCPU_EVENT_KIND_INIT;
+ Ok(())
+ } else if wait.has_io() {
+ let mut io: VcpuResponse_Wait_Io = wait.take_io();
+ event.kind = CROSVM_VCPU_EVENT_KIND_IO_ACCESS;
+ event.event.io_access = anon_io_access {
+ address_space: io.space.value() as u32,
+ __reserved0: Default::default(),
+ address: io.address,
+ data: io.data.as_mut_ptr(),
+ length: io.data.len() as u32,
+ is_write: io.is_write as u8,
+ __reserved1: Default::default(),
+ };
+ self.resume_data = io.data;
+ Ok(())
+ } else if wait.has_user() {
+ let user: &VcpuResponse_Wait_User = wait.get_user();
+ event.kind = CROSVM_VCPU_EVENT_KIND_PAUSED;
+ event.event.user = user.user as *mut c_void;
+ Ok(())
+ } else {
+ Err(EPROTO)
+ }
+ }
+
+ fn resume(&mut self) -> result::Result<(), c_int> {
+ let mut r = VcpuRequest::new();
+ let resume: &mut VcpuRequest_Resume = r.mut_resume();
+ swap(&mut resume.data, &mut self.resume_data);
+
+ self.vcpu_send(&r)?;
+ Ok(())
+ }
+
+ fn get_state(
+ &mut self,
+ state_set: VcpuRequest_StateSet,
+ out: &mut [u8],
+ ) -> result::Result<(), c_int> {
+ let mut r = VcpuRequest::new();
+ r.mut_get_state().set = state_set;
+ let response = self.vcpu_transaction(&r)?;
+ if !response.has_get_state() {
+ return Err(EPROTO);
+ }
+ let get_state: &VcpuResponse_GetState = response.get_get_state();
+ if get_state.state.len() != out.len() {
+ return Err(EPROTO);
+ }
+ out.copy_from_slice(&get_state.state);
+ Ok(())
+ }
+
+ fn set_state(
+ &mut self,
+ state_set: VcpuRequest_StateSet,
+ new_state: &[u8],
+ ) -> result::Result<(), c_int> {
+ let mut r = VcpuRequest::new();
+ let set_state: &mut VcpuRequest_SetState = r.mut_set_state();
+ set_state.set = state_set;
+ set_state.state = new_state.to_vec();
+
+ self.vcpu_transaction(&r)?;
+ Ok(())
+ }
+
+ fn get_msrs(
+ &mut self,
+ msr_entries: &mut [kvm_msr_entry],
+ msr_count: &mut usize,
+ ) -> result::Result<(), c_int> {
+ *msr_count = 0;
+
+ let mut r = VcpuRequest::new();
+ let entry_indices: &mut Vec<u32> = r.mut_get_msrs().mut_entry_indices();
+ for entry in msr_entries.iter() {
+ entry_indices.push(entry.index);
+ }
+
+ let response = self.vcpu_transaction(&r)?;
+ if !response.has_get_msrs() {
+ return Err(EPROTO);
+ }
+ let get_msrs: &VcpuResponse_GetMsrs = response.get_get_msrs();
+ *msr_count = get_msrs.get_entry_data().len();
+ if *msr_count > msr_entries.len() {
+ return Err(E2BIG);
+ }
+ for (&msr_data, msr_entry) in get_msrs.get_entry_data().iter().zip(msr_entries) {
+ msr_entry.data = msr_data;
+ }
+ Ok(())
+ }
+
+ fn set_msrs(&mut self, msr_entries: &[kvm_msr_entry]) -> result::Result<(), c_int> {
+ let mut r = VcpuRequest::new();
+ let set_msrs_entries: &mut RepeatedField<VcpuRequest_MsrEntry> =
+ r.mut_set_msrs().mut_entries();
+ for msr_entry in msr_entries {
+ let mut entry = VcpuRequest_MsrEntry::new();
+ entry.index = msr_entry.index;
+ entry.data = msr_entry.data;
+ set_msrs_entries.push(entry);
+ }
+
+ self.vcpu_transaction(&r)?;
+ Ok(())
+ }
+
+ fn set_cpuid(&mut self, cpuid_entries: &[kvm_cpuid_entry2]) -> result::Result<(), c_int> {
+ let mut r = VcpuRequest::new();
+ let set_cpuid_entries: &mut RepeatedField<CpuidEntry> = r.mut_set_cpuid().mut_entries();
+ for cpuid_entry in cpuid_entries {
+ set_cpuid_entries.push(cpuid_kvm_to_proto(cpuid_entry));
+ }
+
+ self.vcpu_transaction(&r)?;
+ Ok(())
+ }
+}
+
+// crosvm API signals success as 0 and errors as negative values
+// derived from `errno`.
+fn to_crosvm_rc<T>(r: result::Result<T, c_int>) -> c_int {
+ match r {
+ Ok(_) => 0,
+ Err(e) => -e,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_connect(out: *mut *mut crosvm) -> c_int {
+ let _u = STATS.record(Stat::Connect);
+ let socket_name = match env::var("CROSVM_SOCKET") {
+ Ok(v) => v,
+ _ => return -ENOTCONN,
+ };
+
+ let socket = match socket_name.parse() {
+ Ok(v) if v < 0 => return -EINVAL,
+ Ok(v) => v,
+ _ => return -EINVAL,
+ };
+
+ let socket = UnixDatagram::from_raw_fd(socket);
+ let crosvm = match crosvm::from_connection(socket) {
+ Ok(c) => c,
+ Err(e) => return -e,
+ };
+ *out = Box::into_raw(Box::new(crosvm));
+ 0
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_new_connection(self_: *mut crosvm, out: *mut *mut crosvm) -> c_int {
+ let _u = STATS.record(Stat::NewConnection);
+ let self_ = &mut (*self_);
+ match self_.try_clone() {
+ Ok(cloned) => {
+ *out = Box::into_raw(Box::new(cloned));
+ 0
+ }
+ Err(e) => -e,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_destroy_connection(self_: *mut *mut crosvm) -> c_int {
+ let _u = STATS.record(Stat::DestroyConnection);
+ Box::from_raw(*self_);
+ *self_ = null_mut();
+ 0
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_shutdown_eventfd(self_: *mut crosvm) -> c_int {
+ let _u = STATS.record(Stat::GetShutdownEventFd);
+ let self_ = &mut (*self_);
+ match self_.get_shutdown_eventfd() {
+ Ok(f) => f.into_raw_fd(),
+ Err(e) => -e,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_check_extension(
+ self_: *mut crosvm,
+ extension: u32,
+ has_extension: *mut bool,
+) -> c_int {
+ let _u = STATS.record(Stat::CheckExtentsion);
+ let self_ = &mut (*self_);
+ let ret = self_.check_extension(extension);
+
+ if let Ok(supported) = ret {
+ *has_extension = supported;
+ }
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_supported_cpuid(
+ this: *mut crosvm,
+ entry_count: u32,
+ cpuid_entries: *mut kvm_cpuid_entry2,
+ out_count: *mut u32,
+) -> c_int {
+ let _u = STATS.record(Stat::GetSupportedCpuid);
+ let this = &mut *this;
+ let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize);
+ let mut cpuid_count: usize = 0;
+ let ret = this.get_supported_cpuid(cpuid_entries, &mut cpuid_count);
+ *out_count = cpuid_count as u32;
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_emulated_cpuid(
+ this: *mut crosvm,
+ entry_count: u32,
+ cpuid_entries: *mut kvm_cpuid_entry2,
+ out_count: *mut u32,
+) -> c_int {
+ let _u = STATS.record(Stat::GetEmulatedCpuid);
+ let this = &mut *this;
+ let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize);
+ let mut cpuid_count: usize = 0;
+ let ret = this.get_emulated_cpuid(cpuid_entries, &mut cpuid_count);
+ *out_count = cpuid_count as u32;
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_msr_index_list(
+ this: *mut crosvm,
+ entry_count: u32,
+ msr_indices: *mut u32,
+ out_count: *mut u32,
+) -> c_int {
+ let _u = STATS.record(Stat::GetMsrIndexList);
+ let this = &mut *this;
+ let msr_indices = from_raw_parts_mut(msr_indices, entry_count as usize);
+ let mut msr_count: usize = 0;
+ let ret = this.get_msr_index_list(msr_indices, &mut msr_count);
+ *out_count = msr_count as u32;
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_net_get_config(
+ self_: *mut crosvm,
+ config: *mut crosvm_net_config,
+) -> c_int {
+ let _u = STATS.record(Stat::NetGetConfig);
+ let self_ = &mut (*self_);
+ let ret = self_.get_net_config();
+
+ if let Ok(c) = ret {
+ *config = c;
+ }
+
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_reserve_range(
+ self_: *mut crosvm,
+ space: u32,
+ start: u64,
+ length: u64,
+) -> c_int {
+ let _u = STATS.record(Stat::ReserveRange);
+ let self_ = &mut (*self_);
+ let ret = self_.reserve_range(space, start, length);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_set_irq(self_: *mut crosvm, irq_id: u32, active: bool) -> c_int {
+ let _u = STATS.record(Stat::SetIrq);
+ let self_ = &mut (*self_);
+ let ret = self_.set_irq(irq_id, active);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_set_irq_routing(
+ self_: *mut crosvm,
+ route_count: u32,
+ routes: *const crosvm_irq_route,
+) -> c_int {
+ let _u = STATS.record(Stat::SetIrqRouting);
+ let self_ = &mut (*self_);
+ let ret = self_.set_irq_routing(slice::from_raw_parts(routes, route_count as usize));
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_pic_state(
+ this: *mut crosvm,
+ primary: bool,
+ state: *mut kvm_pic_state,
+) -> c_int {
+ let _u = STATS.record(Stat::GetPicState);
+ let this = &mut *this;
+ let state_set = if primary {
+ MainRequest_StateSet::PIC0
+ } else {
+ MainRequest_StateSet::PIC1
+ };
+ let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_pic_state>());
+ let ret = this.get_state(state_set, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_set_pic_state(
+ this: *mut crosvm,
+ primary: bool,
+ state: *mut kvm_pic_state,
+) -> c_int {
+ let _u = STATS.record(Stat::SetPicState);
+ let this = &mut *this;
+ let state_set = if primary {
+ MainRequest_StateSet::PIC0
+ } else {
+ MainRequest_StateSet::PIC1
+ };
+ let state = from_raw_parts(state as *mut u8, size_of::<kvm_pic_state>());
+ let ret = this.set_state(state_set, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_ioapic_state(
+ this: *mut crosvm,
+ state: *mut kvm_ioapic_state,
+) -> c_int {
+ let _u = STATS.record(Stat::GetIoapicState);
+ let this = &mut *this;
+ let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_ioapic_state>());
+ let ret = this.get_state(MainRequest_StateSet::IOAPIC, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_set_ioapic_state(
+ this: *mut crosvm,
+ state: *const kvm_ioapic_state,
+) -> c_int {
+ let _u = STATS.record(Stat::SetIoapicState);
+ let this = &mut *this;
+ let state = from_raw_parts(state as *mut u8, size_of::<kvm_ioapic_state>());
+ let ret = this.set_state(MainRequest_StateSet::IOAPIC, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_pit_state(
+ this: *mut crosvm,
+ state: *mut kvm_pit_state2,
+) -> c_int {
+ let _u = STATS.record(Stat::GetPitState);
+ let this = &mut *this;
+ let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_pit_state2>());
+ let ret = this.get_state(MainRequest_StateSet::PIT, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_set_pit_state(
+ this: *mut crosvm,
+ state: *const kvm_pit_state2,
+) -> c_int {
+ let _u = STATS.record(Stat::SetPitState);
+ let this = &mut *this;
+ let state = from_raw_parts(state as *mut u8, size_of::<kvm_pit_state2>());
+ let ret = this.set_state(MainRequest_StateSet::PIT, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_clock(
+ this: *mut crosvm,
+ clock_data: *mut kvm_clock_data,
+) -> c_int {
+ let _u = STATS.record(Stat::GetClock);
+ let this = &mut *this;
+ let state = from_raw_parts_mut(clock_data as *mut u8, size_of::<kvm_clock_data>());
+ let ret = this.get_state(MainRequest_StateSet::CLOCK, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_set_clock(
+ this: *mut crosvm,
+ clock_data: *const kvm_clock_data,
+) -> c_int {
+ let _u = STATS.record(Stat::SetClock);
+ let this = &mut *this;
+ let state = from_raw_parts(clock_data as *mut u8, size_of::<kvm_clock_data>());
+ let ret = this.set_state(MainRequest_StateSet::CLOCK, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_set_identity_map_addr(self_: *mut crosvm, addr: u32) -> c_int {
+ let _u = STATS.record(Stat::SetIdentityMapAddr);
+ let self_ = &mut (*self_);
+ let ret = self_.set_identity_map_addr(addr);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_pause_vcpus(
+ self_: *mut crosvm,
+ cpu_mask: u64,
+ user: *mut c_void,
+) -> c_int {
+ let _u = STATS.record(Stat::PauseVcpus);
+ let self_ = &mut (*self_);
+ let ret = self_.pause_vcpus(cpu_mask, user);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_start(self_: *mut crosvm) -> c_int {
+ let _u = STATS.record(Stat::Start);
+ let self_ = &mut (*self_);
+ let ret = self_.start();
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_get_vcpu(
+ self_: *mut crosvm,
+ cpu_id: u32,
+ out: *mut *mut crosvm_vcpu,
+) -> c_int {
+ let _u = STATS.record(Stat::GetVcpu);
+ let self_ = &mut (*self_);
+ let ret = self_.get_vcpu(cpu_id);
+
+ if let Ok(vcpu) = ret {
+ *out = vcpu;
+ }
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_wait(
+ this: *mut crosvm_vcpu,
+ event: *mut crosvm_vcpu_event,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuWait);
+ let this = &mut *this;
+ let event = &mut *event;
+ let ret = this.wait(event);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_resume(this: *mut crosvm_vcpu) -> c_int {
+ let _u = STATS.record(Stat::VcpuResume);
+ let this = &mut *this;
+ let ret = this.resume();
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_regs(
+ this: *mut crosvm_vcpu,
+ regs: *mut kvm_regs,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuGetRegs);
+ let this = &mut *this;
+ let regs = from_raw_parts_mut(regs as *mut u8, size_of::<kvm_regs>());
+ let ret = this.get_state(VcpuRequest_StateSet::REGS, regs);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_regs(
+ this: *mut crosvm_vcpu,
+ regs: *const kvm_regs,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuSetRegs);
+ let this = &mut *this;
+ let regs = from_raw_parts(regs as *mut u8, size_of::<kvm_regs>());
+ let ret = this.set_state(VcpuRequest_StateSet::REGS, regs);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_sregs(
+ this: *mut crosvm_vcpu,
+ sregs: *mut kvm_sregs,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuGetSregs);
+ let this = &mut *this;
+ let sregs = from_raw_parts_mut(sregs as *mut u8, size_of::<kvm_sregs>());
+ let ret = this.get_state(VcpuRequest_StateSet::SREGS, sregs);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_sregs(
+ this: *mut crosvm_vcpu,
+ sregs: *const kvm_sregs,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuSetSregs);
+ let this = &mut *this;
+ let sregs = from_raw_parts(sregs as *mut u8, size_of::<kvm_sregs>());
+ let ret = this.set_state(VcpuRequest_StateSet::SREGS, sregs);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_fpu(this: *mut crosvm_vcpu, fpu: *mut kvm_fpu) -> c_int {
+ let _u = STATS.record(Stat::GetFpu);
+ let this = &mut *this;
+ let fpu = from_raw_parts_mut(fpu as *mut u8, size_of::<kvm_fpu>());
+ let ret = this.get_state(VcpuRequest_StateSet::FPU, fpu);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_fpu(this: *mut crosvm_vcpu, fpu: *const kvm_fpu) -> c_int {
+ let _u = STATS.record(Stat::SetFpu);
+ let this = &mut *this;
+ let fpu = from_raw_parts(fpu as *mut u8, size_of::<kvm_fpu>());
+ let ret = this.set_state(VcpuRequest_StateSet::FPU, fpu);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_debugregs(
+ this: *mut crosvm_vcpu,
+ dregs: *mut kvm_debugregs,
+) -> c_int {
+ let _u = STATS.record(Stat::GetDebugRegs);
+ let this = &mut *this;
+ let dregs = from_raw_parts_mut(dregs as *mut u8, size_of::<kvm_debugregs>());
+ let ret = this.get_state(VcpuRequest_StateSet::DEBUGREGS, dregs);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_debugregs(
+ this: *mut crosvm_vcpu,
+ dregs: *const kvm_debugregs,
+) -> c_int {
+ let _u = STATS.record(Stat::SetDebugRegs);
+ let this = &mut *this;
+ let dregs = from_raw_parts(dregs as *mut u8, size_of::<kvm_debugregs>());
+ let ret = this.set_state(VcpuRequest_StateSet::DEBUGREGS, dregs);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_xcrs(
+ this: *mut crosvm_vcpu,
+ xcrs: *mut kvm_xcrs,
+) -> c_int {
+ let _u = STATS.record(Stat::GetXCRegs);
+ let this = &mut *this;
+ let xcrs = from_raw_parts_mut(xcrs as *mut u8, size_of::<kvm_xcrs>());
+ let ret = this.get_state(VcpuRequest_StateSet::XCREGS, xcrs);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_xcrs(
+ this: *mut crosvm_vcpu,
+ xcrs: *const kvm_xcrs,
+) -> c_int {
+ let _u = STATS.record(Stat::SetXCRegs);
+ let this = &mut *this;
+ let xcrs = from_raw_parts(xcrs as *mut u8, size_of::<kvm_xcrs>());
+ let ret = this.set_state(VcpuRequest_StateSet::XCREGS, xcrs);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_msrs(
+ this: *mut crosvm_vcpu,
+ msr_count: u32,
+ msr_entries: *mut kvm_msr_entry,
+ out_count: *mut u32,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuGetMsrs);
+ let this = &mut *this;
+ let msr_entries = from_raw_parts_mut(msr_entries, msr_count as usize);
+ let mut count: usize = 0;
+ let ret = this.get_msrs(msr_entries, &mut count);
+ *out_count = count as u32;
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_msrs(
+ this: *mut crosvm_vcpu,
+ msr_count: u32,
+ msr_entries: *const kvm_msr_entry,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuSetMsrs);
+ let this = &mut *this;
+ let msr_entries = from_raw_parts(msr_entries, msr_count as usize);
+ let ret = this.set_msrs(msr_entries);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_cpuid(
+ this: *mut crosvm_vcpu,
+ cpuid_count: u32,
+ cpuid_entries: *const kvm_cpuid_entry2,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuSetCpuid);
+ let this = &mut *this;
+ let cpuid_entries = from_raw_parts(cpuid_entries, cpuid_count as usize);
+ let ret = this.set_cpuid(cpuid_entries);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_lapic_state(
+ this: *mut crosvm_vcpu,
+ state: *mut kvm_lapic_state,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuGetLapicState);
+ let this = &mut *this;
+ let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_lapic_state>());
+ let ret = this.get_state(VcpuRequest_StateSet::LAPIC, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_lapic_state(
+ this: *mut crosvm_vcpu,
+ state: *const kvm_lapic_state,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuSetLapicState);
+ let this = &mut *this;
+ let state = from_raw_parts(state as *mut u8, size_of::<kvm_lapic_state>());
+ let ret = this.set_state(VcpuRequest_StateSet::LAPIC, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_mp_state(
+ this: *mut crosvm_vcpu,
+ state: *mut kvm_mp_state,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuGetMpState);
+ let this = &mut *this;
+ let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_mp_state>());
+ let ret = this.get_state(VcpuRequest_StateSet::MP, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_mp_state(
+ this: *mut crosvm_vcpu,
+ state: *const kvm_mp_state,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuSetMpState);
+ let this = &mut *this;
+ let state = from_raw_parts(state as *mut u8, size_of::<kvm_mp_state>());
+ let ret = this.set_state(VcpuRequest_StateSet::MP, state);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_vcpu_events(
+ this: *mut crosvm_vcpu,
+ events: *mut kvm_vcpu_events,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuGetVcpuEvents);
+ let this = &mut *this;
+ let events = from_raw_parts_mut(events as *mut u8, size_of::<kvm_vcpu_events>());
+ let ret = this.get_state(VcpuRequest_StateSet::EVENTS, events);
+ to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_vcpu_events(
+ this: *mut crosvm_vcpu,
+ events: *const kvm_vcpu_events,
+) -> c_int {
+ let _u = STATS.record(Stat::VcpuSetVcpuEvents);
+ let this = &mut *this;
+ let events = from_raw_parts(events as *mut u8, size_of::<kvm_vcpu_events>());
+ let ret = this.set_state(VcpuRequest_StateSet::EVENTS, events);
+ to_crosvm_rc(ret)
+}
diff --git a/data_model/Cargo.toml b/data_model/Cargo.toml
new file mode 100644
index 0000000..b1447a6
--- /dev/null
+++ b/data_model/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "data_model"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+include = ["src/**/*", "Cargo.toml"]
+
+[dependencies]
+assertions = { path = "../assertions" } # provided by ebuild
+
+[workspace]
diff --git a/data_model/src/endian.rs b/data_model/src/endian.rs
new file mode 100644
index 0000000..014bb7b
--- /dev/null
+++ b/data_model/src/endian.rs
@@ -0,0 +1,143 @@
+// Copyright 2017 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.
+
+//! Explicit endian types useful for embedding in structs or reinterpreting data.
+//!
+//! Each endian type is guarnteed to have the same size and alignment as a regular unsigned primiive
+//! of the equal size.
+//!
+//! # Examples
+//!
+//! ```
+//! # use data_model::*;
+//! let b: Be32 = From::from(3);
+//! let l: Le32 = From::from(3);
+//!
+//! assert_eq!(b.to_native(), 3);
+//! assert_eq!(l.to_native(), 3);
+//! assert!(b == 3);
+//! assert!(l == 3);
+//!
+//! let b_trans: u32 = unsafe { std::mem::transmute(b) };
+//! let l_trans: u32 = unsafe { std::mem::transmute(l) };
+//!
+//! #[cfg(target_endian = "little")]
+//! assert_eq!(l_trans, 3);
+//! #[cfg(target_endian = "big")]
+//! assert_eq!(b_trans, 3);
+//!
+//! assert_ne!(b_trans, l_trans);
+//! ```
+
+use assertions::const_assert;
+use std::mem::{align_of, size_of};
+
+use crate::DataInit;
+
+macro_rules! endian_type {
+ ($old_type:ident, $new_type:ident, $to_new:ident, $from_new:ident) => {
+ /// An unsigned integer type of with an explicit endianness.
+ ///
+ /// See module level documentation for examples.
+ #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
+ pub struct $new_type($old_type);
+
+ impl $new_type {
+ fn _assert() {
+ const_assert!(align_of::<$new_type>() == align_of::<$old_type>());
+ const_assert!(size_of::<$new_type>() == size_of::<$old_type>());
+ }
+
+ /// Converts `self` to the native endianness.
+ pub fn to_native(self) -> $old_type {
+ $old_type::$from_new(self.0)
+ }
+ }
+
+ unsafe impl DataInit for $new_type {}
+
+ impl PartialEq<$old_type> for $new_type {
+ fn eq(&self, other: &$old_type) -> bool {
+ self.0 == $old_type::$to_new(*other)
+ }
+ }
+
+ impl PartialEq<$new_type> for $old_type {
+ fn eq(&self, other: &$new_type) -> bool {
+ $old_type::$to_new(other.0) == *self
+ }
+ }
+
+ impl Into<$old_type> for $new_type {
+ fn into(self) -> $old_type {
+ $old_type::$from_new(self.0)
+ }
+ }
+
+ impl From<$old_type> for $new_type {
+ fn from(v: $old_type) -> $new_type {
+ $new_type($old_type::$to_new(v))
+ }
+ }
+ };
+}
+
+endian_type!(u16, Le16, to_le, from_le);
+endian_type!(u32, Le32, to_le, from_le);
+endian_type!(u64, Le64, to_le, from_le);
+endian_type!(usize, LeSize, to_le, from_le);
+endian_type!(u16, Be16, to_be, from_be);
+endian_type!(u32, Be32, to_be, from_be);
+endian_type!(u64, Be64, to_be, from_be);
+endian_type!(usize, BeSize, to_be, from_be);
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use std::convert::From;
+ use std::mem::transmute;
+
+ #[cfg(target_endian = "little")]
+ const NATIVE_LITTLE: bool = true;
+ #[cfg(target_endian = "big")]
+ const NATIVE_LITTLE: bool = false;
+ const NATIVE_BIG: bool = !NATIVE_LITTLE;
+
+ macro_rules! endian_test {
+ ($old_type:ty, $new_type:ty, $test_name:ident, $native:expr) => {
+ mod $test_name {
+ use super::*;
+
+ #[allow(overflowing_literals)]
+ #[test]
+ fn equality() {
+ let v = 0x0123456789ABCDEF as $old_type;
+ let endian_v: $new_type = From::from(v);
+ let endian_into: $old_type = endian_v.into();
+ let endian_transmute: $old_type = unsafe { transmute(endian_v) };
+
+ if $native {
+ assert_eq!(endian_v, endian_transmute);
+ } else {
+ assert_eq!(endian_v, endian_transmute.swap_bytes());
+ }
+
+ assert_eq!(v, endian_into);
+ assert!(v == endian_v);
+ assert!(endian_v == v);
+ }
+ }
+ };
+ }
+
+ endian_test!(u16, Le16, test_le16, NATIVE_LITTLE);
+ endian_test!(u32, Le32, test_le32, NATIVE_LITTLE);
+ endian_test!(u64, Le64, test_le64, NATIVE_LITTLE);
+ endian_test!(usize, LeSize, test_le_size, NATIVE_LITTLE);
+ endian_test!(u16, Be16, test_be16, NATIVE_BIG);
+ endian_test!(u32, Be32, test_be32, NATIVE_BIG);
+ endian_test!(u64, Be64, test_be64, NATIVE_BIG);
+ endian_test!(usize, BeSize, test_be_size, NATIVE_BIG);
+}
diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs
new file mode 100644
index 0000000..8288c84
--- /dev/null
+++ b/data_model/src/lib.rs
@@ -0,0 +1,162 @@
+// Copyright 2017 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::mem::size_of;
+use std::slice::{from_raw_parts, from_raw_parts_mut};
+
+/// Types for which it is safe to initialize from raw data.
+///
+/// A type `T` is `DataInit` if and only if it can be initialized by reading its contents from a
+/// byte array. This is generally true for all plain-old-data structs. It is notably not true for
+/// any type that includes a reference.
+///
+/// Implementing this trait guarantees that it is safe to instantiate the struct with random data.
+pub unsafe trait DataInit: Copy + Send + Sync {
+ /// Converts a slice of raw data into a reference of `Self`.
+ ///
+ /// The value of `data` is not copied. Instead a reference is made from the given slice. The
+ /// value of `Self` will depend on the representation of the type in memory, and may change in
+ /// an unstable fashion.
+ ///
+ /// This will return `None` if the length of data does not match the size of `Self`, or if the
+ /// data is not aligned for the type of `Self`.
+ fn from_slice(data: &[u8]) -> Option<&Self> {
+ // Early out to avoid an unneeded `align_to` call.
+ if data.len() != size_of::<Self>() {
+ return None;
+ }
+
+ // Safe because the DataInit trait asserts any data is valid for this type, and we ensured
+ // the size of the pointer's buffer is the correct size. The `align_to` method ensures that
+ // we don't have any unaligned references. This aliases a pointer, but because the pointer
+ // is from a const slice reference, there are no mutable aliases. Finally, the reference
+ // returned can not outlive data because they have equal implicit lifetime constraints.
+ match unsafe { data.align_to::<Self>() } {
+ ([], [mid], []) => Some(mid),
+ _ => None,
+ }
+ }
+
+ /// Converts a mutable slice of raw data into a mutable reference of `Self`.
+ ///
+ /// Because `Self` is made from a reference to the mutable slice`, mutations to the returned
+ /// reference are immediately reflected in `data`. The value of the returned `Self` will depend
+ /// on the representation of the type in memory, and may change in an unstable fashion.
+ ///
+ /// This will return `None` if the length of data does not match the size of `Self`, or if the
+ /// data is not aligned for the type of `Self`.
+ fn from_mut_slice(data: &mut [u8]) -> Option<&mut Self> {
+ // Early out to avoid an unneeded `align_to_mut` call.
+ if data.len() != size_of::<Self>() {
+ return None;
+ }
+
+ // Safe because the DataInit trait asserts any data is valid for this type, and we ensured
+ // the size of the pointer's buffer is the correct size. The `align_to` method ensures that
+ // we don't have any unaligned references. This aliases a pointer, but because the pointer
+ // is from a mut slice reference, we borrow the passed in mutable reference. Finally, the
+ // reference returned can not outlive data because they have equal implicit lifetime
+ // constraints.
+ match unsafe { data.align_to_mut::<Self>() } {
+ ([], [mid], []) => Some(mid),
+ _ => None,
+ }
+ }
+
+ /// Converts a reference to `self` into a slice of bytes.
+ ///
+ /// The value of `self` is not copied. Instead, the slice is made from a reference to `self`.
+ /// The value of bytes in the returned slice will depend on the representation of the type in
+ /// memory, and may change in an unstable fashion.
+ fn as_slice(&self) -> &[u8] {
+ // Safe because the entire size of self is accessible as bytes because the trait guarantees
+ // it. The lifetime of the returned slice is the same as the passed reference, so that no
+ // dangling pointers will result from this pointer alias.
+ unsafe { from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
+ }
+
+ /// Converts a mutable reference to `self` into a mutable slice of bytes.
+ ///
+ /// Because the slice is made from a reference to `self`, mutations to the returned slice are
+ /// immediately reflected in `self`. The value of bytes in the returned slice will depend on
+ /// the representation of the type in memory, and may change in an unstable fashion.
+ fn as_mut_slice(&mut self) -> &mut [u8] {
+ // Safe because the entire size of self is accessible as bytes because the trait guarantees
+ // it. The trait also guarantees that any combination of bytes is valid for this type, so
+ // modifying them in the form of a byte slice is valid. The lifetime of the returned slice
+ // is the same as the passed reference, so that no dangling pointers will result from this
+ // pointer alias. Although this does alias a mutable pointer, we do so by exclusively
+ // borrowing the given mutable reference.
+ unsafe { from_raw_parts_mut(self as *mut Self as *mut u8, size_of::<Self>()) }
+ }
+}
+
+// All intrinsic types and arays of intrinsic types are DataInit. They are just numbers.
+macro_rules! array_data_init {
+ ($T:ty, $($N:expr)+) => {
+ $(
+ unsafe impl DataInit for [$T; $N] {}
+ )+
+ }
+}
+macro_rules! data_init_type {
+ ($($T:ident),*) => {
+ $(
+ unsafe impl DataInit for $T {}
+ array_data_init! {
+ $T,
+ 0 1 2 3 4 5 6 7 8 9
+ 10 11 12 13 14 15 16 17 18 19
+ 20 21 22 23 24 25 26 27 28 29
+ 30 31 32
+ }
+ )*
+ #[cfg(test)]
+ mod data_init_tests {
+ use std::mem::{size_of, align_of};
+ use crate::DataInit;
+
+ #[test]
+ fn from_slice_alignment() {
+ let mut v = [0u8; 32];
+ $(
+ let (pre, _, _) = unsafe { v.align_to::<$T>() };
+ let pre_len = pre.len();
+
+ let aligned_v = &mut v[pre_len..pre_len + size_of::<$T>()];
+
+ let from_aligned = $T::from_slice(aligned_v);
+ assert_eq!(from_aligned, Some(&0));
+
+ let from_aligned_mut = $T::from_mut_slice(aligned_v);
+ assert_eq!(from_aligned_mut, Some(&mut 0));
+
+ for i in 1..size_of::<$T>() {
+ let begin = pre_len + i;
+ let end = begin + size_of::<$T>();
+ let unaligned_v = &mut v[begin..end];
+
+ let from_unaligned = $T::from_slice(unaligned_v);
+ if align_of::<$T>() != 1 {
+ assert_eq!(from_unaligned, None);
+ }
+
+ let from_unaligned_mut = $T::from_mut_slice(unaligned_v);
+ if align_of::<$T>() != 1 {
+ assert_eq!(from_unaligned_mut, None);
+ }
+ }
+ )*
+
+ }
+ }
+ };
+}
+data_init_type!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
+
+pub mod endian;
+pub use crate::endian::*;
+
+pub mod volatile_memory;
+pub use crate::volatile_memory::*;
diff --git a/data_model/src/volatile_memory.rs b/data_model/src/volatile_memory.rs
new file mode 100644
index 0000000..6ce230b
--- /dev/null
+++ b/data_model/src/volatile_memory.rs
@@ -0,0 +1,555 @@
+// Copyright 2017 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.
+
+//! Types for volatile access to memory.
+//!
+//! Two of the core rules for safe rust is no data races and no aliased mutable references.
+//! `VolatileRef` and `VolatileSlice`, along with types that produce those which implement
+//! `VolatileMemory`, allow us to sidestep that rule by wrapping pointers that absolutely have to be
+//! accessed volatile. Some systems really do need to operate on shared memory and can't have the
+//! compiler reordering or eliding access because it has no visibility into what other systems are
+//! doing with that hunk of memory.
+//!
+//! For the purposes of maintaining safety, volatile memory has some rules of its own:
+//! 1. No references or slices to volatile memory (`&` or `&mut`).
+//! 2. Access should always been done with a volatile read or write.
+//! The First rule is because having references of any kind to memory considered volatile would
+//! violate pointer aliasing. The second is because unvolatile accesses are inherently undefined if
+//! done concurrently without synchronization. With volatile access we know that the compiler has
+//! not reordered or elided the access.
+
+use std::cmp::min;
+use std::fmt::{self, Display};
+use std::marker::PhantomData;
+use std::mem::size_of;
+use std::ptr::{copy, null_mut, read_volatile, write_bytes, write_volatile};
+use std::result;
+use std::{isize, usize};
+
+use crate::DataInit;
+
+#[derive(Eq, PartialEq, Debug)]
+pub enum VolatileMemoryError {
+ /// `addr` is out of bounds of the volatile memory slice.
+ OutOfBounds { addr: u64 },
+ /// Taking a slice at `base` with `offset` would overflow `u64`.
+ Overflow { base: u64, offset: u64 },
+}
+
+impl Display for VolatileMemoryError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::VolatileMemoryError::*;
+
+ match self {
+ OutOfBounds { addr } => write!(f, "address 0x{:x} is out of bounds", addr),
+ Overflow { base, offset } => write!(
+ f,
+ "address 0x{:x} offset by 0x{:x} would overflow",
+ base, offset
+ ),
+ }
+ }
+}
+
+pub type VolatileMemoryResult<T> = result::Result<T, VolatileMemoryError>;
+
+use crate::VolatileMemoryError as Error;
+type Result<T> = VolatileMemoryResult<T>;
+
+/// Convenience function for computing `base + offset` which returns
+/// `Err(VolatileMemoryError::Overflow)` instead of panicking in the case `base + offset` exceeds
+/// `u64::MAX`.
+///
+/// # Examples
+///
+/// ```
+/// # use data_model::*;
+/// # fn get_slice(offset: u64, count: u64) -> VolatileMemoryResult<()> {
+/// let mem_end = calc_offset(offset, count)?;
+/// if mem_end > 100 {
+/// return Err(VolatileMemoryError::OutOfBounds{addr: mem_end});
+/// }
+/// # Ok(())
+/// # }
+/// ```
+pub fn calc_offset(base: u64, offset: u64) -> Result<u64> {
+ match base.checked_add(offset) {
+ None => Err(Error::Overflow { base, offset }),
+ Some(m) => Ok(m),
+ }
+}
+
+/// Trait for types that support raw volatile access to their data.
+pub trait VolatileMemory {
+ /// Gets a slice of memory at `offset` that is `count` bytes in length and supports volatile
+ /// access.
+ fn get_slice(&self, offset: u64, count: u64) -> Result<VolatileSlice>;
+
+ /// Gets a `VolatileRef` at `offset`.
+ fn get_ref<T: DataInit>(&self, offset: u64) -> Result<VolatileRef<T>> {
+ let slice = self.get_slice(offset, size_of::<T>() as u64)?;
+ Ok(VolatileRef {
+ addr: slice.addr as *mut T,
+ phantom: PhantomData,
+ })
+ }
+}
+
+impl<'a> VolatileMemory for &'a mut [u8] {
+ fn get_slice(&self, offset: u64, count: u64) -> Result<VolatileSlice> {
+ let mem_end = calc_offset(offset, count)?;
+ if mem_end > self.len() as u64 {
+ return Err(Error::OutOfBounds { addr: mem_end });
+ }
+ Ok(unsafe { VolatileSlice::new((self.as_ptr() as u64 + offset) as *mut _, count) })
+ }
+}
+
+/// A slice of raw memory that supports volatile access.
+#[derive(Copy, Clone, Debug)]
+pub struct VolatileSlice<'a> {
+ addr: *mut u8,
+ size: u64,
+ phantom: PhantomData<&'a u8>,
+}
+
+impl<'a> Default for VolatileSlice<'a> {
+ fn default() -> VolatileSlice<'a> {
+ VolatileSlice {
+ addr: null_mut(),
+ size: 0,
+ phantom: PhantomData,
+ }
+ }
+}
+
+impl<'a> VolatileSlice<'a> {
+ /// Creates a slice of raw memory that must support volatile access.
+ ///
+ /// To use this safely, the caller must guarantee that the memory at `addr` is `size` bytes long
+ /// and is available for the duration of the lifetime of the new `VolatileSlice`. The caller
+ /// must also guarantee that all other users of the given chunk of memory are using volatile
+ /// accesses.
+ pub unsafe fn new(addr: *mut u8, size: u64) -> VolatileSlice<'a> {
+ VolatileSlice {
+ addr,
+ size,
+ phantom: PhantomData,
+ }
+ }
+
+ /// Gets the address of this slice's memory.
+ pub fn as_ptr(&self) -> *mut u8 {
+ self.addr
+ }
+
+ /// Gets the size of this slice.
+ pub fn size(&self) -> u64 {
+ self.size
+ }
+
+ /// Creates a copy of this slice with the address increased by `count` bytes, and the size
+ /// reduced by `count` bytes.
+ pub fn offset(self, count: u64) -> Result<VolatileSlice<'a>> {
+ let new_addr =
+ (self.addr as u64)
+ .checked_add(count)
+ .ok_or(VolatileMemoryError::Overflow {
+ base: self.addr as u64,
+ offset: count,
+ })?;
+ if new_addr > usize::MAX as u64 {
+ return Err(VolatileMemoryError::Overflow {
+ base: self.addr as u64,
+ offset: count,
+ })?;
+ }
+ let new_size = self
+ .size
+ .checked_sub(count)
+ .ok_or(VolatileMemoryError::OutOfBounds { addr: new_addr })?;
+ // Safe because the memory has the same lifetime and points to a subset of the memory of the
+ // original slice.
+ unsafe { Ok(VolatileSlice::new(new_addr as *mut u8, new_size)) }
+ }
+
+ /// Sets each byte of this slice with the given byte, similar to `memset`.
+ ///
+ /// The bytes of this slice are accessed in an arbitray order.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use data_model::VolatileMemory;
+ /// # fn test_write_45() -> Result<(), ()> {
+ /// let mut mem = [0u8; 32];
+ /// let mem_ref = &mut mem[..];
+ /// let vslice = mem_ref.get_slice(0, 32).map_err(|_| ())?;
+ /// vslice.write_bytes(45);
+ /// for &mut v in mem_ref {
+ /// assert_eq!(v, 45);
+ /// }
+ /// # Ok(())
+ /// # }
+ pub fn write_bytes(&self, value: u8) {
+ // Safe because the memory is valid and needs only byte alignment.
+ unsafe {
+ write_bytes(self.as_ptr(), value, self.size as usize);
+ }
+ }
+
+ /// Copies `self.size()` or `buf.len()` times the size of `T` bytes, whichever is smaller, to
+ /// `buf`.
+ ///
+ /// The copy happens from smallest to largest address in `T` sized chunks using volatile reads.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use std::fs::File;
+ /// # use std::path::Path;
+ /// # use data_model::VolatileMemory;
+ /// # fn test_write_null() -> Result<(), ()> {
+ /// let mut mem = [0u8; 32];
+ /// let mem_ref = &mut mem[..];
+ /// let vslice = mem_ref.get_slice(0, 32).map_err(|_| ())?;
+ /// let mut buf = [5u8; 16];
+ /// vslice.copy_to(&mut buf[..]);
+ /// for v in &buf[..] {
+ /// assert_eq!(buf[0], 0);
+ /// }
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn copy_to<T>(&self, buf: &mut [T])
+ where
+ T: DataInit,
+ {
+ let mut addr = self.addr;
+ for v in buf.iter_mut().take(self.size as usize / size_of::<T>()) {
+ unsafe {
+ *v = read_volatile(addr as *const T);
+ addr = addr.add(size_of::<T>());
+ }
+ }
+ }
+
+ /// Copies `self.size()` or `slice.size()` bytes, whichever is smaller, to `slice`.
+ ///
+ /// The copies happen in an undefined order.
+ /// # Examples
+ ///
+ /// ```
+ /// # use data_model::VolatileMemory;
+ /// # fn test_write_null() -> Result<(), ()> {
+ /// let mut mem = [0u8; 32];
+ /// let mem_ref = &mut mem[..];
+ /// let vslice = mem_ref.get_slice(0, 32).map_err(|_| ())?;
+ /// vslice.copy_to_volatile_slice(vslice.get_slice(16, 16).map_err(|_| ())?);
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn copy_to_volatile_slice(&self, slice: VolatileSlice) {
+ unsafe {
+ copy(self.addr, slice.addr, min(self.size, slice.size) as usize);
+ }
+ }
+
+ /// Copies `self.size()` or `buf.len()` times the size of `T` bytes, whichever is smaller, to
+ /// this slice's memory.
+ ///
+ /// The copy happens from smallest to largest address in `T` sized chunks using volatile writes.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use std::fs::File;
+ /// # use std::path::Path;
+ /// # use data_model::VolatileMemory;
+ /// # fn test_write_null() -> Result<(), ()> {
+ /// let mut mem = [0u8; 32];
+ /// let mem_ref = &mut mem[..];
+ /// let vslice = mem_ref.get_slice(0, 32).map_err(|_| ())?;
+ /// let buf = [5u8; 64];
+ /// vslice.copy_from(&buf[..]);
+ /// for i in 0..4 {
+ /// assert_eq!(vslice.get_ref::<u32>(i * 4).map_err(|_| ())?.load(), 0x05050505);
+ /// }
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn copy_from<T>(&self, buf: &[T])
+ where
+ T: DataInit,
+ {
+ let mut addr = self.addr;
+ for &v in buf.iter().take(self.size as usize / size_of::<T>()) {
+ unsafe {
+ write_volatile(addr as *mut T, v);
+ addr = addr.add(size_of::<T>());
+ }
+ }
+ }
+}
+
+impl<'a> VolatileMemory for VolatileSlice<'a> {
+ fn get_slice(&self, offset: u64, count: u64) -> Result<VolatileSlice> {
+ let mem_end = calc_offset(offset, count)?;
+ if mem_end > self.size {
+ return Err(Error::OutOfBounds { addr: mem_end });
+ }
+ Ok(VolatileSlice {
+ addr: (self.addr as u64 + offset) as *mut _,
+ size: count,
+ phantom: PhantomData,
+ })
+ }
+}
+
+/// A memory location that supports volatile access of a `T`.
+///
+/// # Examples
+///
+/// ```
+/// # use data_model::VolatileRef;
+/// let mut v = 5u32;
+/// assert_eq!(v, 5);
+/// let v_ref = unsafe { VolatileRef::new(&mut v as *mut u32) };
+/// assert_eq!(v_ref.load(), 5);
+/// v_ref.store(500);
+/// assert_eq!(v, 500);
+#[derive(Debug)]
+pub struct VolatileRef<'a, T: DataInit>
+where
+ T: 'a,
+{
+ addr: *mut T,
+ phantom: PhantomData<&'a T>,
+}
+
+impl<'a, T: DataInit> VolatileRef<'a, T> {
+ /// Creates a reference to raw memory that must support volatile access of `T` sized chunks.
+ ///
+ /// To use this safely, the caller must guarantee that the memory at `addr` is big enough for a
+ /// `T` and is available for the duration of the lifetime of the new `VolatileRef`. The caller
+ /// must also guarantee that all other users of the given chunk of memory are using volatile
+ /// accesses.
+ pub unsafe fn new(addr: *mut T) -> VolatileRef<'a, T> {
+ VolatileRef {
+ addr,
+ phantom: PhantomData,
+ }
+ }
+
+ /// Gets the address of this slice's memory.
+ pub fn as_ptr(&self) -> *mut T {
+ self.addr
+ }
+
+ /// Gets the size of this slice.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use std::mem::size_of;
+ /// # use data_model::VolatileRef;
+ /// let v_ref = unsafe { VolatileRef::new(0 as *mut u32) };
+ /// assert_eq!(v_ref.size(), size_of::<u32>() as u64);
+ /// ```
+ pub fn size(&self) -> u64 {
+ size_of::<T>() as u64
+ }
+
+ /// Does a volatile write of the value `v` to the address of this ref.
+ #[inline(always)]
+ pub fn store(&self, v: T) {
+ unsafe { write_volatile(self.addr, v) };
+ }
+
+ /// Does a volatile read of the value at the address of this ref.
+ #[inline(always)]
+ pub fn load(&self) -> T {
+ // For the purposes of demonstrating why read_volatile is necessary, try replacing the code
+ // in this function with the commented code below and running `cargo test --release`.
+ // unsafe { *(self.addr as *const T) }
+ unsafe { read_volatile(self.addr) }
+ }
+
+ /// Converts this `T` reference to a raw slice with the same size and address.
+ pub fn to_slice(&self) -> VolatileSlice<'a> {
+ unsafe { VolatileSlice::new(self.addr as *mut u8, size_of::<T>() as u64) }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use std::sync::Arc;
+ use std::thread::{sleep, spawn};
+ use std::time::Duration;
+
+ #[derive(Clone)]
+ struct VecMem {
+ mem: Arc<Vec<u8>>,
+ }
+
+ impl VecMem {
+ fn new(size: usize) -> VecMem {
+ let mut mem = Vec::new();
+ mem.resize(size, 0);
+ VecMem { mem: Arc::new(mem) }
+ }
+ }
+
+ impl VolatileMemory for VecMem {
+ fn get_slice(&self, offset: u64, count: u64) -> Result<VolatileSlice> {
+ let mem_end = calc_offset(offset, count)?;
+ if mem_end > self.mem.len() as u64 {
+ return Err(Error::OutOfBounds { addr: mem_end });
+ }
+ Ok(unsafe { VolatileSlice::new((self.mem.as_ptr() as u64 + offset) as *mut _, count) })
+ }
+ }
+
+ #[test]
+ fn ref_store() {
+ let mut a = [0u8; 1];
+ let a_ref = &mut a[..];
+ let v_ref = a_ref.get_ref(0).unwrap();
+ v_ref.store(2u8);
+ assert_eq!(a[0], 2);
+ }
+
+ #[test]
+ fn ref_load() {
+ let mut a = [5u8; 1];
+ {
+ let a_ref = &mut a[..];
+ let c = {
+ let v_ref = a_ref.get_ref::<u8>(0).unwrap();
+ assert_eq!(v_ref.load(), 5u8);
+ v_ref
+ };
+ // To make sure we can take a v_ref out of the scope we made it in:
+ c.load();
+ // but not too far:
+ // c
+ } //.load()
+ ;
+ }
+
+ #[test]
+ fn ref_to_slice() {
+ let mut a = [1u8; 5];
+ let a_ref = &mut a[..];
+ let v_ref = a_ref.get_ref(1).unwrap();
+ v_ref.store(0x12345678u32);
+ let ref_slice = v_ref.to_slice();
+ assert_eq!(v_ref.as_ptr() as u64, ref_slice.as_ptr() as u64);
+ assert_eq!(v_ref.size(), ref_slice.size());
+ }
+
+ #[test]
+ fn observe_mutate() {
+ let a = VecMem::new(1);
+ let a_clone = a.clone();
+ let v_ref = a.get_ref::<u8>(0).unwrap();
+ v_ref.store(99);
+ spawn(move || {
+ sleep(Duration::from_millis(10));
+ let clone_v_ref = a_clone.get_ref::<u8>(0).unwrap();
+ clone_v_ref.store(0);
+ });
+
+ // Technically this is a race condition but we have to observe the v_ref's value changing
+ // somehow and this helps to ensure the sleep actually happens before the store rather then
+ // being reordered by the compiler.
+ assert_eq!(v_ref.load(), 99);
+
+ // Granted we could have a machine that manages to perform this many volatile loads in the
+ // amount of time the spawned thread sleeps, but the most likely reason the retry limit will
+ // get reached is because v_ref.load() is not actually performing the required volatile read
+ // or v_ref.store() is not doing a volatile write. A timer based solution was avoided
+ // because that might use a syscall which could hint the optimizer to reload v_ref's pointer
+ // regardless of volatile status. Note that we use a longer retry duration for optimized
+ // builds.
+ #[cfg(debug_assertions)]
+ const RETRY_MAX: u64 = 500_000_000;
+ #[cfg(not(debug_assertions))]
+ const RETRY_MAX: u64 = 10_000_000_000;
+
+ let mut retry = 0;
+ while v_ref.load() == 99 && retry < RETRY_MAX {
+ retry += 1;
+ }
+
+ assert_ne!(retry, RETRY_MAX, "maximum retry exceeded");
+ assert_eq!(v_ref.load(), 0);
+ }
+
+ #[test]
+ fn slice_size() {
+ let a = VecMem::new(100);
+ let s = a.get_slice(0, 27).unwrap();
+ assert_eq!(s.size(), 27);
+
+ let s = a.get_slice(34, 27).unwrap();
+ assert_eq!(s.size(), 27);
+
+ let s = s.get_slice(20, 5).unwrap();
+ assert_eq!(s.size(), 5);
+ }
+
+ #[test]
+ fn slice_overflow_error() {
+ use std::u64::MAX;
+ let a = VecMem::new(1);
+ let res = a.get_slice(MAX, 1).unwrap_err();
+ assert_eq!(
+ res,
+ Error::Overflow {
+ base: MAX,
+ offset: 1,
+ }
+ );
+ }
+
+ #[test]
+ fn slice_oob_error() {
+ let a = VecMem::new(100);
+ a.get_slice(50, 50).unwrap();
+ let res = a.get_slice(55, 50).unwrap_err();
+ assert_eq!(res, Error::OutOfBounds { addr: 105 });
+ }
+
+ #[test]
+ fn ref_overflow_error() {
+ use std::u64::MAX;
+ let a = VecMem::new(1);
+ let res = a.get_ref::<u8>(MAX).unwrap_err();
+ assert_eq!(
+ res,
+ Error::Overflow {
+ base: MAX,
+ offset: 1,
+ }
+ );
+ }
+
+ #[test]
+ fn ref_oob_error() {
+ let a = VecMem::new(100);
+ a.get_ref::<u8>(99).unwrap();
+ let res = a.get_ref::<u16>(99).unwrap_err();
+ assert_eq!(res, Error::OutOfBounds { addr: 101 });
+ }
+
+ #[test]
+ fn ref_oob_too_large() {
+ let a = VecMem::new(3);
+ let res = a.get_ref::<u32>(0).unwrap_err();
+ assert_eq!(res, Error::OutOfBounds { addr: 4 });
+ }
+}
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
new file mode 100644
index 0000000..62b570e
--- /dev/null
+++ b/devices/Cargo.toml
@@ -0,0 +1,39 @@
+[package]
+name = "devices"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[features]
+gpu = ["gpu_buffer", "gpu_display", "gpu_renderer"]
+sandboxed-libusb = ["usb_util/sandboxed-libusb"]
+tpm = ["protos/trunks", "tpm2"]
+wl-dmabuf = []
+
+[dependencies]
+audio_streams = "*"
+bit_field = { path = "../bit_field" }
+byteorder = "*"
+data_model = { path = "../data_model" }
+enumn = { path = "../enumn" }
+gpu_buffer = { path = "../gpu_buffer", optional = true }
+gpu_display = { path = "../gpu_display", optional = true }
+gpu_renderer = { path = "../gpu_renderer", optional = true }
+io_jail = { path = "../io_jail" }
+kvm = { path = "../kvm" }
+libc = "*"
+msg_on_socket_derive = { path = "../msg_socket/msg_on_socket_derive" }
+msg_socket = { path = "../msg_socket" }
+net_sys = { path = "../net_sys" }
+net_util = { path = "../net_util" }
+p9 = { path = "../p9" }
+protos = { path = "../protos", optional = true }
+remain = "*"
+resources = { path = "../resources" }
+sync = { path = "../sync" }
+sys_util = { path = "../sys_util" }
+tpm2 = { path = "../tpm2", optional = true }
+usb_util = { path = "../usb_util" }
+vhost = { path = "../vhost" }
+virtio_sys = { path = "../virtio_sys" }
+vm_control = { path = "../vm_control" }
diff --git a/devices/src/bus.rs b/devices/src/bus.rs
new file mode 100644
index 0000000..d4b46eb
--- /dev/null
+++ b/devices/src/bus.rs
@@ -0,0 +1,354 @@
+// Copyright 2017 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.
+
+//! Handles routing to devices in an address space.
+
+use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
+use std::collections::btree_map::BTreeMap;
+use std::fmt::{self, Display};
+use std::result;
+use std::sync::Arc;
+
+use sync::Mutex;
+
+/// 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
+/// into its allocated portion of address space.
+#[allow(unused_variables)]
+pub trait BusDevice: Send {
+ /// 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]) {}
+ /// Writes at `offset` into this device
+ fn write(&mut self, offset: u64, 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.
+ fn config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {}
+ /// Gets a register from the configuration space. Only used by PCI.
+ /// * `reg_idx` - The index of the config register to read.
+ fn config_register_read(&self, reg_idx: usize) -> u32 {
+ 0
+ }
+ /// Invoked when the device is sandboxed.
+ fn on_sandboxed(&mut self) {}
+}
+
+#[derive(Debug)]
+pub enum Error {
+ /// The insertion failed because the new device overlapped with an old device.
+ Overlap,
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ Overlap => write!(f, "new device overlaps with an old device"),
+ }
+ }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+/// Holds a base and length representing the address space occupied by a `BusDevice`.
+///
+/// * 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 {
+ /// Returns true if `addr` is within the range.
+ pub fn contains(&self, addr: u64) -> bool {
+ self.base <= addr && addr < self.base + self.len
+ }
+
+ /// Returns true if there is overlap with the given range.
+ pub fn overlaps(&self, base: u64, len: u64) -> bool {
+ self.base < (base + len) && base < self.base + self.len
+ }
+}
+
+impl Eq for BusRange {}
+
+impl PartialEq for BusRange {
+ fn eq(&self, other: &BusRange) -> bool {
+ self.base == other.base
+ }
+}
+
+impl Ord for BusRange {
+ fn cmp(&self, other: &BusRange) -> Ordering {
+ self.base.cmp(&other.base)
+ }
+}
+
+impl PartialOrd for BusRange {
+ fn partial_cmp(&self, other: &BusRange) -> Option<Ordering> {
+ self.base.partial_cmp(&other.base)
+ }
+}
+
+/// 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
+/// only restriction is that no two devices can overlap in this address space.
+#[derive(Clone)]
+pub struct Bus {
+ devices: BTreeMap<BusRange, Arc<Mutex<dyn BusDevice>>>,
+}
+
+impl Bus {
+ /// Constructs an a bus with an empty address space.
+ pub fn new() -> Bus {
+ Bus {
+ devices: BTreeMap::new(),
+ }
+ }
+
+ fn first_before(&self, addr: u64) -> Option<(BusRange, &Mutex<dyn BusDevice>)> {
+ let (range, dev) = self
+ .devices
+ .range(
+ ..=BusRange {
+ base: addr,
+ len: 1,
+ full_addr: false,
+ },
+ )
+ .rev()
+ .next()?;
+ Some((*range, dev))
+ }
+
+ fn get_device(&self, addr: u64) -> Option<(u64, &Mutex<dyn BusDevice>)> {
+ 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));
+ }
+ }
+ }
+ None
+ }
+
+ /// Puts the given device at the given address space.
+ pub fn insert(
+ &mut self,
+ device: Arc<Mutex<dyn BusDevice>>,
+ base: u64,
+ len: u64,
+ full_addr: bool,
+ ) -> 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,
+ full_addr,
+ },
+ device,
+ )
+ .is_some()
+ {
+ return Err(Error::Overlap);
+ }
+
+ Ok(())
+ }
+
+ /// Reads data from the device that owns the range containing `addr` and puts it into `data`.
+ ///
+ /// 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);
+ true
+ } else {
+ false
+ }
+ }
+
+ /// Writes `data` to the device that owns the range containing `addr`.
+ ///
+ /// 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);
+ true
+ } else {
+ false
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ struct DummyDevice;
+ impl BusDevice for DummyDevice {
+ fn debug_label(&self) -> String {
+ "dummy device".to_owned()
+ }
+ }
+
+ struct ConstantDevice;
+ impl BusDevice for ConstantDevice {
+ fn debug_label(&self) -> String {
+ "constant device".to_owned()
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ for (i, v) in data.iter_mut().enumerate() {
+ *v = (offset as u8) + (i as u8);
+ }
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ for (i, v) in data.iter().enumerate() {
+ assert_eq!(*v, (offset as u8) + (i as u8))
+ }
+ }
+ }
+
+ #[test]
+ 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());
+ }
+
+ #[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());
+ }
+
+ #[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.read(0x10, &mut [0, 0, 0, 0]));
+ assert!(bus.write(0x10, &[0, 0, 0, 0]));
+ assert!(bus.read(0x11, &mut [0, 0, 0, 0]));
+ assert!(bus.write(0x11, &[0, 0, 0, 0]));
+ assert!(bus.read(0x16, &mut [0, 0, 0, 0]));
+ assert!(bus.write(0x16, &[0, 0, 0, 0]));
+ assert!(!bus.read(0x20, &mut [0, 0, 0, 0]));
+ assert!(!bus.write(0x20, &mut [0, 0, 0, 0]));
+ assert!(!bus.read(0x06, &mut [0, 0, 0, 0]));
+ assert!(!bus.write(0x06, &mut [0, 0, 0, 0]));
+ }
+
+ #[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 mut values = [0, 1, 2, 3];
+ assert!(bus.read(0x10, &mut values));
+ assert_eq!(values, [0, 1, 2, 3]);
+ assert!(bus.write(0x10, &values));
+ assert!(bus.read(0x15, &mut values));
+ assert_eq!(values, [5, 6, 7, 8]);
+ assert!(bus.write(0x15, &values));
+ }
+
+ #[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 mut values = [0u8; 4];
+ assert!(bus.read(0x10, &mut values));
+ assert_eq!(values, [0x10, 0x11, 0x12, 0x13]);
+ assert!(bus.write(0x10, &values));
+ assert!(bus.read(0x15, &mut values));
+ assert_eq!(values, [0x15, 0x16, 0x17, 0x18]);
+ assert!(bus.write(0x15, &values));
+ }
+
+ #[test]
+ fn bus_range_contains() {
+ let a = BusRange {
+ base: 0x1000,
+ len: 0x400,
+ full_addr: false,
+ };
+ assert!(a.contains(0x1000));
+ assert!(a.contains(0x13ff));
+ assert!(!a.contains(0xfff));
+ assert!(!a.contains(0x1400));
+ assert!(a.contains(0x1200));
+ }
+
+ #[test]
+ fn bus_range_overlap() {
+ let a = BusRange {
+ base: 0x1000,
+ len: 0x400,
+ full_addr: false,
+ };
+ assert!(a.overlaps(0x1000, 0x400));
+ assert!(a.overlaps(0xf00, 0x400));
+ assert!(a.overlaps(0x1000, 0x01));
+ assert!(a.overlaps(0xfff, 0x02));
+ assert!(a.overlaps(0x1100, 0x100));
+ assert!(a.overlaps(0x13ff, 0x100));
+ assert!(!a.overlaps(0x1400, 0x100));
+ assert!(!a.overlaps(0xf00, 0x100));
+ }
+}
diff --git a/devices/src/cmos.rs b/devices/src/cmos.rs
new file mode 100644
index 0000000..daf391c
--- /dev/null
+++ b/devices/src/cmos.rs
@@ -0,0 +1,119 @@
+// Copyright 2017 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 libc::{gmtime_r, time, time_t, tm};
+use std::cmp::min;
+use std::mem;
+
+use crate::BusDevice;
+
+const INDEX_MASK: u8 = 0x7f;
+const INDEX_OFFSET: u64 = 0x0;
+const DATA_OFFSET: u64 = 0x1;
+const DATA_LEN: usize = 128;
+
+/// A CMOS/RTC device commonly seen on x86 I/O port 0x70/0x71.
+pub struct Cmos {
+ index: u8,
+ data: [u8; DATA_LEN],
+}
+
+impl Cmos {
+ /// Constructs a CMOS/RTC device with initial data.
+ /// `mem_below_4g` is the size of memory in bytes below the 32-bit gap.
+ /// `mem_above_4g` is the size of memory in bytes above the 32-bit gap.
+ pub fn new(mem_below_4g: u64, mem_above_4g: u64) -> Cmos {
+ let mut data = [0u8; DATA_LEN];
+
+ // Extended memory from 16 MB to 4 GB in units of 64 KB
+ let ext_mem = min(
+ 0xFFFF,
+ mem_below_4g.saturating_sub(16 * 1024 * 1024) / (64 * 1024),
+ );
+ data[0x34] = ext_mem as u8;
+ data[0x35] = (ext_mem >> 8) as u8;
+
+ // High memory (> 4GB) in units of 64 KB
+ let high_mem = min(0xFFFFFF, mem_above_4g / (64 * 1024));
+ data[0x5b] = high_mem as u8;
+ data[0x5c] = (high_mem >> 8) as u8;
+ data[0x5d] = (high_mem >> 16) as u8;
+
+ Cmos { index: 0, data }
+ }
+}
+
+impl BusDevice for Cmos {
+ fn debug_label(&self) -> String {
+ "cmos".to_owned()
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ if data.len() != 1 {
+ return;
+ }
+
+ match 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 to_bcd(v: u8) -> u8 {
+ assert!(v < 100);
+ ((v / 10) << 4) | (v % 10)
+ }
+
+ if data.len() != 1 {
+ return;
+ }
+
+ data[0] = match offset {
+ INDEX_OFFSET => self.index,
+ DATA_OFFSET => {
+ let seconds;
+ let minutes;
+ let hours;
+ let week_day;
+ let day;
+ let month;
+ let year;
+ // The time and gmtime_r calls are safe as long as the structs they are given are
+ // large enough, and neither of them fail. It is safe to zero initialize the tm
+ // struct because it contains only plain data.
+ unsafe {
+ let mut tm: tm = mem::zeroed();
+ let mut now: time_t = 0;
+ time(&mut now as *mut _);
+ gmtime_r(&now, &mut tm as *mut _);
+ // The following lines of code are safe but depend on tm being in scope.
+ seconds = tm.tm_sec;
+ minutes = tm.tm_min;
+ hours = tm.tm_hour;
+ week_day = tm.tm_wday + 1;
+ day = tm.tm_mday;
+ month = tm.tm_mon + 1;
+ year = tm.tm_year;
+ };
+ match self.index {
+ 0x00 => to_bcd(seconds as u8),
+ 0x02 => to_bcd(minutes as u8),
+ 0x04 => to_bcd(hours as u8),
+ 0x06 => to_bcd(week_day as u8),
+ 0x07 => to_bcd(day as u8),
+ 0x08 => to_bcd(month as u8),
+ 0x09 => to_bcd((year % 100) as u8),
+ 0x32 => to_bcd(((year + 1900) / 100) as u8),
+ _ => {
+ // self.index is always guaranteed to be in range via INDEX_MASK.
+ self.data[(self.index & INDEX_MASK) as usize]
+ }
+ }
+ }
+ o => panic!("bad read offset on CMOS device: {}", o),
+ }
+ }
+}
diff --git a/devices/src/i8042.rs b/devices/src/i8042.rs
new file mode 100644
index 0000000..1f8a837
--- /dev/null
+++ b/devices/src/i8042.rs
@@ -0,0 +1,46 @@
+// Copyright 2017 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 sys_util::{error, EventFd};
+
+use crate::BusDevice;
+
+/// A i8042 PS/2 controller that emulates just enough to shutdown the machine.
+pub struct I8042Device {
+ reset_evt: EventFd,
+}
+
+impl I8042Device {
+ /// Constructs a i8042 device that will signal the given event when the guest requests it.
+ pub fn new(reset_evt: EventFd) -> I8042Device {
+ I8042Device { reset_evt }
+ }
+}
+
+// i8042 device is mapped I/O address 0x61. We partially implement two 8-bit
+// registers: port 0x61 (I8042_PORT_B_REG, offset 0 from base of 0x61), and
+// port 0x64 (I8042_COMMAND_REG, offset 3 from base of 0x61).
+impl BusDevice for I8042Device {
+ fn debug_label(&self) -> String {
+ "i8042".to_owned()
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ if data.len() == 1 && offset == 3 {
+ data[0] = 0x0;
+ } else if data.len() == 1 && offset == 0 {
+ // 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 == 3 {
+ if let Err(e) = self.reset_evt.write(1) {
+ error!("failed to trigger i8042 reset event: {}", e);
+ }
+ }
+ }
+}
diff --git a/devices/src/ioapic.rs b/devices/src/ioapic.rs
new file mode 100644
index 0000000..ce9f2b2
--- /dev/null
+++ b/devices/src/ioapic.rs
@@ -0,0 +1,737 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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 crate::split_irqchip_common::*;
+use crate::BusDevice;
+use bit_field::*;
+use sys_util::warn;
+
+#[bitfield]
+#[derive(Clone, Copy, PartialEq)]
+pub struct RedirectionTableEntry {
+ vector: BitField8,
+ #[bits = 3]
+ delivery_mode: DeliveryMode,
+ #[bits = 1]
+ dest_mode: DestinationMode,
+ #[bits = 1]
+ delivery_status: DeliveryStatus,
+ polarity: BitField1,
+ remote_irr: bool,
+ #[bits = 1]
+ trigger_mode: TriggerMode,
+ interrupt_mask: bool, // true iff interrupts are masked.
+ reserved: BitField39,
+ dest_id: BitField8,
+}
+
+#[bitfield]
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum DeliveryStatus {
+ Idle = 0,
+ Pending = 1,
+}
+
+const IOAPIC_VERSION_ID: u32 = 0x00170011;
+#[allow(dead_code)]
+const IOAPIC_BASE_ADDRESS: u32 = 0xfec00000;
+// The Intel manual does not specify this size, but KVM uses it.
+#[allow(dead_code)]
+const IOAPIC_MEM_LENGTH_BYTES: usize = 0x100;
+
+// Constants for IOAPIC direct register offset.
+const IOAPIC_REG_ID: u8 = 0x00;
+const IOAPIC_REG_VERSION: u8 = 0x01;
+const IOAPIC_REG_ARBITRATION_ID: u8 = 0x02;
+
+// Register offsets
+pub const IOREGSEL_OFF: u8 = 0x0;
+pub const IOREGSEL_DUMMY_UPPER_32_BITS_OFF: u8 = 0x4;
+pub const IOWIN_OFF: u8 = 0x10;
+pub const IOWIN_SCALE: u8 = 0x2;
+
+/// Given an IRQ and whether or not the selector should refer to the high bits, return a selector
+/// suitable to use as an offset to read to/write from.
+#[allow(dead_code)]
+fn encode_selector_from_irq(irq: usize, is_high_bits: bool) -> u8 {
+ (irq as u8) * IOWIN_SCALE + IOWIN_OFF + (is_high_bits as u8)
+}
+
+/// Given an offset that was read from/written to, return a tuple of the relevant IRQ and whether
+/// the offset refers to the high bits of that register.
+fn decode_irq_from_selector(selector: u8) -> (usize, bool) {
+ (
+ ((selector - IOWIN_OFF) / IOWIN_SCALE) as usize,
+ selector & 1 != 0,
+ )
+}
+
+// The RTC needs special treatment to work properly for Windows (or other OSs that use tick
+// stuffing). In order to avoid time drift, we need to guarantee that the correct number of RTC
+// interrupts are injected into the guest. This hack essentialy treats RTC interrupts as level
+// triggered, which allows the IOAPIC to be responsible for interrupt coalescing and allows the
+// IOAPIC to pass back whether or not the interrupt was coalesced to the CMOS (which allows the
+// CMOS to perform tick stuffing). This deviates from the IOAPIC spec in ways very similar to (but
+// not exactly the same as) KVM's IOAPIC.
+const RTC_IRQ: usize = 0x8;
+
+pub struct Ioapic {
+ id: u32,
+ // 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,
+ current_interrupt_level_bitmap: u32,
+ redirect_table: [RedirectionTableEntry; kvm::NUM_IOAPIC_PINS],
+ // IOREGSEL is technically 32 bits, but only bottom 8 are writable: all others are fixed to 0.
+ ioregsel: u8,
+}
+
+impl BusDevice for Ioapic {
+ fn debug_label(&self) -> String {
+ "userspace IOAPIC".to_string()
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ if data.len() > 8 || data.len() == 0 {
+ warn!("IOAPIC: Bad read size: {}", data.len());
+ return;
+ }
+ if offset >= 1 << 8 {
+ warn!("IOAPIC: Bad read from offset {}", offset);
+ }
+ let out = match offset as u8 {
+ IOREGSEL_OFF => self.ioregsel.into(),
+ IOREGSEL_DUMMY_UPPER_32_BITS_OFF => 0,
+ IOWIN_OFF => self.ioapic_read(),
+ _ => {
+ warn!("IOAPIC: Bad read from offset {}", offset);
+ return;
+ }
+ };
+ let out_arr = out.to_ne_bytes();
+ for i in 0..4 {
+ if i < data.len() {
+ data[i] = out_arr[i];
+ }
+ }
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ if data.len() > 8 || data.len() == 0 {
+ warn!("IOAPIC: Bad write size: {}", data.len());
+ return;
+ }
+ if offset >= 1 << 8 {
+ warn!("IOAPIC: Bad write to offset {}", offset);
+ }
+ match offset as u8 {
+ IOREGSEL_OFF => self.ioregsel = data[0],
+ IOREGSEL_DUMMY_UPPER_32_BITS_OFF => {} // Ignored.
+ IOWIN_OFF => {
+ if data.len() != 4 {
+ warn!("IOAPIC: Bad write size for iowin: {}", data.len());
+ return;
+ }
+ let data_arr = [data[0], data[1], data[2], data[3]];
+ let val = u32::from_ne_bytes(data_arr);
+ self.ioapic_write(val);
+ }
+ _ => {
+ warn!("IOAPIC: Bad write to offset {}", offset);
+ return;
+ }
+ }
+ }
+}
+
+impl Ioapic {
+ pub fn new() -> Ioapic {
+ let mut entry = RedirectionTableEntry::new();
+ entry.set_interrupt_mask(true);
+ let entries = [entry; kvm::NUM_IOAPIC_PINS];
+ Ioapic {
+ id: 0,
+ rtc_remote_irr: false,
+ current_interrupt_level_bitmap: 0,
+ redirect_table: entries,
+ ioregsel: 0,
+ }
+ }
+
+ // The ioapic must be informed about EOIs in order to avoid sending multiple interrupts of the
+ // same type at the same time.
+ pub fn end_of_interrupt(&mut self, vector: u8) {
+ if self.redirect_table[RTC_IRQ].get_vector() == vector && self.rtc_remote_irr {
+ // Specifically clear RTC IRQ field
+ self.rtc_remote_irr = false;
+ }
+
+ for i in 0..kvm::NUM_IOAPIC_PINS {
+ if self.redirect_table[i].get_vector() == vector
+ && self.redirect_table[i].get_trigger_mode() == TriggerMode::Level
+ {
+ self.redirect_table[i].set_remote_irr(false);
+ }
+ // There is an inherent race condition in hardware if the OS is finished processing an
+ // interrupt and a new interrupt is delivered between issuing an EOI and the EOI being
+ // completed. When that happens the ioapic is supposed to re-inject the interrupt.
+ if self.current_interrupt_level_bitmap & (1 << i) != 0 {
+ self.service_irq(i, true);
+ }
+ }
+ }
+
+ pub fn service_irq(&mut self, irq: usize, level: bool) -> bool {
+ let entry = &mut self.redirect_table[irq];
+ let line_status = if entry.get_polarity() == 1 {
+ !level
+ } else {
+ level
+ };
+
+ // De-assert the interrupt.
+ if !line_status {
+ self.current_interrupt_level_bitmap &= !(1 << irq);
+ return true;
+ }
+
+ // If it's an edge-triggered interrupt that's already high we ignore it.
+ if entry.get_trigger_mode() == TriggerMode::Edge
+ && self.current_interrupt_level_bitmap & (1 << irq) != 0
+ {
+ return false;
+ }
+
+ self.current_interrupt_level_bitmap |= 1 << irq;
+
+ // Interrupts are masked, so don't inject.
+ if entry.get_interrupt_mask() {
+ return false;
+ }
+
+ // Level-triggered and remote irr is already active, so we don't inject a new interrupt.
+ // (Coalesce with the prior one(s)).
+ if entry.get_trigger_mode() == TriggerMode::Level && entry.get_remote_irr() {
+ return false;
+ }
+
+ // Coalesce RTC interrupt to make tick stuffing work.
+ if irq == RTC_IRQ && self.rtc_remote_irr {
+ return false;
+ }
+
+ // TODO(mutexlox): Pulse (assert and deassert) interrupt
+ let injected = true;
+
+ if entry.get_trigger_mode() == TriggerMode::Level && line_status && injected {
+ entry.set_remote_irr(true);
+ } else if irq == RTC_IRQ && injected {
+ self.rtc_remote_irr = true;
+ }
+
+ injected
+ }
+
+ fn ioapic_write(&mut self, val: u32) {
+ match self.ioregsel {
+ IOAPIC_REG_VERSION => { /* read-only register */ }
+ IOAPIC_REG_ID => self.id = (val >> 24) & 0xf,
+ IOAPIC_REG_ARBITRATION_ID => { /* read-only register */ }
+ _ => {
+ if self.ioregsel < IOWIN_OFF {
+ // Invalid write; ignore.
+ return;
+ }
+ let (index, is_high_bits) = decode_irq_from_selector(self.ioregsel);
+ if index >= kvm::NUM_IOAPIC_PINS {
+ // Invalid write; ignore.
+ return;
+ }
+
+ let entry = &mut self.redirect_table[index];
+ if is_high_bits {
+ entry.set(32, 32, val.into());
+ } else {
+ let before = *entry;
+ entry.set(0, 32, val.into());
+
+ // respect R/O bits.
+ entry.set_delivery_status(before.get_delivery_status());
+ entry.set_remote_irr(before.get_remote_irr());
+
+ // Clear remote_irr when switching to edge_triggered.
+ if entry.get_trigger_mode() == TriggerMode::Edge {
+ entry.set_remote_irr(false);
+ }
+
+ // NOTE: on pre-4.0 kernels, there's a race we would need to work around.
+ // "KVM: x86: ioapic: Fix level-triggered EOI and IOAPIC reconfigure race"
+ // is the fix for this.
+ }
+
+ // TODO(mutexlox): route MSI.
+ if self.redirect_table[index].get_trigger_mode() == TriggerMode::Level
+ && self.current_interrupt_level_bitmap & (1 << index) != 0
+ && !self.redirect_table[index].get_interrupt_mask()
+ {
+ self.service_irq(index, true);
+ }
+ }
+ }
+ }
+
+ fn ioapic_read(&mut self) -> u32 {
+ match self.ioregsel {
+ IOAPIC_REG_VERSION => IOAPIC_VERSION_ID,
+ IOAPIC_REG_ID | IOAPIC_REG_ARBITRATION_ID => (self.id & 0xf) << 24,
+ _ => {
+ if self.ioregsel < IOWIN_OFF {
+ // Invalid read; ignore and return 0.
+ 0
+ } else {
+ let (index, is_high_bits) = decode_irq_from_selector(self.ioregsel);
+ if index < kvm::NUM_IOAPIC_PINS {
+ let offset = if is_high_bits { 32 } else { 0 };
+ self.redirect_table[index].get(offset, 32) as u32
+ } else {
+ !0 // Invaild index - return all 1s
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ const DEFAULT_VECTOR: u8 = 0x3a;
+ const DEFAULT_DESTINATION_ID: u8 = 0x5f;
+
+ fn set_up(trigger: TriggerMode) -> (Ioapic, usize) {
+ let irq = kvm::NUM_IOAPIC_PINS - 1;
+ let ioapic = set_up_with_irq(irq, trigger);
+ (ioapic, irq)
+ }
+
+ fn set_up_with_irq(irq: usize, trigger: TriggerMode) -> Ioapic {
+ let mut ioapic = Ioapic::new();
+ set_up_redirection_table_entry(&mut ioapic, irq, trigger);
+ 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);
+ 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());
+ }
+
+ fn read_entry(ioapic: &mut Ioapic, irq: usize) -> RedirectionTableEntry {
+ let mut entry = RedirectionTableEntry::new();
+ entry.set(
+ 0,
+ 32,
+ read_reg(ioapic, encode_selector_from_irq(irq, false)).into(),
+ );
+ entry.set(
+ 32,
+ 32,
+ read_reg(ioapic, encode_selector_from_irq(irq, true)).into(),
+ );
+ entry
+ }
+
+ fn write_entry(ioapic: &mut Ioapic, irq: usize, entry: RedirectionTableEntry) {
+ write_reg(
+ ioapic,
+ encode_selector_from_irq(irq, false),
+ entry.get(0, 32) as u32,
+ );
+ write_reg(
+ ioapic,
+ encode_selector_from_irq(irq, true),
+ entry.get(32, 32) as u32,
+ );
+ }
+
+ fn set_up_redirection_table_entry(ioapic: &mut Ioapic, irq: usize, trigger_mode: TriggerMode) {
+ let mut entry = RedirectionTableEntry::new();
+ entry.set_vector(DEFAULT_DESTINATION_ID);
+ entry.set_delivery_mode(DeliveryMode::Startup);
+ entry.set_delivery_status(DeliveryStatus::Pending);
+ entry.set_dest_id(DEFAULT_VECTOR);
+ entry.set_trigger_mode(trigger_mode);
+ write_entry(ioapic, irq, entry);
+ }
+
+ fn set_mask(ioapic: &mut Ioapic, irq: usize, mask: bool) {
+ let mut entry = read_entry(ioapic, irq);
+ entry.set_interrupt_mask(mask);
+ write_entry(ioapic, irq, entry);
+ }
+
+ #[test]
+ fn write_read_ioregsel() {
+ let mut ioapic = Ioapic::new();
+ let data_write = [0x0f, 0xf0, 0x01, 0xff];
+ 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]);
+ assert_eq!(data_write[i], data_read[i]);
+ }
+ }
+
+ // Verify that version register is actually read-only.
+ #[test]
+ fn write_read_ioaic_reg_version() {
+ let mut ioapic = Ioapic::new();
+ let before = read_reg(&mut ioapic, IOAPIC_REG_VERSION);
+ let data_write = !before;
+
+ write_reg(&mut ioapic, IOAPIC_REG_VERSION, data_write);
+ assert_eq!(read_reg(&mut ioapic, IOAPIC_REG_VERSION), before);
+ }
+
+ // Verify that only bits 27:24 of the IOAPICID are readable/writable.
+ #[test]
+ fn write_read_ioapic_reg_id() {
+ let mut ioapic = Ioapic::new();
+
+ write_reg(&mut ioapic, IOAPIC_REG_ID, 0x1f3e5d7c);
+ assert_eq!(read_reg(&mut ioapic, IOAPIC_REG_ID), 0x0f000000);
+ }
+
+ // Write to read-only register IOAPICARB.
+ #[test]
+ fn write_read_ioapic_arbitration_id() {
+ let mut ioapic = Ioapic::new();
+
+ let data_write_id = 0x1f3e5d7c;
+ let expected_result = 0x0f000000;
+
+ // Write to IOAPICID. This should also change IOAPICARB.
+ write_reg(&mut ioapic, IOAPIC_REG_ID, data_write_id);
+
+ // Read IOAPICARB
+ assert_eq!(
+ read_reg(&mut ioapic, IOAPIC_REG_ARBITRATION_ID),
+ expected_result
+ );
+
+ // Try to write to IOAPICARB and verify unchanged result.
+ write_reg(&mut ioapic, IOAPIC_REG_ARBITRATION_ID, !data_write_id);
+ assert_eq!(
+ read_reg(&mut ioapic, IOAPIC_REG_ARBITRATION_ID),
+ expected_result
+ );
+ }
+
+ #[test]
+ #[should_panic(expected = "index out of bounds: the len is 24 but the index is 24")]
+ fn service_invalid_irq() {
+ let mut ioapic = Ioapic::new();
+ ioapic.service_irq(kvm::NUM_IOAPIC_PINS, false);
+ }
+
+ // Test a level triggered IRQ interrupt.
+ #[test]
+ fn service_level_irq() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ // TODO(mutexlox): Check that interrupt is fired once.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+ }
+
+ #[test]
+ fn service_multiple_level_irqs() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+ // TODO(mutexlox): Check that interrupt is fired twice.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+ ioapic.end_of_interrupt(DEFAULT_DESTINATION_ID);
+ ioapic.service_irq(irq, true);
+ }
+
+ // Test multiple level interrupts without an EOI and verify that only one interrupt is
+ // delivered.
+ #[test]
+ fn coalesce_multiple_level_irqs() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ // TODO(mutexlox): Test that only one interrupt is delivered.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+ ioapic.service_irq(irq, true);
+ }
+
+ // Test multiple RTC interrupts without an EOI and verify that only one interrupt is delivered.
+ #[test]
+ fn coalesce_multiple_rtc_irqs() {
+ let irq = RTC_IRQ;
+ let mut ioapic = set_up_with_irq(irq, TriggerMode::Edge);
+
+ // TODO(mutexlox): Verify that only one IRQ is delivered.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+ ioapic.service_irq(irq, true);
+ }
+
+ // Test that a level interrupt that has been coalesced is re-raised if a guest issues an
+ // EndOfInterrupt after the interrupt was coalesced while the line is still asserted.
+ #[test]
+ fn reinject_level_interrupt() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ // TODO(mutexlox): Verify that only one IRQ is delivered.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+ ioapic.service_irq(irq, true);
+
+ // TODO(mutexlox): Verify that this last interrupt occurs as a result of the EOI, rather
+ // than in response to the last service_irq.
+ ioapic.end_of_interrupt(DEFAULT_DESTINATION_ID);
+ }
+
+ #[test]
+ fn service_edge_triggered_irq() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Edge);
+
+ // TODO(mutexlox): Verify that one interrupt is delivered.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, true); // Repeated asserts before a deassert should be ignored.
+ ioapic.service_irq(irq, false);
+ }
+
+ // Verify that the state of an edge-triggered interrupt is properly tracked even when the
+ // interrupt is disabled.
+ #[test]
+ fn edge_trigger_unmask_test() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Edge);
+
+ // TODO(mutexlox): Expect an IRQ.
+
+ ioapic.service_irq(irq, true);
+
+ set_mask(&mut ioapic, irq, true);
+ ioapic.service_irq(irq, false);
+
+ // No interrupt triggered while masked.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+
+ set_mask(&mut ioapic, irq, false);
+
+ // TODO(mutexlox): Expect another interrupt.
+ // Interrupt triggered while unmasked, even though when it was masked the level was high.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+ }
+
+ // Verify that a level-triggered interrupt that is triggered while masked will fire once the
+ // interrupt is unmasked.
+ #[test]
+ fn level_trigger_unmask_test() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ set_mask(&mut ioapic, irq, true);
+ ioapic.service_irq(irq, true);
+
+ // TODO(mutexlox): expect an interrupt after this.
+ set_mask(&mut ioapic, irq, false);
+ }
+
+ // Verify that multiple asserts before a deassert are ignored even if there's an EOI between
+ // them.
+ #[test]
+ fn end_of_interrupt_edge_triggered_irq() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Edge);
+
+ // TODO(mutexlox): Expect 1 interrupt.
+ ioapic.service_irq(irq, true);
+ ioapic.end_of_interrupt(DEFAULT_DESTINATION_ID);
+ // Repeated asserts before a de-assert should be ignored.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+ }
+
+ // Send multiple edge-triggered interrupts in a row.
+ #[test]
+ fn service_multiple_edge_irqs() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Edge);
+
+ ioapic.service_irq(irq, true);
+ // TODO(mutexlox): Verify that an interrupt occurs here.
+ ioapic.service_irq(irq, false);
+
+ ioapic.service_irq(irq, true);
+ // TODO(mutexlox): Verify that an interrupt occurs here.
+ ioapic.service_irq(irq, false);
+ }
+
+ // Test an interrupt line with negative polarity.
+ #[test]
+ fn service_negative_polarity_irq() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ let mut entry = read_entry(&mut ioapic, irq);
+ entry.set_polarity(1);
+ write_entry(&mut ioapic, irq, entry);
+
+ // TODO(mutexlox): Expect an interrupt to fire.
+ ioapic.service_irq(irq, false);
+ }
+
+ // Ensure that remote IRR can't be edited via mmio.
+ #[test]
+ fn remote_irr_read_only() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ ioapic.redirect_table[irq].set_remote_irr(true);
+
+ let mut entry = read_entry(&mut ioapic, irq);
+ entry.set_remote_irr(false);
+ write_entry(&mut ioapic, irq, entry);
+
+ assert_eq!(read_entry(&mut ioapic, irq).get_remote_irr(), true);
+ }
+
+ #[test]
+ fn delivery_status_read_only() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ ioapic.redirect_table[irq].set_delivery_status(DeliveryStatus::Pending);
+
+ let mut entry = read_entry(&mut ioapic, irq);
+ entry.set_delivery_status(DeliveryStatus::Idle);
+ write_entry(&mut ioapic, irq, entry);
+
+ assert_eq!(
+ read_entry(&mut ioapic, irq).get_delivery_status(),
+ DeliveryStatus::Pending
+ );
+ }
+
+ #[test]
+ fn level_to_edge_transition_clears_remote_irr() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ ioapic.redirect_table[irq].set_remote_irr(true);
+
+ let mut entry = read_entry(&mut ioapic, irq);
+ entry.set_trigger_mode(TriggerMode::Edge);
+ write_entry(&mut ioapic, irq, entry);
+
+ assert_eq!(read_entry(&mut ioapic, irq).get_remote_irr(), false);
+ }
+
+ #[test]
+ fn masking_preserves_remote_irr() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ ioapic.redirect_table[irq].set_remote_irr(true);
+
+ set_mask(&mut ioapic, irq, true);
+ set_mask(&mut ioapic, irq, false);
+
+ assert_eq!(read_entry(&mut ioapic, irq).get_remote_irr(), true);
+ }
+
+ // Test reconfiguration racing with EOIs.
+ #[test]
+ fn reconfiguration_race() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ // Fire one level-triggered interrupt.
+ // TODO(mutexlox): Check that it fires.
+ ioapic.service_irq(irq, true);
+
+ // Read the redirection table entry before the EOI...
+ let mut entry = read_entry(&mut ioapic, irq);
+ entry.set_trigger_mode(TriggerMode::Edge);
+
+ ioapic.service_irq(irq, false);
+ ioapic.end_of_interrupt(DEFAULT_DESTINATION_ID);
+
+ // ... and write back that (modified) value.
+ write_entry(&mut ioapic, irq, entry);
+
+ // Fire one *edge* triggered interrupt
+ // TODO(mutexlox): Assert that the interrupt fires once.
+ ioapic.service_irq(irq, true);
+ ioapic.service_irq(irq, false);
+ }
+
+ // Ensure that swapping to edge triggered and back clears the remote irr bit.
+ #[test]
+ fn implicit_eoi() {
+ let (mut ioapic, irq) = set_up(TriggerMode::Level);
+
+ // Fire one level-triggered interrupt.
+ ioapic.service_irq(irq, true);
+ // TODO(mutexlox): Verify that one interrupt was fired.
+ ioapic.service_irq(irq, false);
+
+ // Do an implicit EOI by cycling between edge and level triggered.
+ let mut entry = read_entry(&mut ioapic, irq);
+ entry.set_trigger_mode(TriggerMode::Edge);
+ write_entry(&mut ioapic, irq, entry);
+ entry.set_trigger_mode(TriggerMode::Level);
+ write_entry(&mut ioapic, irq, entry);
+
+ // Fire one level-triggered interrupt.
+ ioapic.service_irq(irq, true);
+ // TODO(mutexlox): Verify that one interrupt fires.
+ ioapic.service_irq(irq, false);
+ }
+
+ #[test]
+ fn set_redirection_entry_by_bits() {
+ let mut entry = RedirectionTableEntry::new();
+ // destination_mode
+ // polarity |
+ // trigger_mode | |
+ // | | |
+ // 0011 1010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1001 0110 0101 1111
+ // |_______| |______________________________________________|| | | |_| |_______|
+ // dest_id reserved | | | | vector
+ // interrupt_mask | | |
+ // remote_irr | |
+ // delivery_status |
+ // delivery_mode
+ entry.set(0, 64, 0x3a0000000000965f);
+ assert_eq!(entry.get_vector(), 0x5f);
+ assert_eq!(entry.get_delivery_mode(), DeliveryMode::Startup);
+ assert_eq!(entry.get_dest_mode(), DestinationMode::Physical);
+ assert_eq!(entry.get_delivery_status(), DeliveryStatus::Pending);
+ assert_eq!(entry.get_polarity(), 0);
+ assert_eq!(entry.get_remote_irr(), false);
+ assert_eq!(entry.get_trigger_mode(), TriggerMode::Level);
+ assert_eq!(entry.get_interrupt_mask(), false);
+ assert_eq!(entry.get_reserved(), 0);
+ assert_eq!(entry.get_dest_id(), 0x3a);
+
+ let (mut ioapic, irq) = set_up(TriggerMode::Edge);
+ write_entry(&mut ioapic, irq, entry);
+ assert_eq!(
+ read_entry(&mut ioapic, irq).get_trigger_mode(),
+ TriggerMode::Level
+ );
+
+ // TODO(mutexlox): Verify that this actually fires an interrupt.
+ ioapic.service_irq(irq, true);
+ }
+}
diff --git a/devices/src/lib.rs b/devices/src/lib.rs
new file mode 100644
index 0000000..bc9c8c1
--- /dev/null
+++ b/devices/src/lib.rs
@@ -0,0 +1,43 @@
+// Copyright 2017 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.
+
+//! Emulates virtual and hardware devices.
+
+mod bus;
+mod cmos;
+mod i8042;
+mod ioapic;
+mod pci;
+mod pic;
+mod pit;
+pub mod pl030;
+mod proxy;
+#[macro_use]
+mod register_space;
+mod serial;
+pub mod split_irqchip_common;
+pub mod usb;
+mod utils;
+pub mod virtio;
+
+pub use self::bus::Error as BusError;
+pub use self::bus::{Bus, BusDevice, BusRange};
+pub use self::cmos::Cmos;
+pub use self::i8042::I8042Device;
+pub use self::ioapic::Ioapic;
+pub use self::pci::{
+ Ac97Dev, PciConfigIo, PciConfigMmio, PciDevice, PciDeviceError, PciInterruptPin, PciRoot,
+};
+pub use self::pic::Pic;
+pub use self::pit::{Pit, PitError};
+pub use self::pl030::Pl030;
+pub use self::proxy::Error as ProxyError;
+pub use self::proxy::ProxyDevice;
+pub use self::serial::Error as SerialError;
+pub use self::serial::{
+ get_serial_tty_string, Serial, SerialParameters, SerialType, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR,
+};
+pub use self::usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider;
+pub use self::usb::xhci::xhci_controller::XhciController;
+pub use self::virtio::VirtioPciDevice;
diff --git a/devices/src/pci/ac97.rs b/devices/src/pci/ac97.rs
new file mode 100644
index 0000000..aeab1f1
--- /dev/null
+++ b/devices/src/pci/ac97.rs
@@ -0,0 +1,253 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::unix::io::RawFd;
+
+use audio_streams::StreamSource;
+use resources::{Alloc, SystemAllocator};
+use sys_util::{error, EventFd, GuestMemory};
+
+use crate::pci::ac97_bus_master::Ac97BusMaster;
+use crate::pci::ac97_mixer::Ac97Mixer;
+use crate::pci::ac97_regs::*;
+use crate::pci::pci_configuration::{
+ PciBarConfiguration, PciClassCode, PciConfiguration, PciHeaderType, PciMultimediaSubclass,
+};
+use crate::pci::pci_device::{self, PciDevice, Result};
+use crate::pci::PciInterruptPin;
+
+// Use 82801AA because it's what qemu does.
+const PCI_DEVICE_ID_INTEL_82801AA_5: u16 = 0x2415;
+
+/// AC97 audio device emulation.
+/// Provides the PCI interface for the internal Ac97 emulation.
+/// Internally the `Ac97BusMaster` and `Ac97Mixer` structs are used to emulated the bus master and
+/// mixer registers respectively. `Ac97BusMaster` handles moving smaples between guest memory and
+/// the audio backend.
+pub struct Ac97Dev {
+ config_regs: PciConfiguration,
+ pci_bus_dev: Option<(u8, u8)>,
+ // The irq events are temporarily saved here. They need to be passed to the device after the
+ // jail forks. This happens when the bus is first written.
+ irq_evt: Option<EventFd>,
+ irq_resample_evt: Option<EventFd>,
+ bus_master: Ac97BusMaster,
+ mixer: Ac97Mixer,
+}
+
+impl Ac97Dev {
+ /// Creates an 'Ac97Dev' that uses the given `GuestMemory` and starts with all registers at
+ /// default values.
+ pub fn new(mem: GuestMemory, audio_server: Box<dyn StreamSource>) -> Self {
+ let config_regs = PciConfiguration::new(
+ 0x8086,
+ PCI_DEVICE_ID_INTEL_82801AA_5,
+ PciClassCode::MultimediaController,
+ &PciMultimediaSubclass::AudioDevice,
+ None, // No Programming interface.
+ PciHeaderType::Device,
+ 0x8086, // Subsystem Vendor ID
+ 0x1, // Subsystem ID.
+ );
+
+ Ac97Dev {
+ config_regs,
+ pci_bus_dev: None,
+ irq_evt: None,
+ irq_resample_evt: None,
+ bus_master: Ac97BusMaster::new(mem, audio_server),
+ mixer: Ac97Mixer::new(),
+ }
+ }
+
+ fn read_mixer(&mut self, offset: u64, data: &mut [u8]) {
+ match data.len() {
+ // The mixer is only accessed with 16-bit words.
+ 2 => {
+ let val: u16 = self.mixer.readw(offset);
+ data[0] = val as u8;
+ data[1] = (val >> 8) as u8;
+ }
+ l => error!("mixer read length of {}", l),
+ }
+ }
+
+ fn write_mixer(&mut self, offset: u64, data: &[u8]) {
+ match data.len() {
+ // The mixer is only accessed with 16-bit words.
+ 2 => self
+ .mixer
+ .writew(offset, u16::from(data[0]) | u16::from(data[1]) << 8),
+ l => error!("mixer write length of {}", l),
+ }
+ // Apply the new mixer settings to the bus master.
+ self.bus_master.update_mixer_settings(&self.mixer);
+ }
+
+ fn read_bus_master(&mut self, offset: u64, data: &mut [u8]) {
+ match data.len() {
+ 1 => data[0] = self.bus_master.readb(offset),
+ 2 => {
+ let val: u16 = self.bus_master.readw(offset);
+ data[0] = val as u8;
+ data[1] = (val >> 8) as u8;
+ }
+ 4 => {
+ let val: u32 = self.bus_master.readl(offset);
+ data[0] = val as u8;
+ data[1] = (val >> 8) as u8;
+ data[2] = (val >> 16) as u8;
+ data[3] = (val >> 24) as u8;
+ }
+ l => error!("read length of {}", l),
+ }
+ }
+
+ fn write_bus_master(&mut self, offset: u64, data: &[u8]) {
+ match data.len() {
+ 1 => self.bus_master.writeb(offset, data[0], &self.mixer),
+ 2 => self
+ .bus_master
+ .writew(offset, u16::from(data[0]) | u16::from(data[1]) << 8),
+ 4 => self.bus_master.writel(
+ offset,
+ (u32::from(data[0]))
+ | (u32::from(data[1]) << 8)
+ | (u32::from(data[2]) << 16)
+ | (u32::from(data[3]) << 24),
+ ),
+ l => error!("write length of {}", l),
+ }
+ }
+}
+
+impl PciDevice for Ac97Dev {
+ fn debug_label(&self) -> String {
+ "AC97".to_owned()
+ }
+
+ fn assign_bus_dev(&mut self, bus: u8, device: u8) {
+ self.pci_bus_dev = Some((bus, device));
+ }
+
+ fn assign_irq(
+ &mut self,
+ irq_evt: EventFd,
+ irq_resample_evt: EventFd,
+ irq_num: u32,
+ irq_pin: PciInterruptPin,
+ ) {
+ self.config_regs.set_irq(irq_num as u8, irq_pin);
+ self.irq_evt = Some(irq_evt);
+ self.irq_resample_evt = Some(irq_resample_evt);
+ }
+
+ fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
+ let (bus, dev) = self
+ .pci_bus_dev
+ .expect("assign_bus_dev must be called prior to allocate_io_bars");
+ let mut ranges = Vec::new();
+ let mixer_regs_addr = resources
+ .mmio_allocator()
+ .allocate(
+ MIXER_REGS_SIZE,
+ Alloc::PciBar { bus, dev, bar: 0 },
+ "ac97-mixer_regs".to_string(),
+ )
+ .map_err(|e| pci_device::Error::IoAllocationFailed(MIXER_REGS_SIZE, e))?;
+ let mixer_config = PciBarConfiguration::default()
+ .set_register_index(0)
+ .set_address(mixer_regs_addr)
+ .set_size(MIXER_REGS_SIZE);
+ self.config_regs
+ .add_pci_bar(&mixer_config)
+ .map_err(|e| pci_device::Error::IoRegistrationFailed(mixer_regs_addr, e))?;
+ ranges.push((mixer_regs_addr, MIXER_REGS_SIZE));
+
+ let master_regs_addr = resources
+ .mmio_allocator()
+ .allocate(
+ MASTER_REGS_SIZE,
+ Alloc::PciBar { bus, dev, bar: 1 },
+ "ac97-master_regs".to_string(),
+ )
+ .map_err(|e| pci_device::Error::IoAllocationFailed(MASTER_REGS_SIZE, e))?;
+ let master_config = PciBarConfiguration::default()
+ .set_register_index(1)
+ .set_address(master_regs_addr)
+ .set_size(MASTER_REGS_SIZE);
+ self.config_regs
+ .add_pci_bar(&master_config)
+ .map_err(|e| pci_device::Error::IoRegistrationFailed(master_regs_addr, e))?;
+ ranges.push((master_regs_addr, MASTER_REGS_SIZE));
+ Ok(ranges)
+ }
+
+ fn config_registers(&self) -> &PciConfiguration {
+ &self.config_regs
+ }
+
+ fn config_registers_mut(&mut self) -> &mut PciConfiguration {
+ &mut self.config_regs
+ }
+
+ fn keep_fds(&self) -> Vec<RawFd> {
+ if let Some(server_fds) = self.bus_master.keep_fds() {
+ server_fds
+ } else {
+ Vec::new()
+ }
+ }
+
+ fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
+ let bar0 = u64::from(self.config_regs.get_bar_addr(0));
+ let bar1 = u64::from(self.config_regs.get_bar_addr(1));
+ match addr {
+ a if a >= bar0 && a < bar0 + MIXER_REGS_SIZE => self.read_mixer(addr - bar0, data),
+ a if a >= bar1 && a < bar1 + MASTER_REGS_SIZE => {
+ self.read_bus_master(addr - bar1, data)
+ }
+ _ => (),
+ }
+ }
+
+ fn write_bar(&mut self, addr: u64, data: &[u8]) {
+ let bar0 = u64::from(self.config_regs.get_bar_addr(0));
+ let bar1 = u64::from(self.config_regs.get_bar_addr(1));
+ match addr {
+ a if a >= bar0 && a < bar0 + MIXER_REGS_SIZE => self.write_mixer(addr - bar0, data),
+ a if a >= bar1 && a < bar1 + MASTER_REGS_SIZE => {
+ // Check if the irq needs to be passed to the device.
+ if let (Some(irq_evt), Some(irq_resample_evt)) =
+ (self.irq_evt.take(), self.irq_resample_evt.take())
+ {
+ self.bus_master.set_irq_event_fd(irq_evt, irq_resample_evt);
+ }
+ self.write_bus_master(addr - bar1, data)
+ }
+ _ => (),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use audio_streams::DummyStreamSource;
+ use sys_util::GuestAddress;
+
+ #[test]
+ fn create() {
+ let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)]).unwrap();
+ let mut ac97_dev = Ac97Dev::new(mem, Box::new(DummyStreamSource::new()));
+ let mut allocator = SystemAllocator::builder()
+ .add_io_addresses(0x1000_0000, 0x1000_0000)
+ .add_mmio_addresses(0x2000_0000, 0x1000_0000)
+ .add_device_addresses(0x3000_0000, 0x1000_0000)
+ .create_allocator(5, false)
+ .unwrap();
+ ac97_dev.assign_bus_dev(0, 0);
+ assert!(ac97_dev.allocate_io_bars(&mut allocator).is_ok());
+ }
+}
diff --git a/devices/src/pci/ac97_bus_master.rs b/devices/src/pci/ac97_bus_master.rs
new file mode 100644
index 0000000..bde5c90
--- /dev/null
+++ b/devices/src/pci/ac97_bus_master.rs
@@ -0,0 +1,1043 @@
+// Copyright 2018 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;
+use std::error::Error;
+use std::fmt::{self, Display};
+use std::io::Write;
+use std::os::unix::io::RawFd;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+use std::thread;
+use std::time::Instant;
+
+use audio_streams::{
+ capture::{CaptureBuffer, CaptureBufferStream},
+ PlaybackBuffer, PlaybackBufferStream, StreamControl, StreamSource,
+};
+use data_model::{VolatileMemory, VolatileSlice};
+use sync::Mutex;
+use sys_util::{
+ self, error, set_rt_prio_limit, set_rt_round_robin, warn, EventFd, GuestAddress, GuestMemory,
+};
+
+use crate::pci::ac97_mixer::Ac97Mixer;
+use crate::pci::ac97_regs::*;
+
+const DEVICE_SAMPLE_RATE: usize = 48000;
+
+// Bus Master registers. Keeps the state of the bus master register values. Used to share the state
+// between the main and audio threads.
+struct Ac97BusMasterRegs {
+ pi_regs: Ac97FunctionRegs, // Input
+ po_regs: Ac97FunctionRegs, // Output
+ po_pointer_update_time: Instant, // Time the picb and civ regs were last updated.
+ mc_regs: Ac97FunctionRegs, // Microphone
+ glob_cnt: u32,
+ glob_sta: u32,
+
+ // IRQ event - driven by the glob_sta register.
+ irq_evt: Option<EventFd>,
+}
+
+impl Ac97BusMasterRegs {
+ fn new() -> Ac97BusMasterRegs {
+ Ac97BusMasterRegs {
+ pi_regs: Ac97FunctionRegs::new(),
+ po_regs: Ac97FunctionRegs::new(),
+ po_pointer_update_time: Instant::now(),
+ mc_regs: Ac97FunctionRegs::new(),
+ glob_cnt: 0,
+ glob_sta: GLOB_STA_RESET_VAL,
+ irq_evt: None,
+ }
+ }
+
+ fn func_regs(&self, func: Ac97Function) -> &Ac97FunctionRegs {
+ match func {
+ Ac97Function::Input => &self.pi_regs,
+ Ac97Function::Output => &self.po_regs,
+ Ac97Function::Microphone => &self.mc_regs,
+ }
+ }
+
+ fn func_regs_mut(&mut self, func: Ac97Function) -> &mut Ac97FunctionRegs {
+ match func {
+ Ac97Function::Input => &mut self.pi_regs,
+ Ac97Function::Output => &mut self.po_regs,
+ Ac97Function::Microphone => &mut self.mc_regs,
+ }
+ }
+}
+
+// Internal error type used for reporting errors from guest memory reading.
+#[derive(Debug)]
+enum GuestMemoryError {
+ // Failure getting the address of the audio buffer.
+ ReadingGuestBufferAddress(sys_util::GuestMemoryError),
+ // Failure reading samples from guest memory.
+ ReadingGuestSamples(data_model::VolatileMemoryError),
+}
+
+impl std::error::Error for GuestMemoryError {}
+
+impl Display for GuestMemoryError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::GuestMemoryError::*;
+
+ match self {
+ ReadingGuestBufferAddress(e) => {
+ write!(f, "Failed to get the address of the audio buffer: {}.", e)
+ }
+ ReadingGuestSamples(e) => write!(f, "Failed to read samples from guest memory: {}.", e),
+ }
+ }
+}
+
+impl From<GuestMemoryError> for PlaybackError {
+ fn from(err: GuestMemoryError) -> Self {
+ PlaybackError::ReadingGuestError(err)
+ }
+}
+
+impl From<GuestMemoryError> for CaptureError {
+ fn from(err: GuestMemoryError) -> Self {
+ CaptureError::ReadingGuestError(err)
+ }
+}
+
+type GuestMemoryResult<T> = std::result::Result<T, GuestMemoryError>;
+
+// Internal error type used for reporting errors from the audio playback thread.
+#[derive(Debug)]
+enum PlaybackError {
+ // Failure to read guest memory.
+ ReadingGuestError(GuestMemoryError),
+ // Failure to get an buffer from the stream.
+ StreamError(Box<dyn Error>),
+ // Failure writing to the audio output.
+ WritingOutput(std::io::Error),
+}
+
+impl std::error::Error for PlaybackError {}
+
+impl Display for PlaybackError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::PlaybackError::*;
+
+ match self {
+ ReadingGuestError(e) => write!(f, "Failed to read guest memory: {}.", e),
+ StreamError(e) => write!(f, "Failed to get a buffer from the stream: {}", e),
+ WritingOutput(e) => write!(f, "Failed to write audio output: {}.", e),
+ }
+ }
+}
+
+type PlaybackResult<T> = std::result::Result<T, PlaybackError>;
+
+// Internal error type used for reporting errors from the audio capture thread.
+#[derive(Debug)]
+enum CaptureError {
+ // Failure to read guest memory.
+ ReadingGuestError(GuestMemoryError),
+ // Failure to get an buffer from the stream.
+ StreamError(Box<dyn Error>),
+}
+
+impl std::error::Error for CaptureError {}
+
+impl Display for CaptureError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::CaptureError::*;
+
+ match self {
+ ReadingGuestError(e) => write!(f, "Failed to read guest memory: {}.", e),
+ StreamError(e) => write!(f, "Failed to get a buffer from the stream: {}", e),
+ }
+ }
+}
+
+type CaptureResult<T> = std::result::Result<T, CaptureError>;
+
+/// `Ac97BusMaster` emulates the bus master portion of AC97. It exposes a register read/write
+/// interface compliant with the ICH bus master.
+pub struct Ac97BusMaster {
+ // Keep guest memory as each function will use it for buffer descriptors.
+ mem: GuestMemory,
+ regs: Arc<Mutex<Ac97BusMasterRegs>>,
+ acc_sema: u8,
+
+ // Audio thread for capture stream.
+ audio_thread_pi: Option<thread::JoinHandle<()>>,
+ audio_thread_pi_run: Arc<AtomicBool>,
+ pi_stream_control: Option<Box<dyn StreamControl>>,
+
+ // Audio thread book keeping.
+ audio_thread_po: Option<thread::JoinHandle<()>>,
+ audio_thread_po_run: Arc<AtomicBool>,
+ po_stream_control: Option<Box<dyn StreamControl>>,
+
+ // Audio server used to create playback or capture streams.
+ audio_server: Box<dyn StreamSource>,
+
+ // Thread for hadlind IRQ resample events from the guest.
+ irq_resample_thread: Option<thread::JoinHandle<()>>,
+}
+
+impl Ac97BusMaster {
+ /// Creates an Ac97BusMaster` object that plays audio from `mem` to streams provided by
+ /// `audio_server`.
+ pub fn new(mem: GuestMemory, audio_server: Box<dyn StreamSource>) -> Self {
+ Ac97BusMaster {
+ mem,
+ regs: Arc::new(Mutex::new(Ac97BusMasterRegs::new())),
+ acc_sema: 0,
+
+ audio_thread_pi: None,
+ audio_thread_pi_run: Arc::new(AtomicBool::new(false)),
+ pi_stream_control: None,
+
+ audio_thread_po: None,
+ audio_thread_po_run: Arc::new(AtomicBool::new(false)),
+ po_stream_control: None,
+
+ audio_server,
+
+ irq_resample_thread: None,
+ }
+ }
+
+ /// Returns any file descriptors that need to be kept open when entering a jail.
+ pub fn keep_fds(&self) -> Option<Vec<RawFd>> {
+ self.audio_server.keep_fds()
+ }
+
+ /// Provides the events needed to raise interrupts in the guest.
+ pub fn set_irq_event_fd(&mut self, irq_evt: EventFd, irq_resample_evt: EventFd) {
+ let thread_regs = self.regs.clone();
+ self.regs.lock().irq_evt = Some(irq_evt);
+ self.irq_resample_thread = Some(thread::spawn(move || {
+ loop {
+ if let Err(e) = irq_resample_evt.read() {
+ error!(
+ "Failed to read the irq event from the resample thread: {}.",
+ e,
+ );
+ break;
+ }
+ {
+ // Scope for the lock on thread_regs.
+ let regs = thread_regs.lock();
+ // Check output irq
+ let po_int_mask = regs.func_regs(Ac97Function::Output).int_mask();
+ if regs.func_regs(Ac97Function::Output).sr & po_int_mask != 0 {
+ if let Some(irq_evt) = regs.irq_evt.as_ref() {
+ if let Err(e) = irq_evt.write(1) {
+ error!("Failed to set the irq from the resample thread: {}.", e);
+ break;
+ }
+ }
+ }
+ // Check input irq
+ let pi_int_mask = regs.func_regs(Ac97Function::Input).int_mask();
+ if regs.func_regs(Ac97Function::Input).sr & pi_int_mask != 0 {
+ if let Some(irq_evt) = regs.irq_evt.as_ref() {
+ if let Err(e) = irq_evt.write(1) {
+ error!("Failed to set the irq from the resample thread: {}.", e);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }));
+ }
+
+ /// Called when `mixer` has been changed and the new values should be applied to currently
+ /// active streams.
+ pub fn update_mixer_settings(&mut self, mixer: &Ac97Mixer) {
+ if let Some(control) = self.po_stream_control.as_mut() {
+ // The audio server only supports one volume, not separate left and right.
+ let (muted, left_volume, _right_volume) = mixer.get_master_volume();
+ control.set_volume(left_volume);
+ control.set_mute(muted);
+ }
+ }
+
+ /// Checks if the bus master is in the cold reset state.
+ pub fn is_cold_reset(&self) -> bool {
+ self.regs.lock().glob_cnt & GLOB_CNT_COLD_RESET == 0
+ }
+
+ /// Reads a byte from the given `offset`.
+ pub fn readb(&mut self, offset: u64) -> u8 {
+ fn readb_func_regs(func_regs: &Ac97FunctionRegs, offset: u64) -> u8 {
+ match offset {
+ CIV_OFFSET => func_regs.civ,
+ LVI_OFFSET => func_regs.lvi,
+ SR_OFFSET => func_regs.sr as u8,
+ PIV_OFFSET => func_regs.piv,
+ CR_OFFSET => func_regs.cr,
+ _ => 0,
+ }
+ }
+
+ let regs = self.regs.lock();
+ match offset {
+ PI_BASE_00...PI_CR_0B => readb_func_regs(®s.pi_regs, offset - PI_BASE_00),
+ PO_BASE_10...PO_CR_1B => readb_func_regs(®s.po_regs, offset - PO_BASE_10),
+ MC_BASE_20...MC_CR_2B => readb_func_regs(®s.mc_regs, offset - MC_BASE_20),
+ ACC_SEMA_34 => self.acc_sema,
+ _ => 0,
+ }
+ }
+
+ /// Reads a word from the given `offset`.
+ pub fn readw(&mut self, offset: u64) -> u16 {
+ let regs = self.regs.lock();
+ match offset {
+ PI_SR_06 => regs.pi_regs.sr,
+ PI_PICB_08 => regs.pi_regs.picb,
+ PO_SR_16 => regs.po_regs.sr,
+ PO_PICB_18 => {
+ // PO PICB
+ if !self.audio_thread_po_run.load(Ordering::Relaxed) {
+ // Not running, no need to estimate what has been consumed.
+ regs.po_regs.picb
+ } else {
+ // Estimate how many samples have been played since the last audio callback.
+ let num_channels = 2;
+ let micros = regs.po_pointer_update_time.elapsed().subsec_micros();
+ // Round down to the next 10 millisecond boundary. The linux driver often
+ // assumes that two rapid reads from picb will return the same value.
+ let millis = micros / 1000 / 10 * 10;
+
+ let frames_consumed = DEVICE_SAMPLE_RATE as u64 * u64::from(millis) / 1000;
+
+ regs.po_regs
+ .picb
+ .saturating_sub((num_channels * frames_consumed) as u16)
+ }
+ }
+ MC_SR_26 => regs.mc_regs.sr,
+ MC_PICB_28 => regs.mc_regs.picb,
+ _ => 0,
+ }
+ }
+
+ /// Reads a 32-bit word from the given `offset`.
+ pub fn readl(&mut self, offset: u64) -> u32 {
+ let regs = self.regs.lock();
+ match offset {
+ PI_BDBAR_00 => regs.pi_regs.bdbar,
+ PI_CIV_04 => regs.pi_regs.atomic_status_regs(),
+ PO_BDBAR_10 => regs.po_regs.bdbar,
+ PO_CIV_14 => regs.po_regs.atomic_status_regs(),
+ MC_BDBAR_20 => regs.mc_regs.bdbar,
+ MC_CIV_24 => regs.mc_regs.atomic_status_regs(),
+ GLOB_CNT_2C => regs.glob_cnt,
+ GLOB_STA_30 => regs.glob_sta,
+ _ => 0,
+ }
+ }
+
+ /// Writes the byte `val` to the register specified by `offset`.
+ pub fn writeb(&mut self, offset: u64, val: u8, mixer: &Ac97Mixer) {
+ // Only process writes to the control register when cold reset is set.
+ if self.is_cold_reset() {
+ return;
+ }
+
+ match offset {
+ PI_CIV_04 => (), // RO
+ PI_LVI_05 => self.set_lvi(Ac97Function::Input, val),
+ PI_SR_06 => self.set_sr(Ac97Function::Input, u16::from(val)),
+ PI_PIV_0A => (), // RO
+ PI_CR_0B => self.set_cr(Ac97Function::Input, val, mixer),
+ PO_CIV_14 => (), // RO
+ PO_LVI_15 => self.set_lvi(Ac97Function::Output, val),
+ PO_SR_16 => self.set_sr(Ac97Function::Output, u16::from(val)),
+ PO_PIV_1A => (), // RO
+ PO_CR_1B => self.set_cr(Ac97Function::Output, val, mixer),
+ MC_CIV_24 => (), // RO
+ MC_LVI_25 => self.set_lvi(Ac97Function::Microphone, val),
+ MC_PIV_2A => (), // RO
+ MC_CR_2B => self.set_cr(Ac97Function::Microphone, val, mixer),
+ ACC_SEMA_34 => self.acc_sema = val,
+ o => warn!("write byte to 0x{:x}", o),
+ }
+ }
+
+ /// Writes the word `val` to the register specified by `offset`.
+ pub fn writew(&mut self, offset: u64, val: u16) {
+ // Only process writes to the control register when cold reset is set.
+ if self.is_cold_reset() {
+ return;
+ }
+ match offset {
+ PI_SR_06 => self.set_sr(Ac97Function::Input, val),
+ PI_PICB_08 => (), // RO
+ PO_SR_16 => self.set_sr(Ac97Function::Output, val),
+ PO_PICB_18 => (), // RO
+ MC_SR_26 => self.set_sr(Ac97Function::Microphone, val),
+ MC_PICB_28 => (), // RO
+ o => warn!("write word to 0x{:x}", o),
+ }
+ }
+
+ /// Writes the 32-bit `val` to the register specified by `offset`.
+ pub fn writel(&mut self, offset: u64, val: u32) {
+ // Only process writes to the control register when cold reset is set.
+ if self.is_cold_reset() && offset != 0x2c {
+ return;
+ }
+ match offset {
+ PI_BDBAR_00 => self.set_bdbar(Ac97Function::Input, val),
+ PO_BDBAR_10 => self.set_bdbar(Ac97Function::Output, val),
+ MC_BDBAR_20 => self.set_bdbar(Ac97Function::Microphone, val),
+ GLOB_CNT_2C => self.set_glob_cnt(val),
+ GLOB_STA_30 => (), // RO
+ o => warn!("write long to 0x{:x}", o),
+ }
+ }
+
+ fn set_bdbar(&mut self, func: Ac97Function, val: u32) {
+ self.regs.lock().func_regs_mut(func).bdbar = val & !0x07;
+ }
+
+ fn set_lvi(&mut self, func: Ac97Function, val: u8) {
+ let mut regs = self.regs.lock();
+ let func_regs = regs.func_regs_mut(func);
+ func_regs.lvi = val % 32; // LVI wraps at 32.
+
+ // If running and stalled waiting for more valid buffers, restart by clearing the "DMA
+ // stopped" bit.
+ if func_regs.cr & CR_RPBM == CR_RPBM
+ && func_regs.sr & SR_DCH == SR_DCH
+ && func_regs.civ != func_regs.lvi
+ {
+ func_regs.sr &= !SR_DCH;
+ }
+ }
+
+ fn set_sr(&mut self, func: Ac97Function, val: u16) {
+ let mut sr = self.regs.lock().func_regs(func).sr;
+ if val & SR_FIFOE != 0 {
+ sr &= !SR_FIFOE;
+ }
+ if val & SR_LVBCI != 0 {
+ sr &= !SR_LVBCI;
+ }
+ if val & SR_BCIS != 0 {
+ sr &= !SR_BCIS;
+ }
+ update_sr(&mut self.regs.lock(), func, sr);
+ }
+
+ fn set_cr(&mut self, func: Ac97Function, val: u8, mixer: &Ac97Mixer) {
+ if val & CR_RR != 0 {
+ self.stop_audio(func);
+ let mut regs = self.regs.lock();
+ regs.func_regs_mut(func).do_reset();
+ } else {
+ let cr = self.regs.lock().func_regs(func).cr;
+ if val & CR_RPBM == 0 {
+ // Run/Pause set to pause.
+ self.stop_audio(func);
+ let mut regs = self.regs.lock();
+ regs.func_regs_mut(func).sr |= SR_DCH;
+ } else if cr & CR_RPBM == 0 {
+ // Not already running.
+ // Run/Pause set to run.
+ {
+ let mut regs = self.regs.lock();
+ let func_regs = regs.func_regs_mut(func);
+ func_regs.piv = 1;
+ func_regs.civ = 0;
+ func_regs.sr &= !SR_DCH;
+ }
+ if self.start_audio(func, mixer).is_err() {
+ warn!("Failed to start audio");
+ }
+ }
+ let mut regs = self.regs.lock();
+ regs.func_regs_mut(func).cr = val & CR_VALID_MASK;
+ }
+ }
+
+ fn set_glob_cnt(&mut self, new_glob_cnt: u32) {
+ // Only the reset bits are emulated, the GPI and PCM formatting are not supported.
+ if new_glob_cnt & GLOB_CNT_COLD_RESET == 0 {
+ self.reset_audio_regs();
+
+ let mut regs = self.regs.lock();
+ regs.glob_cnt = new_glob_cnt & GLOB_CNT_STABLE_BITS;
+ self.acc_sema = 0;
+ return;
+ }
+ if new_glob_cnt & GLOB_CNT_WARM_RESET != 0 {
+ // Check if running and if so, ignore. Warm reset is specified to no-op when the device
+ // is playing or recording audio.
+ if !self.audio_thread_po_run.load(Ordering::Relaxed) {
+ self.stop_all_audio();
+ let mut regs = self.regs.lock();
+ regs.glob_cnt = new_glob_cnt & !GLOB_CNT_WARM_RESET; // Auto-cleared reset bit.
+ return;
+ }
+ }
+ self.regs.lock().glob_cnt = new_glob_cnt;
+ }
+
+ fn start_audio(&mut self, func: Ac97Function, mixer: &Ac97Mixer) -> Result<(), Box<dyn Error>> {
+ const AUDIO_THREAD_RTPRIO: u16 = 12; // Matches other cros audio clients.
+
+ match func {
+ Ac97Function::Input => {
+ let num_channels = 2;
+ let buffer_samples =
+ current_buffer_size(self.regs.lock().func_regs(func), &self.mem)?;
+ let buffer_frames = buffer_samples / num_channels;
+ let (stream_control, input_stream) = self.audio_server.new_capture_stream(
+ num_channels,
+ DEVICE_SAMPLE_RATE,
+ buffer_frames,
+ )?;
+ self.pi_stream_control = Some(stream_control);
+ self.update_mixer_settings(mixer);
+
+ self.audio_thread_pi_run.store(true, Ordering::Relaxed);
+ let thread_run = self.audio_thread_pi_run.clone();
+ let thread_mem = self.mem.clone();
+ let thread_regs = self.regs.clone();
+
+ self.audio_thread_pi = Some(thread::spawn(move || {
+ if set_rt_prio_limit(u64::from(AUDIO_THREAD_RTPRIO)).is_err()
+ || set_rt_round_robin(i32::from(AUDIO_THREAD_RTPRIO)).is_err()
+ {
+ warn!("Failed to set audio thread to real time.");
+ }
+ if let Err(e) =
+ audio_in_thread(thread_regs, thread_mem, &thread_run, input_stream)
+ {
+ error!("Capture error: {}", e);
+ }
+ thread_run.store(false, Ordering::Relaxed);
+ }));
+ }
+ Ac97Function::Output => {
+ let num_channels = 2;
+
+ let buffer_samples =
+ current_buffer_size(self.regs.lock().func_regs(func), &self.mem)?;
+
+ let buffer_frames = buffer_samples / num_channels;
+ let (stream_control, output_stream) = self.audio_server.new_playback_stream(
+ num_channels,
+ DEVICE_SAMPLE_RATE,
+ buffer_frames,
+ )?;
+ self.po_stream_control = Some(stream_control);
+
+ self.update_mixer_settings(mixer);
+
+ self.audio_thread_po_run.store(true, Ordering::Relaxed);
+ let thread_run = self.audio_thread_po_run.clone();
+ let thread_mem = self.mem.clone();
+ let thread_regs = self.regs.clone();
+
+ self.audio_thread_po = Some(thread::spawn(move || {
+ if set_rt_prio_limit(u64::from(AUDIO_THREAD_RTPRIO)).is_err()
+ || set_rt_round_robin(i32::from(AUDIO_THREAD_RTPRIO)).is_err()
+ {
+ warn!("Failed to set audio thread to real time.");
+ }
+ if let Err(e) =
+ audio_out_thread(thread_regs, thread_mem, &thread_run, output_stream)
+ {
+ error!("Playback error: {}", e);
+ }
+ thread_run.store(false, Ordering::Relaxed);
+ }));
+ }
+ Ac97Function::Microphone => (),
+ }
+ Ok(())
+ }
+
+ fn stop_audio(&mut self, func: Ac97Function) {
+ match func {
+ Ac97Function::Input => {
+ self.audio_thread_pi_run.store(false, Ordering::Relaxed);
+ if let Some(thread) = self.audio_thread_pi.take() {
+ if let Err(e) = thread.join() {
+ error!("Failed to join the capture thread: {:?}.", e);
+ }
+ }
+ }
+ Ac97Function::Output => {
+ self.audio_thread_po_run.store(false, Ordering::Relaxed);
+ if let Some(thread) = self.audio_thread_po.take() {
+ if let Err(e) = thread.join() {
+ error!("Failed to join the playback thread: {:?}.", e);
+ }
+ }
+ }
+ Ac97Function::Microphone => (),
+ };
+ }
+
+ fn stop_all_audio(&mut self) {
+ self.stop_audio(Ac97Function::Input);
+ self.stop_audio(Ac97Function::Output);
+ self.stop_audio(Ac97Function::Microphone);
+ }
+
+ fn reset_audio_regs(&mut self) {
+ self.stop_all_audio();
+ let mut regs = self.regs.lock();
+ regs.pi_regs.do_reset();
+ regs.po_regs.do_reset();
+ regs.mc_regs.do_reset();
+ }
+}
+
+// Gets the next buffer from the guest. This will return `None` if the DMA controlled stopped bit is
+// set, such as after an underrun where CIV hits LVI.
+fn next_guest_buffer<'a>(
+ func_regs: &mut Ac97FunctionRegs,
+ mem: &'a GuestMemory,
+) -> GuestMemoryResult<Option<VolatileSlice<'a>>> {
+ let sample_size = 2;
+
+ if func_regs.sr & SR_DCH != 0 {
+ return Ok(None);
+ }
+ let next_buffer = func_regs.civ;
+ let descriptor_addr = func_regs.bdbar + u32::from(next_buffer) * DESCRIPTOR_LENGTH as u32;
+ let buffer_addr_reg: u32 = mem
+ .read_obj_from_addr(GuestAddress(u64::from(descriptor_addr)))
+ .map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
+ let buffer_addr = buffer_addr_reg & !0x03u32; // The address must be aligned to four bytes.
+ let control_reg: u32 = mem
+ .read_obj_from_addr(GuestAddress(u64::from(descriptor_addr) + 4))
+ .map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
+ let buffer_samples: usize = control_reg as usize & 0x0000_ffff;
+
+ func_regs.picb = buffer_samples as u16;
+
+ let samples_remaining = func_regs.picb as usize;
+ if samples_remaining == 0 {
+ return Ok(None);
+ }
+ let read_pos = u64::from(buffer_addr);
+ Ok(Some(
+ mem.get_slice(read_pos, samples_remaining as u64 * sample_size)
+ .map_err(GuestMemoryError::ReadingGuestSamples)?,
+ ))
+}
+
+// Reads the next buffer from guest memory and writes it to `out_buffer`.
+fn play_buffer(
+ regs: &mut Ac97BusMasterRegs,
+ mem: &GuestMemory,
+ out_buffer: &mut PlaybackBuffer,
+) -> PlaybackResult<()> {
+ // If the current buffer had any samples in it, mark it as done.
+ if regs.func_regs_mut(Ac97Function::Output).picb > 0 {
+ buffer_completed(regs, mem, Ac97Function::Output)?
+ }
+ let func_regs = regs.func_regs_mut(Ac97Function::Output);
+ let buffer_len = func_regs.picb * 2;
+ if let Some(buffer) = next_guest_buffer(func_regs, mem)? {
+ out_buffer.copy_cb(buffer.size() as usize, |out| buffer.copy_to(out));
+ } else {
+ let zeros = vec![0u8; buffer_len as usize];
+ out_buffer
+ .write(&zeros)
+ .map_err(PlaybackError::WritingOutput)?;
+ }
+ Ok(())
+}
+
+// Moves to the next buffer for the given function and registers.
+fn buffer_completed(
+ regs: &mut Ac97BusMasterRegs,
+ mem: &GuestMemory,
+ func: Ac97Function,
+) -> GuestMemoryResult<()> {
+ // check if the completed descriptor wanted an interrupt on completion.
+ let civ = regs.func_regs(func).civ;
+ let descriptor_addr = regs.func_regs(func).bdbar + u32::from(civ) * DESCRIPTOR_LENGTH as u32;
+ let control_reg: u32 = mem
+ .read_obj_from_addr(GuestAddress(u64::from(descriptor_addr) + 4))
+ .map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
+
+ let mut new_sr = regs.func_regs(func).sr;
+
+ if control_reg & BD_IOC != 0 {
+ new_sr |= SR_BCIS;
+ }
+
+ let lvi = regs.func_regs(func).lvi;
+ // if the current buffer was the last valid buffer, then update the status register to
+ // indicate that the end of audio was hit and possibly raise an interrupt.
+ if civ == lvi {
+ new_sr |= SR_DCH | SR_CELV | SR_LVBCI;
+ } else {
+ let func_regs = regs.func_regs_mut(func);
+ func_regs.civ = func_regs.piv;
+ func_regs.piv = (func_regs.piv + 1) % 32; // move piv to the next buffer.
+ }
+
+ if new_sr != regs.func_regs(func).sr {
+ update_sr(regs, func, new_sr);
+ }
+
+ regs.po_pointer_update_time = Instant::now();
+
+ Ok(())
+}
+
+// Runs, playing back audio from the guest to `output_stream` until stopped or an error occurs.
+fn audio_out_thread(
+ regs: Arc<Mutex<Ac97BusMasterRegs>>,
+ mem: GuestMemory,
+ thread_run: &AtomicBool,
+ mut output_stream: Box<dyn PlaybackBufferStream>,
+) -> PlaybackResult<()> {
+ while thread_run.load(Ordering::Relaxed) {
+ output_stream
+ .next_playback_buffer()
+ .map_err(PlaybackError::StreamError)
+ .and_then(|mut pb_buf| play_buffer(&mut regs.lock(), &mem, &mut pb_buf))?;
+ }
+ Ok(())
+}
+
+// Reads samples from `in_buffer` and writes it to the next buffer from guest memory.
+fn capture_buffer(
+ regs: &mut Ac97BusMasterRegs,
+ mem: &GuestMemory,
+ in_buffer: &mut CaptureBuffer,
+) -> CaptureResult<()> {
+ // If the current buffer had any samples in it, mark it as done.
+ if regs.func_regs_mut(Ac97Function::Input).picb > 0 {
+ buffer_completed(regs, mem, Ac97Function::Input)?
+ }
+ let func_regs = regs.func_regs_mut(Ac97Function::Input);
+ if let Some(buffer) = next_guest_buffer(func_regs, mem)? {
+ in_buffer.copy_cb(buffer.size() as usize, |inb| buffer.copy_from(inb))
+ }
+ Ok(())
+}
+
+// Runs, capturing audio from `input_stream` to the guest until stopped or an error occurs.
+fn audio_in_thread(
+ regs: Arc<Mutex<Ac97BusMasterRegs>>,
+ mem: GuestMemory,
+ thread_run: &AtomicBool,
+ mut input_stream: Box<dyn CaptureBufferStream>,
+) -> CaptureResult<()> {
+ while thread_run.load(Ordering::Relaxed) {
+ input_stream
+ .next_capture_buffer()
+ .map_err(CaptureError::StreamError)
+ .and_then(|mut cp_buf| capture_buffer(&mut regs.lock(), &mem, &mut cp_buf))?;
+ }
+ Ok(())
+}
+
+// Update the status register and if any interrupts need to fire, raise them.
+fn update_sr(regs: &mut Ac97BusMasterRegs, func: Ac97Function, val: u16) {
+ let int_mask = match func {
+ Ac97Function::Input => GS_PIINT,
+ Ac97Function::Output => GS_POINT,
+ Ac97Function::Microphone => GS_MINT,
+ };
+
+ let mut interrupt_high = false;
+
+ {
+ let func_regs = regs.func_regs_mut(func);
+ func_regs.sr = val;
+ if val & SR_INT_MASK != 0 {
+ if (val & SR_LVBCI) != 0 && (func_regs.cr & CR_LVBIE) != 0 {
+ interrupt_high = true;
+ }
+ if (val & SR_BCIS) != 0 && (func_regs.cr & CR_IOCE) != 0 {
+ interrupt_high = true;
+ }
+ }
+ }
+
+ if interrupt_high {
+ regs.glob_sta |= int_mask;
+ if let Some(irq_evt) = regs.irq_evt.as_ref() {
+ // Ignore write failure, nothing can be done about it from here.
+ let _ = irq_evt.write(1);
+ }
+ } else {
+ regs.glob_sta &= !int_mask;
+ if regs.glob_sta & (GS_PIINT | GS_POINT | GS_MINT) == 0 {
+ if let Some(irq_evt) = regs.irq_evt.as_ref() {
+ // Ignore write failure, nothing can be done about it from here.
+ let _ = irq_evt.write(0);
+ }
+ }
+ }
+}
+
+// Returns the size in samples of the buffer pointed to by the CIV register.
+fn current_buffer_size(
+ func_regs: &Ac97FunctionRegs,
+ mem: &GuestMemory,
+) -> GuestMemoryResult<usize> {
+ let civ = func_regs.civ;
+ let descriptor_addr = func_regs.bdbar + u32::from(civ) * DESCRIPTOR_LENGTH as u32;
+ let control_reg: u32 = mem
+ .read_obj_from_addr(GuestAddress(u64::from(descriptor_addr) + 4))
+ .map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
+ let buffer_len: usize = control_reg as usize & 0x0000_ffff;
+ Ok(buffer_len)
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ use std::time;
+
+ use audio_streams::DummyStreamSource;
+
+ #[test]
+ fn bm_bdbar() {
+ let mut bm = Ac97BusMaster::new(
+ GuestMemory::new(&[]).expect("Creating guest memory failed."),
+ Box::new(DummyStreamSource::new()),
+ );
+
+ let bdbars = [0x00u64, 0x10, 0x20];
+
+ // Make sure writes have no affect during cold reset.
+ bm.writel(0x00, 0x5555_555f);
+ assert_eq!(bm.readl(0x00), 0x0000_0000);
+
+ // Relesase cold reset.
+ bm.writel(GLOB_CNT_2C, 0x0000_0002);
+
+ // Tests that the base address is writable and that the bottom three bits are read only.
+ for bdbar in &bdbars {
+ assert_eq!(bm.readl(*bdbar), 0x0000_0000);
+ bm.writel(*bdbar, 0x5555_555f);
+ assert_eq!(bm.readl(*bdbar), 0x5555_5558);
+ }
+ }
+
+ #[test]
+ fn bm_status_reg() {
+ let mut bm = Ac97BusMaster::new(
+ GuestMemory::new(&[]).expect("Creating guest memory failed."),
+ Box::new(DummyStreamSource::new()),
+ );
+
+ let sr_addrs = [0x06u64, 0x16, 0x26];
+
+ for sr in &sr_addrs {
+ assert_eq!(bm.readw(*sr), 0x0001);
+ bm.writew(*sr, 0xffff);
+ assert_eq!(bm.readw(*sr), 0x0001);
+ }
+ }
+
+ #[test]
+ fn bm_global_control() {
+ let mut bm = Ac97BusMaster::new(
+ GuestMemory::new(&[]).expect("Creating guest memory failed."),
+ Box::new(DummyStreamSource::new()),
+ );
+
+ assert_eq!(bm.readl(GLOB_CNT_2C), 0x0000_0000);
+
+ // Relesase cold reset.
+ bm.writel(GLOB_CNT_2C, 0x0000_0002);
+
+ // Check interrupt enable bits are writable.
+ bm.writel(GLOB_CNT_2C, 0x0000_0072);
+ assert_eq!(bm.readl(GLOB_CNT_2C), 0x0000_0072);
+
+ // A Warm reset should doesn't affect register state and is auto cleared.
+ bm.writel(0x00, 0x5555_5558);
+ bm.writel(GLOB_CNT_2C, 0x0000_0076);
+ assert_eq!(bm.readl(GLOB_CNT_2C), 0x0000_0072);
+ assert_eq!(bm.readl(0x00), 0x5555_5558);
+ // Check that a cold reset works, but setting bdbar and checking it is zeroed.
+ bm.writel(0x00, 0x5555_555f);
+ bm.writel(GLOB_CNT_2C, 0x000_0070);
+ assert_eq!(bm.readl(GLOB_CNT_2C), 0x0000_0070);
+ assert_eq!(bm.readl(0x00), 0x0000_0000);
+ }
+
+ #[test]
+ fn start_playback() {
+ const LVI_MASK: u8 = 0x1f; // Five bits for 32 total entries.
+ const IOC_MASK: u32 = 0x8000_0000; // Interrupt on completion.
+ let num_buffers = LVI_MASK as usize + 1;
+ const BUFFER_SIZE: usize = 32768;
+ const FRAGMENT_SIZE: usize = BUFFER_SIZE / 2;
+
+ const GUEST_ADDR_BASE: u32 = 0x100_0000;
+ let mem = GuestMemory::new(&[(GuestAddress(GUEST_ADDR_BASE as u64), 1024 * 1024 * 1024)])
+ .expect("Creating guest memory failed.");
+ let mut bm = Ac97BusMaster::new(mem.clone(), Box::new(DummyStreamSource::new()));
+ let mixer = Ac97Mixer::new();
+
+ // Release cold reset.
+ bm.writel(GLOB_CNT_2C, 0x0000_0002);
+
+ // Setup ping-pong buffers. A and B repeating for every possible index.
+ bm.writel(PO_BDBAR_10, GUEST_ADDR_BASE);
+ for i in 0..num_buffers {
+ let pointer_addr = GuestAddress(GUEST_ADDR_BASE as u64 + i as u64 * 8);
+ let control_addr = GuestAddress(GUEST_ADDR_BASE as u64 + i as u64 * 8 + 4);
+ if i % 2 == 0 {
+ mem.write_obj_at_addr(GUEST_ADDR_BASE, pointer_addr)
+ .expect("Writing guest memory failed.");
+ } else {
+ mem.write_obj_at_addr(GUEST_ADDR_BASE + FRAGMENT_SIZE as u32, pointer_addr)
+ .expect("Writing guest memory failed.");
+ };
+ mem.write_obj_at_addr(IOC_MASK | (FRAGMENT_SIZE as u32) / 2, control_addr)
+ .expect("Writing guest memory failed.");
+ }
+
+ bm.writeb(PO_LVI_15, LVI_MASK, &mixer);
+
+ // Start.
+ bm.writeb(PO_CR_1B, CR_RPBM, &mixer);
+
+ std::thread::sleep(time::Duration::from_millis(50));
+ let picb = bm.readw(PO_PICB_18);
+ let mut civ = bm.readb(PO_CIV_14);
+ assert_eq!(civ, 0);
+ let pos = (FRAGMENT_SIZE - (picb as usize * 2)) / 4;
+
+ // Check that frames are consumed at least at a reasonable rate.
+ // This wont be exact as during unit tests the thread scheduling is highly variable, so the
+ // test only checks that some samples are consumed.
+ assert!(pos > 1000);
+
+ assert!(bm.readw(PO_SR_16) & SR_DCH == 0); // DMA is running.
+
+ // civ should move eventually.
+ for _i in 0..30 {
+ if civ != 0 {
+ break;
+ }
+ std::thread::sleep(time::Duration::from_millis(20));
+ civ = bm.readb(PO_CIV_14);
+ }
+
+ assert_ne!(0, civ);
+
+ // Buffer complete should be set as the IOC bit was set in the descriptor.
+ assert!(bm.readw(PO_SR_16) & SR_BCIS != 0);
+ // Clear the BCIS bit
+ bm.writew(PO_SR_16, SR_BCIS);
+ assert!(bm.readw(PO_SR_16) & SR_BCIS == 0);
+
+ // Set last valid to the next and wait until it is hit.
+ bm.writeb(PO_LVI_15, civ + 1, &mixer);
+ std::thread::sleep(time::Duration::from_millis(500));
+ assert!(bm.readw(PO_SR_16) & SR_LVBCI != 0); // Hit last buffer
+ assert!(bm.readw(PO_SR_16) & SR_DCH == SR_DCH); // DMA stopped because of lack of buffers.
+ assert_eq!(bm.readb(PO_LVI_15), bm.readb(PO_CIV_14));
+ // Clear the LVB bit
+ bm.writeb(PO_SR_16, SR_LVBCI as u8, &mixer);
+ assert!(bm.readw(PO_SR_16) & SR_LVBCI == 0);
+ // Reset the LVI to the last buffer and check that playback resumes
+ bm.writeb(PO_LVI_15, LVI_MASK, &mixer);
+ assert!(bm.readw(PO_SR_16) & SR_DCH == 0); // DMA restarts.
+
+ let (restart_civ, restart_picb) = (bm.readb(PO_CIV_14), bm.readw(PO_PICB_18));
+ std::thread::sleep(time::Duration::from_millis(20));
+ assert!(bm.readw(PO_PICB_18) != restart_picb || bm.readb(PO_CIV_14) != restart_civ);
+
+ // Stop.
+ bm.writeb(PO_CR_1B, 0, &mixer);
+ assert!(bm.readw(PO_SR_16) & 0x01 != 0); // DMA is not running.
+ }
+
+ #[test]
+ fn start_capture() {
+ const LVI_MASK: u8 = 0x1f; // Five bits for 32 total entries.
+ const IOC_MASK: u32 = 0x8000_0000; // Interrupt on completion.
+ let num_buffers = LVI_MASK as usize + 1;
+ const BUFFER_SIZE: usize = 32768;
+ const FRAGMENT_SIZE: usize = BUFFER_SIZE / 2;
+
+ const GUEST_ADDR_BASE: u32 = 0x100_0000;
+ let mem = GuestMemory::new(&[(GuestAddress(GUEST_ADDR_BASE as u64), 1024 * 1024 * 1024)])
+ .expect("Creating guest memory failed.");
+ let mut bm = Ac97BusMaster::new(mem.clone(), Box::new(DummyStreamSource::new()));
+ let mixer = Ac97Mixer::new();
+
+ // Release cold reset.
+ bm.writel(GLOB_CNT_2C, 0x0000_0002);
+
+ // Setup ping-pong buffers.
+ bm.writel(PI_BDBAR_00, GUEST_ADDR_BASE);
+ for i in 0..num_buffers {
+ let pointer_addr = GuestAddress(GUEST_ADDR_BASE as u64 + i as u64 * 8);
+ let control_addr = GuestAddress(GUEST_ADDR_BASE as u64 + i as u64 * 8 + 4);
+ mem.write_obj_at_addr(GUEST_ADDR_BASE + FRAGMENT_SIZE as u32, pointer_addr)
+ .expect("Writing guest memory failed.");
+ mem.write_obj_at_addr(IOC_MASK | (FRAGMENT_SIZE as u32) / 2, control_addr)
+ .expect("Writing guest memory failed.");
+ }
+
+ bm.writeb(PI_LVI_05, LVI_MASK, &mixer);
+
+ // Start.
+ bm.writeb(PI_CR_0B, CR_RPBM, &mixer);
+ assert_eq!(bm.readw(PI_PICB_08), 0);
+
+ std::thread::sleep(time::Duration::from_millis(50));
+ let picb = bm.readw(PI_PICB_08);
+ assert!(picb > 1000);
+ assert!(bm.readw(PI_SR_06) & SR_DCH == 0); // DMA is running.
+
+ // civ should move eventually.
+ for _i in 0..10 {
+ let civ = bm.readb(PI_CIV_04);
+ if civ != 0 {
+ break;
+ }
+ std::thread::sleep(time::Duration::from_millis(20));
+ }
+ assert_ne!(bm.readb(PI_CIV_04), 0);
+
+ let civ = bm.readb(PI_CIV_04);
+ // Sets LVI to CIV + 1 to trigger last buffer hit
+ bm.writeb(PI_LVI_05, civ + 1, &mixer);
+ std::thread::sleep(time::Duration::from_millis(5000));
+ assert_ne!(bm.readw(PI_SR_06) & SR_LVBCI, 0); // Hit last buffer
+ assert_eq!(bm.readw(PI_SR_06) & SR_DCH, SR_DCH); // DMA stopped because of lack of buffers.
+ assert_eq!(bm.readb(PI_LVI_05), bm.readb(PI_CIV_04));
+
+ // Clear the LVB bit
+ bm.writeb(PI_SR_06, SR_LVBCI as u8, &mixer);
+ assert!(bm.readw(PI_SR_06) & SR_LVBCI == 0);
+ // Reset the LVI to the last buffer and check that playback resumes
+ bm.writeb(PI_LVI_05, LVI_MASK, &mixer);
+ assert!(bm.readw(PI_SR_06) & SR_DCH == 0); // DMA restarts.
+
+ let restart_civ = bm.readb(PI_CIV_04);
+ std::thread::sleep(time::Duration::from_millis(200));
+ assert_ne!(bm.readb(PI_CIV_04), restart_civ);
+
+ // Stop.
+ bm.writeb(PI_CR_0B, 0, &mixer);
+ assert!(bm.readw(PI_SR_06) & 0x01 != 0); // DMA is not running.
+ }
+}
diff --git a/devices/src/pci/ac97_mixer.rs b/devices/src/pci/ac97_mixer.rs
new file mode 100644
index 0000000..8c8e192
--- /dev/null
+++ b/devices/src/pci/ac97_mixer.rs
@@ -0,0 +1,164 @@
+// Copyright 2018 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::pci::ac97_regs::*;
+
+// AC97 Vendor ID
+const AC97_VENDOR_ID1: u16 = 0x8086;
+const AC97_VENDOR_ID2: u16 = 0x8086;
+
+// Master volume register is specified in 1.5dB steps.
+const MASTER_VOLUME_STEP_DB: f64 = 1.5;
+
+/// `Ac97Mixer` holds the mixer state for the AC97 bus.
+/// The mixer is used by calling the `readb`/`readw`/`readl` functions to read register values and
+/// the `writeb`/`writew`/`writel` functions to set register values.
+pub struct Ac97Mixer {
+ // Mixer Registers
+ master_volume_l: u8,
+ master_volume_r: u8,
+ master_mute: bool,
+ mic_muted: bool,
+ mic_20db: bool,
+ mic_volume: u8,
+ record_gain_l: u8,
+ record_gain_r: u8,
+ record_gain_mute: bool,
+ pcm_out_vol_l: u16,
+ pcm_out_vol_r: u16,
+ pcm_out_mute: bool,
+ power_down_control: u16,
+}
+
+impl Ac97Mixer {
+ /// Creates an 'Ac97Mixer' with the standard default register values.
+ pub fn new() -> Self {
+ Ac97Mixer {
+ master_volume_l: 0,
+ master_volume_r: 0,
+ master_mute: true,
+ mic_muted: true,
+ mic_20db: false,
+ mic_volume: 0x8,
+ record_gain_l: 0,
+ record_gain_r: 0,
+ record_gain_mute: true,
+ pcm_out_vol_l: 0x8,
+ pcm_out_vol_r: 0x8,
+ pcm_out_mute: true,
+ power_down_control: PD_REG_STATUS_MASK, // Report everything is ready.
+ }
+ }
+
+ /// Reads a word from the register at `offset`.
+ pub fn readw(&self, offset: u64) -> u16 {
+ match offset {
+ MIXER_MASTER_VOL_MUTE_02 => self.get_master_reg(),
+ MIXER_MIC_VOL_MUTE_0E => self.get_mic_volume(),
+ MIXER_PCM_OUT_VOL_MUTE_18 => self.get_pcm_out_volume(),
+ MIXER_REC_VOL_MUTE_1C => self.get_record_gain_reg(),
+ MIXER_POWER_DOWN_CONTROL_26 => self.power_down_control,
+ MIXER_VENDOR_ID1_7C => AC97_VENDOR_ID1,
+ MIXER_VENDOR_ID2_7E => AC97_VENDOR_ID2,
+ _ => 0,
+ }
+ }
+
+ /// Writes a word `val` to the register `offset`.
+ pub fn writew(&mut self, offset: u64, val: u16) {
+ match offset {
+ MIXER_MASTER_VOL_MUTE_02 => self.set_master_reg(val),
+ MIXER_MIC_VOL_MUTE_0E => self.set_mic_volume(val),
+ MIXER_PCM_OUT_VOL_MUTE_18 => self.set_pcm_out_volume(val),
+ MIXER_REC_VOL_MUTE_1C => self.set_record_gain_reg(val),
+ MIXER_POWER_DOWN_CONTROL_26 => self.set_power_down_reg(val),
+ _ => (),
+ }
+ }
+
+ /// Returns the mute status and left and right attenuation from the master volume register.
+ pub fn get_master_volume(&self) -> (bool, f64, f64) {
+ (
+ self.master_mute,
+ f64::from(self.master_volume_l) * MASTER_VOLUME_STEP_DB,
+ f64::from(self.master_volume_r) * MASTER_VOLUME_STEP_DB,
+ )
+ }
+
+ // Returns the master mute and l/r volumes (reg 0x02).
+ fn get_master_reg(&self) -> u16 {
+ let reg = (u16::from(self.master_volume_l)) << 8 | u16::from(self.master_volume_r);
+ if self.master_mute {
+ reg | MUTE_REG_BIT
+ } else {
+ reg
+ }
+ }
+
+ // Handles writes to the master register (0x02).
+ fn set_master_reg(&mut self, val: u16) {
+ self.master_mute = val & MUTE_REG_BIT != 0;
+ self.master_volume_r = (val & VOL_REG_MASK) as u8;
+ self.master_volume_l = (val >> 8 & VOL_REG_MASK) as u8;
+ }
+
+ // Returns the value read in the Mic volume register (0x0e).
+ fn get_mic_volume(&self) -> u16 {
+ let mut reg = u16::from(self.mic_volume);
+ if self.mic_muted {
+ reg |= MUTE_REG_BIT;
+ }
+ if self.mic_20db {
+ reg |= MIXER_MIC_20DB;
+ }
+ reg
+ }
+
+ // Sets the mic input mute, boost, and volume settings (0x0e).
+ fn set_mic_volume(&mut self, val: u16) {
+ self.mic_volume = (val & MIXER_VOL_MASK) as u8;
+ self.mic_muted = val & MUTE_REG_BIT != 0;
+ self.mic_20db = val & MIXER_MIC_20DB != 0;
+ }
+
+ // Returns the value read in the Mic volume register (0x18).
+ fn get_pcm_out_volume(&self) -> u16 {
+ let reg = (self.pcm_out_vol_l as u16) << 8 | self.pcm_out_vol_r as u16;
+ if self.pcm_out_mute {
+ reg | MUTE_REG_BIT
+ } else {
+ reg
+ }
+ }
+
+ // Sets the pcm output mute and volume states (0x18).
+ fn set_pcm_out_volume(&mut self, val: u16) {
+ self.pcm_out_vol_r = val & MIXER_VOL_MASK;
+ self.pcm_out_vol_l = (val >> MIXER_VOL_LEFT_SHIFT) & MIXER_VOL_MASK;
+ self.pcm_out_mute = val & MUTE_REG_BIT != 0;
+ }
+
+ // Returns the record gain register (0x01c).
+ fn get_record_gain_reg(&self) -> u16 {
+ let reg = u16::from(self.record_gain_l) << 8 | u16::from(self.record_gain_r);
+ if self.record_gain_mute {
+ reg | MUTE_REG_BIT
+ } else {
+ reg
+ }
+ }
+
+ // Handles writes to the record_gain register (0x1c).
+ fn set_record_gain_reg(&mut self, val: u16) {
+ self.record_gain_mute = val & MUTE_REG_BIT != 0;
+ self.record_gain_r = (val & VOL_REG_MASK) as u8;
+ self.record_gain_l = (val >> 8 & VOL_REG_MASK) as u8;
+ }
+
+ // Handles writes to the powerdown ctrl/status register (0x26).
+ fn set_power_down_reg(&mut self, val: u16) {
+ self.power_down_control =
+ (val & !PD_REG_STATUS_MASK) | (self.power_down_control & PD_REG_STATUS_MASK);
+ }
+}
diff --git a/devices/src/pci/ac97_regs.rs b/devices/src/pci/ac97_regs.rs
new file mode 100644
index 0000000..bcca05b
--- /dev/null
+++ b/devices/src/pci/ac97_regs.rs
@@ -0,0 +1,247 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![allow(dead_code)]
+
+// Audio Mixer Registers
+// 00h Reset
+// 02h Master Volume Mute
+// 04h Headphone Volume Mute
+// 06h Master Volume Mono Mute
+// 08h Master Tone (R & L)
+// 0Ah PC_BEEP Volume Mute
+// 0Ch Phone Volume Mute
+// 0Eh Mic Volume Mute
+// 10h Line In Volume Mute
+// 12h CD Volume Mute
+// 14h Video Volume Mute
+// 16h Aux Volume Mute
+// 18h PCM Out Volume Mute
+// 1Ah Record Select
+// 1Ch Record Gain Mute
+// 1Eh Record Gain Mic Mute
+// 20h General Purpose
+// 22h 3D Control
+// 24h AC’97 RESERVED
+// 26h Powerdown Ctrl/Stat
+// 28h Extended Audio
+// 2Ah Extended Audio Ctrl/Stat
+
+// Size of IO register regions
+pub const MIXER_REGS_SIZE: u64 = 0x100;
+pub const MASTER_REGS_SIZE: u64 = 0x400;
+
+pub const MIXER_MASTER_VOL_MUTE_02: u64 = 0x02;
+pub const MIXER_MIC_VOL_MUTE_0E: u64 = 0x0e;
+pub const MIXER_PCM_OUT_VOL_MUTE_18: u64 = 0x18;
+pub const MIXER_REC_VOL_MUTE_1C: u64 = 0x1c;
+pub const MIXER_POWER_DOWN_CONTROL_26: u64 = 0x26;
+pub const MIXER_VENDOR_ID1_7C: u64 = 0x7c;
+pub const MIXER_VENDOR_ID2_7E: u64 = 0x7e;
+
+// Bus Master regs from ICH spec:
+// 00h PI_BDBAR PCM In Buffer Descriptor list Base Address Register
+// 04h PI_CIV PCM In Current Index Value
+// 05h PI_LVI PCM In Last Valid Index
+// 06h PI_SR PCM In Status Register
+// 08h PI_PICB PCM In Position In Current Buffer
+// 0Ah PI_PIV PCM In Prefetched Index Value
+// 0Bh PI_CR PCM In Control Register
+// 10h PO_BDBAR PCM Out Buffer Descriptor list Base Address Register
+// 14h PO_CIV PCM Out Current Index Value
+// 15h PO_LVI PCM Out Last Valid Index
+// 16h PO_SR PCM Out Status Register
+// 18h PO_PICB PCM Out Position In Current Buffer
+// 1Ah PO_PIV PCM Out Prefetched Index Value
+// 1Bh PO_CR PCM Out Control Register
+// 20h MC_BDBAR Mic. In Buffer Descriptor list Base Address Register
+// 24h PM_CIV Mic. In Current Index Value
+// 25h MC_LVI Mic. In Last Valid Index
+// 26h MC_SR Mic. In Status Register
+// 28h MC_PICB Mic In Position In Current Buffer
+// 2Ah MC_PIV Mic. In Prefetched Index Value
+// 2Bh MC_CR Mic. In Control Register
+// 2Ch GLOB_CNT Global Control
+// 30h GLOB_STA Global Status
+// 34h ACC_SEMA Codec Write Semaphore Register
+
+// Global Control
+pub const GLOB_CNT_2C: u64 = 0x2C;
+pub const GLOB_CNT_COLD_RESET: u32 = 0x0000_0002;
+pub const GLOB_CNT_WARM_RESET: u32 = 0x0000_0004;
+pub const GLOB_CNT_STABLE_BITS: u32 = 0x0000_007f; // Bits not affected by reset.
+
+// Global status
+pub const GLOB_STA_30: u64 = 0x30;
+pub const GLOB_STA_RESET_VAL: u32 = 0x0000_0100; // primary codec ready set.
+ // glob_sta bits
+pub const GS_MD3: u32 = 1 << 17;
+pub const GS_AD3: u32 = 1 << 16;
+pub const GS_RCS: u32 = 1 << 15;
+pub const GS_B3S12: u32 = 1 << 14;
+pub const GS_B2S12: u32 = 1 << 13;
+pub const GS_B1S12: u32 = 1 << 12;
+pub const GS_S1R1: u32 = 1 << 11;
+pub const GS_S0R1: u32 = 1 << 10;
+pub const GS_S1CR: u32 = 1 << 9;
+pub const GS_S0CR: u32 = 1 << 8;
+pub const GS_MINT: u32 = 1 << 7;
+pub const GS_POINT: u32 = 1 << 6;
+pub const GS_PIINT: u32 = 1 << 5;
+pub const GS_RSRVD: u32 = 1 << 4 | 1 << 3;
+pub const GS_MOINT: u32 = 1 << 2;
+pub const GS_MIINT: u32 = 1 << 1;
+pub const GS_GSCI: u32 = 1;
+pub const GS_RO_MASK: u32 = GS_B3S12
+ | GS_B2S12
+ | GS_B1S12
+ | GS_S1CR
+ | GS_S0CR
+ | GS_MINT
+ | GS_POINT
+ | GS_PIINT
+ | GS_RSRVD
+ | GS_MOINT
+ | GS_MIINT;
+pub const GS_VALID_MASK: u32 = 0x0003_ffff;
+pub const GS_WCLEAR_MASK: u32 = GS_RCS | GS_S1R1 | GS_S0R1 | GS_GSCI;
+
+pub const ACC_SEMA_34: u64 = 0x34;
+
+// Audio funciton registers.
+pub const CIV_OFFSET: u64 = 0x04;
+pub const LVI_OFFSET: u64 = 0x05;
+pub const SR_OFFSET: u64 = 0x06;
+pub const PICB_OFFSET: u64 = 0x08;
+pub const PIV_OFFSET: u64 = 0x0a;
+pub const CR_OFFSET: u64 = 0x0b;
+
+// Capture
+pub const PI_BASE_00: u64 = 0x00;
+pub const PI_BDBAR_00: u64 = PI_BASE_00;
+pub const PI_CIV_04: u64 = PI_BASE_00 + CIV_OFFSET;
+pub const PI_LVI_05: u64 = PI_BASE_00 + LVI_OFFSET;
+pub const PI_SR_06: u64 = PI_BASE_00 + SR_OFFSET;
+pub const PI_PICB_08: u64 = PI_BASE_00 + PICB_OFFSET;
+pub const PI_PIV_0A: u64 = PI_BASE_00 + PIV_OFFSET;
+pub const PI_CR_0B: u64 = PI_BASE_00 + CR_OFFSET;
+
+// Play Out
+pub const PO_BASE_10: u64 = 0x10;
+pub const PO_BDBAR_10: u64 = PO_BASE_10;
+pub const PO_CIV_14: u64 = PO_BASE_10 + CIV_OFFSET;
+pub const PO_LVI_15: u64 = PO_BASE_10 + LVI_OFFSET;
+pub const PO_SR_16: u64 = PO_BASE_10 + SR_OFFSET;
+pub const PO_PICB_18: u64 = PO_BASE_10 + PICB_OFFSET;
+pub const PO_PIV_1A: u64 = PO_BASE_10 + PIV_OFFSET;
+pub const PO_CR_1B: u64 = PO_BASE_10 + CR_OFFSET;
+
+// Microphone
+pub const MC_BASE_20: u64 = 0x20;
+pub const MC_BDBAR_20: u64 = MC_BASE_20;
+pub const MC_CIV_24: u64 = MC_BASE_20 + CIV_OFFSET;
+pub const MC_LVI_25: u64 = MC_BASE_20 + LVI_OFFSET;
+pub const MC_SR_26: u64 = MC_BASE_20 + SR_OFFSET;
+pub const MC_PICB_28: u64 = MC_BASE_20 + PICB_OFFSET;
+pub const MC_PIV_2A: u64 = MC_BASE_20 + PIV_OFFSET;
+pub const MC_CR_2B: u64 = MC_BASE_20 + CR_OFFSET;
+
+// Status Register Bits.
+pub const SR_DCH: u16 = 0x01;
+pub const SR_CELV: u16 = 0x02;
+pub const SR_LVBCI: u16 = 0x04;
+pub const SR_BCIS: u16 = 0x08;
+pub const SR_FIFOE: u16 = 0x10;
+pub const SR_VALID_MASK: u16 = 0x1f;
+pub const SR_WCLEAR_MASK: u16 = SR_FIFOE | SR_BCIS | SR_LVBCI;
+pub const SR_RO_MASK: u16 = SR_DCH | SR_CELV;
+pub const SR_INT_MASK: u16 = SR_BCIS | SR_LVBCI;
+
+// Control Register Bits.
+pub const CR_RPBM: u8 = 0x01;
+pub const CR_RR: u8 = 0x02;
+pub const CR_LVBIE: u8 = 0x04;
+pub const CR_FEIE: u8 = 0x08;
+pub const CR_IOCE: u8 = 0x10;
+pub const CR_VALID_MASK: u8 = 0x1f;
+pub const CR_DONT_CLEAR_MASK: u8 = CR_IOCE | CR_FEIE | CR_LVBIE;
+
+// Mixer register bits
+pub const MUTE_REG_BIT: u16 = 0x8000;
+pub const VOL_REG_MASK: u16 = 0x003f;
+pub const MIXER_VOL_MASK: u16 = 0x001f;
+pub const MIXER_VOL_LEFT_SHIFT: usize = 8;
+pub const MIXER_MIC_20DB: u16 = 0x0040;
+// Powerdown reg
+pub const PD_REG_STATUS_MASK: u16 = 0x000f;
+pub const PD_REG_OUTPUT_MUTE_MASK: u16 = 0xb200;
+pub const PD_REG_INPUT_MUTE_MASK: u16 = 0x0d00;
+
+// Buffer descriptors are four bytes of pointer and 4 bytes of control/length.
+pub const DESCRIPTOR_LENGTH: usize = 8;
+pub const BD_IOC: u32 = 1 << 31;
+
+/// The functions that are supported by the Ac97 subsystem.
+#[derive(Copy, Clone)]
+pub enum Ac97Function {
+ Input,
+ Output,
+ Microphone,
+}
+
+/// Registers for individual audio functions.
+/// Each audio function in Ac97 gets a set of these registers.
+#[derive(Clone, Default)]
+pub struct Ac97FunctionRegs {
+ pub bdbar: u32,
+ pub civ: u8,
+ pub lvi: u8,
+ pub sr: u16,
+ pub picb: u16,
+ pub piv: u8,
+ pub cr: u8,
+}
+
+impl Ac97FunctionRegs {
+ /// Creates a new set of function registers, these can be used for the capture, playback, or
+ /// microphone functions.
+ pub fn new() -> Self {
+ let mut regs = Ac97FunctionRegs {
+ sr: SR_DCH,
+ ..Default::default()
+ };
+ regs.do_reset();
+ regs
+ }
+
+ /// Reset all the registers to the PoR defaults.
+ pub fn do_reset(&mut self) {
+ self.bdbar = 0;
+ self.civ = 0;
+ self.lvi = 0;
+ self.sr = SR_DCH;
+ self.picb = 0;
+ self.piv = 0;
+ self.cr &= CR_DONT_CLEAR_MASK;
+ }
+
+ /// Read register 4, 5, and 6 as one 32 bit word.
+ /// According to the ICH spec, reading these three with one 32 bit access is allowed.
+ pub fn atomic_status_regs(&self) -> u32 {
+ u32::from(self.civ) | u32::from(self.lvi) << 8 | u32::from(self.sr) << 16
+ }
+
+ /// Returns the mask for enabled interrupts. The returned mask represents the bits in the status
+ /// register that should trigger and interrupt.
+ pub fn int_mask(&self) -> u16 {
+ let mut int_mask = 0;
+ if self.cr & CR_LVBIE != 0 {
+ int_mask |= SR_LVBCI;
+ }
+ if self.cr & CR_IOCE != 0 {
+ int_mask |= SR_BCIS;
+ }
+ int_mask
+ }
+}
diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs
new file mode 100644
index 0000000..791161a
--- /dev/null
+++ b/devices/src/pci/mod.rs
@@ -0,0 +1,38 @@
+// Copyright 2018 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.
+
+//! Implements pci devices and busses.
+
+mod ac97;
+mod ac97_bus_master;
+mod ac97_mixer;
+mod ac97_regs;
+mod pci_configuration;
+mod pci_device;
+mod pci_root;
+
+pub use self::ac97::Ac97Dev;
+pub use self::pci_configuration::{
+ PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityID,
+ PciClassCode, PciConfiguration, PciHeaderType, PciProgrammingInterface, PciSerialBusSubClass,
+ PciSubclass,
+};
+pub use self::pci_device::Error as PciDeviceError;
+pub use self::pci_device::PciDevice;
+pub use self::pci_root::{PciConfigIo, PciConfigMmio, PciRoot};
+
+/// PCI has four interrupt pins A->D.
+#[derive(Copy, Clone)]
+pub enum PciInterruptPin {
+ IntA,
+ IntB,
+ IntC,
+ IntD,
+}
+
+impl PciInterruptPin {
+ pub fn to_mask(self) -> u32 {
+ self as u32
+ }
+}
diff --git a/devices/src/pci/pci_configuration.rs b/devices/src/pci/pci_configuration.rs
new file mode 100644
index 0000000..ab969f7
--- /dev/null
+++ b/devices/src/pci/pci_configuration.rs
@@ -0,0 +1,623 @@
+// Copyright 2018 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::fmt::{self, Display};
+
+use crate::pci::PciInterruptPin;
+use sys_util::warn;
+
+// The number of 32bit registers in the config space, 256 bytes.
+const NUM_CONFIGURATION_REGISTERS: usize = 64;
+
+const STATUS_REG: usize = 1;
+const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
+const BAR0_REG: usize = 4;
+const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
+const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
+const NUM_BAR_REGS: usize = 6;
+const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34;
+const FIRST_CAPABILITY_OFFSET: usize = 0x40;
+const CAPABILITY_MAX_OFFSET: usize = 192;
+
+const INTERRUPT_LINE_PIN_REG: usize = 15;
+
+/// Represents the types of PCI headers allowed in the configuration registers.
+#[derive(Copy, Clone)]
+pub enum PciHeaderType {
+ Device,
+ Bridge,
+}
+
+/// Classes of PCI nodes.
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+pub enum PciClassCode {
+ TooOld,
+ MassStorage,
+ NetworkController,
+ DisplayController,
+ MultimediaController,
+ MemoryController,
+ BridgeDevice,
+ SimpleCommunicationController,
+ BaseSystemPeripheral,
+ InputDevice,
+ DockingStation,
+ Processor,
+ SerialBusController,
+ WirelessController,
+ IntelligentIoController,
+ EncryptionController,
+ DataAcquisitionSignalProcessing,
+ Other = 0xff,
+}
+
+impl PciClassCode {
+ pub fn get_register_value(&self) -> u8 {
+ *self as u8
+ }
+}
+
+/// A PCI sublcass. Each class in `PciClassCode` can specify a unique set of subclasses. This trait
+/// is implemented by each subclass. It allows use of a trait object to generate configurations.
+pub trait PciSubclass {
+ /// Convert this subclass to the value used in the PCI specification.
+ fn get_register_value(&self) -> u8;
+}
+
+/// Subclasses of the MultimediaController class.
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+pub enum PciMultimediaSubclass {
+ VideoController = 0x00,
+ AudioController = 0x01,
+ TelephonyDevice = 0x02,
+ AudioDevice = 0x03,
+ Other = 0x80,
+}
+
+impl PciSubclass for PciMultimediaSubclass {
+ fn get_register_value(&self) -> u8 {
+ *self as u8
+ }
+}
+
+/// Subclasses of the BridgeDevice
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+pub enum PciBridgeSubclass {
+ HostBridge = 0x00,
+ IsaBridge = 0x01,
+ EisaBridge = 0x02,
+ McaBridge = 0x03,
+ PciToPciBridge = 0x04,
+ PcmciaBridge = 0x05,
+ NuBusBridge = 0x06,
+ CardBusBridge = 0x07,
+ RACEwayBridge = 0x08,
+ PciToPciSemiTransparentBridge = 0x09,
+ InfiniBrandToPciHostBridge = 0x0a,
+ OtherBridgeDevice = 0x80,
+}
+
+impl PciSubclass for PciBridgeSubclass {
+ fn get_register_value(&self) -> u8 {
+ *self as u8
+ }
+}
+
+/// Subclass of the SerialBus
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+pub enum PciSerialBusSubClass {
+ Firewire = 0x00,
+ ACCESSbus = 0x01,
+ SSA = 0x02,
+ USB = 0x03,
+}
+
+impl PciSubclass for PciSerialBusSubClass {
+ fn get_register_value(&self) -> u8 {
+ *self as u8
+ }
+}
+
+/// A PCI class programming interface. Each combination of `PciClassCode` and
+/// `PciSubclass` can specify a set of register-level programming interfaces.
+/// This trait is implemented by each programming interface.
+/// It allows use of a trait object to generate configurations.
+pub trait PciProgrammingInterface {
+ /// Convert this programming interface to the value used in the PCI specification.
+ fn get_register_value(&self) -> u8;
+}
+
+/// Types of PCI capabilities.
+pub enum PciCapabilityID {
+ ListID = 0,
+ PowerManagement = 0x01,
+ AcceleratedGraphicsPort = 0x02,
+ VitalProductData = 0x03,
+ SlotIdentification = 0x04,
+ MessageSignalledInterrupts = 0x05,
+ CompactPCIHotSwap = 0x06,
+ PCIX = 0x07,
+ HyperTransport = 0x08,
+ VendorSpecific = 0x09,
+ Debugport = 0x0A,
+ CompactPCICentralResourceControl = 0x0B,
+ PCIStandardHotPlugController = 0x0C,
+ BridgeSubsystemVendorDeviceID = 0x0D,
+ AGPTargetPCIPCIbridge = 0x0E,
+ SecureDevice = 0x0F,
+ PCIExpress = 0x10,
+ MSIX = 0x11,
+ SATADataIndexConf = 0x12,
+ PCIAdvancedFeatures = 0x13,
+ PCIEnhancedAllocation = 0x14,
+}
+
+/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
+pub trait PciCapability {
+ fn bytes(&self) -> &[u8];
+ fn id(&self) -> PciCapabilityID;
+}
+
+/// Contains the configuration space of a PCI node.
+/// See the [specification](https://en.wikipedia.org/wiki/PCI_configuration_space).
+/// The configuration space is accessed with DWORD reads and writes from the guest.
+pub struct PciConfiguration {
+ registers: [u32; NUM_CONFIGURATION_REGISTERS],
+ writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
+ bar_used: [bool; NUM_BAR_REGS],
+ // Contains the byte offset and size of the last capability.
+ last_capability: Option<(usize, usize)>,
+}
+
+/// See pci_regs.h in kernel
+#[derive(Copy, Clone)]
+pub enum PciBarRegionType {
+ Memory32BitRegion = 0,
+ IORegion = 0x01,
+ Memory64BitRegion = 0x04,
+}
+
+#[derive(Copy, Clone)]
+pub enum PciBarPrefetchable {
+ NotPrefetchable = 0,
+ Prefetchable = 0x08,
+}
+
+#[derive(Copy, Clone)]
+pub struct PciBarConfiguration {
+ addr: u64,
+ size: u64,
+ reg_idx: usize,
+ region_type: PciBarRegionType,
+ prefetchable: PciBarPrefetchable,
+}
+
+#[derive(Debug)]
+pub enum Error {
+ BarAddressInvalid(u64, u64),
+ BarInUse(usize),
+ BarInUse64(usize),
+ BarInvalid(usize),
+ BarInvalid64(usize),
+ BarSizeInvalid(u64),
+ CapabilityEmpty,
+ CapabilityLengthInvalid(usize),
+ CapabilitySpaceFull(usize),
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+ match self {
+ BarAddressInvalid(a, s) => write!(f, "address {} size {} too big", a, s),
+ BarInUse(b) => write!(f, "bar {} already used", b),
+ BarInUse64(b) => write!(f, "64bit bar {} already used(requires two regs)", b),
+ BarInvalid(b) => write!(f, "bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
+ BarInvalid64(b) => write!(
+ f,
+ "64bitbar {} invalid, requires two regs, max {}",
+ b,
+ NUM_BAR_REGS - 1
+ ),
+ BarSizeInvalid(s) => write!(f, "bar address {} not a power of two", s),
+ CapabilityEmpty => write!(f, "empty capabilities are invalid"),
+ CapabilityLengthInvalid(l) => write!(f, "Invalid capability length {}", l),
+ CapabilitySpaceFull(s) => write!(f, "capability of size {} doesn't fit", s),
+ }
+ }
+}
+
+impl PciConfiguration {
+ pub fn new(
+ vendor_id: u16,
+ device_id: u16,
+ class_code: PciClassCode,
+ subclass: &dyn PciSubclass,
+ programming_interface: Option<&dyn PciProgrammingInterface>,
+ header_type: PciHeaderType,
+ subsystem_vendor_id: u16,
+ subsystem_id: u16,
+ ) -> Self {
+ let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
+ let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
+ registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
+ // TODO(dverkamp): Status should be write-1-to-clear
+ writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
+ let pi = if let Some(pi) = programming_interface {
+ pi.get_register_value()
+ } else {
+ 0
+ };
+ registers[2] = u32::from(class_code.get_register_value()) << 24
+ | u32::from(subclass.get_register_value()) << 16
+ | u32::from(pi) << 8;
+ writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w)
+ match header_type {
+ PciHeaderType::Device => {
+ registers[3] = 0x0000_0000; // Header type 0 (device)
+ writable_bits[15] = 0x0000_00ff; // Interrupt line (r/w)
+ }
+ PciHeaderType::Bridge => {
+ registers[3] = 0x0001_0000; // Header type 1 (bridge)
+ writable_bits[9] = 0xfff0_fff0; // Memory base and limit
+ writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w)
+ }
+ };
+ registers[11] = u32::from(subsystem_id) << 16 | u32::from(subsystem_vendor_id);
+
+ PciConfiguration {
+ registers,
+ writable_bits,
+ bar_used: [false; NUM_BAR_REGS],
+ last_capability: None,
+ }
+ }
+
+ /// Reads a 32bit register from `reg_idx` in the register map.
+ pub fn read_reg(&self, reg_idx: usize) -> u32 {
+ *(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff))
+ }
+
+ /// Writes a 32bit register to `reg_idx` in the register map.
+ pub fn write_reg(&mut self, reg_idx: usize, value: u32) {
+ if let Some(r) = self.registers.get_mut(reg_idx) {
+ *r = value & self.writable_bits[reg_idx];
+ } else {
+ warn!("bad PCI register write {}", reg_idx);
+ }
+ }
+
+ /// Writes a 16bit word to `offset`. `offset` must be 16bit aligned.
+ pub fn write_word(&mut self, offset: usize, value: u16) {
+ let shift = match offset % 4 {
+ 0 => 0,
+ 2 => 16,
+ _ => {
+ warn!("bad PCI config write offset {}", offset);
+ return;
+ }
+ };
+ let reg_idx = offset / 4;
+
+ if let Some(r) = self.registers.get_mut(reg_idx) {
+ let writable_mask = self.writable_bits[reg_idx];
+ let mask = (0xffffu32 << shift) & writable_mask;
+ let shifted_value = (u32::from(value) << shift) & writable_mask;
+ *r = *r & !mask | shifted_value;
+ } else {
+ warn!("bad PCI config write offset {}", offset);
+ }
+ }
+
+ /// Writes a byte to `offset`.
+ pub fn write_byte(&mut self, offset: usize, value: u8) {
+ self.write_byte_internal(offset, value, true);
+ }
+
+ /// Writes a byte to `offset`, optionally enforcing read-only bits.
+ fn write_byte_internal(&mut self, offset: usize, value: u8, apply_writable_mask: bool) {
+ let shift = (offset % 4) * 8;
+ let reg_idx = offset / 4;
+
+ if let Some(r) = self.registers.get_mut(reg_idx) {
+ let writable_mask = if apply_writable_mask {
+ self.writable_bits[reg_idx]
+ } else {
+ 0xffff_ffff
+ };
+ let mask = (0xffu32 << shift) & writable_mask;
+ let shifted_value = (u32::from(value) << shift) & writable_mask;
+ *r = *r & !mask | shifted_value;
+ } else {
+ warn!("bad PCI config write offset {}", offset);
+ }
+ }
+
+ /// Adds a region specified by `config`. Configures the specified BAR(s) to
+ /// report this region and size to the guest kernel. Enforces a few constraints
+ /// (i.e, region size must be power of two, register not already used). Returns 'None' on
+ /// failure all, `Some(BarIndex)` on success.
+ pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<usize> {
+ if self.bar_used[config.reg_idx] {
+ return Err(Error::BarInUse(config.reg_idx));
+ }
+
+ if config.size.count_ones() != 1 {
+ return Err(Error::BarSizeInvalid(config.size));
+ }
+
+ if config.reg_idx >= NUM_BAR_REGS {
+ return Err(Error::BarInvalid(config.reg_idx));
+ }
+
+ let bar_idx = BAR0_REG + config.reg_idx;
+ let end_addr = config
+ .addr
+ .checked_add(config.size)
+ .ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
+ match config.region_type {
+ PciBarRegionType::Memory32BitRegion | PciBarRegionType::IORegion => {
+ if end_addr > u64::from(u32::max_value()) {
+ return Err(Error::BarAddressInvalid(config.addr, config.size));
+ }
+ }
+ PciBarRegionType::Memory64BitRegion => {
+ if config.reg_idx + 1 >= NUM_BAR_REGS {
+ return Err(Error::BarInvalid64(config.reg_idx));
+ }
+
+ if end_addr > u64::max_value() {
+ return Err(Error::BarAddressInvalid(config.addr, config.size));
+ }
+
+ if self.bar_used[config.reg_idx + 1] {
+ return Err(Error::BarInUse64(config.reg_idx));
+ }
+
+ self.registers[bar_idx + 1] = (config.addr >> 32) as u32;
+ self.writable_bits[bar_idx + 1] = !((config.size >> 32).wrapping_sub(1)) as u32;
+ self.bar_used[config.reg_idx + 1] = true;
+ }
+ }
+
+ let (mask, lower_bits) = match config.region_type {
+ PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => (
+ BAR_MEM_ADDR_MASK,
+ config.prefetchable as u32 | config.region_type as u32,
+ ),
+ PciBarRegionType::IORegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
+ };
+
+ self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
+ self.writable_bits[bar_idx] = !(config.size - 1) as u32;
+ self.bar_used[config.reg_idx] = true;
+ Ok(config.reg_idx)
+ }
+
+ /// Returns the address of the given BAR region.
+ pub fn get_bar_addr(&self, bar_num: usize) -> u32 {
+ let bar_idx = BAR0_REG + bar_num;
+
+ self.registers[bar_idx] & BAR_MEM_ADDR_MASK
+ }
+
+ /// Configures the IRQ line and pin used by this device.
+ pub fn set_irq(&mut self, line: u8, pin: PciInterruptPin) {
+ // `pin` is 1-based in the pci config space.
+ let pin_idx = (pin as u32) + 1;
+ self.registers[INTERRUPT_LINE_PIN_REG] = (self.registers[INTERRUPT_LINE_PIN_REG]
+ & 0xffff_0000)
+ | (pin_idx << 8)
+ | u32::from(line);
+ }
+
+ /// Adds the capability `cap_data` to the list of capabilities.
+ /// `cap_data` should include the two-byte PCI capability header (type, next),
+ /// but not populate it. Correct values will be generated automatically based
+ /// on `cap_data.id()`.
+ pub fn add_capability(&mut self, cap_data: &dyn PciCapability) -> Result<usize> {
+ let total_len = cap_data.bytes().len();
+ // Check that the length is valid.
+ if cap_data.bytes().is_empty() {
+ return Err(Error::CapabilityEmpty);
+ }
+ let (cap_offset, tail_offset) = match self.last_capability {
+ Some((offset, len)) => (Self::next_dword(offset, len), offset + 1),
+ None => (FIRST_CAPABILITY_OFFSET, CAPABILITY_LIST_HEAD_OFFSET),
+ };
+ let end_offset = cap_offset
+ .checked_add(total_len)
+ .ok_or(Error::CapabilitySpaceFull(total_len))?;
+ if end_offset > CAPABILITY_MAX_OFFSET {
+ return Err(Error::CapabilitySpaceFull(total_len));
+ }
+ self.registers[STATUS_REG] |= STATUS_REG_CAPABILITIES_USED_MASK;
+ self.write_byte_internal(tail_offset, cap_offset as u8, false);
+ self.write_byte_internal(cap_offset, cap_data.id() as u8, false);
+ self.write_byte_internal(cap_offset + 1, 0, false); // Next pointer.
+ for (i, byte) in cap_data.bytes().iter().enumerate().skip(2) {
+ self.write_byte_internal(cap_offset + i, *byte, false);
+ }
+ self.last_capability = Some((cap_offset, total_len));
+ Ok(cap_offset)
+ }
+
+ // Find the next aligned offset after the one given.
+ fn next_dword(offset: usize, len: usize) -> usize {
+ let next = offset + len;
+ (next + 3) & !3
+ }
+}
+
+impl Default for PciBarConfiguration {
+ fn default() -> Self {
+ PciBarConfiguration {
+ reg_idx: 0,
+ addr: 0,
+ size: 0,
+ region_type: PciBarRegionType::Memory32BitRegion,
+ prefetchable: PciBarPrefetchable::NotPrefetchable,
+ }
+ }
+}
+
+impl PciBarConfiguration {
+ pub fn new(
+ reg_idx: usize,
+ size: u64,
+ region_type: PciBarRegionType,
+ prefetchable: PciBarPrefetchable,
+ ) -> Self {
+ PciBarConfiguration {
+ reg_idx,
+ addr: 0,
+ size,
+ region_type,
+ prefetchable,
+ }
+ }
+
+ pub fn set_register_index(mut self, reg_idx: usize) -> Self {
+ self.reg_idx = reg_idx;
+ self
+ }
+
+ pub fn get_register_index(&self) -> usize {
+ self.reg_idx
+ }
+
+ pub fn set_address(mut self, addr: u64) -> Self {
+ self.addr = addr;
+ self
+ }
+
+ pub fn set_size(mut self, size: u64) -> Self {
+ self.size = size;
+ self
+ }
+
+ pub fn get_size(&self) -> u64 {
+ self.size
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use data_model::DataInit;
+
+ use super::*;
+
+ #[repr(packed)]
+ #[derive(Clone, Copy)]
+ #[allow(dead_code)]
+ struct TestCap {
+ _vndr: u8,
+ _next: u8,
+ len: u8,
+ foo: u8,
+ }
+
+ // It is safe to implement DataInit; all members are simple numbers and any value is valid.
+ unsafe impl DataInit for TestCap {}
+
+ impl PciCapability for TestCap {
+ fn bytes(&self) -> &[u8] {
+ self.as_slice()
+ }
+
+ fn id(&self) -> PciCapabilityID {
+ PciCapabilityID::VendorSpecific
+ }
+ }
+
+ #[test]
+ fn add_capability() {
+ let mut cfg = PciConfiguration::new(
+ 0x1234,
+ 0x5678,
+ PciClassCode::MultimediaController,
+ &PciMultimediaSubclass::AudioController,
+ None,
+ PciHeaderType::Device,
+ 0xABCD,
+ 0x2468,
+ );
+
+ // Add two capabilities with different contents.
+ let cap1 = TestCap {
+ _vndr: 0,
+ _next: 0,
+ len: 4,
+ foo: 0xAA,
+ };
+ let cap1_offset = cfg.add_capability(&cap1).unwrap();
+ assert_eq!(cap1_offset % 4, 0);
+
+ let cap2 = TestCap {
+ _vndr: 0,
+ _next: 0,
+ len: 0x04,
+ foo: 0x55,
+ };
+ let cap2_offset = cfg.add_capability(&cap2).unwrap();
+ assert_eq!(cap2_offset % 4, 0);
+
+ // The capability list head should be pointing to cap1.
+ let cap_ptr = cfg.read_reg(CAPABILITY_LIST_HEAD_OFFSET / 4) & 0xFF;
+ assert_eq!(cap1_offset, cap_ptr as usize);
+
+ // Verify the contents of the capabilities.
+ let cap1_data = cfg.read_reg(cap1_offset / 4);
+ assert_eq!(cap1_data & 0xFF, 0x09); // capability ID
+ assert_eq!((cap1_data >> 8) & 0xFF, cap2_offset as u32); // next capability pointer
+ assert_eq!((cap1_data >> 16) & 0xFF, 0x04); // cap1.len
+ assert_eq!((cap1_data >> 24) & 0xFF, 0xAA); // cap1.foo
+
+ let cap2_data = cfg.read_reg(cap2_offset / 4);
+ assert_eq!(cap2_data & 0xFF, 0x09); // capability ID
+ assert_eq!((cap2_data >> 8) & 0xFF, 0x00); // next capability pointer
+ assert_eq!((cap2_data >> 16) & 0xFF, 0x04); // cap2.len
+ assert_eq!((cap2_data >> 24) & 0xFF, 0x55); // cap2.foo
+ }
+
+ #[derive(Copy, Clone)]
+ enum TestPI {
+ Test = 0x5a,
+ }
+
+ impl PciProgrammingInterface for TestPI {
+ fn get_register_value(&self) -> u8 {
+ *self as u8
+ }
+ }
+
+ #[test]
+ fn class_code() {
+ let cfg = PciConfiguration::new(
+ 0x1234,
+ 0x5678,
+ PciClassCode::MultimediaController,
+ &PciMultimediaSubclass::AudioController,
+ Some(&TestPI::Test),
+ PciHeaderType::Device,
+ 0xABCD,
+ 0x2468,
+ );
+
+ let class_reg = cfg.read_reg(2);
+ let class_code = (class_reg >> 24) & 0xFF;
+ let subclass = (class_reg >> 16) & 0xFF;
+ let prog_if = (class_reg >> 8) & 0xFF;
+ assert_eq!(class_code, 0x04);
+ assert_eq!(subclass, 0x01);
+ assert_eq!(prog_if, 0x5a);
+ }
+}
diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs
new file mode 100644
index 0000000..8ea3548
--- /dev/null
+++ b/devices/src/pci/pci_device.rs
@@ -0,0 +1,197 @@
+// Copyright 2018 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 byteorder::{ByteOrder, LittleEndian};
+
+use std;
+use std::fmt::{self, Display};
+use std::os::unix::io::RawFd;
+
+use kvm::Datamatch;
+use resources::{Error as SystemAllocatorFaliure, SystemAllocator};
+use sys_util::EventFd;
+
+use crate::pci::pci_configuration::{self, PciConfiguration};
+use crate::pci::PciInterruptPin;
+use crate::BusDevice;
+
+#[derive(Debug)]
+pub enum Error {
+ /// Setup of the device capabilities failed.
+ CapabilitiesSetup(pci_configuration::Error),
+ /// Allocating space for an IO BAR failed.
+ IoAllocationFailed(u64, SystemAllocatorFaliure),
+ /// Registering an IO BAR failed.
+ IoRegistrationFailed(u64, pci_configuration::Error),
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ CapabilitiesSetup(e) => write!(f, "failed to add capability {}", e),
+ IoAllocationFailed(size, e) => write!(
+ f,
+ "failed to allocate space for an IO BAR, size={}: {}",
+ size, e
+ ),
+ IoRegistrationFailed(addr, e) => {
+ write!(f, "failed to register an IO BAR, addr={} err={}", addr, e)
+ }
+ }
+ }
+}
+
+pub trait PciDevice: Send {
+ /// Returns a label suitable for debug output.
+ fn debug_label(&self) -> String;
+ /// Assign a unique bus and device number to this device.
+ fn assign_bus_dev(&mut self, _bus: u8, _device: u8 /*u5*/) {}
+ /// 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>;
+ /// 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.
+ fn assign_irq(
+ &mut self,
+ _irq_evt: EventFd,
+ _irq_resample_evt: EventFd,
+ _irq_num: u32,
+ _irq_pin: PciInterruptPin,
+ ) {
+ }
+ /// Allocates the needed IO BAR space using the `allocate` function which takes a size and
+ /// returns an address. Returns a Vec of (address, length) tuples.
+ fn allocate_io_bars(&mut self, _resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
+ Ok(Vec::new())
+ }
+
+ /// Allocates the needed device BAR space. Returns a Vec of (address, length) tuples.
+ /// Unlike MMIO BARs (see allocate_io_bars), device BARs are not expected to incur VM exits
+ /// - these BARs represent normal memory.
+ fn allocate_device_bars(
+ &mut self,
+ _resources: &mut SystemAllocator,
+ ) -> Result<Vec<(u64, u64)>> {
+ Ok(Vec::new())
+ }
+
+ /// Register any capabilties specified by the device.
+ fn register_device_capabilities(&mut self) -> Result<()> {
+ Ok(())
+ }
+
+ /// Gets a list of ioeventfds that should be registered with the running VM. The list is
+ /// returned as a Vec of (eventfd, addr, datamatch) tuples.
+ fn ioeventfds(&self) -> Vec<(&EventFd, u64, Datamatch)> {
+ Vec::new()
+ }
+ /// Gets the configuration registers of the Pci Device.
+ fn config_registers(&self) -> &PciConfiguration; // TODO - remove these
+ /// Gets the configuration registers of the Pci Device for modification.
+ fn config_registers_mut(&mut self) -> &mut PciConfiguration;
+ /// Reads from a BAR region mapped in to the device.
+ /// * `addr` - The guest address inside the BAR.
+ /// * `data` - Filled with the data from `addr`.
+ fn read_bar(&mut self, addr: u64, data: &mut [u8]);
+ /// Writes to a BAR region mapped in to the device.
+ /// * `addr` - The guest address inside the BAR.
+ /// * `data` - The data to write.
+ fn write_bar(&mut self, addr: u64, data: &[u8]);
+ /// Invoked when the device is sandboxed.
+ fn on_device_sandboxed(&mut self) {}
+}
+
+impl<T: PciDevice> BusDevice for T {
+ fn debug_label(&self) -> String {
+ PciDevice::debug_label(self)
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ self.read_bar(offset, data)
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ self.write_bar(offset, data)
+ }
+
+ fn config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
+ if offset as usize + data.len() > 4 {
+ return;
+ }
+
+ let regs = self.config_registers_mut();
+
+ match data.len() {
+ 1 => regs.write_byte(reg_idx * 4 + offset as usize, data[0]),
+ 2 => regs.write_word(
+ reg_idx * 4 + offset as usize,
+ (data[0] as u16) | (data[1] as u16) << 8,
+ ),
+ 4 => regs.write_reg(reg_idx, LittleEndian::read_u32(data)),
+ _ => (),
+ }
+ }
+
+ fn config_register_read(&self, reg_idx: usize) -> u32 {
+ self.config_registers().read_reg(reg_idx)
+ }
+
+ fn on_sandboxed(&mut self) {
+ self.on_device_sandboxed();
+ }
+}
+
+impl<T: PciDevice + ?Sized> PciDevice for Box<T> {
+ /// Returns a label suitable for debug output.
+ fn debug_label(&self) -> String {
+ (**self).debug_label()
+ }
+ fn assign_bus_dev(&mut self, bus: u8, device: u8 /*u5*/) {
+ (**self).assign_bus_dev(bus, device)
+ }
+ fn keep_fds(&self) -> Vec<RawFd> {
+ (**self).keep_fds()
+ }
+ fn assign_irq(
+ &mut self,
+ irq_evt: EventFd,
+ irq_resample_evt: EventFd,
+ irq_num: u32,
+ irq_pin: PciInterruptPin,
+ ) {
+ (**self).assign_irq(irq_evt, irq_resample_evt, irq_num, irq_pin)
+ }
+ fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
+ (**self).allocate_io_bars(resources)
+ }
+ fn allocate_device_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
+ (**self).allocate_device_bars(resources)
+ }
+ fn register_device_capabilities(&mut self) -> Result<()> {
+ (**self).register_device_capabilities()
+ }
+ fn ioeventfds(&self) -> Vec<(&EventFd, u64, Datamatch)> {
+ (**self).ioeventfds()
+ }
+ fn config_registers(&self) -> &PciConfiguration {
+ (**self).config_registers()
+ }
+ fn config_registers_mut(&mut self) -> &mut PciConfiguration {
+ (**self).config_registers_mut()
+ }
+ fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
+ (**self).read_bar(addr, data)
+ }
+ fn write_bar(&mut self, addr: u64, data: &[u8]) {
+ (**self).write_bar(addr, data)
+ }
+ /// Invoked when the device is sandboxed.
+ fn on_device_sandboxed(&mut self) {
+ (**self).on_device_sandboxed()
+ }
+}
diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs
new file mode 100644
index 0000000..187b564
--- /dev/null
+++ b/devices/src/pci/pci_root.rs
@@ -0,0 +1,303 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::unix::io::RawFd;
+use std::sync::Arc;
+
+use byteorder::{ByteOrder, LittleEndian};
+use sync::Mutex;
+
+use crate::pci::pci_configuration::{
+ PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
+};
+use crate::pci::pci_device::PciDevice;
+use crate::BusDevice;
+
+// A PciDevice that holds the root hub's configuration.
+struct PciRootConfiguration {
+ config: PciConfiguration,
+}
+
+impl PciDevice for PciRootConfiguration {
+ fn debug_label(&self) -> String {
+ "pci root device".to_owned()
+ }
+ fn keep_fds(&self) -> Vec<RawFd> {
+ Vec::new()
+ }
+ fn config_registers(&self) -> &PciConfiguration {
+ &self.config
+ }
+
+ fn config_registers_mut(&mut self) -> &mut PciConfiguration {
+ &mut self.config
+ }
+
+ fn read_bar(&mut self, _addr: u64, _data: &mut [u8]) {}
+
+ fn write_bar(&mut self, _addr: u64, _data: &[u8]) {}
+}
+
+/// Emulates the PCI Root bridge.
+pub struct PciRoot {
+ /// Bus configuration for the root device.
+ root_configuration: PciRootConfiguration,
+ /// Devices attached to this bridge.
+ devices: Vec<Arc<Mutex<dyn BusDevice>>>,
+}
+
+impl PciRoot {
+ /// Create an empty PCI root bus.
+ pub fn new() -> Self {
+ PciRoot {
+ root_configuration: PciRootConfiguration {
+ config: PciConfiguration::new(
+ 0,
+ 0,
+ PciClassCode::BridgeDevice,
+ &PciBridgeSubclass::HostBridge,
+ None,
+ PciHeaderType::Bridge,
+ 0,
+ 0,
+ ),
+ },
+ devices: Vec::new(),
+ }
+ }
+
+ /// Add a `device` to this root PCI bus.
+ pub fn add_device(&mut self, device: Arc<Mutex<dyn BusDevice>>) {
+ self.devices.push(device);
+ }
+
+ pub fn config_space_read(
+ &self,
+ bus: usize,
+ device: usize,
+ _function: usize,
+ register: usize,
+ ) -> u32 {
+ // Only support one bus.
+ if bus != 0 {
+ return 0xffff_ffff;
+ }
+
+ match device {
+ 0 => {
+ // If bus and device are both zero, then read from the root config.
+ self.root_configuration.config_register_read(register)
+ }
+ dev_num => self
+ .devices
+ .get(dev_num - 1)
+ .map_or(0xffff_ffff, |d| d.lock().config_register_read(register)),
+ }
+ }
+
+ pub fn config_space_write(
+ &mut self,
+ bus: usize,
+ device: usize,
+ _function: usize,
+ register: usize,
+ offset: u64,
+ data: &[u8],
+ ) {
+ if offset as usize + data.len() > 4 {
+ return;
+ }
+
+ // Only support one bus.
+ if bus != 0 {
+ return;
+ }
+
+ match device {
+ 0 => {
+ // If bus and device are both zero, then read from the root config.
+ self.root_configuration
+ .config_register_write(register, offset, data);
+ }
+ dev_num => {
+ // dev_num is 1-indexed here.
+ if let Some(d) = self.devices.get(dev_num - 1) {
+ d.lock().config_register_write(register, offset, data);
+ }
+ }
+ }
+ }
+}
+
+/// Emulates PCI configuration access mechanism #1 (I/O ports 0xcf8 and 0xcfc).
+pub struct PciConfigIo {
+ /// PCI root bridge.
+ pci_root: PciRoot,
+ /// Current address to read/write from (0xcf8 register, litte endian).
+ config_address: u32,
+}
+
+impl PciConfigIo {
+ pub fn new(pci_root: PciRoot) -> Self {
+ PciConfigIo {
+ pci_root,
+ config_address: 0,
+ }
+ }
+
+ fn config_space_read(&self) -> u32 {
+ let enabled = (self.config_address & 0x8000_0000) != 0;
+ if !enabled {
+ return 0xffff_ffff;
+ }
+
+ let (bus, device, function, register) =
+ parse_config_address(self.config_address & !0x8000_0000);
+ self.pci_root
+ .config_space_read(bus, device, function, register)
+ }
+
+ fn config_space_write(&mut self, offset: u64, data: &[u8]) {
+ let enabled = (self.config_address & 0x8000_0000) != 0;
+ if !enabled {
+ return;
+ }
+
+ let (bus, device, function, register) =
+ parse_config_address(self.config_address & !0x8000_0000);
+ self.pci_root
+ .config_space_write(bus, device, function, register, offset, data)
+ }
+
+ fn set_config_address(&mut self, offset: u64, data: &[u8]) {
+ if offset as usize + data.len() > 4 {
+ return;
+ }
+ let (mask, value): (u32, u32) = match data.len() {
+ 1 => (
+ 0x0000_00ff << (offset * 8),
+ (data[0] as u32) << (offset * 8),
+ ),
+ 2 => (
+ 0x0000_ffff << (offset * 16),
+ ((data[1] as u32) << 8 | data[0] as u32) << (offset * 16),
+ ),
+ 4 => (0xffff_ffff, LittleEndian::read_u32(data)),
+ _ => return,
+ };
+ self.config_address = (self.config_address & !mask) | value;
+ }
+}
+
+impl BusDevice for PciConfigIo {
+ fn debug_label(&self) -> String {
+ format!("pci config io-port 0x{:03x}", self.config_address)
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ // `offset` is relative to 0xcf8
+ let value = match 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 end = start + data.len();
+ if end <= 4 {
+ for i in start..end {
+ data[i - start] = (value >> (i * 8)) as u8;
+ }
+ } else {
+ for d in data {
+ *d = 0xff;
+ }
+ }
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ // `offset` is relative to 0xcf8
+ match offset {
+ o @ 0...3 => self.set_config_address(o, data),
+ o @ 4...7 => self.config_space_write(o - 4, data),
+ _ => (),
+ };
+ }
+}
+
+/// Emulates PCI memory-mapped configuration access mechanism.
+pub struct PciConfigMmio {
+ /// PCI root bridge.
+ pci_root: PciRoot,
+}
+
+impl PciConfigMmio {
+ pub fn new(pci_root: PciRoot) -> Self {
+ PciConfigMmio { pci_root }
+ }
+
+ fn config_space_read(&self, config_address: u32) -> u32 {
+ let (bus, device, function, register) = parse_config_address(config_address);
+ self.pci_root
+ .config_space_read(bus, device, function, register)
+ }
+
+ fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) {
+ let (bus, device, function, register) = parse_config_address(config_address);
+ self.pci_root
+ .config_space_write(bus, device, function, register, offset, data)
+ }
+}
+
+impl BusDevice for PciConfigMmio {
+ fn debug_label(&self) -> String {
+ "pci config mmio".to_owned()
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ // Only allow reads to the register boundary.
+ let start = offset as usize % 4;
+ let end = start + data.len();
+ if end > 4 || offset > u32::max_value() as u64 {
+ for d in data {
+ *d = 0xff;
+ }
+ return;
+ }
+
+ let value = self.config_space_read(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 {
+ return;
+ }
+ self.config_space_write(offset as u32, offset % 4, data)
+ }
+}
+
+// Parse the CONFIG_ADDRESS register to a (bus, device, function, register) tuple.
+fn parse_config_address(config_address: u32) -> (usize, usize, usize, usize) {
+ const BUS_NUMBER_OFFSET: usize = 16;
+ const BUS_NUMBER_MASK: u32 = 0x00ff;
+ const DEVICE_NUMBER_OFFSET: usize = 11;
+ const DEVICE_NUMBER_MASK: u32 = 0x1f;
+ const FUNCTION_NUMBER_OFFSET: usize = 8;
+ const FUNCTION_NUMBER_MASK: u32 = 0x07;
+ const REGISTER_NUMBER_OFFSET: usize = 2;
+ const REGISTER_NUMBER_MASK: u32 = 0x3f;
+
+ let bus_number = ((config_address >> BUS_NUMBER_OFFSET) & BUS_NUMBER_MASK) as usize;
+ let device_number = ((config_address >> DEVICE_NUMBER_OFFSET) & DEVICE_NUMBER_MASK) as usize;
+ let function_number =
+ ((config_address >> FUNCTION_NUMBER_OFFSET) & FUNCTION_NUMBER_MASK) as usize;
+ let register_number =
+ ((config_address >> REGISTER_NUMBER_OFFSET) & REGISTER_NUMBER_MASK) as usize;
+
+ (bus_number, device_number, function_number, register_number)
+}
diff --git a/devices/src/pic.rs b/devices/src/pic.rs
new file mode 100644
index 0000000..9b8235f
--- /dev/null
+++ b/devices/src/pic.rs
@@ -0,0 +1,1136 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Software implementation of an Intel 8259A Programmable Interrupt Controller
+// This is a legacy device used by older OSs and briefly during start-up by
+// modern OSs that use a legacy BIOS.
+// The PIC is connected to the Local APIC on CPU0.
+
+// Terminology note: The 8259A spec refers to "master" and "slave" PITs; the "slave"s are daisy
+// chained to the "master"s.
+// 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" PITs.
+
+use crate::BusDevice;
+use sys_util::{debug, warn};
+
+#[repr(usize)]
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum PicSelect {
+ Primary = 0,
+ Secondary = 1,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum PicInitState {
+ Icw1 = 0,
+ Icw2 = 1,
+ Icw3 = 2,
+ Icw4 = 3,
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+struct PicState {
+ last_irr: u8, // Edge detection.
+ irr: u8, // Interrupt Request Register.
+ imr: u8, // Interrupt Mask Register.
+ isr: u8, // Interrupt Service Register.
+ priority_add: u8, // Highest priority, for priority rotation.
+ irq_base: u8,
+ read_reg_select: bool,
+ poll: bool,
+ special_mask: bool,
+ auto_eoi: bool,
+ rotate_on_auto_eoi: bool,
+ special_fully_nested_mode: bool,
+ // PIC takes either 3 or 4 bytes of initialization command word during
+ // initialization. use_4_byte_icw is true if 4 bytes of ICW are needed.
+ use_4_byte_icw: bool,
+ // "Edge/Level Control Registers", for edge trigger selection.
+ // When a particular bit is set, the corresponding IRQ is in level-triggered mode. Otherwise it
+ // is in edge-triggered mode.
+ elcr: u8,
+ elcr_mask: u8,
+ init_state: Option<PicInitState>,
+}
+
+pub struct Pic {
+ // TODO(mutexlox): Implement an APIC and add necessary state to the Pic.
+
+ // index 0 (aka PicSelect::Primary) is the primary pic, the rest are secondary.
+ pics: [PicState; 2],
+}
+
+const PIC_NUM_PINS: u8 = 16;
+
+// Register offsets.
+const PIC_PRIMARY: u64 = 0x20;
+const PIC_PRIMARY_COMMAND: u64 = PIC_PRIMARY;
+const PIC_PRIMARY_DATA: u64 = PIC_PRIMARY + 1;
+const PIC_PRIMARY_ELCR: u64 = 0x4d0;
+
+const PIC_SECONDARY: u64 = 0xa0;
+const PIC_SECONDARY_COMMAND: u64 = PIC_SECONDARY;
+const PIC_SECONDARY_DATA: u64 = PIC_SECONDARY + 1;
+const PIC_SECONDARY_ELCR: u64 = 0x4d1;
+
+const LEVEL_HIGH: bool = true;
+const LEVEL_LOW: bool = false;
+const INVALID_PRIORITY: u8 = 8;
+const SPURIOUS_IRQ: u8 = 0x07;
+const PRIMARY_PIC_CASCADE_PIN: u8 = 2;
+const PRIMARY_PIC_CASCADE_PIN_MASK: u8 = 0x04;
+const PRIMARY_PIC_MAX_IRQ: u8 = 7;
+
+// Command Words
+const ICW1_MASK: u8 = 0x10;
+const OCW3_MASK: u8 = 0x08;
+
+// ICW1 bits
+const ICW1_NEED_ICW4: u8 = 0x01; // ICW4 needed
+const ICW1_SINGLE_PIC_MODE: u8 = 0x02;
+const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
+
+const ICW2_IRQ_BASE_MASK: u8 = 0xf8;
+
+const ICW4_SPECIAL_FULLY_NESTED_MODE: u8 = 0x10;
+const ICW4_AUTO_EOI: u8 = 0x02;
+
+// OCW2 bits
+const OCW2_IRQ_MASK: u8 = 0x07;
+const OCW2_COMMAND_MASK: u8 = 0xe0;
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+enum Ocw2 {
+ RotateAutoEoiClear = 0x00,
+ NonSpecificEoi = 0x20,
+ NoOp = 0x40,
+ SpecificEoi = 0x60,
+ RotateAutoEoiSet = 0x80,
+ RotateNonSpecificEoi = 0xa0,
+ SetPriority = 0xc0,
+ RotateSpecificEoi = 0xe0,
+}
+
+// OCW3 bits
+const OCW3_POLL_COMMAND: u8 = 0x04;
+const OCW3_READ_REGISTER: u8 = 0x02;
+// OCW3_READ_IRR (0x00) intentionally omitted.
+const OCW3_READ_ISR: u8 = 0x01;
+const OCW3_SPECIAL_MASK: u8 = 0x40;
+const OCW3_SPECIAL_MASK_VALUE: u8 = 0x20;
+
+impl BusDevice for Pic {
+ fn debug_label(&self) -> String {
+ "userspace PIC".to_string()
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ if data.len() != 1 {
+ warn!("PIC: Bad write size: {}", data.len());
+ return;
+ }
+ match offset {
+ 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),
+ }
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ if data.len() != 1 {
+ warn!("PIC: Bad read size: {}", data.len());
+ return;
+ }
+ data[0] = match offset {
+ 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),
+ PIC_SECONDARY_COMMAND => self.pic_read_command(PicSelect::Secondary),
+ 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);
+ return;
+ }
+ };
+ }
+}
+
+impl Pic {
+ pub fn new() -> Pic {
+ let mut primary_pic: PicState = Default::default();
+ let mut secondary_pic: PicState = Default::default();
+ // These two masks are taken from KVM code (but do not appear in the 8259 specification).
+
+ // These IRQ lines are edge triggered, and so have 0 bits in the masks:
+ // - IRQs 0, 1, 8, and 13 are dedicated to special I/O devices on the system board.
+ // - IRQ 2 is the primary pic's cascade line.
+ // The primary pic has IRQs 0-7.
+ primary_pic.elcr_mask = !((1 << 0) | (1 << 1) | (1 << 2));
+ // The secondary PIC has IRQs 8-15, so we subtract 8 from the IRQ number to get the bit
+ // that should be masked here. In this case, bits 8 - 8 = 0 and 13 - 8 = 5.
+ secondary_pic.elcr_mask = !((1 << 0) | (1 << 5));
+ // TODO(mutexlox): Add logic to initialize APIC interrupt-related fields.
+
+ Pic {
+ pics: [primary_pic, secondary_pic],
+ }
+ }
+
+ pub fn service_irq(&mut self, irq: u8, level: bool) -> bool {
+ assert!(irq <= 15, "Unexpectedly high value irq: {} vs 15", irq);
+
+ let pic = if irq <= PRIMARY_PIC_MAX_IRQ {
+ PicSelect::Primary
+ } else {
+ PicSelect::Secondary
+ };
+ Pic::set_irq_internal(&mut self.pics[pic as usize], irq & 7, level);
+
+ self.update_irq()
+ }
+
+ /// Determines whether the (primary) PIC is completely masked.
+ pub fn masked(&self) -> bool {
+ self.pics[PicSelect::Primary as usize].imr == 0xFF
+ }
+
+ /// Determines whether the PIC has an interrupt ready.
+ pub fn has_interrupt(&self) -> bool {
+ self.get_irq(PicSelect::Primary).is_some()
+ }
+
+ /// Determines the external interrupt number that the PIC is prepared to inject, if any.
+ pub fn get_external_interrupt(&mut self) -> Option<u8> {
+ let irq_primary = if let Some(irq) = self.get_irq(PicSelect::Primary) {
+ irq
+ } else {
+ // The architecturally correct behavior in this case is to inject a spurious interrupt.
+ // Although this case only occurs as a result of a race condition where the interrupt
+ // might also be avoided entirely. Here we return `None` to avoid the interrupt
+ // entirely. The KVM unit test OS, which several unit tests rely upon, doesn't
+ // properly handle spurious interrupts. Also spurious interrupts are much more common
+ // in this code than real hardware because the hardware race is much much much smaller.
+ return None;
+ };
+
+ Pic::interrupt_ack(&mut self.pics[PicSelect::Primary as usize], irq_primary);
+ let int_num = if irq_primary == PRIMARY_PIC_CASCADE_PIN {
+ // IRQ on secondary pic.
+ let irq_secondary = if let Some(irq) = self.get_irq(PicSelect::Secondary) {
+ Pic::interrupt_ack(&mut self.pics[PicSelect::Secondary as usize], irq);
+ irq
+ } else {
+ SPURIOUS_IRQ
+ };
+ self.pics[PicSelect::Secondary as usize].irq_base + irq_secondary
+ } else {
+ self.pics[PicSelect::Primary as usize].irq_base + irq_primary
+ };
+
+ self.update_irq();
+ Some(int_num)
+ }
+
+ fn pic_read_command(&mut self, pic_type: PicSelect) -> u8 {
+ if self.pics[pic_type as usize].poll {
+ let (ret, update_irq_needed) = self.poll_read(pic_type);
+ self.pics[pic_type as usize].poll = false;
+
+ if update_irq_needed {
+ self.update_irq();
+ }
+
+ ret
+ } else if self.pics[pic_type as usize].read_reg_select {
+ self.pics[pic_type as usize].isr
+ } else {
+ self.pics[pic_type as usize].irr
+ }
+ }
+
+ fn pic_read_data(&mut self, pic_type: PicSelect) -> u8 {
+ if self.pics[pic_type as usize].poll {
+ let (ret, update_needed) = self.poll_read(pic_type);
+ self.pics[pic_type as usize].poll = false;
+ if update_needed {
+ self.update_irq();
+ }
+ ret
+ } else {
+ self.pics[pic_type as usize].imr
+ }
+ }
+
+ fn pic_read_elcr(&mut self, pic_type: PicSelect) -> u8 {
+ self.pics[pic_type as usize].elcr
+ }
+
+ fn pic_write_command(&mut self, pic_type: PicSelect, value: u8) {
+ if value & ICW1_MASK != 0 {
+ Pic::init_command_word_1(&mut self.pics[pic_type as usize], value);
+ } else if value & OCW3_MASK != 0 {
+ Pic::operation_command_word_3(&mut self.pics[pic_type as usize], value);
+ } else {
+ self.operation_command_word_2(pic_type, value);
+ }
+ }
+
+ fn pic_write_data(&mut self, pic_type: PicSelect, value: u8) {
+ match self.pics[pic_type as usize].init_state {
+ Some(PicInitState::Icw1) | None => {
+ if self.pics[pic_type as usize].init_state.is_none() {
+ debug!(
+ "PIC: {:?}: Uninitialized data write of {:#x}",
+ pic_type, value
+ );
+ }
+ self.pics[pic_type as usize].imr = value;
+ self.update_irq();
+ }
+ Some(PicInitState::Icw2) => {
+ self.pics[pic_type as usize].irq_base = value & ICW2_IRQ_BASE_MASK;
+ self.pics[pic_type as usize].init_state = Some(PicInitState::Icw3);
+ }
+ Some(PicInitState::Icw3) => {
+ if self.pics[pic_type as usize].use_4_byte_icw {
+ self.pics[pic_type as usize].init_state = Some(PicInitState::Icw4);
+ } else {
+ self.pics[pic_type as usize].init_state = Some(PicInitState::Icw1);
+ }
+ }
+ Some(PicInitState::Icw4) => {
+ self.pics[pic_type as usize].special_fully_nested_mode =
+ (value & ICW4_SPECIAL_FULLY_NESTED_MODE) != 0;
+ self.pics[pic_type as usize].auto_eoi = (value & ICW4_AUTO_EOI) != 0;
+ self.pics[pic_type as usize].init_state = Some(PicInitState::Icw1);
+ }
+ }
+ }
+
+ fn pic_write_elcr(&mut self, pic_type: PicSelect, value: u8) {
+ self.pics[pic_type as usize].elcr = value & !self.pics[pic_type as usize].elcr;
+ }
+
+ fn reset_pic(pic: &mut PicState) {
+ let edge_irr = pic.irr & !pic.elcr;
+
+ pic.last_irr = 0;
+ pic.irr &= pic.elcr;
+ pic.imr = 0;
+ pic.priority_add = 0;
+ pic.special_mask = false;
+ pic.read_reg_select = false;
+ if !pic.use_4_byte_icw {
+ pic.special_fully_nested_mode = false;
+ pic.auto_eoi = false;
+ }
+ pic.init_state = Some(PicInitState::Icw2);
+
+ for irq in 0..PIC_NUM_PINS / 2 {
+ if edge_irr & (1 << irq) != 0 {
+ Pic::clear_isr(pic, irq);
+ }
+ }
+ }
+
+ /// Determine the priority and whether an update_irq call is needed.
+ fn poll_read(&mut self, pic_type: PicSelect) -> (u8, bool) {
+ if let Some(mut irq) = self.get_irq(pic_type) {
+ irq &= 0xff;
+ if pic_type == PicSelect::Secondary {
+ self.pics[PicSelect::Primary as usize].isr &= !PRIMARY_PIC_CASCADE_PIN_MASK;
+ self.pics[PicSelect::Primary as usize].irr &= !PRIMARY_PIC_CASCADE_PIN_MASK;
+ }
+ self.pics[pic_type as usize].irr &= !(1 << irq);
+ Pic::clear_isr(&mut self.pics[pic_type as usize], irq);
+ let update_irq_needed =
+ pic_type == PicSelect::Secondary && irq != PRIMARY_PIC_CASCADE_PIN;
+ (irq, update_irq_needed)
+ } else {
+ // Spurious interrupt
+ (SPURIOUS_IRQ, true)
+ }
+ }
+
+ fn get_irq(&self, pic_type: PicSelect) -> Option<u8> {
+ let pic = &self.pics[pic_type as usize];
+ let mut irq_bitmap = pic.irr & !pic.imr;
+ let priority = if let Some(p) = Pic::get_priority(pic, irq_bitmap) {
+ p
+ } else {
+ return None;
+ };
+
+ // If the primary is in fully-nested mode, the IRQ coming from the secondary is not taken
+ // into account for the priority computation below.
+ irq_bitmap = pic.isr;
+ if pic_type == PicSelect::Primary && pic.special_fully_nested_mode {
+ irq_bitmap &= !PRIMARY_PIC_CASCADE_PIN_MASK;
+ }
+ let new_priority = Pic::get_priority(pic, irq_bitmap).unwrap_or(INVALID_PRIORITY);
+ if priority < new_priority {
+ // Higher priority found. IRQ should be generated.
+ Some((priority + pic.priority_add) & 7)
+ } else {
+ None
+ }
+ }
+
+ fn clear_isr(pic: &mut PicState, irq: u8) {
+ assert!(irq <= 7, "Unexpectedly high value for irq: {} vs 7", irq);
+ pic.isr &= !(1 << irq);
+ }
+
+ fn update_irq(&mut self) -> bool {
+ if self.get_irq(PicSelect::Secondary).is_some() {
+ // If secondary pic has an IRQ request, signal primary's cascade pin.
+ Pic::set_irq_internal(
+ &mut self.pics[PicSelect::Primary as usize],
+ PRIMARY_PIC_CASCADE_PIN,
+ LEVEL_HIGH,
+ );
+ Pic::set_irq_internal(
+ &mut self.pics[PicSelect::Primary as usize],
+ PRIMARY_PIC_CASCADE_PIN,
+ LEVEL_LOW,
+ );
+ }
+
+ if self.get_irq(PicSelect::Primary).is_some() {
+ // TODO(mutexlox): Signal local interrupt on APIC bus.
+ // Note: this does not check if the interrupt is succesfully injected into
+ // the CPU, just whether or not one is fired.
+ true
+ } else {
+ false
+ }
+ }
+
+ /// Set Irq level. If edge is detected, then IRR is set to 1.
+ fn set_irq_internal(pic: &mut PicState, irq: u8, level: bool) {
+ assert!(irq <= 7, "Unexpectedly high value for irq: {} vs 7", irq);
+ let irq_bitmap = 1 << irq;
+ if (pic.elcr & irq_bitmap) != 0 {
+ // Level-triggered.
+ if level {
+ // Same IRQ already requested.
+ pic.irr |= irq_bitmap;
+ pic.last_irr |= irq_bitmap;
+ } else {
+ pic.irr &= !irq_bitmap;
+ pic.last_irr &= !irq_bitmap;
+ }
+ } else {
+ // Edge-triggered
+ if level {
+ if (pic.last_irr & irq_bitmap) == 0 {
+ // Raising edge detected.
+ pic.irr |= irq_bitmap;
+ }
+ pic.last_irr |= irq_bitmap;
+ } else {
+ pic.last_irr &= !irq_bitmap;
+ }
+ }
+ }
+
+ fn get_priority(pic: &PicState, irq_bitmap: u8) -> Option<u8> {
+ if irq_bitmap == 0 {
+ None
+ } else {
+ // Find the highest priority bit in irq_bitmap considering the priority
+ // rotation mechanism (priority_add).
+ let mut priority = 0;
+ let mut priority_mask = 1 << ((priority + pic.priority_add) & 7);
+ while (irq_bitmap & priority_mask) == 0 {
+ priority += 1;
+ priority_mask = 1 << ((priority + pic.priority_add) & 7);
+ }
+ Some(priority)
+ }
+ }
+
+ /// Move interrupt from IRR to ISR to indicate that the interrupt is injected. If
+ /// auto EOI is set, then ISR is immediately cleared (since the purpose of EOI is
+ /// to clear ISR bit).
+ fn interrupt_ack(pic: &mut PicState, irq: u8) {
+ assert!(irq <= 7, "Unexpectedly high value for irq: {} vs 7", irq);
+
+ let irq_bitmap = 1 << irq;
+ pic.isr |= irq_bitmap;
+
+ if (pic.elcr & irq_bitmap) == 0 {
+ pic.irr &= !irq_bitmap;
+ }
+
+ if pic.auto_eoi {
+ if pic.rotate_on_auto_eoi {
+ pic.priority_add = (irq + 1) & 7;
+ }
+ Pic::clear_isr(pic, irq);
+ }
+ }
+
+ fn init_command_word_1(pic: &mut PicState, value: u8) {
+ pic.use_4_byte_icw = (value & ICW1_NEED_ICW4) != 0;
+ if (value & ICW1_SINGLE_PIC_MODE) != 0 {
+ debug!("PIC: Single PIC mode not supported.");
+ }
+ if (value & ICW1_LEVEL_TRIGGER_MODE) != 0 {
+ debug!("PIC: Level triggered IRQ not supported.");
+ }
+ Pic::reset_pic(pic);
+ }
+
+ fn operation_command_word_2(&mut self, pic_type: PicSelect, value: u8) {
+ let mut irq = value & OCW2_IRQ_MASK;
+ if let Some(cmd) = Ocw2::n(value & OCW2_COMMAND_MASK) {
+ match cmd {
+ Ocw2::RotateAutoEoiSet => self.pics[pic_type as usize].rotate_on_auto_eoi = true,
+ Ocw2::RotateAutoEoiClear => self.pics[pic_type as usize].rotate_on_auto_eoi = false,
+ Ocw2::NonSpecificEoi | Ocw2::RotateNonSpecificEoi => {
+ if let Some(priority) = Pic::get_priority(
+ &self.pics[pic_type as usize],
+ self.pics[pic_type as usize].isr,
+ ) {
+ irq = (priority + self.pics[pic_type as usize].priority_add) & 7;
+ if cmd == Ocw2::RotateNonSpecificEoi {
+ self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
+ }
+ Pic::clear_isr(&mut self.pics[pic_type as usize], irq);
+ self.update_irq();
+ }
+ }
+ Ocw2::SpecificEoi => {
+ Pic::clear_isr(&mut self.pics[pic_type as usize], irq);
+ self.update_irq();
+ }
+ Ocw2::SetPriority => {
+ self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
+ self.update_irq();
+ }
+ Ocw2::RotateSpecificEoi => {
+ self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
+ Pic::clear_isr(&mut self.pics[pic_type as usize], irq);
+ self.update_irq();
+ }
+ Ocw2::NoOp => {} /* noop */
+ }
+ }
+ }
+
+ fn operation_command_word_3(pic: &mut PicState, value: u8) {
+ if value & OCW3_POLL_COMMAND != 0 {
+ pic.poll = true;
+ }
+ if value & OCW3_READ_REGISTER != 0 {
+ // Select to read ISR or IRR
+ pic.read_reg_select = value & OCW3_READ_ISR != 0;
+ }
+ if value & OCW3_SPECIAL_MASK != 0 {
+ pic.special_mask = value & OCW3_SPECIAL_MASK_VALUE != 0;
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ // ICW4: Special fully nested mode with no auto EOI.
+ const FULLY_NESTED_NO_AUTO_EOI: u8 = 0x11;
+ use super::*;
+
+ struct TestData {
+ pic: Pic,
+ }
+
+ 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]);
+ TestData { pic }
+ }
+
+ /// Convenience wrapper to initialize PIC using 4 ICWs. Validity of values is NOT checked.
+ fn icw_init(pic: &mut Pic, pic_type: PicSelect, icw1: u8, icw2: u8, icw3: u8, icw4: u8) {
+ let command_offset = match pic_type {
+ PicSelect::Primary => PIC_PRIMARY_COMMAND,
+ PicSelect::Secondary => PIC_SECONDARY_COMMAND,
+ };
+ let data_offset = match pic_type {
+ PicSelect::Primary => PIC_PRIMARY_DATA,
+ 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]);
+ }
+
+ /// Convenience function for primary ICW init.
+ fn icw_init_primary(pic: &mut Pic) {
+ // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
+ // ICW2 0x08: Interrupt vector base address 0x08.
+ // ICW3 0xff: Value written does not matter.
+ // ICW4 0x13: Special fully nested mode, auto EOI.
+ icw_init(pic, PicSelect::Primary, 0x11, 0x08, 0xff, 0x13);
+ }
+
+ /// Convenience function for secondary ICW init.
+ fn icw_init_secondary(pic: &mut Pic) {
+ // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
+ // ICW2 0x70: Interrupt vector base address 0x70.
+ // ICW3 0xff: Value written does not matter.
+ // ICW4 0x13: Special fully nested mode, auto EOI.
+ icw_init(pic, PicSelect::Secondary, 0x11, 0x70, 0xff, 0x13);
+ }
+
+ /// Convenience function for initializing ICW with custom value for ICW4.
+ fn icw_init_both_with_icw4(pic: &mut Pic, icw4: u8) {
+ // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
+ // ICW2 0x08: Interrupt vector base address 0x08.
+ // ICW3 0xff: Value written does not matter.
+ icw_init(pic, PicSelect::Primary, 0x11, 0x08, 0xff, icw4);
+ // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
+ // ICW2 0x70: Interrupt vector base address 0x70.
+ // ICW3 0xff: Value written does not matter.
+ icw_init(pic, PicSelect::Secondary, 0x11, 0x70, 0xff, icw4);
+ }
+
+ fn icw_init_both(pic: &mut Pic) {
+ icw_init_primary(pic);
+ icw_init_secondary(pic);
+ }
+
+ /// Test that elcr register can be written and read correctly.
+ #[test]
+ fn write_read_elcr() {
+ let mut data = set_up();
+ 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);
+ assert_eq!(data_read, data_write);
+
+ data.pic.write(PIC_SECONDARY_ELCR, &data_write);
+ data.pic.read(PIC_SECONDARY_ELCR, &mut data_read);
+ assert_eq!(data_read, data_write);
+ }
+
+ /// Test the three-word ICW.
+ #[test]
+ fn icw_2_step() {
+ let mut data = set_up();
+
+ // ICW1
+ let mut data_write = [0x10];
+ data.pic.write(PIC_PRIMARY_COMMAND, &data_write);
+
+ data_write[0] = 0x08;
+ data.pic.write(PIC_PRIMARY_DATA, &data_write);
+
+ data_write[0] = 0xff;
+ data.pic.write(PIC_PRIMARY_DATA, &data_write);
+
+ assert_eq!(
+ data.pic.pics[PicSelect::Primary as usize].init_state,
+ Some(PicInitState::Icw1)
+ );
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irq_base, 0x08);
+ assert_eq!(
+ data.pic.pics[PicSelect::Primary as usize].use_4_byte_icw,
+ false
+ );
+ }
+
+ /// Verify that PIC is in expected state after initialization.
+ #[test]
+ fn initial_values() {
+ let mut data = set_up();
+ icw_init_primary(&mut data.pic);
+
+ let primary_pic = &data.pic.pics[PicSelect::Primary as usize];
+ assert_eq!(primary_pic.last_irr, 0x00);
+ assert_eq!(primary_pic.irr, 0x00);
+ assert_eq!(primary_pic.imr, 0x00);
+ assert_eq!(primary_pic.isr, 0x00);
+ assert_eq!(primary_pic.priority_add, 0);
+ assert_eq!(primary_pic.irq_base, 0x08);
+ assert_eq!(primary_pic.read_reg_select, false);
+ assert_eq!(primary_pic.poll, false);
+ assert_eq!(primary_pic.special_mask, false);
+ assert_eq!(primary_pic.init_state, Some(PicInitState::Icw1));
+ assert_eq!(primary_pic.auto_eoi, true);
+ assert_eq!(primary_pic.rotate_on_auto_eoi, false);
+ assert_eq!(primary_pic.special_fully_nested_mode, true);
+ assert_eq!(primary_pic.use_4_byte_icw, true);
+ assert_eq!(primary_pic.elcr, 0x00);
+ assert_eq!(primary_pic.elcr_mask, 0xf8);
+ }
+
+ /// Verify effect that OCW has on PIC registers & state.
+ #[test]
+ fn ocw() {
+ let mut data = set_up();
+
+ icw_init_secondary(&mut data.pic);
+
+ // OCW1: Write to IMR.
+ data.pic.write(PIC_SECONDARY_DATA, &[0x5f]);
+
+ // OCW2: Set rotate on auto EOI.
+ data.pic.write(PIC_SECONDARY_COMMAND, &[0x80]);
+
+ // OCW2: Set priority.
+ data.pic.write(PIC_SECONDARY_COMMAND, &[0xc0]);
+
+ // OCW3: Change flags.
+ data.pic.write(PIC_SECONDARY_COMMAND, &[0x6b]);
+
+ let mut data_read = [0];
+ data.pic.read(PIC_SECONDARY_DATA, &mut data_read);
+ assert_eq!(data_read, [0x5f]);
+
+ let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
+
+ // Check OCW1 result.
+ assert_eq!(secondary_pic.imr, 0x5f);
+
+ // Check OCW2 result.
+ assert!(secondary_pic.rotate_on_auto_eoi);
+ assert_eq!(secondary_pic.priority_add, 1);
+
+ // Check OCW3 result.
+ assert!(secondary_pic.special_mask);
+ assert_eq!(secondary_pic.poll, false);
+ assert!(secondary_pic.read_reg_select);
+ }
+
+ /// Verify that we can set and clear the AutoRotate bit in OCW.
+ #[test]
+ fn ocw_auto_rotate_set_and_clear() {
+ let mut data = set_up();
+
+ icw_init_secondary(&mut data.pic);
+
+ // OCW2: Set rotate on auto EOI.
+ data.pic.write(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]);
+
+ let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
+ assert!(!secondary_pic.rotate_on_auto_eoi);
+ }
+
+ /// Test basic auto EOI case.
+ #[test]
+ fn auto_eoi() {
+ let mut data = set_up();
+
+ icw_init_both(&mut data.pic);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
+
+ // Check that IRQ is requesting acknowledgment.
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, (1 << 4));
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, (1 << 2));
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
+
+ // 0x70 is interrupt base on secondary PIC. 0x70 + 4 is the interrupt entry number.
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
+
+ // Check that IRQ is acknowledged and EOI is automatically done.
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
+ }
+
+ /// Test with fully-nested mode on. When the secondary PIC has an IRQ in service, it shouldn't
+ /// be locked out by the primary's priority logic.
+ /// This means that the secondary should still be able to request a higher-priority IRQ.
+ /// Auto EOI is off in order to keep IRQ in service.
+ #[test]
+ fn fully_nested_mode_on() {
+ let mut data = set_up();
+
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ // Request higher-priority IRQ on secondary.
+ data.pic.service_irq(/*irq=*/ 8, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
+
+ // Check that IRQ is ack'd and EOI is automatically done.
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
+ assert_eq!(
+ data.pic.pics[PicSelect::Secondary as usize].isr,
+ (1 << 4) + (1 << 0)
+ );
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
+ }
+
+ /// Test with fully-nested mode off. When the secondary PIC has an IRQ in service, it should
+ /// NOT be able to request another higher-priority IRQ.
+ /// Auto EOI is off in order to keep IRQ in service.
+ #[test]
+ fn fully_nested_mode_off() {
+ let mut data = set_up();
+
+ // ICW4 0x01: No special fully nested mode, no auto EOI.
+ icw_init_both_with_icw4(&mut data.pic, 0x01);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
+
+ data.pic.service_irq(/*irq=*/ 8, /*level=*/ true);
+ // Primary cannot get any IRQ, so this should not provide any interrupt.
+ assert_eq!(data.pic.get_external_interrupt(), None);
+
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 0);
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 4);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
+
+ // 2 EOIs will cause 2 interrupts.
+ // 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]);
+
+ // 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));
+
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
+ }
+
+ /// Write IMR to mask an IRQ. The masked IRQ can't be served until unmasked.
+ #[test]
+ fn mask_irq() {
+ let mut data = set_up();
+
+ 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.service_irq(/*irq=*/ 14, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), None);
+
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 6);
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
+
+ // 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]);
+
+ // Previously-masked interrupt can now be served.
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
+
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 6);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
+ }
+
+ /// Write IMR to mask multiple IRQs. They masked IRQs cannot be served until they're unmasked.
+ /// The highest priority IRQ must be served first, no matter the original order of request.
+ /// (To simplify the test, we won't check irr and isr and so we'll leave auto EOI on.)
+ #[test]
+ fn mask_multiple_irq() {
+ let mut data = set_up();
+ 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.service_irq(/*irq=*/ 14, /*level=*/ true);
+ data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
+ data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
+
+ // Primary cannot get any IRQs since they're all masked.
+ assert_eq!(data.pic.get_external_interrupt(), None);
+
+ // OCW2: Unmask IRQ lines on secondary.
+ data.pic.write(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]);
+
+ // Previously-masked IRQs should now be served in order of priority.
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
+
+ // Unmask all other IRQ lines on primary.
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.write(PIC_PRIMARY_DATA, &[0x00]);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
+ }
+
+ /// Test OCW3 poll (reading irr and isr).
+ #[test]
+ fn ocw3() {
+ let mut data = set_up();
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ // Poplate some data on irr/isr. IRQ4 will be in isr and IRQ5 in irr.
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
+ data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
+
+ // Read primary IRR.
+ data.pic.write(PIC_PRIMARY_COMMAND, &[0x0a]);
+ let mut data_read = [0];
+ data.pic.read(PIC_PRIMARY_COMMAND, &mut data_read);
+ assert_eq!(data_read[0], 1 << 5);
+
+ // Read primary ISR.
+ data.pic.write(PIC_PRIMARY_COMMAND, &[0x0b]);
+ data_read = [0];
+ data.pic.read(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]);
+
+ // Poll command on primary.
+ data.pic.write(PIC_PRIMARY_COMMAND, &[0x0c]);
+ data_read = [0];
+ data.pic.read(PIC_PRIMARY_COMMAND, &mut data_read);
+ assert_eq!(data_read[0], 5);
+ }
+
+ /// Assert on primary PIC's IRQ2 without any IRQ on secondary asserted. This should result in a
+ /// spurious IRQ on secondary.
+ #[test]
+ fn fake_irq_on_primary_irq2() {
+ let mut data = set_up();
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 2, /*level=*/ true);
+ // 0x70 is secondary IRQ base, 7 is for a spurious IRQ.
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 7));
+ }
+
+ /// Raising the same IRQ line twice in edge trigger mode should only send one IRQ request out.
+ #[test]
+ fn edge_trigger_mode() {
+ let mut data = set_up();
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
+ // get_external_interrupt clears the irr so it is possible to request the same IRQ again.
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
+
+ data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
+
+ // 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]);
+ }
+
+ /// Raising the same IRQ line twice in level-triggered mode should send two IRQ requests out.
+ #[test]
+ fn level_trigger_mode() {
+ let mut data = set_up();
+ 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]);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
+ // get_external_interrupt clears the irr so it is possible to request the same IRQ again.
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
+
+ data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
+
+ // 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]);
+ }
+
+ /// Specific EOI command in OCW2.
+ #[test]
+ fn specific_eoi() {
+ let mut data = set_up();
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
+
+ // 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]);
+ 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]);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
+ }
+
+ /// Test rotate on auto EOI.
+ #[test]
+ fn rotate_on_auto_eoi() {
+ let mut data = set_up();
+ icw_init_both(&mut data.pic);
+
+ // OCW3: Clear rotate on auto EOI mode.
+ data.pic.write(PIC_PRIMARY_COMMAND, &[0x00]);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
+
+ // EOI automatically happened. Now priority should not be rotated.
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].imr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].last_irr, 0);
+ 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]);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 5, /*level*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
+
+ // EOI automatically happened, and the priority *should* be rotated.
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
+ }
+
+ /// Test rotate on specific (non-auto) EOI.
+ #[test]
+ fn rotate_on_specific_eoi() {
+ let mut data = set_up();
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
+
+ // 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]);
+
+ 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]);
+
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
+ }
+
+ /// Test rotate on non-specific EOI.
+ #[test]
+ fn rotate_non_specific_eoi() {
+ let mut data = set_up();
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
+
+ // Rotate on non-specific EOI.
+ data.pic.write(PIC_PRIMARY_COMMAND, &[0xa0]);
+
+ // The EOI should have cleared isr.
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
+ }
+
+ /// Verify that no-op doesn't change state.
+ #[test]
+ fn no_op_ocw2() {
+ let mut data = set_up();
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
+ data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
+
+ let orig = data.pic.pics[PicSelect::Primary as usize].clone();
+
+ // Run a no-op.
+ data.pic.write(PIC_PRIMARY_COMMAND, &[0x40]);
+
+ // Nothing should have changed.
+ assert_eq!(orig, data.pic.pics[PicSelect::Primary as usize]);
+ }
+
+ /// Tests cascade IRQ that happens on secondary PIC.
+ #[test]
+ fn cascade_irq() {
+ let mut data = set_up();
+ icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
+
+ // TODO(mutexlox): Verify APIC interaction when it is implemented.
+ data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
+
+ assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 4);
+
+ assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
+
+ // Check that the IRQ is now acknowledged after get_external_interrupt().
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 4);
+
+ // OCW2: Two non-specific EOIs to primary rather than secondary.
+ // We need two non-specific EOIs:
+ // - 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]);
+ // Rotate non-specific EOI.
+ data.pic.write(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);
+ assert_eq!(data.pic.pics[PicSelect::Secondary as usize].priority_add, 5);
+ }
+}
diff --git a/devices/src/pit.rs b/devices/src/pit.rs
new file mode 100644
index 0000000..63f31f5
--- /dev/null
+++ b/devices/src/pit.rs
@@ -0,0 +1,1280 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Based heavily on GCE VMM's pit.cc.
+
+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 bit_field::BitField1;
+use bit_field::*;
+use sync::Mutex;
+use sys_util::{error, warn, Error as SysError, EventFd, Fd, PollContext, PollToken};
+
+#[cfg(not(test))]
+use sys_util::Clock;
+#[cfg(test)]
+use sys_util::FakeClock as Clock;
+
+#[cfg(test)]
+use sys_util::FakeTimerFd as TimerFd;
+#[cfg(not(test))]
+use sys_util::TimerFd;
+
+use crate::BusDevice;
+
+// Bitmask for areas of standard (non-ReadBack) Control Word Format. Constant
+// names are kept the same as Intel PIT data sheet.
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+enum CommandBit {
+ CommandBCD = 0x01, // Binary/BCD input. x86 only uses binary mode.
+ CommandMode = 0x0e, // Operating Mode (mode 0-5).
+ CommandRW = 0x30, // Access mode: Choose high/low byte(s) to Read/Write.
+ CommandSC = 0xc0, // Select Counter/Read-back command.
+}
+
+// Selects which counter is to be used by the associated command in the lower
+// six bits of the byte. However, if 0xc0 is specified, it indicates that the
+// command is a "Read-Back", which can latch count and/or status of the
+// counters selected in the lower bits. See Intel 8254 data sheet for details.
+#[allow(dead_code)]
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+enum CommandCounter {
+ CommandCounter0 = 0x00, // Select counter 0.
+ CommandCounter1 = 0x40, // Select counter 1.
+ CommandCounter2 = 0x80, // Select counter 2.
+ CommandReadBack = 0xc0, // Execute Read-Back.
+}
+
+// Used for both CommandRW and ReadBackAccess.
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+enum CommandAccess {
+ CommandLatch = 0x00, // Latch specified counter.
+ CommandRWLeast = 0x10, // Read/Write least significant byte.
+ CommandRWMost = 0x20, // Read/Write most significant byte.
+ CommandRWBoth = 0x30, // Read/Write both bytes.
+}
+
+// Used for both CommandMode and ReadBackMode.
+// For mode 2 & 3, bit 3 is don't care bit (does not matter to be 0 or 1) but
+// per 8254 spec, should be 0 to insure compatibility with future Intel
+// products.
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+enum CommandMode {
+ // NOTE: No h/w modes are currently implemented.
+ CommandInterrupt = 0x00, // Mode 0, interrupt on terminal count.
+ CommandHWOneShot = 0x02, // Mode 1, h/w re-triggerable one-shot.
+ CommandRateGen = 0x04, // Mode 2, rate generator.
+ CommandSquareWaveGen = 0x06, // Mode 3, square wave generator.
+ CommandSWStrobe = 0x08, // Mode 4, s/w triggered strobe.
+ CommandHWStrobe = 0x0a, // Mode 5, h/w triggered strobe.
+}
+
+// Bitmask for the latch portion of the ReadBack command.
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+#[rustfmt::skip] // rustfmt mangles comment indentation for trailing line comments.
+enum CommandReadBackLatch {
+ CommandRBLatchBits = 0x30, // Mask bits that determine latching.
+ CommandRBLatchBoth = 0x00, // Latch both count and status. This should
+ // never happen in device, since bit 4 and 5 in
+ // read back command are inverted.
+ CommandRBLatchCount = 0x10, // Latch count.
+ CommandRBLatchStatus = 0x20, // Latch status.
+}
+
+// Bitmask for the counter portion of the ReadBack command.
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+enum CommandReadBackCounters {
+ //CommandRBCounters = 0x0e, // Counters for which to provide ReadBack info.
+ CommandRBCounter2 = 0x08,
+ CommandRBCounter1 = 0x04,
+ CommandRBCounter0 = 0x02,
+}
+
+// Bitmask for the ReadBack status command.
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+#[rustfmt::skip] // rustfmt mangles comment indentation for last line of this enum.
+enum ReadBackData {
+ // Output format for ReadBack command.
+ ReadBackOutput = 0x80, // Output pin status.
+ ReadBackNullCount = 0x40, // Whether counter has value.
+ // ReadBackAccess, ReadBackMode, and ReadBackBCD intentionally omitted.
+}
+
+// I/O Port mappings in I/O bus.
+#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
+enum PortIOSpace {
+ PortCounter0Data = 0x40, // Read/write.
+ PortCounter1Data = 0x41, // Read/write.
+ PortCounter2Data = 0x42, // Read/write.
+ PortCommand = 0x43, // Write only.
+ PortSpeaker = 0x61, // Read/write.
+}
+
+#[bitfield]
+#[derive(Clone, Copy, PartialEq)]
+pub struct SpeakerPortFields {
+ // This field is documented in the chipset spec as NMI status and control
+ // register. Bits 2, 3, 6, 7 and low level hardware bits that need no
+ // emulation for virtualized environments. We call it speaker port because
+ // kvm, qemu, linux, and plan9 still call it speaker port, even though it
+ // has these other uses and is called something differently in the spec.
+ gate: BitField1,
+ speaker_on: BitField1,
+ pic_serr: BitField1,
+ iochk_enable: BitField1,
+ // This value changes as part of the refresh frequency of the board for
+ // piix4, this is about 1/15us.
+ refresh_clock: BitField1,
+ output: BitField1,
+ iochk_nmi: BitField1,
+ serr_nmi: BitField1,
+}
+
+// PIT frequency (in Hertz). See http://wiki.osdev.org/pit.
+const FREQUENCY_HZ: u64 = 1193182;
+
+const NUM_OF_COUNTERS: usize = 3;
+
+const NANOS_PER_SEC: u64 = 1_000_000_000;
+
+const MAX_TIMER_FREQ: u32 = 65536;
+
+#[derive(Debug)]
+pub enum PitError {
+ TimerFdCreateError(SysError),
+ /// Creating PollContext failed.
+ CreatePollContext(SysError),
+ /// Error while polling for events.
+ PollError(SysError),
+ /// Error while trying to create worker thread.
+ SpawnThread(IoError),
+ /// Error while creating event FD.
+ CreateEventFd(SysError),
+ /// Error while cloning event FD for worker thread.
+ CloneEventFd(SysError),
+}
+
+impl Display for PitError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::PitError::*;
+
+ match self {
+ TimerFdCreateError(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),
+ SpawnThread(err) => write!(f, "failed to spawn thread: {}", err),
+ CreateEventFd(err) => write!(f, "failed to create event fd: {}", err),
+ CloneEventFd(err) => write!(f, "failed to clone event fd: {}", err),
+ }
+ }
+}
+
+impl std::error::Error for PitError {}
+
+type PitResult<T> = std::result::Result<T, PitError>;
+
+pub struct Pit {
+ // Structs that store each counter's state.
+ counters: Vec<Arc<Mutex<PitCounter>>>,
+ // Worker thread to update counter 0's state asynchronously. Counter 0 needs to send interrupts
+ // when timers expire, so it needs asynchronous updates. All other counters need only update
+ // when queried directly by the guest.
+ worker_thread: Option<thread::JoinHandle<PitResult<()>>>,
+ kill_evt: EventFd,
+}
+
+impl Drop for Pit {
+ fn drop(&mut self) {
+ if let Err(e) = self.kill_evt.write(1) {
+ error!("failed to kill PIT worker threads: {}", e);
+ return;
+ }
+ if let Some(thread) = self.worker_thread.take() {
+ match thread.join() {
+ Ok(r) => {
+ if let Err(e) = r {
+ error!("pit worker thread exited with error: {}", e)
+ }
+ }
+ Err(e) => error!("pit worker thread panicked: {:?}", e),
+ }
+ }
+ }
+}
+
+impl BusDevice for Pit {
+ fn debug_label(&self) -> String {
+ "userspace PIT".to_string()
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ if data.len() != 1 {
+ warn!("Bad write size for Pit: {}", data.len());
+ return;
+ }
+ match PortIOSpace::n(offset 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),
+ }
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ if data.len() != 1 {
+ warn!("Bad read size for Pit: {}", data.len());
+ return;
+ }
+ data[0] = match PortIOSpace::n(offset 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(),
+ // This should function as a no-op, since the specification doesn't allow the
+ // command register to be read. However, software is free to ask for it to
+ // to be read.
+ Some(PortIOSpace::PortCommand) => {
+ warn!("Ignoring read to command reg");
+ 0
+ }
+ Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().read_speaker(),
+ None => {
+ warn!("PIT: bad read from offset {}", offset);
+ return;
+ }
+ };
+ }
+}
+
+impl Pit {
+ pub fn new(interrupt_evt: EventFd, clock: Arc<Mutex<Clock>>) -> PitResult<Pit> {
+ let mut counters = Vec::new();
+ let mut interrupt = Some(interrupt_evt);
+ for i in 0..NUM_OF_COUNTERS {
+ let pit_counter = PitCounter::new(i, interrupt, clock.clone())?;
+ counters.push(Arc::new(Mutex::new(pit_counter)));
+ // pass interrupt IrqFd ONLY to counter 0; the rest do not deliver interrupts.
+ interrupt = None;
+ }
+ // We asssert here because:
+ // (a) this code only gets invoked at VM startup
+ // (b) the assert is very loud and would be easy to notice in tests
+ // (c) if we have the wrong number of counters, something is very wrong with the PIT and it
+ // may not make sense to continue operation.
+ assert_eq!(counters.len(), NUM_OF_COUNTERS);
+ let (self_kill_evt, kill_evt) = EventFd::new()
+ .and_then(|e| Ok((e.try_clone()?, e)))
+ .map_err(PitError::CreateEventFd)?;
+ let mut worker = Worker {
+ pit_counter: counters[0].clone(),
+ fd: Fd(counters[0].lock().timer.as_raw_fd()),
+ };
+ let evt = kill_evt.try_clone().map_err(PitError::CloneEventFd)?;
+ let worker_thread = thread::Builder::new()
+ .name("pit counter worker".to_string())
+ .spawn(move || worker.run(evt))
+ .map_err(PitError::SpawnThread)?;
+ Ok(Pit {
+ counters,
+ worker_thread: Some(worker_thread),
+ kill_evt: self_kill_evt,
+ })
+ }
+
+ fn command_write(&mut self, control_word: u8) {
+ let command: u16 = (control_word & CommandBit::CommandSC as u8).into();
+ let counter_index: usize = (command >> 6).into();
+ if command == (CommandCounter::CommandReadBack as u16) {
+ // ReadBack commands can apply to multiple counters.
+ if (control_word & (CommandReadBackCounters::CommandRBCounter0 as u8)) != 0 {
+ self.counters[0].lock().read_back_command(control_word);
+ }
+ if (control_word & (CommandReadBackCounters::CommandRBCounter1 as u8)) != 0 {
+ self.counters[1].lock().read_back_command(control_word);
+ }
+ if (control_word & (CommandReadBackCounters::CommandRBCounter2 as u8)) != 0 {
+ self.counters[2].lock().read_back_command(control_word);
+ }
+ } else if (control_word & (CommandBit::CommandRW as u8))
+ == (CommandAccess::CommandLatch as u8)
+ {
+ self.counters[counter_index].lock().latch_counter();
+ } else {
+ self.counters[counter_index]
+ .lock()
+ .store_command(control_word);
+ }
+ }
+}
+
+// Each instance of this represents one of the PIT counters. They are used to
+// implement one-shot and repeating timer alarms. An 8254 has three counters.
+struct PitCounter {
+ // EventFd to write when asserting an interrupt.
+ interrupt_evt: Option<EventFd>,
+ // Stores the value with which the counter was initialized. Counters are 16-
+ // bit values with an effective range of 1-65536 (65536 represented by 0).
+ reload_value: u16,
+ // Stores value when latch was called.
+ latched_value: u16,
+ // Stores last command from command register.
+ command: u8,
+ // Stores status from readback command
+ status: u8,
+ // Stores time of starting timer. Used for calculating remaining count, if an alarm is
+ // scheduled.
+ start: Option<Clock>,
+ // Current time.
+ clock: Arc<Mutex<Clock>>,
+ // Time when object was created. Used for a 15us counter.
+ creation_time: Clock,
+ // The number of the counter. The behavior for each counter is slightly different.
+ // Note that once a PitCounter is created, this value should never change.
+ counter_id: usize,
+ // Indicates if the low byte has been written in RWBoth.
+ wrote_low_byte: bool,
+ // Indicates if the low byte has been read in RWBoth.
+ read_low_byte: bool,
+ // Indicates whether counter has been latched.
+ latched: bool,
+ // Indicates whether ReadBack status has been latched.
+ status_latched: bool,
+ // Only should be used for counter 2. See http://wiki.osdev.org/PIT.
+ gate: bool,
+ speaker_on: bool,
+ // The starting value for the counter.
+ count: u32,
+ // Indicates whether the current timer is valid.
+ timer_valid: bool,
+ // Timer to set and receive periodic notifications.
+ timer: TimerFd,
+}
+
+impl Drop for PitCounter {
+ fn drop(&mut self) {
+ if self.timer_valid {
+ // This should not fail - timer.clear() only fails if timerfd_settime fails, which
+ // only happens due to invalid arguments or bad file descriptors. The arguments to
+ // timerfd_settime are constant, so its arguments won't be invalid, and it manages
+ // the file descriptor safely (we don't use the unsafe FromRawFd) so its file
+ // descriptor will be valid.
+ self.timer.clear().unwrap();
+ }
+ }
+}
+
+fn adjust_count(count: u32) -> u32 {
+ // As per spec 0 means max.
+ if count == 0 {
+ MAX_TIMER_FREQ
+ } else {
+ count
+ }
+}
+
+impl PitCounter {
+ fn new(
+ counter_id: usize,
+ interrupt_evt: Option<EventFd>,
+ clock: Arc<Mutex<Clock>>,
+ ) -> PitResult<PitCounter> {
+ #[cfg(not(test))]
+ let timer = TimerFd::new().map_err(PitError::TimerFdCreateError)?;
+ #[cfg(test)]
+ let timer = TimerFd::new(clock.clone());
+ Ok(PitCounter {
+ interrupt_evt,
+ reload_value: 0,
+ latched_value: 0,
+ command: 0,
+ status: 0,
+ start: None,
+ clock: clock.clone(),
+ creation_time: clock.lock().now(),
+ counter_id,
+ wrote_low_byte: false,
+ read_low_byte: false,
+ latched: false,
+ status_latched: false,
+ gate: false,
+ speaker_on: false,
+ // `count` is undefined in real hardware and can't ever be programmed to 0, so we
+ // initialize it to max to prevent a misbehaving guest from triggering a divide by 0.
+ count: MAX_TIMER_FREQ,
+ timer_valid: false,
+ timer,
+ })
+ }
+
+ fn get_access_mode(&self) -> Option<CommandAccess> {
+ CommandAccess::n(self.command & (CommandBit::CommandRW as u8))
+ }
+
+ fn get_command_mode(&self) -> Option<CommandMode> {
+ CommandMode::n(self.command & CommandBit::CommandMode as u8)
+ }
+
+ fn read_counter(&mut self) -> u8 {
+ if self.status_latched {
+ self.status_latched = false;
+ return self.status;
+ };
+ let data_value: u16 = if self.latched {
+ self.latched_value
+ } else {
+ self.get_read_value()
+ };
+
+ let access_mode = self.get_access_mode();
+ // Latch may be true without being indicated by the access mode if
+ // a ReadBack was issued.
+ match (access_mode, self.read_low_byte) {
+ (Some(CommandAccess::CommandRWLeast), _) => {
+ self.latched = false; // Unlatch if only reading the low byte.
+ (data_value & 0xff) as u8
+ }
+ (Some(CommandAccess::CommandRWBoth), false) => {
+ self.read_low_byte = true;
+ (data_value & 0xff) as u8
+ }
+ (Some(CommandAccess::CommandRWBoth), true)
+ | (Some(CommandAccess::CommandRWMost), _) => {
+ self.read_low_byte = false; // Allow for future reads for RWBoth.
+ self.latched = false;
+ (data_value >> 8) as u8
+ }
+ (_, _) => 0, // Default for erroneous call
+ }
+ }
+
+ fn write_counter(&mut self, written_datum: u8) {
+ let access_mode = self.get_access_mode();
+ let datum: u16 = written_datum.into();
+ let mut should_start_timer = true;
+ self.reload_value = match access_mode {
+ Some(CommandAccess::CommandRWLeast) => datum,
+ Some(CommandAccess::CommandRWMost) => datum << 8,
+ Some(CommandAccess::CommandRWBoth) => {
+ // In kCommandRWBoth mode, the first guest write is the low byte and the
+ // the second guest write is the high byte. The timer isn't started
+ // until after the second byte is written.
+ if self.wrote_low_byte {
+ self.wrote_low_byte = false;
+ self.reload_value | (datum << 8)
+ } else {
+ self.wrote_low_byte = true;
+ should_start_timer = false; // Don't start until high byte written.
+ datum
+ }
+ }
+ _ => {
+ should_start_timer = false;
+ self.reload_value
+ }
+ };
+ if should_start_timer {
+ let reload: u32 = self.reload_value.into();
+ self.load_and_start_timer(reload);
+ }
+ }
+
+ fn get_output(&self) -> bool {
+ let ticks_passed = self.get_ticks_passed();
+ let count: u64 = self.count.into();
+ match self.get_command_mode() {
+ Some(CommandMode::CommandInterrupt) => ticks_passed >= count,
+ Some(CommandMode::CommandHWOneShot) => ticks_passed < count,
+ Some(CommandMode::CommandRateGen) => ticks_passed != 0 && ticks_passed % count == 0,
+ Some(CommandMode::CommandSquareWaveGen) => ticks_passed < (count + 1) / 2,
+ Some(CommandMode::CommandSWStrobe) | Some(CommandMode::CommandHWStrobe) => {
+ ticks_passed == count
+ }
+ None => {
+ warn!("Invalid command mode based on command: {:#x}", self.command);
+ false
+ }
+ }
+ }
+
+ fn read_speaker(&self) -> u8 {
+ // Refresh clock is a value independent of the actual
+ // counter that goes up and down approx every 15 us (~66000/s).
+ let us = self
+ .clock
+ .lock()
+ .now()
+ .duration_since(&self.creation_time)
+ .subsec_micros();
+ let refresh_clock = us % 15 == 0;
+ let mut speaker = SpeakerPortFields::new();
+ speaker.set_gate(self.gate.into());
+ speaker.set_speaker_on(self.speaker_on.into());
+ speaker.set_iochk_enable(0);
+ speaker.set_refresh_clock(refresh_clock.into());
+ speaker.set_output(self.get_output().into());
+ speaker.set_iochk_nmi(0);
+ speaker.set_serr_nmi(0);
+ speaker.get(/*offset=*/ 0, /*width=*/ 8) as u8
+ }
+
+ fn write_speaker(&mut self, datum: u8) {
+ let mut speaker = SpeakerPortFields::new();
+ speaker.set(/*offset=*/ 0, /*width=*/ 8, datum.into());
+ let new_gate = speaker.get_gate() != 0;
+ match self.get_command_mode() {
+ Some(CommandMode::CommandInterrupt) | Some(CommandMode::CommandSWStrobe) => (),
+ Some(_) => {
+ if new_gate && !self.gate {
+ self.start = Some(self.clock.lock().now());
+ }
+ }
+ None => {
+ warn!("Invalid command mode based on command {:#x}", self.command);
+ return;
+ }
+ }
+ self.speaker_on = speaker.get_speaker_on() != 0;
+ self.gate = new_gate;
+ }
+
+ fn load_and_start_timer(&mut self, initial_count: u32) {
+ self.count = adjust_count(initial_count);
+ self.start_timer();
+ }
+
+ fn start_timer(&mut self) {
+ self.start = Some(self.clock.lock().now());
+
+ // Counter 0 is the only counter that generates interrupts, so we
+ // don't need to set a timer for the other two counters.
+ if self.counter_id != 0 {
+ return;
+ }
+
+ let timer_len = Duration::from_nanos(u64::from(self.count) * NANOS_PER_SEC / FREQUENCY_HZ);
+
+ let period_ns = match self.get_command_mode() {
+ Some(CommandMode::CommandInterrupt)
+ | Some(CommandMode::CommandHWOneShot)
+ | Some(CommandMode::CommandSWStrobe)
+ | Some(CommandMode::CommandHWStrobe) => Duration::new(0, 0),
+ Some(CommandMode::CommandRateGen) | Some(CommandMode::CommandSquareWaveGen) => {
+ timer_len
+ }
+ // Don't arm timer if invalid mode.
+ None => {
+ // This will still result in start being set to the current time.
+ // Per spec:
+ // A new initial count may be written to a Counter at any time without affecting
+ // the Counter’s programmed Mode in any way. Counting will be affected as
+ // described in the Mode definitions. The new count must follow the programmed
+ // count format
+ // It's unclear whether setting `self.start` in this case is entirely compliant,
+ // but the spec is fairly quiet on expected behavior in error cases, so OSs
+ // shouldn't enter invalid modes in the first place. If they do, and then try to
+ // get out of it by first setting the counter then the command, this behavior will
+ // (perhaps) be minimally surprising, but arguments can be made for other behavior.
+ // It's uncertain if this behavior matches real PIT hardware.
+ warn!("Invalid command mode based on command {:#x}", self.command);
+ return;
+ }
+ };
+
+ self.safe_arm_timer(timer_len, period_ns);
+ self.timer_valid = true;
+ }
+
+ fn read_back_command(&mut self, control_word: u8) {
+ let latch_cmd =
+ CommandReadBackLatch::n(control_word & CommandReadBackLatch::CommandRBLatchBits as u8);
+ match latch_cmd {
+ Some(CommandReadBackLatch::CommandRBLatchCount) => {
+ self.latch_counter();
+ }
+ Some(CommandReadBackLatch::CommandRBLatchStatus) => {
+ self.latch_status();
+ }
+ _ => warn!(
+ "Unexpected ReadBackLatch. control_word: {:#x}",
+ control_word
+ ),
+ };
+ }
+
+ fn latch_counter(&mut self) {
+ if self.latched {
+ return;
+ }
+
+ self.latched_value = self.get_read_value();
+ self.latched = true;
+ self.read_low_byte = false;
+ }
+
+ fn latch_status(&mut self) {
+ // Including BCD here, even though it currently never gets used.
+ self.status = self.command
+ & (CommandBit::CommandRW as u8
+ | CommandBit::CommandMode as u8
+ | CommandBit::CommandBCD as u8);
+ if self.start.is_none() {
+ self.status |= ReadBackData::ReadBackNullCount as u8;
+ }
+ if self.get_output() {
+ self.status |= ReadBackData::ReadBackOutput as u8;
+ }
+ self.status_latched = true;
+ }
+
+ fn store_command(&mut self, datum: u8) {
+ self.command = datum;
+ self.latched = false;
+
+ // If a new RW command is written, cancel the current timer.
+ if self.timer_valid {
+ self.start = None;
+ self.timer_valid = false;
+ // See the comment in the impl of Drop for PitCounter for justification of the unwrap()
+ self.timer.clear().unwrap();
+ }
+
+ self.wrote_low_byte = false;
+ self.read_low_byte = false;
+ }
+
+ fn timer_handler(&mut self) {
+ if let Err(e) = self.timer.wait() {
+ // Under the current timerfd implementation (as of Jan 2019), this failure shouldn't
+ // happen but implementation details may change in the future, and the failure
+ // cases are complex to reason about. Because of this, avoid unwrap().
+ error!("pit: timer wait unexpectedly failed: {}", e);
+ return;
+ }
+ let mode = self.get_command_mode();
+ if mode == Some(CommandMode::CommandRateGen)
+ || mode == Some(CommandMode::CommandSquareWaveGen)
+ {
+ // Reset the start time for timer modes that repeat.
+ self.start = Some(self.clock.lock().now());
+ }
+
+ // For square wave mode, this isn't quite accurate to the spec, but the
+ // difference isn't meaningfully visible to the guest in any important way,
+ // and the code is simpler without the special case.
+ if let Some(interrupt) = &mut self.interrupt_evt {
+ // This is safe because the file descriptor is nonblocking and we're writing 1.
+ interrupt.write(1).unwrap();
+ }
+ }
+
+ fn safe_arm_timer(&mut self, mut due: Duration, period: Duration) {
+ if due == Duration::new(0, 0) {
+ due = Duration::from_nanos(1);
+ }
+
+ if let Err(e) = self.timer.reset(due, Some(period)) {
+ error!("failed to reset timer: {}", e);
+ }
+ }
+
+ fn get_ticks_passed(&self) -> u64 {
+ match &self.start {
+ None => 0,
+ Some(t) => {
+ let dur = self.clock.lock().now().duration_since(t);
+ let dur_ns: u64 = dur.as_secs() * NANOS_PER_SEC + u64::from(dur.subsec_nanos());
+ (dur_ns * FREQUENCY_HZ / NANOS_PER_SEC)
+ }
+ }
+ }
+
+ fn get_read_value(&self) -> u16 {
+ match self.start {
+ None => 0,
+ Some(_) => {
+ let count: u64 = adjust_count(self.reload_value.into()).into();
+ let ticks_passed = self.get_ticks_passed();
+ match self.get_command_mode() {
+ Some(CommandMode::CommandInterrupt)
+ | Some(CommandMode::CommandHWOneShot)
+ | Some(CommandMode::CommandSWStrobe)
+ | Some(CommandMode::CommandHWStrobe) => {
+ if ticks_passed > count {
+ // Some risk of raciness here in that the count may return a value
+ // indicating that the count has expired when the interrupt hasn't
+ // yet been injected.
+ 0
+ } else {
+ ((count - ticks_passed) & 0xFFFF) as u16
+ }
+ }
+ Some(CommandMode::CommandRateGen) => (count - (ticks_passed % count)) as u16,
+ Some(CommandMode::CommandSquareWaveGen) => {
+ (count - ((ticks_passed * 2) % count)) as u16
+ }
+ None => {
+ warn!("Invalid command mode: command = {:#x}", self.command);
+ 0
+ }
+ }
+ }
+ }
+ }
+}
+
+struct Worker {
+ pit_counter: Arc<Mutex<PitCounter>>,
+ fd: Fd,
+}
+
+impl Worker {
+ fn run(&mut self, kill_evt: EventFd) -> PitResult<()> {
+ #[derive(PollToken)]
+ enum Token {
+ // The timer expired.
+ TimerExpire,
+ // The parent thread requested an exit.
+ Kill,
+ }
+
+ let poll_ctx: PollContext<Token> = PollContext::new()
+ .and_then(|pc| pc.add(&self.fd, Token::TimerExpire).and(Ok(pc)))
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ .map_err(PitError::CreatePollContext)?;
+
+ loop {
+ let events = poll_ctx.wait().map_err(PitError::PollError)?;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::TimerExpire => {
+ let mut pit = self.pit_counter.lock();
+ pit.timer_handler();
+ }
+ Token::Kill => return Ok(()),
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ struct TestData {
+ pit: Pit,
+ irqfd: EventFd,
+ clock: Arc<Mutex<Clock>>,
+ }
+
+ /// 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])
+ }
+
+ /// 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])
+ }
+
+ /// Utility method for writing to a counter.
+ fn write_counter(pit: &mut Pit, counter_idx: usize, data: u16, access_mode: CommandAccess) {
+ let port = match counter_idx {
+ 0 => PortIOSpace::PortCounter0Data,
+ 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]);
+ }
+ if access_mode == CommandAccess::CommandRWMost
+ || access_mode == CommandAccess::CommandRWBoth
+ {
+ pit.write(port, &[(data >> 8) as u8]);
+ }
+ }
+
+ /// Utility method for reading a counter. Check if the read value matches expected_value.
+ fn read_counter(pit: &mut Pit, counter_idx: usize, expected: u16, access_mode: CommandAccess) {
+ let port = match counter_idx {
+ 0 => PortIOSpace::PortCounter0Data,
+ 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);
+ result = buffer[0].into();
+ }
+ if access_mode == CommandAccess::CommandRWMost
+ || access_mode == CommandAccess::CommandRWBoth
+ {
+ let mut buffer = [0];
+ pit.read(port, &mut buffer);
+ result |= u16::from(buffer[0]) << 8;
+ }
+ assert_eq!(result, expected);
+ }
+
+ fn set_up() -> TestData {
+ let irqfd = EventFd::new().unwrap();
+ let clock = Arc::new(Mutex::new(Clock::new()));
+ TestData {
+ pit: Pit::new(irqfd.try_clone().unwrap(), clock.clone()).unwrap(),
+ irqfd,
+ clock,
+ }
+ }
+
+ fn advance_by_tick(data: &mut TestData) {
+ advance_by_ticks(data, 1);
+ }
+
+ fn advance_by_ticks(data: &mut TestData, ticks: u64) {
+ println!(
+ "Advancing by {:#x} ticks ({} ns)",
+ ticks,
+ (NANOS_PER_SEC * ticks) / FREQUENCY_HZ + 1
+ );
+ let mut lock = data.clock.lock();
+ lock.add_ns((NANOS_PER_SEC * ticks) / FREQUENCY_HZ + 1);
+ }
+
+ /// Tests the ability to write a command and data and read the data back using latch.
+ #[test]
+ fn write_and_latch() {
+ let mut data = set_up();
+ let both_interrupt =
+ CommandAccess::CommandRWBoth as u8 | CommandMode::CommandInterrupt as u8;
+ // Issue a command to write both digits of counter 0 in interrupt mode.
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8 | both_interrupt,
+ );
+ write_counter(&mut data.pit, 0, 24, CommandAccess::CommandRWBoth);
+ // Advance time by one tick -- value read back should decrease.
+ advance_by_tick(&mut data);
+
+ // Latch and read back the value written.
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
+ );
+ // Advance again after latching to verify that value read back doesn't change.
+ advance_by_tick(&mut data);
+ read_counter(&mut data.pit, 0, 23, CommandAccess::CommandRWBoth);
+
+ // Repeat with counter 1.
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter1 as u8 | both_interrupt,
+ );
+ write_counter(&mut data.pit, 1, 314, CommandAccess::CommandRWBoth);
+ advance_by_tick(&mut data);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter1 as u8 | CommandAccess::CommandLatch as u8,
+ );
+ advance_by_tick(&mut data);
+ read_counter(&mut data.pit, 1, 313, CommandAccess::CommandRWBoth);
+
+ // Repeat with counter 2.
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter2 as u8 | both_interrupt,
+ );
+ write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
+ advance_by_tick(&mut data);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter2 as u8 | CommandAccess::CommandLatch as u8,
+ );
+ advance_by_tick(&mut data);
+ read_counter(&mut data.pit, 2, 0xfffe, CommandAccess::CommandRWBoth);
+ }
+
+ /// Tests the ability to read only the least significant byte.
+ #[test]
+ fn write_and_read_least() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWLeast as u8
+ | CommandMode::CommandInterrupt as u8,
+ );
+ write_counter(&mut data.pit, 0, 0x3424, CommandAccess::CommandRWLeast);
+ read_counter(&mut data.pit, 0, 0x0024, CommandAccess::CommandRWLeast);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
+ );
+ advance_by_tick(&mut data);
+ read_counter(&mut data.pit, 0, 0x0024, CommandAccess::CommandRWLeast);
+ }
+
+ /// Tests the ability to read only the most significant byte.
+ #[test]
+ fn write_and_read_most() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWMost as u8
+ | CommandMode::CommandInterrupt as u8,
+ );
+ write_counter(&mut data.pit, 0, 0x3424, CommandAccess::CommandRWMost);
+ read_counter(&mut data.pit, 0, 0x3400, CommandAccess::CommandRWMost);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
+ );
+ advance_by_tick(&mut data);
+ read_counter(&mut data.pit, 0, 0x3400, CommandAccess::CommandRWMost);
+ }
+
+ /// Tests that reading the command register does nothing.
+ #[test]
+ fn read_command() {
+ let mut data = set_up();
+ let mut buf = [0];
+ data.pit.read(PortIOSpace::PortCommand as u64, &mut buf);
+ assert_eq!(buf, [0]);
+ }
+
+ /// Tests that latching prevents the read time from actually advancing.
+ #[test]
+ fn test_timed_latch() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandInterrupt as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
+ );
+ data.clock.lock().add_ns(25_000_000);
+ // The counter should ignore this second latch.
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
+ );
+ read_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
+ // It should, however, store the count for this latch.
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
+ );
+ read_counter(
+ &mut data.pit,
+ 0,
+ 0xffff - ((25_000_000 * FREQUENCY_HZ) / NANOS_PER_SEC) as u16,
+ CommandAccess::CommandRWBoth,
+ );
+ }
+
+ /// Tests Mode 0 (Interrupt on terminal count); checks whether IRQ has been asserted.
+ #[test]
+ fn interrupt_mode() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandInterrupt as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
+ // Advance clock enough to trigger interrupt.
+ advance_by_ticks(&mut data, 0xffff);
+ assert_eq!(data.irqfd.read().unwrap(), 1);
+ }
+
+ /// Tests that Rate Generator mode (mode 2) handls the interrupt properly when the timer
+ /// expires and that it resets the timer properly.
+ #[test]
+ fn rate_gen_mode() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandRateGen as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
+ // Repatedly advance clock and expect interrupt.
+ advance_by_ticks(&mut data, 0xffff);
+ assert_eq!(data.irqfd.read().unwrap(), 1);
+
+ // Repatedly advance clock and expect interrupt.
+ advance_by_ticks(&mut data, 0xffff);
+ assert_eq!(data.irqfd.read().unwrap(), 1);
+
+ // Repatedly advance clock and expect interrupt.
+ advance_by_ticks(&mut data, 0xffff);
+ assert_eq!(data.irqfd.read().unwrap(), 1);
+ }
+
+ /// Tests that square wave mode advances the counter correctly.
+ #[test]
+ fn square_wave_counter_read() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandSquareWaveGen as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
+
+ advance_by_ticks(&mut data, 10_000);
+ read_counter(
+ &mut data.pit,
+ 0,
+ 0xffff - 10_000 * 2,
+ CommandAccess::CommandRWBoth,
+ );
+ }
+
+ /// Tests that rategen mode updates the counter correctly.
+ #[test]
+ fn rate_gen_counter_read() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandRateGen as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
+
+ advance_by_ticks(&mut data, 10_000);
+ read_counter(
+ &mut data.pit,
+ 0,
+ 0xffff - 10_000,
+ CommandAccess::CommandRWBoth,
+ );
+ }
+
+ /// Tests that interrupt counter mode updates the counter correctly.
+ #[test]
+ fn interrupt_counter_read() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandInterrupt as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
+
+ advance_by_ticks(&mut data, 10_000);
+ read_counter(
+ &mut data.pit,
+ 0,
+ 0xffff - 10_000,
+ CommandAccess::CommandRWBoth,
+ );
+
+ advance_by_ticks(&mut data, (3 * FREQUENCY_HZ).into());
+ read_counter(&mut data.pit, 0, 0, CommandAccess::CommandRWBoth);
+ }
+
+ /// Tests that ReadBack count works properly for `low` access mode.
+ #[test]
+ fn read_back_count_access_low() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWLeast as u8
+ | CommandMode::CommandInterrupt as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWLeast);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandReadBack as u8
+ | CommandReadBackLatch::CommandRBLatchCount as u8
+ | CommandReadBackCounters::CommandRBCounter0 as u8,
+ );
+
+ // Advance 100 ticks and verify that low byte of counter is appropriately updated.
+ advance_by_ticks(&mut data, 100);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandReadBack as u8
+ | CommandReadBackLatch::CommandRBLatchCount as u8
+ | CommandReadBackCounters::CommandRBCounter0 as u8,
+ );
+ read_counter(&mut data.pit, 0, 0x00ff, CommandAccess::CommandRWLeast);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandReadBack as u8
+ | CommandReadBackLatch::CommandRBLatchCount as u8
+ | CommandReadBackCounters::CommandRBCounter0 as u8,
+ );
+ read_counter(
+ &mut data.pit,
+ 0,
+ (0xffff - 100) & 0x00ff,
+ CommandAccess::CommandRWLeast,
+ );
+ }
+
+ /// Tests that ReadBack count works properly for `high` access mode.
+ #[test]
+ fn read_back_count_access_high() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWMost as u8
+ | CommandMode::CommandInterrupt as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWLeast);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandReadBack as u8
+ | CommandReadBackLatch::CommandRBLatchCount as u8
+ | CommandReadBackCounters::CommandRBCounter0 as u8,
+ );
+
+ // Advance 100 ticks and verify that low byte of counter is appropriately updated.
+ advance_by_ticks(&mut data, 512);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandReadBack as u8
+ | CommandReadBackLatch::CommandRBLatchCount as u8
+ | CommandReadBackCounters::CommandRBCounter0 as u8,
+ );
+ read_counter(&mut data.pit, 0, 0xff00, CommandAccess::CommandRWMost);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandReadBack as u8
+ | CommandReadBackLatch::CommandRBLatchCount as u8
+ | CommandReadBackCounters::CommandRBCounter0 as u8,
+ );
+ read_counter(
+ &mut data.pit,
+ 0,
+ (0xffff - 512) & 0xff00,
+ CommandAccess::CommandRWMost,
+ );
+ }
+
+ /// Tests that ReadBack status returns the expected values.
+ #[test]
+ fn read_back_status() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter0 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandSWStrobe as u8,
+ );
+ write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandReadBack as u8
+ | CommandReadBackLatch::CommandRBLatchStatus as u8
+ | CommandReadBackCounters::CommandRBCounter0 as u8,
+ );
+
+ read_counter(
+ &mut data.pit,
+ 0,
+ CommandAccess::CommandRWBoth as u16 | CommandMode::CommandSWStrobe as u16,
+ CommandAccess::CommandRWLeast,
+ );
+ }
+
+ #[test]
+ fn speaker_square_wave() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter2 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandSquareWaveGen as u8,
+ );
+ write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
+
+ advance_by_ticks(&mut data, 128);
+ read_counter(
+ &mut data.pit,
+ 2,
+ 0xffff - 128 * 2,
+ CommandAccess::CommandRWBoth,
+ );
+ }
+
+ #[test]
+ fn speaker_rate_gen() {
+ let mut data = set_up();
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter2 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandRateGen as u8,
+ );
+ write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
+
+ // In Rate Gen mode, the counter should start over when the gate is
+ // set to high using SpeakerWrite.
+ advance_by_ticks(&mut data, 128);
+ read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
+
+ write_speaker(&mut data.pit, 0x1);
+ advance_by_ticks(&mut data, 128);
+ read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
+ }
+
+ #[test]
+ fn speaker_interrupt() {
+ let mut data = set_up();
+
+ write_command(
+ &mut data.pit,
+ CommandCounter::CommandCounter2 as u8
+ | CommandAccess::CommandRWBoth as u8
+ | CommandMode::CommandInterrupt as u8,
+ );
+ write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
+
+ // In Interrupt mode, the counter should NOT start over when the gate is
+ // set to high using SpeakerWrite.
+ advance_by_ticks(&mut data, 128);
+ read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
+
+ write_speaker(&mut data.pit, 0x1);
+ advance_by_ticks(&mut data, 128);
+ read_counter(&mut data.pit, 2, 0xffff - 256, CommandAccess::CommandRWBoth);
+ }
+
+ /// Verify that invalid reads and writes do not cause crashes.
+ #[test]
+ fn invalid_write_and_read() {
+ let mut data = set_up();
+ data.pit.write(0x44, &[0]);
+ data.pit.read(0x55, &mut [0]);
+ }
+}
diff --git a/devices/src/pl030.rs b/devices/src/pl030.rs
new file mode 100644
index 0000000..b5338ac
--- /dev/null
+++ b/devices/src/pl030.rs
@@ -0,0 +1,145 @@
+// Copyright 2018 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::time::{SystemTime, UNIX_EPOCH};
+use sys_util::{warn, EventFd};
+
+use crate::BusDevice;
+
+// Register offsets
+// Data register
+const RTCDR: u64 = 0x0;
+// Match register
+const RTCMR: u64 = 0x4;
+// Interrupt status register
+const RTCSTAT: u64 = 0x8;
+// Interrupt clear register
+const RTCEOI: u64 = 0x8;
+// Counter load register
+const RTCLR: u64 = 0xC;
+// Counter register
+const RTCCR: u64 = 0x10;
+
+// A single 4K page is mapped for this device
+pub const PL030_AMBA_IOMEM_SIZE: u64 = 0x1000;
+
+// AMBA id registers are at the end of the allocated memory space
+const AMBA_ID_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x20;
+const AMBA_MASK_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x28;
+
+// This is the AMBA id for this device
+pub const PL030_AMBA_ID: u32 = 0x00041030;
+pub const PL030_AMBA_MASK: u32 = 0x000FFFFF;
+
+/// An emulated ARM pl030 RTC
+pub struct Pl030 {
+ // EventFD to be used to interrupt the guest for an alarm event
+ alarm_evt: EventFd,
+
+ // This is the delta we subtract from current time to get the
+ // counter value
+ counter_delta_time: u32,
+
+ // This is the value that triggers an alarm interrupt when it
+ // matches with the rtc time
+ match_value: u32,
+
+ // status flag to keep track of whether the interrupt is cleared
+ // or not
+ interrupt_active: bool,
+}
+
+fn get_epoch_time() -> u32 {
+ let epoch_time = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .expect("SystemTime::duration_since failed");
+ epoch_time.as_secs() as u32
+}
+
+impl Pl030 {
+ /// Constructs a Pl030 device
+ pub fn new(evt: EventFd) -> Pl030 {
+ Pl030 {
+ alarm_evt: evt,
+ counter_delta_time: get_epoch_time(),
+ match_value: 0,
+ interrupt_active: false,
+ }
+ }
+}
+
+impl BusDevice for Pl030 {
+ fn debug_label(&self) -> String {
+ "Pl030".to_owned()
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ if data.len() != 4 {
+ 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 {
+ RTCDR => {
+ warn!("invalid write to read-only RTCDR register");
+ }
+ RTCMR => {
+ self.match_value = reg_val;
+ // TODO(sonnyrao): here we need to set up a timer for
+ // when host time equals the value written here and
+ // fire the interrupt
+ warn!("Not implemented: VM tried to set an RTC alarm");
+ }
+ RTCEOI => {
+ if reg_val == 0 {
+ self.interrupt_active = false;
+ } else {
+ self.alarm_evt.write(1).unwrap();
+ self.interrupt_active = true;
+ }
+ }
+ RTCLR => {
+ // TODO(sonnyrao): if we ever need to let the VM set it's own time
+ // then we'll need to keep track of the delta between
+ // the rtc time it sets and the host's rtc time and
+ // record that here
+ warn!("Not implemented: VM tried to set the RTC");
+ }
+ RTCCR => {
+ self.counter_delta_time = get_epoch_time();
+ }
+ o => panic!("pl030: bad write offset {}", o),
+ }
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ if data.len() != 4 {
+ warn!("bad read size: {} for pl030", data.len());
+ return;
+ }
+
+ let reg_content: u32 = match offset {
+ RTCDR => get_epoch_time(),
+ RTCMR => self.match_value,
+ RTCSTAT => self.interrupt_active as u32,
+ RTCLR => {
+ warn!("invalid read of RTCLR register");
+ 0
+ }
+ RTCCR => get_epoch_time() - self.counter_delta_time,
+ AMBA_ID_OFFSET => PL030_AMBA_ID,
+ AMBA_MASK_OFFSET => PL030_AMBA_MASK,
+
+ o => panic!("pl030: bad read offset {}", 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;
+ }
+}
diff --git a/devices/src/proxy.rs b/devices/src/proxy.rs
new file mode 100644
index 0000000..dc64212
--- /dev/null
+++ b/devices/src/proxy.rs
@@ -0,0 +1,251 @@
+// Copyright 2017 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.
+
+//! Runs hardware devices in child processes.
+
+use std::fmt::{self, Display};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::process;
+use std::time::Duration;
+use std::{self, io};
+
+use io_jail::{self, Minijail};
+use libc::pid_t;
+use msg_socket::{MsgOnSocket, MsgReceiver, MsgSender, MsgSocket};
+use sys_util::{error, net::UnixSeqpacket};
+
+use crate::BusDevice;
+
+/// Errors for proxy devices.
+#[derive(Debug)]
+pub enum Error {
+ ForkingJail(io_jail::Error),
+ Io(io::Error),
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ ForkingJail(e) => write!(f, "Failed to fork jail process: {}", e),
+ Io(e) => write!(f, "IO error configuring proxy device {}.", e),
+ }
+ }
+}
+
+const SOCKET_TIMEOUT_MS: u64 = 2000;
+
+#[derive(MsgOnSocket)]
+enum Command {
+ Read {
+ len: u32,
+ offset: u64,
+ },
+ Write {
+ len: u32,
+ offset: u64,
+ data: [u8; 8],
+ },
+ ReadConfig(u32),
+ WriteConfig {
+ reg_idx: u32,
+ offset: u32,
+ len: u32,
+ data: [u8; 4],
+ },
+ Shutdown,
+}
+
+#[derive(MsgOnSocket)]
+enum CommandResult {
+ Ok,
+ ReadResult([u8; 8]),
+ ReadConfigResult(u32),
+}
+
+fn child_proc(sock: UnixSeqpacket, device: &mut dyn BusDevice) {
+ let mut running = true;
+ let sock = MsgSocket::<CommandResult, Command>::new(sock);
+
+ while running {
+ let cmd = match sock.recv() {
+ Ok(cmd) => cmd,
+ Err(err) => {
+ error!("child device process failed recv: {}", err);
+ break;
+ }
+ };
+
+ let res = match cmd {
+ Command::Read { len, offset } => {
+ let mut buffer = [0u8; 8];
+ device.read(offset, &mut buffer[0..len as usize]);
+ sock.send(&CommandResult::ReadResult(buffer))
+ }
+ Command::Write { len, offset, data } => {
+ let len = len as usize;
+ device.write(offset, &data[0..len]);
+ sock.send(&CommandResult::Ok)
+ }
+ Command::ReadConfig(idx) => {
+ let val = device.config_register_read(idx as usize);
+ sock.send(&CommandResult::ReadConfigResult(val))
+ }
+ Command::WriteConfig {
+ reg_idx,
+ offset,
+ len,
+ data,
+ } => {
+ let len = len as usize;
+ device.config_register_write(reg_idx as usize, offset as u64, &data[0..len]);
+ sock.send(&CommandResult::Ok)
+ }
+ Command::Shutdown => {
+ running = false;
+ sock.send(&CommandResult::Ok)
+ }
+ };
+ if let Err(e) = res {
+ error!("child device process failed send: {}", e);
+ }
+ }
+}
+
+/// Wraps an inner `BusDevice` that is run inside a child process via fork.
+///
+/// Because forks are very unfriendly to destructors and all memory mappings and file descriptors
+/// are inherited, this should be used as early as possible in the main process.
+pub struct ProxyDevice {
+ sock: MsgSocket<Command, CommandResult>,
+ pid: pid_t,
+ debug_label: String,
+}
+
+impl ProxyDevice {
+ /// Takes the given device and isolates it into another process via fork before returning.
+ ///
+ /// The forked process will automatically be terminated when this is dropped, so be sure to keep
+ /// a reference.
+ ///
+ /// # Arguments
+ /// * `device` - The device to isolate to another process.
+ /// * `keep_fds` - 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>,
+ ) -> 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());
+ // 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)? {
+ 0 => {
+ device.on_sandboxed();
+ child_proc(child_sock, &mut device);
+ // ! Never returns
+ process::exit(0);
+ }
+ p => p,
+ }
+ };
+
+ parent_sock
+ .set_write_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
+ .map_err(Error::Io)?;
+ parent_sock
+ .set_read_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
+ .map_err(Error::Io)?;
+ Ok(ProxyDevice {
+ sock: MsgSocket::<Command, CommandResult>::new(parent_sock),
+ pid,
+ debug_label,
+ })
+ }
+
+ pub fn pid(&self) -> pid_t {
+ self.pid
+ }
+
+ fn sync_send(&self, cmd: Command) -> Option<CommandResult> {
+ let res = self.sock.send(&cmd);
+ if let Err(e) = res {
+ error!(
+ "failed write to child device process {}: {}",
+ self.debug_label, e,
+ );
+ };
+ match self.sock.recv() {
+ Err(e) => {
+ error!(
+ "failed read from child device process {}: {}",
+ self.debug_label, e,
+ );
+ None
+ }
+ Ok(r) => Some(r),
+ }
+ }
+}
+
+impl BusDevice for ProxyDevice {
+ fn debug_label(&self) -> String {
+ self.debug_label.clone()
+ }
+
+ fn config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
+ let len = data.len() as u32;
+ let mut buffer = [0u8; 4];
+ buffer[0..data.len()].clone_from_slice(data);
+ let reg_idx = reg_idx as u32;
+ let offset = offset as u32;
+ self.sync_send(Command::WriteConfig {
+ reg_idx,
+ offset,
+ len,
+ data: buffer,
+ });
+ }
+
+ fn config_register_read(&self, reg_idx: usize) -> u32 {
+ let res = self.sync_send(Command::ReadConfig(reg_idx as u32));
+ if let Some(CommandResult::ReadConfigResult(val)) = res {
+ val
+ } else {
+ 0
+ }
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ let len = data.len() as u32;
+ if let Some(CommandResult::ReadResult(buffer)) =
+ self.sync_send(Command::Read { len, offset })
+ {
+ let len = data.len();
+ data.clone_from_slice(&buffer[0..len]);
+ }
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ let mut buffer = [0u8; 8];
+ let len = data.len() as u32;
+ buffer[0..data.len()].clone_from_slice(data);
+ self.sync_send(Command::Write {
+ len,
+ offset,
+ data: buffer,
+ });
+ }
+}
+
+impl Drop for ProxyDevice {
+ fn drop(&mut self) {
+ self.sync_send(Command::Shutdown);
+ }
+}
diff --git a/devices/src/register_space/mod.rs b/devices/src/register_space/mod.rs
new file mode 100644
index 0000000..58d35e3
--- /dev/null
+++ b/devices/src/register_space/mod.rs
@@ -0,0 +1,10 @@
+// Copyright 2018 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.
+
+#[macro_use]
+mod register;
+mod register_space;
+
+pub use self::register::*;
+pub use self::register_space::*;
diff --git a/devices/src/register_space/register.rs b/devices/src/register_space/register.rs
new file mode 100644
index 0000000..268b945
--- /dev/null
+++ b/devices/src/register_space/register.rs
@@ -0,0 +1,589 @@
+// Copyright 2018 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;
+use std::boxed::Box;
+use std::cmp::{max, min, Ord, Ordering, PartialOrd};
+use std::mem::size_of;
+use std::sync::{Arc, MutexGuard};
+use sync::Mutex;
+
+use data_model::DataInit;
+use sys_util::error;
+
+/// Type of offset in the register space.
+pub type RegisterOffset = u64;
+
+/// This represents a range of memory in the register space starting.
+/// Both from and to are inclusive.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct RegisterRange {
+ pub from: RegisterOffset,
+ pub to: RegisterOffset,
+}
+
+impl Ord for RegisterRange {
+ fn cmp(&self, other: &RegisterRange) -> Ordering {
+ self.from.cmp(&other.from)
+ }
+}
+
+impl PartialOrd for RegisterRange {
+ fn partial_cmp(&self, other: &RegisterRange) -> Option<Ordering> {
+ self.from.partial_cmp(&other.from)
+ }
+}
+
+impl RegisterRange {
+ /// Return true if those range overlaps.
+ pub fn overlap_with(&self, other: &RegisterRange) -> bool {
+ !(self.from > other.to || self.to < other.from)
+ }
+
+ /// Get the overlapping part of two RegisterRange.
+ /// Return is Option(overlap_from, overlap_to).
+ /// For example, (4,7).overlap_range(5, 8) will be Some(5, 7).
+ pub fn overlap_range(&self, other: &RegisterRange) -> Option<RegisterRange> {
+ if !self.overlap_with(other) {
+ return None;
+ }
+ Some(RegisterRange {
+ from: max(self.from, other.from),
+ to: min(self.to, other.to),
+ })
+ }
+}
+
+/// RegisterValue trait should be satisfied by register value types.
+pub trait RegisterValue:
+ 'static
+ + Into<u64>
+ + Clone
+ + DataInit
+ + std::ops::BitOr<Self, Output = Self>
+ + std::ops::BitAnd<Self, Output = Self>
+ + std::ops::Not<Output = Self>
+ + std::fmt::LowerHex
+{
+ // Get byte of the offset.
+ fn get_byte(&self, offset: usize) -> u8 {
+ let val: u64 = self.clone().into();
+ (val >> (offset * 8)) as u8
+ }
+ // Set masked bits.
+ fn set_bits(&mut self, mask: Self) {
+ *self = *self | mask;
+ }
+ // Clear masked bits.
+ fn clear_bits(&mut self, mask: Self) {
+ *self = *self & (!mask);
+ }
+}
+impl RegisterValue for u8 {}
+impl RegisterValue for u16 {}
+impl RegisterValue for u32 {}
+impl RegisterValue for u64 {}
+
+// Helper function to read a register. If the read range overlaps with value's range, it will load
+// corresponding bytes into data.
+fn read_reg_helper<T: RegisterValue>(
+ val: T,
+ val_range: RegisterRange,
+ addr: RegisterOffset,
+ data: &mut [u8],
+) {
+ let read_range = RegisterRange {
+ from: addr,
+ to: addr + data.len() as u64 - 1,
+ };
+
+ let overlap = match val_range.overlap_range(&read_range) {
+ Some(overlap) => overlap,
+ None => {
+ error!("calling read_reg_helper with non overlapping range. mmio_register might have a bug");
+ return;
+ }
+ };
+ let val_start_idx = (overlap.from - val_range.from) as usize;
+ let read_start_idx = (overlap.from - read_range.from) as usize;
+ let total_size = (overlap.to - overlap.from) as usize + 1;
+ for i in 0..total_size {
+ data[read_start_idx + i] = val.get_byte(val_start_idx + i);
+ }
+}
+
+/// Interface for register, as seen by guest driver.
+pub trait RegisterInterface: Send {
+ /// Range of this register.
+ fn range(&self) -> RegisterRange;
+ /// Handle read.
+ fn read(&self, addr: RegisterOffset, data: &mut [u8]);
+ /// Handle write.
+ fn write(&self, _addr: RegisterOffset, _data: &[u8]) {}
+ /// Reset this register to default value.
+ fn reset(&self) {}
+}
+
+// Spec for hardware init Read Only Registers.
+// The value of this register won't change.
+pub struct StaticRegisterSpec<T: RegisterValue> {
+ pub offset: RegisterOffset,
+ pub value: T,
+}
+
+/// A static register is a register inited by hardware. The value won't change in it's lifetime.
+/// All functions implemented on this one is thread safe.
+#[derive(Clone)]
+pub struct StaticRegister<T>
+where
+ T: RegisterValue,
+{
+ spec: &'static StaticRegisterSpec<T>,
+}
+
+impl<T> StaticRegister<T>
+where
+ T: RegisterValue,
+{
+ /// Create an new static register from spec.
+ pub fn new(spec: &'static StaticRegisterSpec<T>) -> StaticRegister<T> {
+ StaticRegister { spec }
+ }
+}
+
+impl<T> RegisterInterface for StaticRegister<T>
+where
+ T: RegisterValue,
+{
+ fn range(&self) -> RegisterRange {
+ RegisterRange {
+ from: self.spec.offset,
+ to: self.spec.offset + (size_of::<T>() as u64) - 1,
+ }
+ }
+
+ fn read(&self, addr: RegisterOffset, data: &mut [u8]) {
+ let val_range = self.range();
+ read_reg_helper(self.spec.value, val_range, addr, data);
+ }
+}
+
+/// Macro helps to build a static register.
+#[macro_export]
+macro_rules! static_register {
+ (ty: $ty:ty,offset: $offset:expr,value: $value:expr,) => {{
+ use crate::register_space::*;
+ static REG_SPEC: StaticRegisterSpec<$ty> = StaticRegisterSpec::<$ty> {
+ offset: $offset,
+ value: $value,
+ };
+ StaticRegister::new(®_SPEC)
+ }};
+}
+
+/// Spec for a regular register. It specifies it's location on register space, guest writable mask
+/// and guest write to clear mask.
+pub struct RegisterSpec<T> {
+ pub name: String,
+ pub offset: RegisterOffset,
+ pub reset_value: T,
+ /// Only masked bits could be written by guest.
+ pub guest_writeable_mask: T,
+ /// When write 1 to bits masked, those bits will be cleared. See Xhci spec 5.1
+ /// for more details.
+ pub guest_write_1_to_clear_mask: T,
+}
+
+struct RegisterInner<T: RegisterValue> {
+ spec: RegisterSpec<T>,
+ value: T,
+ write_cb: Option<Box<dyn Fn(T) -> T + Send>>,
+}
+
+/// Register is a thread safe struct. It can be safely changed from any thread.
+#[derive(Clone)]
+pub struct Register<T: RegisterValue> {
+ inner: Arc<Mutex<RegisterInner<T>>>,
+}
+
+impl<T: RegisterValue> Register<T> {
+ pub fn new(spec: RegisterSpec<T>, val: T) -> Self {
+ Register {
+ inner: Arc::new(Mutex::new(RegisterInner {
+ spec,
+ value: val,
+ write_cb: None,
+ })),
+ }
+ }
+
+ fn lock(&self) -> MutexGuard<RegisterInner<T>> {
+ self.inner.lock()
+ }
+}
+
+// All functions implemented on this one is thread safe.
+impl<T: RegisterValue> RegisterInterface for Register<T> {
+ fn range(&self) -> RegisterRange {
+ let locked = self.lock();
+ let spec = &locked.spec;
+ RegisterRange {
+ from: spec.offset,
+ to: spec.offset + (size_of::<T>() as u64) - 1,
+ }
+ }
+
+ fn read(&self, addr: RegisterOffset, data: &mut [u8]) {
+ let val_range = self.range();
+ let value = self.lock().value;
+ read_reg_helper(value, val_range, addr, data);
+ }
+
+ fn write(&self, addr: RegisterOffset, data: &[u8]) {
+ let my_range = self.range();
+ let write_range = RegisterRange {
+ from: addr,
+ to: addr + data.len() as u64 - 1,
+ };
+
+ let overlap = match my_range.overlap_range(&write_range) {
+ Some(range) => range,
+ None => {
+ error!("write should not be invoked on this register");
+ return;
+ }
+ };
+ let my_start_idx = (overlap.from - my_range.from) as usize;
+ let write_start_idx = (overlap.from - write_range.from) as usize;
+ let total_size = (overlap.to - overlap.from) as usize + 1;
+
+ let mut reg_value: T = self.lock().value;
+ let value: &mut [u8] = reg_value.as_mut_slice();
+ for i in 0..total_size {
+ value[my_start_idx + i] = self.apply_write_masks_to_byte(
+ value[my_start_idx + i],
+ data[write_start_idx + i],
+ my_start_idx + i,
+ );
+ }
+
+ // A single u64 register is done by write to lower 32 bit and then higher 32 bit. Callback
+ // should only be invoked when higher is written.
+ if my_range.to != overlap.to {
+ self.lock().value = reg_value;
+ return;
+ }
+
+ // Taking the callback out of register when executing it. This prevent dead lock if
+ // callback want to read current register value.
+ // Note that the only source of callback comes from mmio writing, which is synchronized.
+ let cb = {
+ let mut inner = self.lock();
+ match inner.write_cb.take() {
+ Some(cb) => cb,
+ None => {
+ // Write value if there is no callback.
+ inner.value = reg_value;
+ return;
+ }
+ }
+ };
+ // Callback is invoked without holding any lock.
+ let value = cb(reg_value);
+ let mut inner = self.lock();
+ inner.value = value;
+ inner.write_cb = Some(cb);
+ }
+
+ fn reset(&self) {
+ let mut locked = self.lock();
+ locked.value = locked.spec.reset_value;
+ }
+}
+
+impl<T: RegisterValue> Register<T> {
+ /// Get current value of this register.
+ pub fn get_value(&self) -> T {
+ self.lock().value
+ }
+
+ /// This function apply "write 1 to clear mask" and "guest writeable mask".
+ /// All write operations should go through this, the result of this function
+ /// is the new state of correspoding byte.
+ pub fn apply_write_masks_to_byte(&self, old_byte: u8, write_byte: u8, offset: usize) -> u8 {
+ let locked = self.lock();
+ let spec = &locked.spec;
+ let guest_write_1_to_clear_mask: u64 = spec.guest_write_1_to_clear_mask.into();
+ let guest_writeable_mask: u64 = spec.guest_writeable_mask.into();
+ // Mask with w1c mask.
+ let w1c_mask = (guest_write_1_to_clear_mask >> (offset * 8)) as u8;
+ let val = (!w1c_mask & write_byte) | (w1c_mask & old_byte & !write_byte);
+ // Mask with writable mask.
+ let w_mask = (guest_writeable_mask >> (offset * 8)) as u8;
+ (old_byte & (!w_mask)) | (val & w_mask)
+ }
+
+ /// Set a callback. It will be invoked when write happens.
+ pub fn set_write_cb<C: 'static + Fn(T) -> T + Send>(&self, callback: C) {
+ self.lock().write_cb = Some(Box::new(callback));
+ }
+
+ /// Set value from device side. Callback won't be invoked.
+ pub fn set_value(&self, val: T) {
+ self.lock().value = val;
+ }
+
+ /// Set masked bits.
+ pub fn set_bits(&self, mask: T) {
+ self.lock().value.set_bits(mask);
+ }
+
+ /// Clear masked bits.
+ pub fn clear_bits(&self, mask: T) {
+ self.lock().value.clear_bits(mask);
+ }
+}
+
+#[macro_export]
+macro_rules! register {
+ (
+ name: $name:tt,
+ ty: $ty:ty,
+ offset: $offset:expr,
+ reset_value: $rv:expr,
+ guest_writeable_mask: $mask:expr,
+ guest_write_1_to_clear_mask: $w1tcm:expr,
+ ) => {{
+ use crate::register_space::*;
+ let spec: RegisterSpec<$ty> = RegisterSpec::<$ty> {
+ name: String::from($name),
+ offset: $offset,
+ reset_value: $rv,
+ guest_writeable_mask: $mask,
+ guest_write_1_to_clear_mask: $w1tcm,
+ };
+ Register::<$ty>::new(spec, $rv)
+ }};
+ (name: $name:tt, ty: $ty:ty,offset: $offset:expr,reset_value: $rv:expr,) => {{
+ use crate::register_space::*;
+ let spec: RegisterSpec<$ty> = RegisterSpec::<$ty> {
+ name: String::from($name),
+ offset: $offset,
+ reset_value: $rv,
+ guest_writeable_mask: !0,
+ guest_write_1_to_clear_mask: 0,
+ };
+ Register::<$ty>::new(spec, $rv)
+ }};
+}
+
+#[macro_export]
+macro_rules! register_array {
+ (
+ name: $name:tt,
+ ty:
+ $ty:ty,cnt:
+ $cnt:expr,base_offset:
+ $base_offset:expr,stride:
+ $stride:expr,reset_value:
+ $rv:expr,guest_writeable_mask:
+ $gwm:expr,guest_write_1_to_clear_mask:
+ $gw1tcm:expr,
+ ) => {{
+ use crate::register_space::*;
+ let mut v: Vec<Register<$ty>> = Vec::new();
+ for i in 0..$cnt {
+ let offset = $base_offset + ($stride * i) as RegisterOffset;
+ let spec: RegisterSpec<$ty> = RegisterSpec::<$ty> {
+ name: format!("{}-{}", $name, i),
+ offset,
+ reset_value: $rv,
+ guest_writeable_mask: $gwm,
+ guest_write_1_to_clear_mask: $gw1tcm,
+ };
+ v.push(Register::<$ty>::new(spec, $rv));
+ }
+ v
+ }};
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ static REG_SPEC0: StaticRegisterSpec<u8> = StaticRegisterSpec::<u8> {
+ offset: 3,
+ value: 32,
+ };
+
+ static REG_SPEC1: StaticRegisterSpec<u16> = StaticRegisterSpec::<u16> {
+ offset: 3,
+ value: 32,
+ };
+
+ #[test]
+ fn static_register_basic_test_u8() {
+ let r = StaticRegister::<u8> { spec: ®_SPEC0 };
+ let mut data: [u8; 4] = [0, 0, 0, 0];
+ assert_eq!(r.range().from, 3);
+ assert_eq!(r.range().to, 3);
+ r.read(0, &mut data);
+ assert_eq!(data, [0, 0, 0, 32]);
+ r.read(2, &mut data);
+ assert_eq!(data, [0, 32, 0, 32]);
+ }
+
+ #[test]
+ fn static_register_basic_test_u16() {
+ let r = StaticRegister::<u16> { spec: ®_SPEC1 };
+ let mut data: [u8; 4] = [0, 0, 0, 0];
+ assert_eq!(r.range().from, 3);
+ assert_eq!(r.range().to, 4);
+ r.read(0, &mut data);
+ assert_eq!(data, [0, 0, 0, 32]);
+ r.read(2, &mut data);
+ assert_eq!(data, [0, 32, 0, 32]);
+ }
+
+ #[test]
+ fn static_register_interface_test() {
+ let r: Box<RegisterInterface> = Box::new(static_register! {
+ ty: u8,
+ offset: 3,
+ value: 32,
+ });
+ let mut data: [u8; 4] = [0, 0, 0, 0];
+ assert_eq!(r.range().from, 3);
+ assert_eq!(r.range().to, 3);
+ r.read(0, &mut data);
+ assert_eq!(data, [0, 0, 0, 32]);
+ r.read(2, &mut data);
+ assert_eq!(data, [0, 32, 0, 32]);
+ }
+
+ #[test]
+ fn register_basic_rw_test() {
+ let r = register! {
+ name: "",
+ ty: u8,
+ offset: 3,
+ reset_value: 0xf1,
+ guest_writeable_mask: 0xff,
+ guest_write_1_to_clear_mask: 0x0,
+ };
+ let mut data: [u8; 4] = [0, 0, 0, 0];
+ assert_eq!(r.range().from, 3);
+ assert_eq!(r.range().to, 3);
+ r.read(0, &mut data);
+ assert_eq!(data, [0, 0, 0, 0xf1]);
+ r.read(2, &mut data);
+ assert_eq!(data, [0, 0xf1, 0, 0xf1]);
+ data = [0, 0, 0, 0xab];
+ r.write(0, &data);
+ assert_eq!(r.get_value(), 0xab);
+ r.reset();
+ assert_eq!(r.get_value(), 0xf1);
+ r.set_value(0xcc);
+ assert_eq!(r.get_value(), 0xcc);
+ }
+
+ #[test]
+ fn register_basic_writeable_mask_test() {
+ let r = register! {
+ name: "",
+ ty: u8,
+ offset: 3,
+ reset_value: 0x0,
+ guest_writeable_mask: 0xf,
+ guest_write_1_to_clear_mask: 0x0,
+ };
+ let mut data: [u8; 4] = [0, 0, 0, 0];
+ assert_eq!(r.range().from, 3);
+ assert_eq!(r.range().to, 3);
+ r.read(0, &mut data);
+ assert_eq!(data, [0, 0, 0, 0]);
+ data = [0, 0, 0, 0xab];
+ r.write(0, &data);
+ assert_eq!(r.get_value(), 0x0b);
+ r.reset();
+ assert_eq!(r.get_value(), 0x0);
+ r.set_value(0xcc);
+ assert_eq!(r.get_value(), 0xcc);
+ }
+
+ #[test]
+ fn register_basic_write_1_to_clear_mask_test() {
+ let r = register! {
+ name: "",
+ ty: u8,
+ offset: 3,
+ reset_value: 0xf1,
+ guest_writeable_mask: 0xff,
+ guest_write_1_to_clear_mask: 0xf0,
+ };
+ let mut data: [u8; 4] = [0, 0, 0, 0];
+ assert_eq!(r.range().from, 3);
+ assert_eq!(r.range().to, 3);
+ r.read(0, &mut data);
+ assert_eq!(data, [0, 0, 0, 0xf1]);
+ data = [0, 0, 0, 0xfa];
+ r.write(0, &data);
+ assert_eq!(r.get_value(), 0x0a);
+ r.reset();
+ assert_eq!(r.get_value(), 0xf1);
+ r.set_value(0xcc);
+ assert_eq!(r.get_value(), 0xcc);
+ }
+
+ #[test]
+ fn register_basic_write_1_to_clear_mask_test_u32() {
+ let r = register! {
+ name: "",
+ ty: u32,
+ offset: 0,
+ reset_value: 0xfff1,
+ guest_writeable_mask: 0xff,
+ guest_write_1_to_clear_mask: 0xf0,
+ };
+ let mut data: [u8; 4] = [0, 0, 0, 0];
+ assert_eq!(r.range().from, 0);
+ assert_eq!(r.range().to, 3);
+ r.read(0, &mut data);
+ assert_eq!(data, [0xf1, 0xff, 0, 0]);
+ data = [0xfa, 0, 0, 0];
+ r.write(0, &data);
+ assert_eq!(r.get_value(), 0xff0a);
+ r.reset();
+ assert_eq!(r.get_value(), 0xfff1);
+ r.set_value(0xcc);
+ assert_eq!(r.get_value(), 0xcc);
+ }
+
+ #[test]
+ fn register_callback_test() {
+ let state = Arc::new(Mutex::new(0u8));
+ let r = register! {
+ name: "",
+ ty: u8,
+ offset: 3,
+ reset_value: 0xf1,
+ guest_writeable_mask: 0xff,
+ guest_write_1_to_clear_mask: 0xf0,
+ };
+
+ let s2 = state.clone();
+ r.set_write_cb(move |val: u8| {
+ *s2.lock() = val as u8;
+ val
+ });
+ let data: [u8; 4] = [0, 0, 0, 0xff];
+ r.write(0, &data);
+ assert_eq!(*state.lock(), 0xf);
+ r.set_value(0xab);
+ assert_eq!(*state.lock(), 0xf);
+ let data: [u8; 1] = [0xfc];
+ r.write(3, &data);
+ assert_eq!(*state.lock(), 0xc);
+ }
+}
diff --git a/devices/src/register_space/register_space.rs b/devices/src/register_space/register_space.rs
new file mode 100644
index 0000000..892961a
--- /dev/null
+++ b/devices/src/register_space/register_space.rs
@@ -0,0 +1,297 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::register::{Register, RegisterInterface, RegisterOffset, RegisterRange, RegisterValue};
+use std::collections::btree_map::BTreeMap;
+
+/// Register space repesents a set of registers. It can handle read/write operations.
+pub struct RegisterSpace {
+ regs: BTreeMap<RegisterRange, Box<dyn RegisterInterface>>,
+}
+
+impl RegisterSpace {
+ /// Creates a new empty RegisterSpace.
+ pub fn new() -> RegisterSpace {
+ RegisterSpace {
+ regs: BTreeMap::new(),
+ }
+ }
+
+ /// Add a register to register space.
+ pub fn add_register<T: RegisterInterface + 'static>(&mut self, reg: T) {
+ let range = reg.range();
+ debug_assert!(self.get_register(range.from).is_none());
+ if cfg!(debug_assertions) {
+ if let Some(r) = self.first_before(range.to) {
+ debug_assert!(r.range().to < range.to);
+ }
+ }
+
+ let insert_result = self.regs.insert(range, Box::new(reg)).is_none();
+ debug_assert!(insert_result);
+ }
+
+ /// Add an array of registers.
+ pub fn add_register_array<T: RegisterValue>(&mut self, regs: &[Register<T>]) {
+ for r in regs {
+ self.add_register(r.clone());
+ }
+ }
+
+ /// Read range.
+ pub fn read(&self, addr: RegisterOffset, data: &mut [u8]) {
+ let mut current_addr: RegisterOffset = addr;
+ while current_addr < addr + data.len() as RegisterOffset {
+ if let Some(r) = self.get_register(current_addr) {
+ // Next addr to read is.
+ current_addr = r.range().to + 1;
+ r.read(addr, data);
+ } else {
+ // TODO(jkwang) Add logging for debug here.
+ current_addr += 1;
+ }
+ }
+ }
+
+ /// Write range. If the targeted register has a callback, it will be invoked with the new
+ /// value.
+ pub fn write(&self, addr: RegisterOffset, data: &[u8]) {
+ let mut current_addr: RegisterOffset = addr;
+ while current_addr < addr + data.len() as RegisterOffset {
+ if let Some(r) = self.get_register(current_addr) {
+ // Next addr to read is, range is inclusive.
+ current_addr = r.range().to + 1;
+ r.write(addr, data);
+ } else {
+ current_addr += 1;
+ }
+ }
+ }
+
+ /// Get first register before this addr.
+ fn first_before(&self, addr: RegisterOffset) -> Option<&dyn RegisterInterface> {
+ for (range, r) in self.regs.iter().rev() {
+ if range.from <= addr {
+ return Some(r.as_ref());
+ }
+ }
+ None
+ }
+
+ /// Get register at this addr.
+ fn get_register(&self, addr: RegisterOffset) -> Option<&dyn RegisterInterface> {
+ let r = self.first_before(addr)?;
+ let range = r.range();
+ if addr <= range.to {
+ Some(r)
+ } else {
+ None
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::sync::Arc;
+ use sync::Mutex;
+
+ #[test]
+ fn regs_no_reg() {
+ let regs = RegisterSpace::new();
+ let mut data: [u8; 4] = [4, 3, 2, 1];
+ // Read should be no op cause no register.
+ regs.read(0, &mut data);
+ assert_eq!([4, 3, 2, 1], data);
+ // Write should be no op.
+ regs.write(0, &[0, 0, 0, 0]);
+ regs.read(0, &mut data);
+ assert_eq!([4, 3, 2, 1], data);
+ }
+
+ #[test]
+ #[should_panic]
+ #[cfg(debug_assertions)]
+ fn regs_reg_overlap() {
+ let mut regs = RegisterSpace::new();
+ regs.add_register(static_register!(
+ ty: u32,
+ offset: 4,
+ value: 11,
+ ));
+
+ regs.add_register(static_register!(
+ ty: u16,
+ offset: 7,
+ value: 11,
+ ));
+ }
+
+ #[test]
+ fn regs_static_reg() {
+ let mut regs = RegisterSpace::new();
+ regs.add_register(static_register!(
+ ty: u8,
+ offset: 0,
+ value: 11,
+ ));
+ let mut data: [u8; 4] = [4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([11, 3, 2, 1], data);
+ // Write should be no op.
+ regs.write(0, &[0, 0, 0, 0]);
+ let mut data: [u8; 4] = [4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([11, 3, 2, 1], data);
+ }
+
+ #[test]
+ fn regs_static_reg_offset() {
+ let mut regs = RegisterSpace::new();
+ regs.add_register(static_register!(
+ ty: u32,
+ offset: 2,
+ value: 0xaabbccdd,
+ ));
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+ // Write should be no op.
+ regs.write(0, &[0, 0, 0, 0, 0, 0, 0, 0]);
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+ }
+
+ #[test]
+ fn regs_reg_write() {
+ let mut regs = RegisterSpace::new();
+ regs.add_register(register!(
+ name: "",
+ ty: u32,
+ offset: 2,
+ reset_value: 0xaabbccdd,
+ ));
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+ regs.write(0, &[0, 0, 0, 0, 0, 0, 0, 0]);
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0, 0, 0, 0, 2, 1], data);
+ }
+
+ #[test]
+ fn regs_reg_writeable() {
+ let mut regs = RegisterSpace::new();
+ regs.add_register(register!(
+ name: "",
+ ty: u32,
+ offset: 2,
+ reset_value: 0xaabbccdd,
+ guest_writeable_mask: 0x00f0000f,
+ guest_write_1_to_clear_mask: 0,
+ ));
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+ regs.write(0, &[0, 0, 0, 0, 0, 0, 0, 0]);
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0xd0, 0xcc, 0x0b, 0xaa, 2, 1], data);
+ }
+
+ #[test]
+ fn regs_reg_writeable_callback() {
+ let state = Arc::new(Mutex::new(0u32));
+ let mut regs = RegisterSpace::new();
+ let reg = register!(
+ name: "",
+ ty: u32,
+ offset: 2,
+ reset_value: 0xaabbccdd,
+ guest_writeable_mask: 0x00f0000f,
+ guest_write_1_to_clear_mask: 0,
+ );
+ regs.add_register(reg.clone());
+ let state_clone = state.clone();
+ reg.set_write_cb(move |val: u32| {
+ *state_clone.lock() = val;
+ val
+ });
+
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+ regs.write(0, &[0, 0, 0, 0, 0, 0, 0, 0]);
+ assert_eq!(0xaa0bccd0, *state.lock());
+ }
+
+ #[test]
+ fn regs_reg_write_to_clear() {
+ let mut regs = RegisterSpace::new();
+ regs.add_register(register!(
+ name: "",
+ ty: u32,
+ offset: 2,
+ reset_value: 0xaabbccdd,
+ guest_writeable_mask: 0xfff0000f,
+ guest_write_1_to_clear_mask: 0xf0000000,
+ ));
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+ regs.write(0, &[0, 0, 0, 0, 0, 0xad, 0, 0]);
+ let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+ regs.read(0, &mut data);
+ assert_eq!([8, 7, 0xd0, 0xcc, 0x0b, 0x0d, 2, 1], data);
+ }
+
+ #[test]
+ fn regs_reg_array() {
+ let mut regs = RegisterSpace::new();
+ regs.add_register_array(®ister_array!(
+ name: "",
+ ty: u8,
+ cnt: 8,
+ base_offset: 10,
+ stride: 2,
+ reset_value: 0xff,
+ guest_writeable_mask: !0,
+ guest_write_1_to_clear_mask: 0,
+ ));
+ let mut data: [u8; 8] = [0; 8];
+ regs.read(8, &mut data);
+ assert_eq!([0, 0, 0xff, 0, 0xff, 0, 0xff, 0], data);
+ }
+
+ #[test]
+ fn regs_reg_multi_array() {
+ let mut regs = RegisterSpace::new();
+ regs.add_register_array(®ister_array!(
+ name: "",
+ ty: u8,
+ cnt: 8,
+ base_offset: 10,
+ stride: 2,
+ reset_value: 0xff,
+ guest_writeable_mask: !0,
+ guest_write_1_to_clear_mask: 0,
+ ));
+ regs.add_register_array(®ister_array!(
+ name: "",
+ ty: u8,
+ cnt: 8,
+ base_offset: 11,
+ stride: 2,
+ reset_value: 0xee,
+ guest_writeable_mask: !0,
+ guest_write_1_to_clear_mask: 0,
+ ));
+ let mut data: [u8; 8] = [0; 8];
+ regs.read(8, &mut data);
+ assert_eq!([0, 0, 0xff, 0xee, 0xff, 0xee, 0xff, 0xee], data);
+ }
+
+}
diff --git a/devices/src/serial.rs b/devices/src/serial.rs
new file mode 100644
index 0000000..0bf5642
--- /dev/null
+++ b/devices/src/serial.rs
@@ -0,0 +1,485 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::collections::VecDeque;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::{self, stdout};
+use std::path::PathBuf;
+use std::str::FromStr;
+
+use sys_util::{error, syslog, EventFd, Result};
+
+use crate::BusDevice;
+
+const LOOP_SIZE: usize = 0x40;
+
+const DATA: u8 = 0;
+const IER: u8 = 1;
+const IIR: u8 = 2;
+const LCR: u8 = 3;
+const MCR: u8 = 4;
+const LSR: u8 = 5;
+const MSR: u8 = 6;
+const SCR: u8 = 7;
+const DLAB_LOW: u8 = 0;
+const DLAB_HIGH: u8 = 1;
+
+const IER_RECV_BIT: u8 = 0x1;
+const IER_THR_BIT: u8 = 0x2;
+const IER_FIFO_BITS: u8 = 0x0f;
+
+const IIR_FIFO_BITS: u8 = 0xc0;
+const IIR_NONE_BIT: u8 = 0x1;
+const IIR_THR_BIT: u8 = 0x2;
+const IIR_RECV_BIT: u8 = 0x4;
+
+const LSR_DATA_BIT: u8 = 0x1;
+const LSR_EMPTY_BIT: u8 = 0x20;
+const LSR_IDLE_BIT: u8 = 0x40;
+
+const MCR_DTR_BIT: u8 = 0x01; // Data Terminal Ready
+const MCR_RTS_BIT: u8 = 0x02; // Request to Send
+const MCR_OUT1_BIT: u8 = 0x04;
+const MCR_OUT2_BIT: u8 = 0x08;
+const MCR_LOOP_BIT: u8 = 0x10;
+
+const MSR_CTS_BIT: u8 = 0x10; // Clear to Send
+const MSR_DSR_BIT: u8 = 0x20; // Data Set Ready
+const MSR_RI_BIT: u8 = 0x40; // Ring Indicator
+const MSR_DCD_BIT: u8 = 0x80; // Data Carrier Detect
+
+const DEFAULT_INTERRUPT_IDENTIFICATION: u8 = IIR_NONE_BIT; // no pending interrupt
+const DEFAULT_LINE_STATUS: u8 = LSR_EMPTY_BIT | LSR_IDLE_BIT; // THR empty and line is idle
+const DEFAULT_LINE_CONTROL: u8 = 0x3; // 8-bits per character
+const DEFAULT_MODEM_CONTROL: u8 = MCR_OUT2_BIT;
+const DEFAULT_MODEM_STATUS: u8 = MSR_DSR_BIT | MSR_CTS_BIT | MSR_DCD_BIT;
+const DEFAULT_BAUD_DIVISOR: u16 = 12; // 9600 bps
+
+#[derive(Debug)]
+pub enum Error {
+ CloneEventFd(sys_util::Error),
+ InvalidSerialType(String),
+ PathRequired,
+ FileError(std::io::Error),
+ Unimplemented(SerialType),
+}
+
+impl Display for Error {
+ #[remain::check]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ #[sorted]
+ match self {
+ CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
+ FileError(e) => write!(f, "Unable to open/create file: {}", e),
+ InvalidSerialType(e) => write!(f, "invalid serial type: {}", e),
+ PathRequired => write!(f, "serial device type file requires a path"),
+ Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()),
+ }
+ }
+}
+
+/// Enum for possible type of serial devices
+#[derive(Debug)]
+pub enum SerialType {
+ File,
+ Stdout,
+ Sink,
+ Syslog,
+ UnixSocket, // NOT IMPLEMENTED
+}
+
+impl Display for SerialType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match &self {
+ SerialType::File => "File".to_string(),
+ SerialType::Stdout => "Stdout".to_string(),
+ SerialType::Sink => "Sink".to_string(),
+ SerialType::Syslog => "Syslog".to_string(),
+ SerialType::UnixSocket => "UnixSocket".to_string(),
+ };
+
+ write!(f, "{}", s)
+ }
+}
+
+impl FromStr for SerialType {
+ type Err = Error;
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ match s {
+ "file" | "File" => Ok(SerialType::File),
+ "stdout" | "Stdout" => Ok(SerialType::Stdout),
+ "sink" | "Sink" => Ok(SerialType::Sink),
+ "syslog" | "Syslog" => Ok(SerialType::Syslog),
+ "unix" | "UnixSocket" => Ok(SerialType::UnixSocket),
+ _ => Err(Error::InvalidSerialType(s.to_string())),
+ }
+ }
+}
+
+/// Holds the parameters for a serial device
+#[derive(Debug)]
+pub struct SerialParameters {
+ pub type_: SerialType,
+ pub path: Option<PathBuf>,
+ pub num: u8,
+ pub console: bool,
+}
+
+impl SerialParameters {
+ /// Helper function to create a serial device from the defined parameters.
+ ///
+ /// # Arguments
+ /// * `evt_fd` - eventfd used for interrupt events
+ pub fn create_serial_device(&self, evt_fd: &EventFd) -> std::result::Result<Serial, Error> {
+ match self.type_ {
+ SerialType::Stdout => Ok(Serial::new_out(
+ evt_fd.try_clone().map_err(Error::CloneEventFd)?,
+ Box::new(stdout()),
+ )),
+ SerialType::Sink => Ok(Serial::new_sink(
+ evt_fd.try_clone().map_err(Error::CloneEventFd)?,
+ )),
+ SerialType::Syslog => Ok(Serial::new_out(
+ evt_fd.try_clone().map_err(Error::CloneEventFd)?,
+ Box::new(syslog::Syslogger::new(
+ syslog::Priority::Info,
+ syslog::Facility::Daemon,
+ )),
+ )),
+ SerialType::File => match &self.path {
+ None => Err(Error::PathRequired),
+ Some(path) => Ok(Serial::new_out(
+ evt_fd.try_clone().map_err(Error::CloneEventFd)?,
+ Box::new(File::create(path.as_path()).map_err(Error::FileError)?),
+ )),
+ },
+ SerialType::UnixSocket => Err(Error::Unimplemented(SerialType::UnixSocket)),
+ }
+ }
+}
+
+// Structure for holding the default configuration of the serial devices.
+pub const DEFAULT_SERIAL_PARAMS: [SerialParameters; 4] = [
+ SerialParameters {
+ type_: SerialType::Stdout,
+ path: None,
+ num: 1,
+ console: true,
+ },
+ SerialParameters {
+ type_: SerialType::Sink,
+ path: None,
+ num: 2,
+ console: false,
+ },
+ SerialParameters {
+ type_: SerialType::Sink,
+ path: None,
+ num: 3,
+ console: false,
+ },
+ SerialParameters {
+ type_: SerialType::Sink,
+ path: None,
+ num: 4,
+ console: false,
+ },
+];
+
+/// Address for Serial ports in x86
+pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
+
+/// String representations of serial devices
+pub const SERIAL_TTY_STRINGS: [&str; 4] = ["ttyS0", "ttyS1", "ttyS2", "ttyS3"];
+
+/// Helper function to get the tty string of a serial device based on the port number. Will default
+/// to ttyS0 if an invalid number is given.
+pub fn get_serial_tty_string(stdio_serial_num: u8) -> String {
+ match stdio_serial_num {
+ 1 => SERIAL_TTY_STRINGS[0].to_string(),
+ 2 => SERIAL_TTY_STRINGS[1].to_string(),
+ 3 => SERIAL_TTY_STRINGS[2].to_string(),
+ 4 => SERIAL_TTY_STRINGS[3].to_string(),
+ _ => SERIAL_TTY_STRINGS[0].to_string(),
+ }
+}
+
+/// Emulates serial COM ports commonly seen on x86 I/O ports 0x3f8/0x2f8/0x3e8/0x2e8.
+///
+/// This can optionally write the guest's output to a Write trait object. To send input to the
+/// guest, use `queue_input_bytes`.
+pub struct Serial {
+ interrupt_enable: u8,
+ interrupt_identification: u8,
+ interrupt_evt: EventFd,
+ line_control: u8,
+ line_status: u8,
+ modem_control: u8,
+ modem_status: u8,
+ scratch: u8,
+ baud_divisor: u16,
+ in_buffer: VecDeque<u8>,
+ out: Option<Box<dyn io::Write + Send>>,
+}
+
+impl Serial {
+ fn new(interrupt_evt: EventFd, out: Option<Box<dyn io::Write + Send>>) -> Serial {
+ Serial {
+ interrupt_enable: 0,
+ interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION,
+ interrupt_evt,
+ line_control: DEFAULT_LINE_CONTROL,
+ line_status: DEFAULT_LINE_STATUS,
+ modem_control: DEFAULT_MODEM_CONTROL,
+ modem_status: DEFAULT_MODEM_STATUS,
+ scratch: 0,
+ baud_divisor: DEFAULT_BAUD_DIVISOR,
+ in_buffer: VecDeque::new(),
+ out,
+ }
+ }
+
+ /// Constructs a Serial port ready for output.
+ pub fn new_out(interrupt_evt: EventFd, out: Box<dyn io::Write + Send>) -> Serial {
+ Self::new(interrupt_evt, Some(out))
+ }
+
+ /// Constructs a Serial port with no connected output.
+ pub fn new_sink(interrupt_evt: EventFd) -> Serial {
+ Self::new(interrupt_evt, None)
+ }
+
+ /// Queues raw bytes for the guest to read and signals the interrupt if the line status would
+ /// change.
+ pub fn queue_input_bytes(&mut self, c: &[u8]) -> Result<()> {
+ if !self.is_loop() {
+ self.in_buffer.extend(c);
+ self.recv_data()?;
+ }
+ Ok(())
+ }
+
+ fn is_dlab_set(&self) -> bool {
+ (self.line_control & 0x80) != 0
+ }
+
+ fn is_recv_intr_enabled(&self) -> bool {
+ (self.interrupt_enable & IER_RECV_BIT) != 0
+ }
+
+ fn is_thr_intr_enabled(&self) -> bool {
+ (self.interrupt_enable & IER_THR_BIT) != 0
+ }
+
+ fn is_loop(&self) -> bool {
+ (self.modem_control & MCR_LOOP_BIT) != 0
+ }
+
+ fn add_intr_bit(&mut self, bit: u8) {
+ self.interrupt_identification &= !IIR_NONE_BIT;
+ self.interrupt_identification |= bit;
+ }
+
+ fn del_intr_bit(&mut self, bit: u8) {
+ self.interrupt_identification &= !bit;
+ if self.interrupt_identification == 0x0 {
+ self.interrupt_identification = IIR_NONE_BIT;
+ }
+ }
+
+ fn thr_empty(&mut self) -> Result<()> {
+ if self.is_thr_intr_enabled() {
+ self.add_intr_bit(IIR_THR_BIT);
+ self.trigger_interrupt()?
+ }
+ Ok(())
+ }
+
+ fn recv_data(&mut self) -> Result<()> {
+ if self.is_recv_intr_enabled() {
+ self.add_intr_bit(IIR_RECV_BIT);
+ self.trigger_interrupt()?
+ }
+ self.line_status |= LSR_DATA_BIT;
+ Ok(())
+ }
+
+ fn trigger_interrupt(&mut self) -> Result<()> {
+ self.interrupt_evt.write(1)
+ }
+
+ fn iir_reset(&mut self) {
+ self.interrupt_identification = DEFAULT_INTERRUPT_IDENTIFICATION;
+ }
+
+ fn handle_write(&mut self, offset: u8, v: u8) -> Result<()> {
+ match offset as u8 {
+ DLAB_LOW if self.is_dlab_set() => {
+ self.baud_divisor = (self.baud_divisor & 0xff00) | v as u16
+ }
+ DLAB_HIGH if self.is_dlab_set() => {
+ self.baud_divisor = (self.baud_divisor & 0x00ff) | ((v as u16) << 8)
+ }
+ DATA => {
+ if self.is_loop() {
+ if self.in_buffer.len() < LOOP_SIZE {
+ self.in_buffer.push_back(v);
+ self.recv_data()?;
+ }
+ } else {
+ if let Some(out) = self.out.as_mut() {
+ out.write_all(&[v])?;
+ out.flush()?;
+ }
+ self.thr_empty()?;
+ }
+ }
+ IER => self.interrupt_enable = v & IER_FIFO_BITS,
+ LCR => self.line_control = v,
+ MCR => self.modem_control = v,
+ SCR => self.scratch = v,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+impl BusDevice for Serial {
+ fn debug_label(&self) -> String {
+ "serial".to_owned()
+ }
+
+ fn write(&mut self, offset: u64, data: &[u8]) {
+ if data.len() != 1 {
+ return;
+ }
+
+ if let Err(e) = self.handle_write(offset as u8, data[0]) {
+ error!("serial failed write: {}", e);
+ }
+ }
+
+ fn read(&mut self, offset: u64, data: &mut [u8]) {
+ if data.len() != 1 {
+ return;
+ }
+
+ data[0] = match 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 => {
+ self.del_intr_bit(IIR_RECV_BIT);
+ if self.in_buffer.len() <= 1 {
+ self.line_status &= !LSR_DATA_BIT;
+ }
+ self.in_buffer.pop_front().unwrap_or_default()
+ }
+ IER => self.interrupt_enable,
+ IIR => {
+ let v = self.interrupt_identification | IIR_FIFO_BITS;
+ self.iir_reset();
+ v
+ }
+ LCR => self.line_control,
+ MCR => self.modem_control,
+ LSR => self.line_status,
+ MSR => {
+ if self.is_loop() {
+ let mut msr =
+ self.modem_status & !(MSR_DSR_BIT | MSR_CTS_BIT | MSR_RI_BIT | MSR_DCD_BIT);
+ if self.modem_control & MCR_DTR_BIT != 0 {
+ msr |= MSR_DSR_BIT;
+ }
+ if self.modem_control & MCR_RTS_BIT != 0 {
+ msr |= MSR_CTS_BIT;
+ }
+ if self.modem_control & MCR_OUT1_BIT != 0 {
+ msr |= MSR_RI_BIT;
+ }
+ if self.modem_control & MCR_OUT2_BIT != 0 {
+ msr |= MSR_DCD_BIT;
+ }
+ msr
+ } else {
+ self.modem_status
+ }
+ }
+ SCR => self.scratch,
+ _ => 0,
+ };
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::io;
+ use std::sync::Arc;
+
+ use sync::Mutex;
+
+ #[derive(Clone)]
+ struct SharedBuffer {
+ buf: Arc<Mutex<Vec<u8>>>,
+ }
+
+ impl SharedBuffer {
+ fn new() -> SharedBuffer {
+ SharedBuffer {
+ buf: Arc::new(Mutex::new(Vec::new())),
+ }
+ }
+ }
+
+ impl io::Write for SharedBuffer {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.buf.lock().write(buf)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ self.buf.lock().flush()
+ }
+ }
+
+ #[test]
+ fn serial_output() {
+ let intr_evt = EventFd::new().unwrap();
+ let serial_out = SharedBuffer::new();
+
+ let mut serial = Serial::new_out(intr_evt, Box::new(serial_out.clone()));
+
+ serial.write(DATA as u64, &['a' as u8]);
+ serial.write(DATA as u64, &['b' as u8]);
+ serial.write(DATA as u64, &['c' as u8]);
+ assert_eq!(
+ serial_out.buf.lock().as_slice(),
+ &['a' as u8, 'b' as u8, 'c' as u8]
+ );
+ }
+
+ #[test]
+ fn serial_input() {
+ let intr_evt = EventFd::new().unwrap();
+ let serial_out = SharedBuffer::new();
+
+ let mut serial =
+ Serial::new_out(intr_evt.try_clone().unwrap(), Box::new(serial_out.clone()));
+
+ serial.write(IER as u64, &[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[..]);
+ assert_eq!(data[0], 'a' as u8);
+ serial.read(DATA as u64, &mut data[..]);
+ assert_eq!(data[0], 'b' as u8);
+ serial.read(DATA as u64, &mut data[..]);
+ assert_eq!(data[0], 'c' as u8);
+ }
+}
diff --git a/devices/src/split_irqchip_common.rs b/devices/src/split_irqchip_common.rs
new file mode 100644
index 0000000..65ba809
--- /dev/null
+++ b/devices/src/split_irqchip_common.rs
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Common constants and types used for Split IRQ chip devices (e.g. PIC, PIT, IOAPIC).
+
+use bit_field::*;
+
+#[bitfield]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum DestinationMode {
+ Physical = 0,
+ Logical = 1,
+}
+
+#[bitfield]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum TriggerMode {
+ Edge = 0,
+ Level = 1,
+}
+
+#[bitfield]
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum DeliveryMode {
+ Fixed = 0b000,
+ Lowest = 0b001,
+ SMI = 0b010, // System management interrupt
+ RemoteRead = 0b011, // This is no longer supported by intel.
+ NMI = 0b100, // Non maskable interrupt
+ Init = 0b101,
+ Startup = 0b110,
+ External = 0b111,
+}
+
+#[bitfield]
+#[derive(Clone, Copy, PartialEq)]
+pub struct MsiAddressMessage {
+ reserved: BitField2,
+ #[bits = 1]
+ destination_mode: DestinationMode,
+ redirection_hint: BitField1,
+ reserved_2: BitField8,
+ destination_id: BitField8,
+ // According to Intel's implementation of MSI, these bits must always be 0xfee.
+ always_0xfee: BitField12,
+}
+
+#[bitfield]
+#[derive(Clone, Copy, PartialEq)]
+struct MsiDataMessage {
+ vector: BitField8,
+ #[bits = 3]
+ delivery_mode: DeliveryMode,
+ reserved: BitField3,
+ level: BitField1,
+ #[bits = 1]
+ trigger: TriggerMode,
+ reserved2: BitField16,
+}
diff --git a/devices/src/usb/host_backend/context.rs b/devices/src/usb/host_backend/context.rs
new file mode 100644
index 0000000..960d066
--- /dev/null
+++ b/devices/src/usb/host_backend/context.rs
@@ -0,0 +1,157 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::error::*;
+use crate::utils::{EventHandler, EventLoop};
+use std::os::raw::c_short;
+use std::os::unix::io::RawFd;
+use std::sync::{Arc, Weak};
+use sys_util::{error, WatchingEvents};
+use usb_util::hotplug::UsbHotplugHandler;
+use usb_util::libusb_context::{LibUsbContext, LibUsbPollfdChangeHandler};
+use usb_util::libusb_device::LibUsbDevice;
+use vm_control::MaybeOwnedFd;
+
+/// Context wraps libusb context with libusb event handling.
+pub struct Context {
+ context: LibUsbContext,
+ event_loop: Arc<EventLoop>,
+ event_handler: Arc<dyn EventHandler>,
+}
+
+impl Context {
+ /// Create a new context.
+ #[cfg(not(feature = "sandboxed-libusb"))]
+ pub fn new(event_loop: Arc<EventLoop>) -> Result<Context> {
+ let context = LibUsbContext::new().map_err(Error::CreateLibUsbContext)?;
+ let ctx = Context {
+ context: context.clone(),
+ event_loop,
+ event_handler: Arc::new(LibUsbEventHandler {
+ context: context.clone(),
+ }),
+ };
+ ctx.init_event_handler()?;
+ Ok(ctx)
+ }
+
+ #[cfg(feature = "sandboxed-libusb")]
+ pub fn new(event_loop: Arc<EventLoop>) -> Result<Context> {
+ let context = LibUsbContext::new_jailed().map_err(Error::CreateLibUsbContext)?;
+ let ctx = Context {
+ context: context.clone(),
+ event_loop,
+ event_handler: Arc::new(LibUsbEventHandler {
+ context: context.clone(),
+ }),
+ };
+ ctx.init_event_handler()?;
+ Ok(ctx)
+ }
+
+ pub fn set_hotplug_handler<H: UsbHotplugHandler + Sized>(&self, handler: H) {
+ if let Err(e) = self.context.set_hotplug_cb(handler) {
+ error!("cannot set hotplug handler: {:?}", e);
+ }
+ }
+
+ fn init_event_handler(&self) -> Result<()> {
+ for pollfd in self.context.get_pollfd_iter() {
+ usb_debug!("event loop add event {} events handler", pollfd.fd);
+ self.event_loop
+ .add_event(
+ &MaybeOwnedFd::Borrowed(pollfd.fd),
+ WatchingEvents::new(pollfd.events as u32),
+ Arc::downgrade(&self.event_handler),
+ )
+ .map_err(Error::AddToEventLoop)?;
+ }
+
+ self.context
+ .set_pollfd_notifiers(Box::new(PollfdChangeHandler {
+ event_loop: self.event_loop.clone(),
+ event_handler: Arc::downgrade(&self.event_handler),
+ }));
+ Ok(())
+ }
+
+ /// Get libusb device with matching bus, addr, vid and pid.
+ #[cfg(not(feature = "sandboxed-libusb"))]
+ pub fn get_device(&self, bus: u8, addr: u8, vid: u16, pid: u16) -> Option<LibUsbDevice> {
+ let device_iter = match self.context.get_device_iter() {
+ Ok(iter) => iter,
+ Err(e) => {
+ error!("could not get libusb device iterator: {:?}", e);
+ return None;
+ }
+ };
+ for device in device_iter {
+ if device.get_bus_number() == bus && device.get_address() == addr {
+ if let Ok(descriptor) = device.get_device_descriptor() {
+ if descriptor.idProduct == pid && descriptor.idVendor == vid {
+ return Some(device);
+ }
+ }
+ }
+ }
+ error!("device not found bus {}, addr {}", bus, addr);
+ None
+ }
+
+ #[cfg(feature = "sandboxed-libusb")]
+ pub fn get_device(&self, fd: std::fs::File) -> Option<LibUsbDevice> {
+ match self.context.get_device_from_fd(fd) {
+ Ok(dev) => Some(dev),
+ Err(e) => {
+ error!("could not build device from fd: {:?}", e);
+ None
+ }
+ }
+ }
+}
+
+struct LibUsbEventHandler {
+ context: LibUsbContext,
+}
+
+impl EventHandler for LibUsbEventHandler {
+ fn on_event(&self) -> std::result::Result<(), ()> {
+ self.context.handle_events_nonblock();
+ Ok(())
+ }
+}
+
+struct PollfdChangeHandler {
+ event_loop: Arc<EventLoop>,
+ event_handler: Weak<dyn EventHandler>,
+}
+
+impl LibUsbPollfdChangeHandler for PollfdChangeHandler {
+ fn add_poll_fd(&self, fd: RawFd, events: c_short) {
+ if let Err(e) = self.event_loop.add_event(
+ &MaybeOwnedFd::Borrowed(fd),
+ WatchingEvents::new(events as u32),
+ self.event_handler.clone(),
+ ) {
+ error!("cannot add event to event loop: {}", e);
+ }
+ }
+
+ fn remove_poll_fd(&self, fd: RawFd) {
+ if let Some(h) = self.event_handler.upgrade() {
+ if let Err(e) = h.on_event() {
+ error!("cannot handle event: {:?}", e);
+ }
+ }
+ if let Err(e) = self
+ .event_loop
+ .remove_event_for_fd(&MaybeOwnedFd::Borrowed(fd))
+ {
+ error!(
+ "failed to remove poll change handler from event loop: {}",
+ e
+ );
+ }
+ }
+}
diff --git a/devices/src/usb/host_backend/error.rs b/devices/src/usb/host_backend/error.rs
new file mode 100644
index 0000000..ef097f9
--- /dev/null
+++ b/devices/src/usb/host_backend/error.rs
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::usb::xhci::scatter_gather_buffer::Error as BufferError;
+use crate::usb::xhci::xhci_transfer::Error as XhciTransferError;
+use crate::utils::Error as UtilsError;
+use msg_socket::MsgError;
+use std::fmt::{self, Display};
+use usb_util::error::Error as UsbUtilError;
+
+#[derive(Debug)]
+pub enum Error {
+ AddToEventLoop(UtilsError),
+ StartAsyncJobQueue(UtilsError),
+ QueueAsyncJob(UtilsError),
+ CreateLibUsbContext(UsbUtilError),
+ GetActiveConfig(UsbUtilError),
+ SetActiveConfig(UsbUtilError),
+ SetInterfaceAltSetting(UsbUtilError),
+ ClearHalt(UsbUtilError),
+ GetEndpointType,
+ CreateControlSock(std::io::Error),
+ SetupControlSock(std::io::Error),
+ ReadControlSock(MsgError),
+ WriteControlSock(MsgError),
+ GetXhciTransferType(XhciTransferError),
+ TransferComplete(XhciTransferError),
+ ReadBuffer(BufferError),
+ WriteBuffer(BufferError),
+ BufferLen(BufferError),
+ /// Cannot get interface descriptor for (interface, altsetting).
+ GetInterfaceDescriptor((i32, u16)),
+ GetEndpointDescriptor(u8),
+ BadXhciTransferState,
+ BadBackendProviderState,
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ AddToEventLoop(e) => write!(f, "failed to add to event loop: {}", e),
+ StartAsyncJobQueue(e) => write!(f, "failed to start async job queue: {}", e),
+ QueueAsyncJob(e) => write!(f, "failed to queue async job: {}", e),
+ CreateLibUsbContext(e) => write!(f, "failed to create libusb context: {:?}", e),
+ GetActiveConfig(e) => write!(f, "failed to get active config: {:?}", e),
+ SetActiveConfig(e) => write!(f, "failed to set active config: {:?}", e),
+ SetInterfaceAltSetting(e) => write!(f, "failed to set interface alt setting: {:?}", e),
+ ClearHalt(e) => write!(f, "failed to clear halt: {:?}", e),
+ GetEndpointType => write!(f, "failed to get endpoint type"),
+ CreateControlSock(e) => write!(f, "failed to create contro sock: {}", e),
+ SetupControlSock(e) => write!(f, "failed to setup control sock: {}", e),
+ ReadControlSock(e) => write!(f, "failed to read control sock: {}", e),
+ WriteControlSock(e) => write!(f, "failed to write control sock: {}", e),
+ GetXhciTransferType(e) => write!(f, "failed to get xhci transfer type: {}", e),
+ TransferComplete(e) => write!(f, "xhci transfer completed: {}", e),
+ ReadBuffer(e) => write!(f, "failed to read buffer: {}", e),
+ WriteBuffer(e) => write!(f, "failed to write buffer: {}", e),
+ BufferLen(e) => write!(f, "failed to get buffer length: {}", e),
+ GetInterfaceDescriptor((i, alt_setting)) => write!(
+ f,
+ "failed to get interface descriptor for interface {}, alt setting {}",
+ i, alt_setting
+ ),
+ GetEndpointDescriptor(ep_idx) => {
+ write!(f, "failed to get endpoint descriptor for ep: {}", ep_idx)
+ }
+ BadXhciTransferState => write!(f, "xhci transfer is in a bad state"),
+ BadBackendProviderState => write!(f, "backend provider is in a bad state"),
+ }
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
diff --git a/devices/src/usb/host_backend/host_backend_device_provider.rs b/devices/src/usb/host_backend/host_backend_device_provider.rs
new file mode 100644
index 0000000..01fd84c
--- /dev/null
+++ b/devices/src/usb/host_backend/host_backend_device_provider.rs
@@ -0,0 +1,337 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::sync::Arc;
+
+use super::context::Context;
+use super::error::*;
+use super::host_device::HostDevice;
+use super::hotplug::HotplugHandler;
+use crate::usb::xhci::usb_hub::UsbHub;
+use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider;
+use crate::utils::AsyncJobQueue;
+use crate::utils::{EventHandler, EventLoop, FailHandle};
+use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
+use std::mem;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::time::Duration;
+use sys_util::net::UnixSeqpacket;
+use sys_util::{error, WatchingEvents};
+use vm_control::{
+ UsbControlAttachedDevice, UsbControlCommand, UsbControlResult, UsbControlSocket,
+ USB_CONTROL_MAX_PORTS,
+};
+
+const SOCKET_TIMEOUT_MS: u64 = 2000;
+
+/// Host backend device provider is a xhci backend device provider that would provide pass through
+/// devices.
+pub enum HostBackendDeviceProvider {
+ // The provider is created but not yet started.
+ Created {
+ sock: MsgSocket<UsbControlResult, UsbControlCommand>,
+ },
+ // The provider is started on an event loop.
+ Started {
+ inner: Arc<ProviderInner>,
+ },
+ // The provider has failed.
+ Failed,
+}
+
+impl HostBackendDeviceProvider {
+ pub fn new() -> Result<(UsbControlSocket, HostBackendDeviceProvider)> {
+ let (child_sock, control_sock) = UnixSeqpacket::pair().map_err(Error::CreateControlSock)?;
+ control_sock
+ .set_write_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
+ .map_err(Error::SetupControlSock)?;
+ control_sock
+ .set_read_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
+ .map_err(Error::SetupControlSock)?;
+
+ let provider = HostBackendDeviceProvider::Created {
+ sock: MsgSocket::new(child_sock),
+ };
+ Ok((MsgSocket::new(control_sock), provider))
+ }
+
+ fn start_helper(
+ &mut self,
+ fail_handle: Arc<dyn FailHandle>,
+ event_loop: Arc<EventLoop>,
+ hub: Arc<UsbHub>,
+ ) -> Result<()> {
+ match mem::replace(self, HostBackendDeviceProvider::Failed) {
+ HostBackendDeviceProvider::Created { sock } => {
+ let ctx = Context::new(event_loop.clone())?;
+ let hotplug_handler = HotplugHandler::new(hub.clone());
+ ctx.set_hotplug_handler(hotplug_handler);
+ let job_queue =
+ AsyncJobQueue::init(&event_loop).map_err(Error::StartAsyncJobQueue)?;
+ let inner = Arc::new(ProviderInner::new(fail_handle, job_queue, ctx, sock, hub));
+ let handler: Arc<dyn EventHandler> = inner.clone();
+ event_loop
+ .add_event(
+ &inner.sock,
+ WatchingEvents::empty().set_read(),
+ Arc::downgrade(&handler),
+ )
+ .map_err(Error::AddToEventLoop)?;
+ *self = HostBackendDeviceProvider::Started { inner };
+ Ok(())
+ }
+ HostBackendDeviceProvider::Started { .. } => {
+ error!("Host backend provider has already started");
+ Err(Error::BadBackendProviderState)
+ }
+ HostBackendDeviceProvider::Failed => {
+ error!("Host backend provider has already failed");
+ Err(Error::BadBackendProviderState)
+ }
+ }
+ }
+}
+
+impl XhciBackendDeviceProvider for HostBackendDeviceProvider {
+ fn start(
+ &mut self,
+ fail_handle: Arc<dyn FailHandle>,
+ event_loop: Arc<EventLoop>,
+ hub: Arc<UsbHub>,
+ ) -> std::result::Result<(), ()> {
+ self.start_helper(fail_handle, event_loop, hub)
+ .map_err(|e| {
+ error!("failed to start host backend device provider: {}", e);
+ })
+ }
+
+ fn keep_fds(&self) -> Vec<RawFd> {
+ match self {
+ HostBackendDeviceProvider::Created { sock } => vec![sock.as_raw_fd()],
+ _ => {
+ error!(
+ "Trying to get keepfds when HostBackendDeviceProvider is not in created state"
+ );
+ vec![]
+ }
+ }
+ }
+}
+
+/// ProviderInner listens to control socket.
+pub struct ProviderInner {
+ fail_handle: Arc<dyn FailHandle>,
+ job_queue: Arc<AsyncJobQueue>,
+ ctx: Context,
+ sock: MsgSocket<UsbControlResult, UsbControlCommand>,
+ usb_hub: Arc<UsbHub>,
+}
+
+impl ProviderInner {
+ fn new(
+ fail_handle: Arc<dyn FailHandle>,
+ job_queue: Arc<AsyncJobQueue>,
+ ctx: Context,
+ sock: MsgSocket<UsbControlResult, UsbControlCommand>,
+ usb_hub: Arc<UsbHub>,
+ ) -> ProviderInner {
+ ProviderInner {
+ fail_handle,
+ job_queue,
+ ctx,
+ sock,
+ usb_hub,
+ }
+ }
+
+ fn handle_list_devices(&self, ports: [u8; USB_CONTROL_MAX_PORTS]) -> UsbControlResult {
+ let mut devices: [UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS] = Default::default();
+ for (result_index, &port_id) in ports.iter().enumerate() {
+ match self.usb_hub.get_port(port_id).and_then(|p| {
+ p.get_backend_device()
+ .as_ref()
+ .map(|d| (d.get_vid(), d.get_pid()))
+ }) {
+ Some((vendor_id, product_id)) => {
+ devices[result_index] = UsbControlAttachedDevice {
+ port: port_id,
+ vendor_id,
+ product_id,
+ }
+ }
+ None => continue,
+ }
+ }
+ UsbControlResult::Devices(devices)
+ }
+
+ fn on_event_helper(&self) -> Result<()> {
+ let cmd = self.sock.recv().map_err(Error::ReadControlSock)?;
+ match cmd {
+ UsbControlCommand::AttachDevice {
+ bus,
+ addr,
+ vid,
+ pid,
+ fd: usb_fd,
+ } => {
+ let _ = usb_fd;
+ #[cfg(not(feature = "sandboxed-libusb"))]
+ let device = match self.ctx.get_device(bus, addr, vid, pid) {
+ Some(d) => d,
+ None => {
+ error!(
+ "cannot get device bus: {}, addr: {}, vid: {}, pid: {}",
+ bus, addr, vid, pid
+ );
+ // The send failure will be logged, but event loop still think the event is
+ // handled.
+ let _ = self
+ .sock
+ .send(&UsbControlResult::NoSuchDevice)
+ .map_err(Error::WriteControlSock)?;
+ return Ok(());
+ }
+ };
+ #[cfg(feature = "sandboxed-libusb")]
+ let (device, device_handle) = {
+ use vm_control::MaybeOwnedFd;
+
+ let usb_file = match usb_fd {
+ Some(MaybeOwnedFd::Owned(file)) => file,
+ _ => {
+ let _ = self
+ .sock
+ .send(&UsbControlResult::FailedToOpenDevice)
+ .map_err(Error::WriteControlSock);
+ return Ok(());
+ }
+ };
+
+ let device_fd = usb_file.as_raw_fd();
+
+ let device = match self.ctx.get_device(usb_file) {
+ Some(d) => d,
+ None => {
+ error!(
+ "cannot get device bus: {}, addr: {}, vid: {}, pid: {}",
+ bus, addr, vid, pid
+ );
+ // The send failure will be logged, but event loop still think the event
+ // is handled.
+ let _ = self
+ .sock
+ .send(&UsbControlResult::NoSuchDevice)
+ .map_err(Error::WriteControlSock);
+ return Ok(());
+ }
+ };
+
+ let device_handle = {
+ // This is safe only when fd is an fd of the current device.
+ match unsafe { device.open_fd(device_fd) } {
+ Ok(handle) => handle,
+ Err(e) => {
+ error!("fail to open device: {:?}", e);
+ // The send failure will be logged, but event loop still think
+ // the event is handled.
+ let _ = self
+ .sock
+ .send(&UsbControlResult::FailedToOpenDevice)
+ .map_err(Error::WriteControlSock);
+ return Ok(());
+ }
+ }
+ };
+
+ // Resetting the device is used to make sure it is in a known state, but it may
+ // still function if the reset fails.
+ if let Err(e) = device_handle.reset() {
+ error!("failed to reset device after attach: {:?}", e);
+ }
+ (device, device_handle)
+ };
+
+ #[cfg(not(feature = "sandboxed-libusb"))]
+ let device_handle = match device.open() {
+ Ok(handle) => handle,
+ Err(e) => {
+ error!("fail to open device: {:?}", e);
+ // The send failure will be logged, but event loop still think the event is
+ // handled.
+ let _ = self
+ .sock
+ .send(&UsbControlResult::FailedToOpenDevice)
+ .map_err(Error::WriteControlSock);
+ return Ok(());
+ }
+ };
+ let device = Box::new(HostDevice::new(
+ self.fail_handle.clone(),
+ self.job_queue.clone(),
+ device,
+ device_handle,
+ ));
+ let port = self.usb_hub.connect_backend(device);
+ match port {
+ Ok(port) => {
+ // The send failure will be logged, but event loop still think the event is
+ // handled.
+ let _ = self
+ .sock
+ .send(&UsbControlResult::Ok { port })
+ .map_err(Error::WriteControlSock);
+ }
+ Err(e) => {
+ error!("failed to connect device to hub: {}", e);
+ // The send failure will be logged, but event loop still think the event is
+ // handled.
+ let _ = self
+ .sock
+ .send(&UsbControlResult::NoAvailablePort)
+ .map_err(Error::WriteControlSock);
+ }
+ }
+ Ok(())
+ }
+ UsbControlCommand::DetachDevice { port } => {
+ match self.usb_hub.disconnect_port(port) {
+ Ok(()) => {
+ // The send failure will be logged, but event loop still think the event is
+ // handled.
+ let _ = self
+ .sock
+ .send(&UsbControlResult::Ok { port })
+ .map_err(Error::WriteControlSock);
+ }
+ Err(e) => {
+ error!("failed to disconnect device from port {}: {}", port, e);
+ // The send failure will be logged, but event loop still think the event is
+ // handled.
+ let _ = self
+ .sock
+ .send(&UsbControlResult::NoSuchDevice)
+ .map_err(Error::WriteControlSock);
+ }
+ }
+ Ok(())
+ }
+ UsbControlCommand::ListDevice { ports } => {
+ let result = self.handle_list_devices(ports);
+ // The send failure will be logged, but event loop still think the event is
+ // handled.
+ let _ = self.sock.send(&result).map_err(Error::WriteControlSock);
+ Ok(())
+ }
+ }
+ }
+}
+
+impl EventHandler for ProviderInner {
+ fn on_event(&self) -> std::result::Result<(), ()> {
+ self.on_event_helper().map_err(|e| {
+ error!("host backend device provider failed: {}", e);
+ })
+ }
+}
diff --git a/devices/src/usb/host_backend/host_device.rs b/devices/src/usb/host_backend/host_device.rs
new file mode 100644
index 0000000..8841f00
--- /dev/null
+++ b/devices/src/usb/host_backend/host_device.rs
@@ -0,0 +1,503 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::mem::drop;
+use std::sync::Arc;
+use sync::Mutex;
+
+use super::error::*;
+use super::usb_endpoint::UsbEndpoint;
+use super::utils::{submit_transfer, update_transfer_state};
+use crate::usb::xhci::scatter_gather_buffer::ScatterGatherBuffer;
+use crate::usb::xhci::xhci_backend_device::{BackendType, UsbDeviceAddress, XhciBackendDevice};
+use crate::usb::xhci::xhci_transfer::{XhciTransfer, XhciTransferState, XhciTransferType};
+use crate::utils::AsyncJobQueue;
+use crate::utils::FailHandle;
+use std::collections::HashMap;
+use sys_util::{error, warn};
+use usb_util::device_handle::DeviceHandle;
+use usb_util::error::Error as LibUsbError;
+use usb_util::libusb_device::LibUsbDevice;
+use usb_util::types::{
+ ControlRequestDataPhaseTransferDirection, ControlRequestRecipient, StandardControlRequest,
+ UsbRequestSetup,
+};
+use usb_util::usb_transfer::{
+ control_transfer, ControlTransferBuffer, TransferStatus, UsbTransfer,
+};
+
+#[derive(PartialEq)]
+pub enum ControlEndpointState {
+ /// Control endpoint should receive setup stage next.
+ SetupStage,
+ /// Control endpoint should receive data stage next.
+ DataStage,
+ /// Control endpoint should receive status stage next.
+ StatusStage,
+}
+
+/// Host device is a device connected to host.
+pub struct HostDevice {
+ fail_handle: Arc<dyn FailHandle>,
+ // Endpoints only contains data endpoints (1 to 30). Control transfers are handled at device
+ // level.
+ endpoints: Vec<UsbEndpoint>,
+ device: LibUsbDevice,
+ device_handle: Arc<Mutex<DeviceHandle>>,
+ ctl_ep_state: ControlEndpointState,
+ alt_settings: HashMap<u16, u16>,
+ claimed_interfaces: Vec<i32>,
+ control_request_setup: UsbRequestSetup,
+ executed: bool,
+ job_queue: Arc<AsyncJobQueue>,
+}
+
+impl Drop for HostDevice {
+ fn drop(&mut self) {
+ self.release_interfaces();
+ }
+}
+
+impl HostDevice {
+ /// Create a new host device.
+ pub fn new(
+ fail_handle: Arc<dyn FailHandle>,
+ job_queue: Arc<AsyncJobQueue>,
+ device: LibUsbDevice,
+ device_handle: DeviceHandle,
+ ) -> HostDevice {
+ HostDevice {
+ fail_handle,
+ endpoints: vec![],
+ device,
+ device_handle: Arc::new(Mutex::new(device_handle)),
+ ctl_ep_state: ControlEndpointState::SetupStage,
+ alt_settings: HashMap::new(),
+ claimed_interfaces: vec![],
+ control_request_setup: UsbRequestSetup::new(0, 0, 0, 0, 0),
+ executed: false,
+ job_queue,
+ }
+ }
+
+ fn get_interface_number_of_active_config(&self) -> i32 {
+ match self.device.get_active_config_descriptor() {
+ Err(LibUsbError::NotFound) => {
+ usb_debug!("device is in unconfigured state");
+ 0
+ }
+ Err(e) => {
+ // device might be disconnected now.
+ error!("unexpected error: {:?}", e);
+ 0
+ }
+ Ok(descriptor) => descriptor.bNumInterfaces as i32,
+ }
+ }
+
+ fn release_interfaces(&mut self) {
+ for i in &self.claimed_interfaces {
+ if let Err(e) = self.device_handle.lock().release_interface(*i) {
+ error!("could not release interface: {:?}", e);
+ }
+ }
+ self.claimed_interfaces = Vec::new();
+ }
+
+ // Check for requests that should be intercepted and emulated using libusb
+ // functions rather than passed directly to the device.
+ // Returns true if the request has been intercepted or false if the request
+ // should be passed through to the device.
+ fn intercepted_control_transfer(&mut self, xhci_transfer: &XhciTransfer) -> Result<bool> {
+ let direction = self.control_request_setup.get_direction();
+ let recipient = self.control_request_setup.get_recipient();
+ let standard_request = self.control_request_setup.get_standard_request();
+
+ if direction != ControlRequestDataPhaseTransferDirection::HostToDevice {
+ // Only host to device requests are intercepted currently.
+ return Ok(false);
+ }
+
+ let status = match standard_request {
+ Some(StandardControlRequest::SetAddress) => {
+ if recipient != ControlRequestRecipient::Device {
+ return Ok(false);
+ }
+ usb_debug!("host device handling set address");
+ let addr = self.control_request_setup.value as u32;
+ self.set_address(addr);
+ TransferStatus::Completed
+ }
+ Some(StandardControlRequest::SetConfiguration) => {
+ if recipient != ControlRequestRecipient::Device {
+ return Ok(false);
+ }
+ usb_debug!("host device handling set config");
+ self.set_config()?
+ }
+ Some(StandardControlRequest::SetInterface) => {
+ if recipient != ControlRequestRecipient::Interface {
+ return Ok(false);
+ }
+ usb_debug!("host device handling set interface");
+ self.set_interface()?
+ }
+ Some(StandardControlRequest::ClearFeature) => {
+ if recipient != ControlRequestRecipient::Endpoint {
+ return Ok(false);
+ }
+ usb_debug!("host device handling clear feature");
+ self.clear_feature()?
+ }
+ _ => {
+ // Other requests will be passed through to the device.
+ return Ok(false);
+ }
+ };
+
+ xhci_transfer
+ .on_transfer_complete(&status, 0)
+ .map_err(Error::TransferComplete)?;
+ Ok(true)
+ }
+
+ fn execute_control_transfer(
+ &mut self,
+ xhci_transfer: Arc<XhciTransfer>,
+ buffer: Option<ScatterGatherBuffer>,
+ ) -> Result<()> {
+ let mut control_transfer = control_transfer(0);
+ control_transfer
+ .buffer_mut()
+ .set_request_setup(&self.control_request_setup);
+
+ if self.intercepted_control_transfer(&xhci_transfer)? {
+ return Ok(());
+ }
+
+ let direction = self.control_request_setup.get_direction();
+ let buffer = if direction == ControlRequestDataPhaseTransferDirection::HostToDevice {
+ if let Some(buffer) = buffer {
+ buffer
+ .read(&mut control_transfer.buffer_mut().data_buffer)
+ .map_err(Error::ReadBuffer)?;
+ }
+ // buffer is consumed here for HostToDevice transfers.
+ None
+ } else {
+ // buffer will be used later in the callback for DeviceToHost transfers.
+ buffer
+ };
+
+ let tmp_transfer = xhci_transfer.clone();
+ let callback = move |t: UsbTransfer<ControlTransferBuffer>| {
+ usb_debug!("setup token control transfer callback invoked");
+ update_transfer_state(&xhci_transfer, &t)?;
+ let state = xhci_transfer.state().lock();
+ match *state {
+ XhciTransferState::Cancelled => {
+ usb_debug!("transfer cancelled");
+ drop(state);
+ xhci_transfer
+ .on_transfer_complete(&TransferStatus::Cancelled, 0)
+ .map_err(Error::TransferComplete)?;
+ }
+ XhciTransferState::Completed => {
+ let status = t.status();
+ let actual_length = t.actual_length();
+ if direction == ControlRequestDataPhaseTransferDirection::DeviceToHost {
+ if let Some(buffer) = &buffer {
+ buffer
+ .write(&t.buffer().data_buffer)
+ .map_err(Error::WriteBuffer)?;
+ }
+ }
+ drop(state);
+ usb_debug!("transfer completed with actual length {}", actual_length);
+ xhci_transfer
+ .on_transfer_complete(&status, actual_length as u32)
+ .map_err(Error::TransferComplete)?;
+ }
+ _ => {
+ // update_transfer_state is already invoked before match.
+ // This transfer could only be `Cancelled` or `Completed`.
+ // Any other state means there is a bug in crosvm implementation.
+ error!("should not take this branch");
+ return Err(Error::BadXhciTransferState);
+ }
+ }
+ Ok(())
+ };
+
+ let fail_handle = self.fail_handle.clone();
+ control_transfer.set_callback(
+ move |t: UsbTransfer<ControlTransferBuffer>| match callback(t) {
+ Ok(_) => {}
+ Err(e) => {
+ error!("control transfer callback failed {:?}", e);
+ fail_handle.fail();
+ }
+ },
+ );
+ submit_transfer(
+ self.fail_handle.clone(),
+ &self.job_queue,
+ tmp_transfer,
+ &self.device_handle,
+ control_transfer,
+ )
+ }
+
+ fn handle_control_transfer(&mut self, transfer: XhciTransfer) -> Result<()> {
+ let xhci_transfer = Arc::new(transfer);
+ match xhci_transfer
+ .get_transfer_type()
+ .map_err(Error::GetXhciTransferType)?
+ {
+ XhciTransferType::SetupStage(setup) => {
+ if self.ctl_ep_state != ControlEndpointState::SetupStage {
+ error!("Control endpoint is in an inconsistant state");
+ return Ok(());
+ }
+ usb_debug!("setup stage setup buffer: {:?}", setup);
+ self.control_request_setup = setup;
+ xhci_transfer
+ .on_transfer_complete(&TransferStatus::Completed, 0)
+ .map_err(Error::TransferComplete)?;
+ self.ctl_ep_state = ControlEndpointState::DataStage;
+ }
+ XhciTransferType::DataStage(buffer) => {
+ if self.ctl_ep_state != ControlEndpointState::DataStage {
+ error!("Control endpoint is in an inconsistant state");
+ return Ok(());
+ }
+ // Requests with a DataStage will be executed here.
+ // Requests without a DataStage will be executed in StatusStage.
+ self.execute_control_transfer(xhci_transfer, Some(buffer))?;
+ self.executed = true;
+ self.ctl_ep_state = ControlEndpointState::StatusStage;
+ }
+ XhciTransferType::StatusStage => {
+ if self.ctl_ep_state == ControlEndpointState::SetupStage {
+ error!("Control endpoint is in an inconsistant state");
+ return Ok(());
+ }
+ if self.executed {
+ // Request was already executed during DataStage.
+ // Just complete the StatusStage transfer.
+ xhci_transfer
+ .on_transfer_complete(&TransferStatus::Completed, 0)
+ .map_err(Error::TransferComplete)?;
+ } else {
+ // Execute the request now since there was no DataStage.
+ self.execute_control_transfer(xhci_transfer, None)?;
+ }
+ self.executed = false;
+ self.ctl_ep_state = ControlEndpointState::SetupStage;
+ }
+ _ => {
+ // Non control transfer should not be handled in this function.
+ error!("Non control (could be noop) transfer sent to control endpoint.");
+ xhci_transfer
+ .on_transfer_complete(&TransferStatus::Completed, 0)
+ .map_err(Error::TransferComplete)?;
+ }
+ }
+ Ok(())
+ }
+
+ fn set_config(&mut self) -> Result<TransferStatus> {
+ // It's a standard, set_config, device request.
+ let config = (self.control_request_setup.value & 0xff) as i32;
+ usb_debug!(
+ "Set config control transfer is received with config: {}",
+ config
+ );
+ self.release_interfaces();
+ let cur_config = self
+ .device_handle
+ .lock()
+ .get_active_configuration()
+ .map_err(Error::GetActiveConfig)?;
+ usb_debug!("current config is: {}", cur_config);
+ if config != cur_config {
+ self.device_handle
+ .lock()
+ .set_active_configuration(config)
+ .map_err(Error::SetActiveConfig)?;
+ }
+ self.claim_interfaces();
+ self.create_endpoints()?;
+ Ok(TransferStatus::Completed)
+ }
+
+ fn set_interface(&mut self) -> Result<TransferStatus> {
+ usb_debug!("set interface");
+ // It's a standard, set_interface, interface request.
+ let interface = self.control_request_setup.index;
+ let alt_setting = self.control_request_setup.value;
+ self.device_handle
+ .lock()
+ .set_interface_alt_setting(interface as i32, alt_setting as i32)
+ .map_err(Error::SetInterfaceAltSetting)?;
+ self.alt_settings.insert(interface, alt_setting);
+ self.create_endpoints()?;
+ Ok(TransferStatus::Completed)
+ }
+
+ fn clear_feature(&mut self) -> Result<TransferStatus> {
+ usb_debug!("clear feature");
+ let request_setup = &self.control_request_setup;
+ // It's a standard, clear_feature, endpoint request.
+ const STD_FEATURE_ENDPOINT_HALT: u16 = 0;
+ if request_setup.value == STD_FEATURE_ENDPOINT_HALT {
+ self.device_handle
+ .lock()
+ .clear_halt(request_setup.index as u8)
+ .map_err(Error::ClearHalt)?;
+ }
+ Ok(TransferStatus::Completed)
+ }
+
+ fn claim_interfaces(&mut self) {
+ for i in 0..self.get_interface_number_of_active_config() {
+ match self.device_handle.lock().claim_interface(i) {
+ Ok(()) => {
+ usb_debug!("claimed interface {}", i);
+ self.claimed_interfaces.push(i);
+ }
+ Err(e) => {
+ error!("unable to claim interface {}: {:?}", i, e);
+ }
+ }
+ }
+ }
+
+ fn create_endpoints(&mut self) -> Result<()> {
+ self.endpoints = Vec::new();
+ let config_descriptor = match self.device.get_active_config_descriptor() {
+ Err(e) => {
+ error!("device might be disconnected: {:?}", e);
+ return Ok(());
+ }
+ Ok(descriptor) => descriptor,
+ };
+ for i in &self.claimed_interfaces {
+ let alt_setting = self.alt_settings.get(&(*i as u16)).unwrap_or(&0);
+ let interface = config_descriptor
+ .get_interface_descriptor(*i as u8, *alt_setting as i32)
+ .ok_or(Error::GetInterfaceDescriptor((*i, *alt_setting)))?;
+ for ep_idx in 0..interface.bNumEndpoints {
+ let ep_dp = interface
+ .endpoint_descriptor(ep_idx)
+ .ok_or(Error::GetEndpointDescriptor(ep_idx))?;
+ let ep_num = ep_dp.get_endpoint_number();
+ if ep_num == 0 {
+ usb_debug!("endpoint 0 in endpoint descriptors");
+ continue;
+ }
+ let direction = ep_dp.get_direction();
+ let ty = ep_dp.get_endpoint_type().ok_or(Error::GetEndpointType)?;
+ self.endpoints.push(UsbEndpoint::new(
+ self.fail_handle.clone(),
+ self.job_queue.clone(),
+ self.device_handle.clone(),
+ ep_num,
+ direction,
+ ty,
+ ));
+ }
+ }
+ Ok(())
+ }
+
+ fn submit_transfer_helper(&mut self, transfer: XhciTransfer) -> Result<()> {
+ if transfer.get_endpoint_number() == 0 {
+ return self.handle_control_transfer(transfer);
+ }
+ for ep in &self.endpoints {
+ if ep.match_ep(transfer.get_endpoint_number(), transfer.get_transfer_dir()) {
+ return ep.handle_transfer(transfer);
+ }
+ }
+ warn!("Could not find endpoint for transfer");
+ transfer
+ .on_transfer_complete(&TransferStatus::Error, 0)
+ .map_err(Error::TransferComplete)
+ }
+}
+
+impl XhciBackendDevice for HostDevice {
+ fn get_backend_type(&self) -> BackendType {
+ let d = match self.device.get_device_descriptor() {
+ Ok(d) => d,
+ Err(_) => return BackendType::Usb2,
+ };
+
+ // See definition of bcdUsb.
+ const USB3_MASK: u16 = 0x0300;
+ match d.bcdUSB & USB3_MASK {
+ USB3_MASK => BackendType::Usb3,
+ _ => BackendType::Usb2,
+ }
+ }
+
+ fn host_bus(&self) -> u8 {
+ self.device.get_bus_number()
+ }
+
+ fn host_address(&self) -> u8 {
+ self.device.get_address()
+ }
+
+ fn get_vid(&self) -> u16 {
+ match self.device.get_device_descriptor() {
+ Ok(d) => d.idVendor,
+ Err(e) => {
+ error!("cannot get device descriptor: {:?}", e);
+ 0
+ }
+ }
+ }
+
+ fn get_pid(&self) -> u16 {
+ match self.device.get_device_descriptor() {
+ Ok(d) => d.idProduct,
+ Err(e) => {
+ error!("cannot get device descriptor: {:?}", e);
+ 0
+ }
+ }
+ }
+
+ fn submit_transfer(&mut self, transfer: XhciTransfer) -> std::result::Result<(), ()> {
+ self.submit_transfer_helper(transfer).map_err(|e| {
+ error!("failed to submit transfer: {}", e);
+ })
+ }
+
+ fn set_address(&mut self, _address: UsbDeviceAddress) {
+ // It's a standard, set_address, device request. We do nothing here. As described in XHCI
+ // spec. See set address command ring trb.
+ usb_debug!(
+ "Set address control transfer is received with address: {}",
+ _address
+ );
+ }
+
+ fn reset(&mut self) -> std::result::Result<(), ()> {
+ usb_debug!("resetting host device");
+ let result = self.device_handle.lock().reset();
+ match result {
+ Err(LibUsbError::NotFound) => {
+ // libusb will return NotFound if it fails to re-claim
+ // the interface after the reset.
+ Ok(())
+ }
+ _ => result.map_err(|e| {
+ error!("failed to reset device: {:?}", e);
+ }),
+ }
+ }
+}
diff --git a/devices/src/usb/host_backend/hotplug.rs b/devices/src/usb/host_backend/hotplug.rs
new file mode 100644
index 0000000..0764660
--- /dev/null
+++ b/devices/src/usb/host_backend/hotplug.rs
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::sync::Arc;
+
+use crate::usb::xhci::usb_hub::UsbHub;
+use sys_util::error;
+use usb_util::hotplug::{HotplugEvent, UsbHotplugHandler};
+use usb_util::libusb_device::LibUsbDevice;
+
+pub struct HotplugHandler {
+ hub: Arc<UsbHub>,
+}
+
+impl HotplugHandler {
+ pub fn new(hub: Arc<UsbHub>) -> Self {
+ HotplugHandler { hub }
+ }
+}
+
+impl UsbHotplugHandler for HotplugHandler {
+ fn hotplug_event(&self, device: LibUsbDevice, event: HotplugEvent) {
+ if event != HotplugEvent::DeviceLeft {
+ return;
+ }
+
+ let bus = device.get_bus_number();
+ let address = device.get_address();
+ let descriptor = match device.get_device_descriptor() {
+ Ok(d) => d,
+ Err(e) => {
+ error!("cannot get device descriptor: {:?}", e);
+ return;
+ }
+ };
+ let vid = descriptor.idVendor;
+ let pid = descriptor.idProduct;
+
+ if let Err(e) = self.hub.try_detach(bus, address, vid, pid) {
+ error!("device left event triggered failed detach from hub: {}", e);
+ return;
+ }
+ }
+}
diff --git a/devices/src/usb/host_backend/mod.rs b/devices/src/usb/host_backend/mod.rs
new file mode 100644
index 0000000..ea0f44c
--- /dev/null
+++ b/devices/src/usb/host_backend/mod.rs
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+pub mod context;
+pub mod error;
+pub mod host_backend_device_provider;
+pub mod host_device;
+mod hotplug;
+pub mod usb_endpoint;
+mod utils;
diff --git a/devices/src/usb/host_backend/usb_endpoint.rs b/devices/src/usb/host_backend/usb_endpoint.rs
new file mode 100644
index 0000000..329d6ff
--- /dev/null
+++ b/devices/src/usb/host_backend/usb_endpoint.rs
@@ -0,0 +1,254 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::cmp;
+use std::sync::Arc;
+use sync::Mutex;
+
+use super::error::*;
+use super::utils::{submit_transfer, update_transfer_state};
+use crate::usb::xhci::scatter_gather_buffer::ScatterGatherBuffer;
+use crate::usb::xhci::xhci_transfer::{
+ TransferDirection, XhciTransfer, XhciTransferState, XhciTransferType,
+};
+use crate::utils::AsyncJobQueue;
+use crate::utils::FailHandle;
+use sys_util::error;
+use usb_util::device_handle::DeviceHandle;
+use usb_util::types::{EndpointDirection, EndpointType, ENDPOINT_DIRECTION_OFFSET};
+use usb_util::usb_transfer::{
+ bulk_transfer, interrupt_transfer, BulkTransferBuffer, TransferStatus, UsbTransfer,
+};
+
+/// Isochronous, Bulk or Interrupt endpoint.
+pub struct UsbEndpoint {
+ fail_handle: Arc<dyn FailHandle>,
+ job_queue: Arc<AsyncJobQueue>,
+ device_handle: Arc<Mutex<DeviceHandle>>,
+ endpoint_number: u8,
+ direction: EndpointDirection,
+ ty: EndpointType,
+}
+
+impl UsbEndpoint {
+ /// Create new endpoint. This function will panic if endpoint type is control.
+ pub fn new(
+ fail_handle: Arc<dyn FailHandle>,
+ job_queue: Arc<AsyncJobQueue>,
+ device_handle: Arc<Mutex<DeviceHandle>>,
+ endpoint_number: u8,
+ direction: EndpointDirection,
+ ty: EndpointType,
+ ) -> UsbEndpoint {
+ assert!(ty != EndpointType::Control);
+ UsbEndpoint {
+ fail_handle,
+ job_queue,
+ device_handle,
+ endpoint_number,
+ direction,
+ ty,
+ }
+ }
+
+ fn ep_addr(&self) -> u8 {
+ self.endpoint_number | ((self.direction as u8) << ENDPOINT_DIRECTION_OFFSET)
+ }
+
+ /// Returns true is this endpoint matches number and direction.
+ pub fn match_ep(&self, endpoint_number: u8, dir: TransferDirection) -> bool {
+ let self_dir = match self.direction {
+ EndpointDirection::HostToDevice => TransferDirection::Out,
+ EndpointDirection::DeviceToHost => TransferDirection::In,
+ };
+ self.endpoint_number == endpoint_number && self_dir == dir
+ }
+
+ /// Handle a xhci transfer.
+ pub fn handle_transfer(&self, transfer: XhciTransfer) -> Result<()> {
+ let buffer = match transfer
+ .get_transfer_type()
+ .map_err(Error::GetXhciTransferType)?
+ {
+ XhciTransferType::Normal(buffer) => buffer,
+ XhciTransferType::Noop => {
+ return transfer
+ .on_transfer_complete(&TransferStatus::Completed, 0)
+ .map_err(Error::TransferComplete);
+ }
+ _ => {
+ error!("unhandled xhci transfer type by usb endpoint");
+ return transfer
+ .on_transfer_complete(&TransferStatus::Error, 0)
+ .map_err(Error::TransferComplete);
+ }
+ };
+
+ match self.ty {
+ EndpointType::Bulk => {
+ self.handle_bulk_transfer(transfer, buffer)?;
+ }
+ EndpointType::Interrupt => {
+ self.handle_interrupt_transfer(transfer, buffer)?;
+ }
+ _ => {
+ return transfer
+ .on_transfer_complete(&TransferStatus::Error, 0)
+ .map_err(Error::TransferComplete);
+ }
+ }
+ Ok(())
+ }
+
+ fn handle_bulk_transfer(
+ &self,
+ xhci_transfer: XhciTransfer,
+ buffer: ScatterGatherBuffer,
+ ) -> Result<()> {
+ let usb_transfer =
+ bulk_transfer(self.ep_addr(), 0, buffer.len().map_err(Error::BufferLen)?);
+ self.do_handle_transfer(xhci_transfer, usb_transfer, buffer)
+ }
+
+ fn handle_interrupt_transfer(
+ &self,
+ xhci_transfer: XhciTransfer,
+ buffer: ScatterGatherBuffer,
+ ) -> Result<()> {
+ let usb_transfer =
+ interrupt_transfer(self.ep_addr(), 0, buffer.len().map_err(Error::BufferLen)?);
+ self.do_handle_transfer(xhci_transfer, usb_transfer, buffer)
+ }
+
+ fn do_handle_transfer(
+ &self,
+ xhci_transfer: XhciTransfer,
+ mut usb_transfer: UsbTransfer<BulkTransferBuffer>,
+ buffer: ScatterGatherBuffer,
+ ) -> Result<()> {
+ let xhci_transfer = Arc::new(xhci_transfer);
+ let tmp_transfer = xhci_transfer.clone();
+ match self.direction {
+ EndpointDirection::HostToDevice => {
+ // Read data from ScatterGatherBuffer to a continuous memory.
+ buffer
+ .read(usb_transfer.buffer_mut().as_mut_slice())
+ .map_err(Error::ReadBuffer)?;
+ usb_debug!(
+ "out transfer ep_addr {:#x}, buffer len {:?}, data {:#x?}",
+ self.ep_addr(),
+ buffer.len(),
+ usb_transfer.buffer_mut().as_mut_slice()
+ );
+ let callback = move |t: UsbTransfer<BulkTransferBuffer>| {
+ usb_debug!("out transfer callback");
+ update_transfer_state(&xhci_transfer, &t)?;
+ let state = xhci_transfer.state().lock();
+ match *state {
+ XhciTransferState::Cancelled => {
+ usb_debug!("transfer has been cancelled");
+ drop(state);
+ xhci_transfer
+ .on_transfer_complete(&TransferStatus::Cancelled, 0)
+ .map_err(Error::TransferComplete)
+ }
+ XhciTransferState::Completed => {
+ let status = t.status();
+ let actual_length = t.actual_length();
+ drop(state);
+ xhci_transfer
+ .on_transfer_complete(&status, actual_length as u32)
+ .map_err(Error::TransferComplete)
+ }
+ _ => {
+ error!("xhci trasfer state (host to device) is invalid");
+ Err(Error::BadXhciTransferState)
+ }
+ }
+ };
+ let fail_handle = self.fail_handle.clone();
+ usb_transfer.set_callback(
+ move |t: UsbTransfer<BulkTransferBuffer>| match callback(t) {
+ Ok(_) => {}
+ Err(e) => {
+ error!("bulk transfer callback failed: {:?}", e);
+ fail_handle.fail();
+ }
+ },
+ );
+ submit_transfer(
+ self.fail_handle.clone(),
+ &self.job_queue,
+ tmp_transfer,
+ &self.device_handle,
+ usb_transfer,
+ )?;
+ }
+ EndpointDirection::DeviceToHost => {
+ usb_debug!(
+ "in transfer ep_addr {:#x}, buffer len {:?}",
+ self.ep_addr(),
+ buffer.len()
+ );
+ let _addr = self.ep_addr();
+ let callback = move |t: UsbTransfer<BulkTransferBuffer>| {
+ usb_debug!(
+ "ep {:#x} in transfer data {:?}",
+ _addr,
+ t.buffer().as_slice()
+ );
+ update_transfer_state(&xhci_transfer, &t)?;
+ let state = xhci_transfer.state().lock();
+ match *state {
+ XhciTransferState::Cancelled => {
+ usb_debug!("transfer has been cancelled");
+ drop(state);
+ xhci_transfer
+ .on_transfer_complete(&TransferStatus::Cancelled, 0)
+ .map_err(Error::TransferComplete)
+ }
+ XhciTransferState::Completed => {
+ let status = t.status();
+ let actual_length = t.actual_length() as usize;
+ let copied_length = buffer
+ .write(t.buffer().as_slice())
+ .map_err(Error::WriteBuffer)?;
+ let actual_length = cmp::min(actual_length, copied_length);
+ drop(state);
+ xhci_transfer
+ .on_transfer_complete(&status, actual_length as u32)
+ .map_err(Error::TransferComplete)
+ }
+ _ => {
+ // update state is already invoked. This match should not be in any
+ // other state.
+ error!("xhci trasfer state (device to host) is invalid");
+ Err(Error::BadXhciTransferState)
+ }
+ }
+ };
+ let fail_handle = self.fail_handle.clone();
+
+ usb_transfer.set_callback(
+ move |t: UsbTransfer<BulkTransferBuffer>| match callback(t) {
+ Ok(_) => {}
+ Err(e) => {
+ error!("bulk transfer callback {:?}", e);
+ fail_handle.fail();
+ }
+ },
+ );
+
+ submit_transfer(
+ self.fail_handle.clone(),
+ &self.job_queue,
+ tmp_transfer,
+ &self.device_handle,
+ usb_transfer,
+ )?;
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/devices/src/usb/host_backend/utils.rs b/devices/src/usb/host_backend/utils.rs
new file mode 100644
index 0000000..7d23bd4
--- /dev/null
+++ b/devices/src/usb/host_backend/utils.rs
@@ -0,0 +1,110 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::mem;
+use std::sync::Arc;
+use sync::Mutex;
+
+use super::error::*;
+use crate::usb::xhci::xhci_transfer::{XhciTransfer, XhciTransferState};
+use crate::utils::AsyncJobQueue;
+use crate::utils::FailHandle;
+use sys_util::{error, warn};
+use usb_util::device_handle::DeviceHandle;
+use usb_util::usb_transfer::{TransferStatus, UsbTransfer, UsbTransferBuffer};
+
+/// Helper function to update xhci_transfer state.
+pub fn update_transfer_state<T: UsbTransferBuffer>(
+ xhci_transfer: &Arc<XhciTransfer>,
+ usb_transfer: &UsbTransfer<T>,
+) -> Result<()> {
+ let status = usb_transfer.status();
+ let mut state = xhci_transfer.state().lock();
+
+ if status == TransferStatus::Cancelled {
+ *state = XhciTransferState::Cancelled;
+ return Ok(());
+ }
+
+ match *state {
+ XhciTransferState::Cancelling => {
+ *state = XhciTransferState::Cancelled;
+ }
+ XhciTransferState::Submitted { .. } => {
+ *state = XhciTransferState::Completed;
+ }
+ _ => {
+ error!("xhci trasfer state is invalid");
+ *state = XhciTransferState::Completed;
+ return Err(Error::BadXhciTransferState);
+ }
+ }
+ Ok(())
+}
+
+/// Helper function to submit usb_transfer to device handle.
+pub fn submit_transfer<T: UsbTransferBuffer>(
+ fail_handle: Arc<dyn FailHandle>,
+ job_queue: &Arc<AsyncJobQueue>,
+ xhci_transfer: Arc<XhciTransfer>,
+ device_handle: &Arc<Mutex<DeviceHandle>>,
+ usb_transfer: UsbTransfer<T>,
+) -> Result<()> {
+ let transfer_status = {
+ // We need to hold the lock to avoid race condition.
+ // While we are trying to submit the transfer, another thread might want to cancel the same
+ // transfer. Holding the lock here makes sure one of them is cancelled.
+ let mut state = xhci_transfer.state().lock();
+ match mem::replace(&mut *state, XhciTransferState::Cancelled) {
+ XhciTransferState::Created => {
+ let canceller = usb_transfer.get_canceller();
+ // TODO(jkwang) refactor canceller to return Cancel::Ok or Cancel::Err.
+ let cancel_callback = Box::new(move || match canceller.try_cancel() {
+ true => {
+ usb_debug!("cancel issued to libusb backend");
+ }
+ false => {
+ usb_debug!("fail to cancel");
+ }
+ });
+ *state = XhciTransferState::Submitted { cancel_callback };
+ match device_handle.lock().submit_async_transfer(usb_transfer) {
+ Err(e) => {
+ error!("fail to submit transfer {:?}", e);
+ *state = XhciTransferState::Completed;
+ TransferStatus::NoDevice
+ }
+ // If it's submitted, we don't need to send on_transfer_complete now.
+ Ok(_) => return Ok(()),
+ }
+ }
+ XhciTransferState::Cancelled => {
+ warn!("Transfer is already cancelled");
+ TransferStatus::Cancelled
+ }
+ _ => {
+ // The transfer could not be in the following states:
+ // Submitted: A transfer should only be submitted once.
+ // Cancelling: Transfer is cancelling only when it's submitted and someone is
+ // trying to cancel it.
+ // Completed: A completed transfer should not be submitted again.
+ error!("xhci trasfer state is invalid");
+ return Err(Error::BadXhciTransferState);
+ }
+ }
+ };
+ // We are holding locks to of backends, we want to call on_transfer_complete
+ // without any lock.
+ job_queue
+ .queue_job(
+ move || match xhci_transfer.on_transfer_complete(&transfer_status, 0) {
+ Ok(_) => {}
+ Err(e) => {
+ error!("transfer complete failed: {:?}", e);
+ fail_handle.fail();
+ }
+ },
+ )
+ .map_err(Error::QueueAsyncJob)
+}
diff --git a/devices/src/usb/log.rs b/devices/src/usb/log.rs
new file mode 100644
index 0000000..4cb906b
--- /dev/null
+++ b/devices/src/usb/log.rs
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(jkwang) remove this macro once we have a proper tracing system.
+#[macro_export]
+macro_rules! usb_debug {
+ ($($args:tt)+) => {
+ // Set true to enable logging.
+ if false {
+ sys_util::debug!($($args)*);
+ }
+ };
+}
diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs
new file mode 100644
index 0000000..dde6189
--- /dev/null
+++ b/devices/src/usb/mod.rs
@@ -0,0 +1,8 @@
+// Copyright 2018 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.
+
+#[macro_use]
+mod log;
+pub mod host_backend;
+pub mod xhci;
diff --git a/devices/src/usb/xhci/command_ring_controller.rs b/devices/src/usb/xhci/command_ring_controller.rs
new file mode 100644
index 0000000..e893194
--- /dev/null
+++ b/devices/src/usb/xhci/command_ring_controller.rs
@@ -0,0 +1,395 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::device_slot::{DeviceSlot, DeviceSlots, Error as DeviceSlotError};
+use super::interrupter::{Error as InterrupterError, Interrupter};
+use super::ring_buffer_controller::{
+ Error as RingBufferControllerError, RingBufferController, TransferDescriptorHandler,
+};
+use super::xhci_abi::{
+ AddressDeviceCommandTrb, AddressedTrb, ConfigureEndpointCommandTrb, DisableSlotCommandTrb,
+ Error as TrbError, EvaluateContextCommandTrb, ResetDeviceCommandTrb,
+ SetTRDequeuePointerCommandTrb, StopEndpointCommandTrb, TransferDescriptor, TrbCast,
+ TrbCompletionCode, TrbType,
+};
+use super::xhci_regs::{valid_slot_id, MAX_SLOTS};
+use crate::utils::EventLoop;
+use std::fmt::{self, Display};
+use std::sync::Arc;
+use sync::Mutex;
+use sys_util::{error, warn, Error as SysError, EventFd, GuestAddress, GuestMemory};
+
+#[derive(Debug)]
+pub enum Error {
+ WriteEventFd(SysError),
+ SendInterrupt(InterrupterError),
+ CastTrb(TrbError),
+ BadSlotId(u8),
+ StopEndpoint(DeviceSlotError),
+ ConfigEndpoint(DeviceSlotError),
+ SetAddress(DeviceSlotError),
+ SetDequeuePointer(DeviceSlotError),
+ EvaluateContext(DeviceSlotError),
+ DisableSlot(DeviceSlotError),
+ ResetSlot(DeviceSlotError),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ WriteEventFd(e) => write!(f, "failed to write event fd: {}", e),
+ SendInterrupt(e) => write!(f, "failed to send interrupt: {}", e),
+ CastTrb(e) => write!(f, "failed to cast trb: {}", e),
+ BadSlotId(id) => write!(f, "bad slot id: {}", id),
+ StopEndpoint(e) => write!(f, "failed to stop endpoint: {}", e),
+ ConfigEndpoint(e) => write!(f, "failed to config endpoint: {}", e),
+ SetAddress(e) => write!(f, "failed to set address: {}", e),
+ SetDequeuePointer(e) => write!(f, "failed to set dequeue pointer: {}", e),
+ EvaluateContext(e) => write!(f, "failed to evaluate context: {}", e),
+ DisableSlot(e) => write!(f, "failed to disable slot: {}", e),
+ ResetSlot(e) => write!(f, "failed to reset slot: {}", e),
+ }
+ }
+}
+
+pub type CommandRingController = RingBufferController<CommandRingTrbHandler>;
+pub type CommandRingControllerError = RingBufferControllerError;
+
+impl CommandRingController {
+ pub fn new(
+ mem: GuestMemory,
+ event_loop: Arc<EventLoop>,
+ slots: DeviceSlots,
+ interrupter: Arc<Mutex<Interrupter>>,
+ ) -> std::result::Result<Arc<CommandRingController>, RingBufferControllerError> {
+ RingBufferController::new_with_handler(
+ String::from("command ring"),
+ mem,
+ event_loop,
+ CommandRingTrbHandler::new(slots, interrupter),
+ )
+ }
+}
+
+pub struct CommandRingTrbHandler {
+ slots: DeviceSlots,
+ interrupter: Arc<Mutex<Interrupter>>,
+}
+
+impl CommandRingTrbHandler {
+ fn new(slots: DeviceSlots, interrupter: Arc<Mutex<Interrupter>>) -> Self {
+ CommandRingTrbHandler { slots, interrupter }
+ }
+
+ fn slot(&self, slot_id: u8) -> Result<Arc<DeviceSlot>> {
+ self.slots.slot(slot_id).ok_or(Error::BadSlotId(slot_id))
+ }
+
+ fn command_completion_callback(
+ interrupter: &Arc<Mutex<Interrupter>>,
+ completion_code: TrbCompletionCode,
+ slot_id: u8,
+ trb_addr: u64,
+ event_fd: &EventFd,
+ ) -> Result<()> {
+ interrupter
+ .lock()
+ .send_command_completion_trb(completion_code, slot_id, GuestAddress(trb_addr))
+ .map_err(Error::SendInterrupt)?;
+ event_fd.write(1).map_err(Error::WriteEventFd)
+ }
+
+ fn enable_slot(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> {
+ for slot_id in 1..=MAX_SLOTS {
+ if self.slot(slot_id)?.enable() {
+ return CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ TrbCompletionCode::Success,
+ slot_id,
+ atrb.gpa,
+ &event_fd,
+ );
+ }
+ }
+
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ TrbCompletionCode::NoSlotsAvailableError,
+ 0,
+ atrb.gpa,
+ &event_fd,
+ )
+ }
+
+ fn disable_slot(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> {
+ let trb = atrb
+ .trb
+ .cast::<DisableSlotCommandTrb>()
+ .map_err(Error::CastTrb)?;
+ let slot_id = trb.get_slot_id();
+ if valid_slot_id(slot_id) {
+ let gpa = atrb.gpa;
+ let interrupter = self.interrupter.clone();
+ self.slots
+ .disable_slot(slot_id, move |completion_code| {
+ CommandRingTrbHandler::command_completion_callback(
+ &interrupter,
+ completion_code,
+ slot_id,
+ gpa,
+ &event_fd,
+ )
+ .map_err(|e| {
+ error!("failed to run command completion callback: {}", e);
+ })
+ })
+ .map_err(Error::DisableSlot)
+ } else {
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ TrbCompletionCode::TrbError,
+ slot_id,
+ atrb.gpa,
+ &event_fd,
+ )
+ }
+ }
+
+ fn address_device(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> {
+ let trb = atrb
+ .trb
+ .cast::<AddressDeviceCommandTrb>()
+ .map_err(Error::CastTrb)?;
+ let slot_id = trb.get_slot_id();
+ let completion_code = {
+ if valid_slot_id(slot_id) {
+ self.slot(slot_id)?
+ .set_address(trb)
+ .map_err(Error::SetAddress)?
+ } else {
+ TrbCompletionCode::TrbError
+ }
+ };
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ completion_code,
+ slot_id,
+ atrb.gpa,
+ &event_fd,
+ )
+ }
+
+ fn configure_endpoint(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> {
+ let trb = atrb
+ .trb
+ .cast::<ConfigureEndpointCommandTrb>()
+ .map_err(Error::CastTrb)?;
+ let slot_id = trb.get_slot_id();
+ let completion_code = {
+ if valid_slot_id(slot_id) {
+ self.slot(slot_id)?
+ .configure_endpoint(trb)
+ .map_err(Error::ConfigEndpoint)?
+ } else {
+ TrbCompletionCode::TrbError
+ }
+ };
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ completion_code,
+ slot_id,
+ atrb.gpa,
+ &event_fd,
+ )
+ }
+
+ fn evaluate_context(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> {
+ let trb = atrb
+ .trb
+ .cast::<EvaluateContextCommandTrb>()
+ .map_err(Error::CastTrb)?;
+ let slot_id = trb.get_slot_id();
+ let completion_code = {
+ if valid_slot_id(slot_id) {
+ self.slot(slot_id)?
+ .evaluate_context(trb)
+ .map_err(Error::EvaluateContext)?
+ } else {
+ TrbCompletionCode::TrbError
+ }
+ };
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ completion_code,
+ slot_id,
+ atrb.gpa,
+ &event_fd,
+ )
+ }
+
+ fn reset_device(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> {
+ let trb = atrb
+ .trb
+ .cast::<ResetDeviceCommandTrb>()
+ .map_err(Error::CastTrb)?;
+ let slot_id = trb.get_slot_id();
+ if valid_slot_id(slot_id) {
+ let gpa = atrb.gpa;
+ let interrupter = self.interrupter.clone();
+ self.slots
+ .reset_slot(slot_id, move |completion_code| {
+ CommandRingTrbHandler::command_completion_callback(
+ &interrupter,
+ completion_code,
+ slot_id,
+ gpa,
+ &event_fd,
+ )
+ .map_err(|e| {
+ error!("command completion callback failed: {}", e);
+ })
+ })
+ .map_err(Error::ResetSlot)
+ } else {
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ TrbCompletionCode::TrbError,
+ slot_id,
+ atrb.gpa,
+ &event_fd,
+ )
+ }
+ }
+
+ fn stop_endpoint(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> {
+ let trb = atrb
+ .trb
+ .cast::<StopEndpointCommandTrb>()
+ .map_err(Error::CastTrb)?;
+ let slot_id = trb.get_slot_id();
+ let endpoint_id = trb.get_endpoint_id();
+ if valid_slot_id(slot_id) {
+ let gpa = atrb.gpa;
+ let interrupter = self.interrupter.clone();
+ self.slots
+ .stop_endpoint(slot_id, endpoint_id, move |completion_code| {
+ CommandRingTrbHandler::command_completion_callback(
+ &interrupter,
+ completion_code,
+ slot_id,
+ gpa,
+ &event_fd,
+ )
+ .map_err(|e| {
+ error!("command completion callback failed: {}", e);
+ })
+ })
+ .map_err(Error::StopEndpoint)?;
+ Ok(())
+ } else {
+ error!("stop endpoint trb has invalid slot id {}", slot_id);
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ TrbCompletionCode::TrbError,
+ slot_id,
+ atrb.gpa,
+ &event_fd,
+ )
+ }
+ }
+
+ fn set_tr_dequeue_ptr(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> {
+ let trb = atrb
+ .trb
+ .cast::<SetTRDequeuePointerCommandTrb>()
+ .map_err(Error::CastTrb)?;
+ let slot_id = trb.get_slot_id();
+ let endpoint_id = trb.get_endpoint_id();
+ // See Set TR Dequeue Pointer Trb in spec.
+ let dequeue_ptr = trb.get_dequeue_ptr().get_gpa().offset();
+ let completion_code = {
+ if valid_slot_id(slot_id) {
+ self.slot(slot_id)?
+ .set_tr_dequeue_ptr(endpoint_id, dequeue_ptr)
+ .map_err(Error::SetDequeuePointer)?
+ } else {
+ error!("stop endpoint trb has invalid slot id {}", slot_id);
+ TrbCompletionCode::TrbError
+ }
+ };
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ completion_code,
+ slot_id,
+ atrb.gpa,
+ &event_fd,
+ )
+ }
+}
+
+impl TransferDescriptorHandler for CommandRingTrbHandler {
+ fn handle_transfer_descriptor(
+ &self,
+ descriptor: TransferDescriptor,
+ complete_event: EventFd,
+ ) -> std::result::Result<(), ()> {
+ // Command descriptor always consist of a single TRB.
+ assert_eq!(descriptor.len(), 1);
+ let atrb = &descriptor[0];
+ let command_result = match atrb.trb.get_trb_type() {
+ Ok(TrbType::EnableSlotCommand) => self.enable_slot(atrb, complete_event),
+ Ok(TrbType::DisableSlotCommand) => self.disable_slot(atrb, complete_event),
+ Ok(TrbType::AddressDeviceCommand) => self.address_device(atrb, complete_event),
+ Ok(TrbType::ConfigureEndpointCommand) => self.configure_endpoint(atrb, complete_event),
+ Ok(TrbType::EvaluateContextCommand) => self.evaluate_context(atrb, complete_event),
+ Ok(TrbType::ResetDeviceCommand) => self.reset_device(atrb, complete_event),
+ Ok(TrbType::NoopCommand) => CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ TrbCompletionCode::Success,
+ 0,
+ atrb.gpa,
+ &complete_event,
+ ),
+ Ok(TrbType::ResetEndpointCommand) => {
+ error!(
+ "Receiving reset endpoint command. \
+ It should only happen when cmd ring stall"
+ );
+ CommandRingTrbHandler::command_completion_callback(
+ &self.interrupter,
+ TrbCompletionCode::TrbError,
+ 0,
+ atrb.gpa,
+ &complete_event,
+ )
+ }
+ Ok(TrbType::StopEndpointCommand) => self.stop_endpoint(atrb, complete_event),
+ Ok(TrbType::SetTRDequeuePointerCommand) => {
+ self.set_tr_dequeue_ptr(atrb, complete_event)
+ }
+ _ => {
+ warn!(
+ // We are not handling type 14,15,16. See table 6.4.6.
+ "Unexpected command ring trb type: {}",
+ atrb.trb
+ );
+ match self.interrupter.lock().send_command_completion_trb(
+ TrbCompletionCode::TrbError,
+ 0,
+ GuestAddress(atrb.gpa),
+ ) {
+ Err(e) => Err(Error::SendInterrupt(e)),
+ Ok(_) => complete_event.write(1).map_err(Error::WriteEventFd),
+ }
+ }
+ };
+ command_result.map_err(|e| {
+ error!("command failed: {}", e);
+ })
+ }
+}
diff --git a/devices/src/usb/xhci/device_slot.rs b/devices/src/usb/xhci/device_slot.rs
new file mode 100644
index 0000000..e2b5e5d
--- /dev/null
+++ b/devices/src/usb/xhci/device_slot.rs
@@ -0,0 +1,778 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::interrupter::Interrupter;
+use super::transfer_ring_controller::{TransferRingController, TransferRingControllerError};
+use super::usb_hub::{self, UsbHub};
+use super::xhci_abi::{
+ AddressDeviceCommandTrb, ConfigureEndpointCommandTrb, DequeuePtr, DeviceContext,
+ DeviceSlotState, EndpointContext, EndpointState, EvaluateContextCommandTrb,
+ InputControlContext, SlotContext, TrbCompletionCode, DEVICE_CONTEXT_ENTRY_SIZE,
+};
+use super::xhci_regs::{valid_slot_id, MAX_PORTS, MAX_SLOTS};
+use crate::register_space::Register;
+use crate::usb::xhci::ring_buffer_stop_cb::{fallible_closure, RingBufferStopCallback};
+use crate::utils::{EventLoop, FailHandle};
+use bit_field::Error as BitFieldError;
+use std::fmt::{self, Display};
+use std::mem::size_of;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+use sync::Mutex;
+use sys_util::{error, GuestAddress, GuestMemory, GuestMemoryError};
+
+#[derive(Debug)]
+pub enum Error {
+ BadPortId(u8),
+ ReadGuestMemory(GuestMemoryError),
+ WriteGuestMemory(GuestMemoryError),
+ WeakReferenceUpgrade,
+ CallbackFailed,
+ GetSlotContextState(BitFieldError),
+ GetEndpointState(BitFieldError),
+ GetPort(u8),
+ GetTrc(u8),
+ BadInputContextAddr(GuestAddress),
+ BadDeviceContextAddr(GuestAddress),
+ CreateTransferController(TransferRingControllerError),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ BadPortId(id) => write!(f, "device slot get a bad port id: {}", id),
+ ReadGuestMemory(e) => write!(f, "failed to read guest memory: {}", e),
+ WriteGuestMemory(e) => write!(f, "failed to write guest memory: {}", e),
+ WeakReferenceUpgrade => write!(f, "failed to upgrade weak reference"),
+ CallbackFailed => write!(f, "callback failed"),
+ GetSlotContextState(e) => write!(f, "failed to get slot context state: {}", e),
+ GetEndpointState(e) => write!(f, "failed to get endpoint state: {}", e),
+ GetPort(v) => write!(f, "failed to get port: {}", v),
+ GetTrc(v) => write!(f, "failed to get trc: {}", v),
+ BadInputContextAddr(addr) => write!(f, "bad input context address: {}", addr),
+ BadDeviceContextAddr(addr) => write!(f, "bad device context: {}", addr),
+ CreateTransferController(e) => write!(f, "failed to create transfer controller: {}", e),
+ }
+ }
+}
+
+/// See spec 4.5.1 for dci.
+/// index 0: Control endpoint. Device Context Index: 1.
+/// index 1: Endpoint 1 out. Device Context Index: 2
+/// index 2: Endpoint 1 in. Device Context Index: 3.
+/// index 3: Endpoint 2 out. Device Context Index: 4
+/// ...
+/// index 30: Endpoint 15 in. Device Context Index: 31
+pub const TRANSFER_RING_CONTROLLERS_INDEX_END: usize = 31;
+/// End of device context index.
+pub const DCI_INDEX_END: u8 = (TRANSFER_RING_CONTROLLERS_INDEX_END + 1) as u8;
+/// Device context index of first transfer endpoint.
+pub const FIRST_TRANSFER_ENDPOINT_DCI: u8 = 2;
+
+fn valid_endpoint_id(endpoint_id: u8) -> bool {
+ endpoint_id < DCI_INDEX_END && endpoint_id > 0
+}
+
+#[derive(Clone)]
+pub struct DeviceSlots {
+ fail_handle: Arc<dyn FailHandle>,
+ hub: Arc<UsbHub>,
+ slots: Vec<Arc<DeviceSlot>>,
+}
+
+impl DeviceSlots {
+ pub fn new(
+ fail_handle: Arc<dyn FailHandle>,
+ dcbaap: Register<u64>,
+ hub: Arc<UsbHub>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ event_loop: Arc<EventLoop>,
+ mem: GuestMemory,
+ ) -> DeviceSlots {
+ let mut slots = Vec::new();
+ for slot_id in 1..=MAX_SLOTS {
+ slots.push(Arc::new(DeviceSlot::new(
+ slot_id,
+ dcbaap.clone(),
+ hub.clone(),
+ interrupter.clone(),
+ event_loop.clone(),
+ mem.clone(),
+ )));
+ }
+ DeviceSlots {
+ fail_handle,
+ hub,
+ slots,
+ }
+ }
+
+ /// Note that slot id starts from 1. Slot index start from 0.
+ pub fn slot(&self, slot_id: u8) -> Option<Arc<DeviceSlot>> {
+ if valid_slot_id(slot_id) {
+ Some(self.slots[slot_id as usize - 1].clone())
+ } else {
+ error!(
+ "trying to index a wrong slot id {}, max slot = {}",
+ slot_id, MAX_SLOTS
+ );
+ None
+ }
+ }
+
+ /// Reset the device connected to a specific port.
+ pub fn reset_port(&self, port_id: u8) -> std::result::Result<(), ()> {
+ if let Some(port) = self.hub.get_port(port_id) {
+ if let Some(backend_device) = port.get_backend_device().as_mut() {
+ backend_device.reset()?;
+ }
+ }
+
+ // No device on port, so nothing to reset.
+ Ok(())
+ }
+
+ /// Stop all device slots and reset them.
+ pub fn stop_all_and_reset<C: FnMut() + 'static + Send>(&self, mut callback: C) {
+ usb_debug!("stopping all device slots and resetting host hub");
+ let slots = self.slots.clone();
+ let hub = self.hub.clone();
+ let auto_callback = RingBufferStopCallback::new(fallible_closure(
+ self.fail_handle.clone(),
+ move || -> std::result::Result<(), usb_hub::Error> {
+ for slot in &slots {
+ slot.reset();
+ }
+ hub.reset()?;
+ callback();
+ Ok(())
+ },
+ ));
+ self.stop_all(auto_callback);
+ }
+
+ /// Stop all devices. The auto callback will be executed when all trc is stopped. It could
+ /// happen asynchronously, if there are any pending transfers.
+ pub fn stop_all(&self, auto_callback: RingBufferStopCallback) {
+ for slot in &self.slots {
+ slot.stop_all_trc(auto_callback.clone());
+ }
+ }
+
+ /// Disable a slot. This might happen asynchronously, if there is any pending transfers. The
+ /// callback will be invoked when slot is actually disabled.
+ pub fn disable_slot<
+ C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
+ >(
+ &self,
+ slot_id: u8,
+ cb: C,
+ ) -> Result<()> {
+ usb_debug!("device slot {} is being disabled", slot_id);
+ DeviceSlot::disable(
+ self.fail_handle.clone(),
+ &self.slots[slot_id as usize - 1],
+ cb,
+ )
+ }
+
+ /// Reset a slot. This is a shortcut call for DeviceSlot::reset_slot.
+ pub fn reset_slot<
+ C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
+ >(
+ &self,
+ slot_id: u8,
+ cb: C,
+ ) -> Result<()> {
+ usb_debug!("device slot {} is resetting", slot_id);
+ DeviceSlot::reset_slot(
+ self.fail_handle.clone(),
+ &self.slots[slot_id as usize - 1],
+ cb,
+ )
+ }
+
+ pub fn stop_endpoint<
+ C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
+ >(
+ &self,
+ slot_id: u8,
+ endpoint_id: u8,
+ cb: C,
+ ) -> Result<()> {
+ self.slots[slot_id as usize - 1].stop_endpoint(self.fail_handle.clone(), endpoint_id, cb)
+ }
+}
+
+// Usb port id. Valid ids starts from 1, to MAX_PORTS.
+struct PortId(Mutex<u8>);
+
+impl PortId {
+ fn new() -> Self {
+ PortId(Mutex::new(0))
+ }
+
+ fn set(&self, value: u8) -> Result<()> {
+ if value < 1 || value > MAX_PORTS {
+ return Err(Error::BadPortId(value));
+ }
+ *self.0.lock() = value;
+ Ok(())
+ }
+
+ fn reset(&self) {
+ *self.0.lock() = 0;
+ }
+
+ fn get(&self) -> Result<(u8)> {
+ let val = *self.0.lock();
+ if val == 0 {
+ return Err(Error::BadPortId(val));
+ }
+ Ok(val)
+ }
+}
+
+pub struct DeviceSlot {
+ slot_id: u8,
+ port_id: PortId, // Valid port id starts from 1, to MAX_PORTS.
+ dcbaap: Register<u64>,
+ hub: Arc<UsbHub>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ event_loop: Arc<EventLoop>,
+ mem: GuestMemory,
+ enabled: AtomicBool,
+ transfer_ring_controllers: Mutex<Vec<Option<Arc<TransferRingController>>>>,
+}
+
+impl DeviceSlot {
+ /// Create a new device slot.
+ pub fn new(
+ slot_id: u8,
+ dcbaap: Register<u64>,
+ hub: Arc<UsbHub>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ event_loop: Arc<EventLoop>,
+ mem: GuestMemory,
+ ) -> Self {
+ let transfer_ring_controllers = vec![None; TRANSFER_RING_CONTROLLERS_INDEX_END];
+ DeviceSlot {
+ slot_id,
+ port_id: PortId::new(),
+ dcbaap,
+ hub,
+ interrupter,
+ event_loop,
+ mem,
+ enabled: AtomicBool::new(false),
+ transfer_ring_controllers: Mutex::new(transfer_ring_controllers),
+ }
+ }
+
+ fn get_trc(&self, i: usize) -> Option<Arc<TransferRingController>> {
+ let trcs = self.transfer_ring_controllers.lock();
+ trcs[i].clone()
+ }
+
+ fn set_trc(&self, i: usize, trc: Option<Arc<TransferRingController>>) {
+ let mut trcs = self.transfer_ring_controllers.lock();
+ trcs[i] = trc;
+ }
+
+ fn trc_len(&self) -> usize {
+ self.transfer_ring_controllers.lock().len()
+ }
+
+ /// The arguments are identical to the fields in each doorbell register. The
+ /// target value:
+ /// 1: Reserved
+ /// 2: Control endpoint
+ /// 3: Endpoint 1 out
+ /// 4: Endpoint 1 in
+ /// 5: Endpoint 2 out
+ /// ...
+ /// 32: Endpoint 15 in
+ ///
+ /// Steam ID will be useful when host controller support streams.
+ /// The stream ID must be zero for endpoints that do not have streams
+ /// configured.
+ /// This function will return false if it fails to trigger transfer ring start.
+ pub fn ring_doorbell(&self, target: u8, _stream_id: u16) -> Result<bool> {
+ if !valid_endpoint_id(target) {
+ error!(
+ "device slot {}: Invalid target written to doorbell register. target: {}",
+ self.slot_id, target
+ );
+ return Ok(false);
+ }
+ usb_debug!(
+ "device slot {}: ding-dong. who is that? target = {}",
+ self.slot_id,
+ target
+ );
+ // See DCI in spec.
+ let endpoint_index = (target - 1) as usize;
+ let transfer_ring_controller = match self.get_trc(endpoint_index) {
+ Some(tr) => tr,
+ None => {
+ error!("Device endpoint is not inited");
+ return Ok(false);
+ }
+ };
+ let context = self.get_device_context()?;
+ if context.endpoint_context[endpoint_index]
+ .get_endpoint_state()
+ .map_err(Error::GetEndpointState)?
+ == EndpointState::Running
+ {
+ usb_debug!("endpoint is started, start transfer ring");
+ transfer_ring_controller.start();
+ } else {
+ error!("doorbell rung when endpoint is not started");
+ }
+ Ok(true)
+ }
+
+ /// Enable the slot. This function returns false if it's already enabled.
+ pub fn enable(&self) -> bool {
+ let was_already_enabled = self.enabled.swap(true, Ordering::SeqCst);
+ if was_already_enabled {
+ error!("device slot is already enabled");
+ } else {
+ usb_debug!("device slot {} enabled", self.slot_id);
+ }
+ !was_already_enabled
+ }
+
+ /// Disable this device slot. If the slot is not enabled, callback will be invoked immediately
+ /// with error. Otherwise, callback will be invoked when all trc is stopped.
+ pub fn disable<C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send>(
+ fail_handle: Arc<dyn FailHandle>,
+ slot: &Arc<DeviceSlot>,
+ mut callback: C,
+ ) -> Result<()> {
+ if slot.enabled.load(Ordering::SeqCst) {
+ let slot_weak = Arc::downgrade(slot);
+ let auto_callback =
+ RingBufferStopCallback::new(fallible_closure(fail_handle, move || {
+ // Slot should still be alive when the callback is invoked. If it's not, there
+ // must be a bug somewhere.
+ let slot = slot_weak.upgrade().ok_or(Error::WeakReferenceUpgrade)?;
+ let mut device_context = slot.get_device_context()?;
+ device_context
+ .slot_context
+ .set_slot_state(DeviceSlotState::DisabledOrEnabled);
+ slot.set_device_context(device_context)?;
+ slot.reset();
+ usb_debug!(
+ "device slot {}: all trc disabled, sending trb",
+ slot.slot_id
+ );
+ callback(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed)
+ }));
+ slot.stop_all_trc(auto_callback);
+ Ok(())
+ } else {
+ callback(TrbCompletionCode::SlotNotEnabledError).map_err(|_| Error::CallbackFailed)
+ }
+ }
+
+ // Assigns the device address and initializes slot and endpoint 0 context.
+ pub fn set_address(&self, trb: &AddressDeviceCommandTrb) -> Result<TrbCompletionCode> {
+ if !self.enabled.load(Ordering::SeqCst) {
+ error!(
+ "trying to set address to a disabled device slot {}",
+ self.slot_id
+ );
+ return Ok(TrbCompletionCode::SlotNotEnabledError);
+ }
+ let device_context = self.get_device_context()?;
+ let state = device_context
+ .slot_context
+ .get_slot_state()
+ .map_err(Error::GetSlotContextState)?;
+ match state {
+ DeviceSlotState::DisabledOrEnabled => {}
+ DeviceSlotState::Default if !trb.get_block_set_address_request() => {}
+ _ => {
+ error!("slot {} has unexpected slot state", self.slot_id);
+ return Ok(TrbCompletionCode::ContextStateError);
+ }
+ }
+
+ // Copy all fields of the slot context and endpoint 0 context from the input context
+ // to the output context.
+ let input_context_ptr = GuestAddress(trb.get_input_context_pointer());
+ // Copy slot context.
+ self.copy_context(input_context_ptr, 0)?;
+ // Copy control endpoint context.
+ self.copy_context(input_context_ptr, 1)?;
+
+ // Read back device context.
+ let mut device_context = self.get_device_context()?;
+ let port_id = device_context.slot_context.get_root_hub_port_number();
+ self.port_id.set(port_id)?;
+ usb_debug!(
+ "port id {} is assigned to slot id {}",
+ port_id,
+ self.slot_id
+ );
+
+ // Initialize the control endpoint. Endpoint id = 1.
+ self.set_trc(
+ 0,
+ Some(
+ TransferRingController::new(
+ self.mem.clone(),
+ self.hub.get_port(port_id).ok_or(Error::GetPort(port_id))?,
+ self.event_loop.clone(),
+ self.interrupter.clone(),
+ self.slot_id,
+ 1,
+ )
+ .map_err(Error::CreateTransferController)?,
+ ),
+ );
+
+ // Assign slot ID as device address if block_set_address_request is not set.
+ if trb.get_block_set_address_request() {
+ device_context
+ .slot_context
+ .set_slot_state(DeviceSlotState::Default);
+ } else {
+ let port = self.hub.get_port(port_id).ok_or(Error::GetPort(port_id))?;
+ match port.get_backend_device().as_mut() {
+ Some(backend) => {
+ backend.set_address(self.slot_id as u32);
+ }
+ None => {
+ return Ok(TrbCompletionCode::TransactionError);
+ }
+ }
+
+ device_context
+ .slot_context
+ .set_usb_device_address(self.slot_id);
+ device_context
+ .slot_context
+ .set_slot_state(DeviceSlotState::Addressed);
+ }
+
+ // TODO(jkwang) trc should always exists. Fix this.
+ self.get_trc(0)
+ .ok_or(Error::GetTrc(0))?
+ .set_dequeue_pointer(
+ device_context.endpoint_context[0]
+ .get_tr_dequeue_pointer()
+ .get_gpa(),
+ );
+
+ self.get_trc(0)
+ .ok_or(Error::GetTrc(0))?
+ .set_consumer_cycle_state(device_context.endpoint_context[0].get_dequeue_cycle_state());
+
+ usb_debug!("Setting endpoint 0 to running");
+ device_context.endpoint_context[0].set_endpoint_state(EndpointState::Running);
+ self.set_device_context(device_context)?;
+ Ok(TrbCompletionCode::Success)
+ }
+
+ // Adds or drops multiple endpoints in the device slot.
+ pub fn configure_endpoint(
+ &self,
+ trb: &ConfigureEndpointCommandTrb,
+ ) -> Result<TrbCompletionCode> {
+ usb_debug!("configuring endpoint");
+ let input_control_context = if trb.get_deconfigure() {
+ // From section 4.6.6 of the xHCI spec:
+ // Setting the deconfigure (DC) flag to '1' in the Configure Endpoint Command
+ // TRB is equivalent to setting Input Context Drop Context flags 2-31 to '1'
+ // and Add Context 2-31 flags to '0'.
+ let mut c = InputControlContext::new();
+ c.set_add_context_flags(0);
+ c.set_drop_context_flags(0xfffffffc);
+ c
+ } else {
+ self.mem
+ .read_obj_from_addr(GuestAddress(trb.get_input_context_pointer()))
+ .map_err(Error::ReadGuestMemory)?
+ };
+
+ for device_context_index in 1..DCI_INDEX_END {
+ if input_control_context.drop_context_flag(device_context_index) {
+ self.drop_one_endpoint(device_context_index)?;
+ }
+ if input_control_context.add_context_flag(device_context_index) {
+ self.copy_context(
+ GuestAddress(trb.get_input_context_pointer()),
+ device_context_index,
+ )?;
+ self.add_one_endpoint(device_context_index)?;
+ }
+ }
+
+ if trb.get_deconfigure() {
+ self.set_state(DeviceSlotState::Addressed)?;
+ } else {
+ self.set_state(DeviceSlotState::Configured)?;
+ }
+ Ok(TrbCompletionCode::Success)
+ }
+
+ // Evaluates the device context by reading new values for certain fields of
+ // the slot context and/or control endpoint context.
+ pub fn evaluate_context(&self, trb: &EvaluateContextCommandTrb) -> Result<TrbCompletionCode> {
+ if !self.enabled.load(Ordering::SeqCst) {
+ return Ok(TrbCompletionCode::SlotNotEnabledError);
+ }
+ // TODO(jkwang) verify this
+ // The spec has multiple contradictions about validating context parameters in sections
+ // 4.6.7, 6.2.3.3. To keep things as simple as possible we do no further validation here.
+ let input_control_context: InputControlContext = self
+ .mem
+ .read_obj_from_addr(GuestAddress(trb.get_input_context_pointer()))
+ .map_err(Error::ReadGuestMemory)?;
+
+ let mut device_context = self.get_device_context()?;
+ if input_control_context.add_context_flag(0) {
+ let input_slot_context: SlotContext = self
+ .mem
+ .read_obj_from_addr(GuestAddress(
+ trb.get_input_context_pointer() + DEVICE_CONTEXT_ENTRY_SIZE as u64,
+ ))
+ .map_err(Error::ReadGuestMemory)?;
+ device_context
+ .slot_context
+ .set_interrupter_target(input_slot_context.get_interrupter_target());
+
+ device_context
+ .slot_context
+ .set_max_exit_latency(input_slot_context.get_max_exit_latency());
+ }
+
+ // From 6.2.3.3: "Endpoint Contexts 2 throught 31 shall not be evaluated by the Evaluate
+ // Context Command".
+ if input_control_context.add_context_flag(1) {
+ let ep0_context: EndpointContext = self
+ .mem
+ .read_obj_from_addr(GuestAddress(
+ trb.get_input_context_pointer() + 2 * DEVICE_CONTEXT_ENTRY_SIZE as u64,
+ ))
+ .map_err(Error::ReadGuestMemory)?;
+ device_context.endpoint_context[0]
+ .set_max_packet_size(ep0_context.get_max_packet_size());
+ }
+ self.set_device_context(device_context)?;
+ Ok(TrbCompletionCode::Success)
+ }
+
+ /// Reset the device slot to default state and deconfigures all but the
+ /// control endpoint.
+ pub fn reset_slot<
+ C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
+ >(
+ fail_handle: Arc<dyn FailHandle>,
+ slot: &Arc<DeviceSlot>,
+ mut callback: C,
+ ) -> Result<()> {
+ let weak_s = Arc::downgrade(&slot);
+ let auto_callback =
+ RingBufferStopCallback::new(fallible_closure(fail_handle, move || -> Result<()> {
+ let s = weak_s.upgrade().ok_or(Error::WeakReferenceUpgrade)?;
+ for i in FIRST_TRANSFER_ENDPOINT_DCI..DCI_INDEX_END {
+ s.drop_one_endpoint(i)?;
+ }
+ let mut ctx = s.get_device_context()?;
+ ctx.slot_context.set_slot_state(DeviceSlotState::Default);
+ ctx.slot_context.set_context_entries(1);
+ ctx.slot_context.set_root_hub_port_number(0);
+ s.set_device_context(ctx)?;
+ callback(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed)?;
+ Ok(())
+ }));
+ slot.stop_all_trc(auto_callback);
+ Ok(())
+ }
+
+ /// Stop all transfer ring controllers.
+ pub fn stop_all_trc(&self, auto_callback: RingBufferStopCallback) {
+ for i in 0..self.trc_len() {
+ if let Some(trc) = self.get_trc(i) {
+ trc.stop(auto_callback.clone());
+ }
+ }
+ }
+
+ /// Stop a endpoint.
+ pub fn stop_endpoint<
+ C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
+ >(
+ &self,
+ fail_handle: Arc<dyn FailHandle>,
+ endpoint_id: u8,
+ mut cb: C,
+ ) -> Result<()> {
+ if !valid_endpoint_id(endpoint_id) {
+ error!("trb indexing wrong endpoint id");
+ return cb(TrbCompletionCode::TrbError).map_err(|_| Error::CallbackFailed);
+ }
+ let index = endpoint_id - 1;
+ match self.get_trc(index as usize) {
+ Some(trc) => {
+ usb_debug!("stopping endpoint");
+ let auto_cb = RingBufferStopCallback::new(fallible_closure(
+ fail_handle,
+ move || -> Result<()> {
+ cb(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed)
+ },
+ ));
+ trc.stop(auto_cb);
+ }
+ None => {
+ error!("endpoint at index {} is not started", index);
+ cb(TrbCompletionCode::ContextStateError).map_err(|_| Error::CallbackFailed)?;
+ }
+ }
+ Ok(())
+ }
+
+ /// Set transfer ring dequeue pointer.
+ pub fn set_tr_dequeue_ptr(&self, endpoint_id: u8, ptr: u64) -> Result<TrbCompletionCode> {
+ if !valid_endpoint_id(endpoint_id) {
+ error!("trb indexing wrong endpoint id");
+ return Ok(TrbCompletionCode::TrbError);
+ }
+ let index = (endpoint_id - 1) as usize;
+ match self.get_trc(index) {
+ Some(trc) => {
+ trc.set_dequeue_pointer(GuestAddress(ptr));
+ let mut ctx = self.get_device_context()?;
+ ctx.endpoint_context[index]
+ .set_tr_dequeue_pointer(DequeuePtr::new(GuestAddress(ptr)));
+ self.set_device_context(ctx)?;
+ Ok(TrbCompletionCode::Success)
+ }
+ None => {
+ error!("set tr dequeue ptr failed due to no trc started");
+ Ok(TrbCompletionCode::ContextStateError)
+ }
+ }
+ }
+
+ // Reset and reset_slot are different.
+ // Reset_slot handles command ring `reset slot` command. It will reset the slot state.
+ // Reset handles xhci reset. It will destroy everything.
+ fn reset(&self) {
+ for i in 0..self.trc_len() {
+ self.set_trc(i, None);
+ }
+ usb_debug!("reseting device slot {}!", self.slot_id);
+ self.enabled.store(false, Ordering::SeqCst);
+ self.port_id.reset();
+ }
+
+ fn add_one_endpoint(&self, device_context_index: u8) -> Result<()> {
+ usb_debug!(
+ "adding one endpoint, device context index {}",
+ device_context_index
+ );
+ let mut device_context = self.get_device_context()?;
+ let transfer_ring_index = (device_context_index - 1) as usize;
+ let trc = TransferRingController::new(
+ self.mem.clone(),
+ self.hub
+ .get_port(self.port_id.get()?)
+ .ok_or(Error::GetPort(self.port_id.get()?))?,
+ self.event_loop.clone(),
+ self.interrupter.clone(),
+ self.slot_id,
+ device_context_index,
+ )
+ .map_err(Error::CreateTransferController)?;
+ trc.set_dequeue_pointer(
+ device_context.endpoint_context[transfer_ring_index]
+ .get_tr_dequeue_pointer()
+ .get_gpa(),
+ );
+ trc.set_consumer_cycle_state(
+ device_context.endpoint_context[transfer_ring_index].get_dequeue_cycle_state(),
+ );
+ self.set_trc(transfer_ring_index, Some(trc));
+ device_context.endpoint_context[transfer_ring_index]
+ .set_endpoint_state(EndpointState::Running);
+ self.set_device_context(device_context)
+ }
+
+ fn drop_one_endpoint(&self, device_context_index: u8) -> Result<()> {
+ let endpoint_index = (device_context_index - 1) as usize;
+ self.set_trc(endpoint_index, None);
+ let mut ctx = self.get_device_context()?;
+ ctx.endpoint_context[endpoint_index].set_endpoint_state(EndpointState::Disabled);
+ self.set_device_context(ctx)
+ }
+
+ fn get_device_context(&self) -> Result<DeviceContext> {
+ let ctx = self
+ .mem
+ .read_obj_from_addr(self.get_device_context_addr()?)
+ .map_err(Error::ReadGuestMemory)?;
+ usb_debug!("read device ctx: {:?}", ctx);
+ Ok(ctx)
+ }
+
+ fn set_device_context(&self, device_context: DeviceContext) -> Result<()> {
+ self.mem
+ .write_obj_at_addr(device_context, self.get_device_context_addr()?)
+ .map_err(Error::WriteGuestMemory)
+ }
+
+ fn copy_context(
+ &self,
+ input_context_ptr: GuestAddress,
+ device_context_index: u8,
+ ) -> Result<()> {
+ // Note that it could be slot context or device context. They have the same size. Won't
+ // make a difference here.
+ let ctx: EndpointContext = self
+ .mem
+ .read_obj_from_addr(
+ input_context_ptr
+ .checked_add(
+ (device_context_index as u64 + 1) * DEVICE_CONTEXT_ENTRY_SIZE as u64,
+ )
+ .ok_or(Error::BadInputContextAddr(input_context_ptr))?,
+ )
+ .map_err(Error::ReadGuestMemory)?;
+ usb_debug!("context being copied {:?}", ctx);
+ let device_context_ptr = self.get_device_context_addr()?;
+ self.mem
+ .write_obj_at_addr(
+ ctx,
+ device_context_ptr
+ .checked_add(device_context_index as u64 * DEVICE_CONTEXT_ENTRY_SIZE as u64)
+ .ok_or(Error::BadDeviceContextAddr(device_context_ptr))?,
+ )
+ .map_err(Error::WriteGuestMemory)
+ }
+
+ fn get_device_context_addr(&self) -> Result<GuestAddress> {
+ let addr: u64 = self
+ .mem
+ .read_obj_from_addr(GuestAddress(
+ self.dcbaap.get_value() + size_of::<u64>() as u64 * self.slot_id as u64,
+ ))
+ .map_err(Error::ReadGuestMemory)?;
+ Ok(GuestAddress(addr))
+ }
+
+ fn set_state(&self, state: DeviceSlotState) -> Result<()> {
+ let mut ctx = self.get_device_context()?;
+ ctx.slot_context.set_slot_state(state);
+ self.set_device_context(ctx)
+ }
+}
diff --git a/devices/src/usb/xhci/event_ring.rs b/devices/src/usb/xhci/event_ring.rs
new file mode 100644
index 0000000..fcaff23
--- /dev/null
+++ b/devices/src/usb/xhci/event_ring.rs
@@ -0,0 +1,413 @@
+// Copyright 2018 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 data_model::DataInit;
+use std;
+use std::fmt::{self, Display};
+use std::mem::size_of;
+use std::sync::atomic::{fence, Ordering};
+use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
+
+use super::xhci_abi::*;
+
+#[derive(Debug)]
+pub enum Error {
+ Uninitialized,
+ EventRingFull,
+ BadEnqueuePointer(GuestAddress),
+ BadSegTableIndex(u16),
+ BadSegTableAddress(GuestAddress),
+ MemoryRead(GuestMemoryError),
+ MemoryWrite(GuestMemoryError),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ Uninitialized => write!(f, "event ring is uninitialized"),
+ EventRingFull => write!(f, "event ring is full"),
+ BadEnqueuePointer(addr) => write!(f, "event ring has a bad enqueue pointer: {}", addr),
+ BadSegTableIndex(i) => write!(f, "event ring has a bad seg table index: {}", i),
+ BadSegTableAddress(addr) => write!(f, "event ring has a bad seg table addr: {}", addr),
+ MemoryRead(e) => write!(f, "event ring cannot read from guest memory: {}", e),
+ MemoryWrite(e) => write!(f, "event ring cannot write to guest memory: {}", e),
+ }
+ }
+}
+
+/// Event rings are segmented circular buffers used to pass event TRBs from the xHCI device back to
+/// the guest. Each event ring is associated with a single interrupter. See section 4.9.4 of the
+/// xHCI specification for more details.
+/// This implementation is only for primary interrupter. Please review xhci spec before using it
+/// for secondary.
+pub struct EventRing {
+ mem: GuestMemory,
+ segment_table_size: u16,
+ segment_table_base_address: GuestAddress,
+ current_segment_index: u16,
+ trb_count: u16,
+ enqueue_pointer: GuestAddress,
+ dequeue_pointer: GuestAddress,
+ producer_cycle_state: bool,
+}
+
+impl EventRing {
+ /// Create an empty, uninitialized event ring.
+ pub fn new(mem: GuestMemory) -> Self {
+ EventRing {
+ mem,
+ segment_table_size: 0,
+ segment_table_base_address: GuestAddress(0),
+ current_segment_index: 0,
+ enqueue_pointer: GuestAddress(0),
+ dequeue_pointer: GuestAddress(0),
+ trb_count: 0,
+ // As specified in xHCI spec 4.9.4, cycle state should be initialized to 1.
+ producer_cycle_state: true,
+ }
+ }
+
+ /// This function implements left side of xHCI spec, Figure 4-12.
+ pub fn add_event(&mut self, mut trb: Trb) -> Result<()> {
+ self.check_inited()?;
+ if self.is_full()? {
+ return Err(Error::EventRingFull);
+ }
+ // Event is write twice to avoid race condition.
+ // Guest kernel use cycle bit to check ownership, thus we should write cycle last.
+ trb.set_cycle(!self.producer_cycle_state);
+ self.mem
+ .write_obj_at_addr(trb, self.enqueue_pointer)
+ .map_err(Error::MemoryWrite)?;
+
+ // Updating the cycle state bit should always happen after updating other parts.
+ fence(Ordering::SeqCst);
+
+ trb.set_cycle(self.producer_cycle_state);
+
+ // Offset of cycle state byte.
+ const CYCLE_STATE_OFFSET: usize = 12usize;
+ let data = trb.as_slice();
+ // Trb contains 4 dwords, the last one contains cycle bit.
+ let cycle_bit_dword = &data[CYCLE_STATE_OFFSET..];
+ let address = self.enqueue_pointer;
+ let address = address
+ .checked_add(CYCLE_STATE_OFFSET as u64)
+ .ok_or(Error::BadEnqueuePointer(self.enqueue_pointer))?;
+ self.mem
+ .write_all_at_addr(cycle_bit_dword, address)
+ .map_err(Error::MemoryWrite)?;
+
+ usb_debug!(
+ "event write to pointer {:#x}, trb_count {}, {}",
+ self.enqueue_pointer.0,
+ self.trb_count,
+ trb
+ );
+ self.enqueue_pointer = match self.enqueue_pointer.checked_add(size_of::<Trb>() as u64) {
+ Some(addr) => addr,
+ None => return Err(Error::BadEnqueuePointer(self.enqueue_pointer)),
+ };
+ self.trb_count -= 1;
+ if self.trb_count == 0 {
+ self.current_segment_index += 1;
+ if self.current_segment_index == self.segment_table_size {
+ self.producer_cycle_state ^= true;
+ self.current_segment_index = 0;
+ }
+ self.load_current_seg_table_entry()?;
+ }
+ Ok(())
+ }
+
+ /// Set segment table size.
+ pub fn set_seg_table_size(&mut self, size: u16) -> Result<()> {
+ usb_debug!("event ring seg table size is set to {}", size);
+ self.segment_table_size = size;
+ self.try_reconfigure_event_ring()
+ }
+
+ /// Set segment table base addr.
+ pub fn set_seg_table_base_addr(&mut self, addr: GuestAddress) -> Result<()> {
+ usb_debug!("event ring seg table base addr is set to {:#x}", addr.0);
+ self.segment_table_base_address = addr;
+ self.try_reconfigure_event_ring()
+ }
+
+ /// Set dequeue pointer.
+ pub fn set_dequeue_pointer(&mut self, addr: GuestAddress) {
+ usb_debug!("event ring dequeue pointer set to {:#x}", addr.0);
+ self.dequeue_pointer = addr;
+ }
+
+ /// Get the enqueue pointer.
+ pub fn get_enqueue_pointer(&self) -> GuestAddress {
+ self.enqueue_pointer
+ }
+
+ /// Check if event ring is empty.
+ pub fn is_empty(&self) -> bool {
+ self.enqueue_pointer == self.dequeue_pointer
+ }
+
+ /// Event ring is considered full when there is only space for one last TRB. In this case, xHC
+ /// should write an error Trb and do a bunch of handlings. See spec, figure 4-12 for more
+ /// details.
+ /// For now, we just check event ring full and fail (as it's unlikely to happen).
+ pub fn is_full(&self) -> Result<bool> {
+ if self.trb_count == 1 {
+ // erst == event ring segment table
+ let next_erst_idx = (self.current_segment_index + 1) % self.segment_table_size;
+ let erst_entry = self.read_seg_table_entry(next_erst_idx)?;
+ Ok(self.dequeue_pointer.0 == erst_entry.get_ring_segment_base_address())
+ } else {
+ Ok(self.dequeue_pointer.0 == self.enqueue_pointer.0 + size_of::<Trb>() as u64)
+ }
+ }
+
+ /// Try to init event ring. Will fail if seg table size/address are invalid.
+ fn try_reconfigure_event_ring(&mut self) -> Result<()> {
+ if self.segment_table_size == 0 || self.segment_table_base_address.0 == 0 {
+ return Ok(());
+ }
+ self.load_current_seg_table_entry()
+ }
+
+ // Check if this event ring is inited.
+ fn check_inited(&self) -> Result<()> {
+ if self.segment_table_size == 0
+ || self.segment_table_base_address == GuestAddress(0)
+ || self.enqueue_pointer == GuestAddress(0)
+ {
+ return Err(Error::Uninitialized);
+ }
+ Ok(())
+ }
+
+ // Load entry of current seg table.
+ fn load_current_seg_table_entry(&mut self) -> Result<()> {
+ let entry = self.read_seg_table_entry(self.current_segment_index)?;
+ self.enqueue_pointer = GuestAddress(entry.get_ring_segment_base_address());
+ self.trb_count = entry.get_ring_segment_size();
+ Ok(())
+ }
+
+ // Get seg table entry at index.
+ fn read_seg_table_entry(&self, index: u16) -> Result<EventRingSegmentTableEntry> {
+ let seg_table_addr = self.get_seg_table_addr(index)?;
+ // TODO(jkwang) We can refactor GuestMemory to allow in-place memory operation.
+ self.mem
+ .read_obj_from_addr(seg_table_addr)
+ .map_err(Error::MemoryRead)
+ }
+
+ // Get seg table addr at index.
+ fn get_seg_table_addr(&self, index: u16) -> Result<GuestAddress> {
+ if index > self.segment_table_size {
+ return Err(Error::BadSegTableIndex(index));
+ }
+ self.segment_table_base_address
+ .checked_add(((size_of::<EventRingSegmentTableEntry>() as u16) * index) as u64)
+ .ok_or(Error::BadSegTableAddress(self.segment_table_base_address))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::mem::size_of;
+
+ #[test]
+ fn test_uninited() {
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut er = EventRing::new(gm.clone());
+ let trb = Trb::new();
+ match er.add_event(trb).err().unwrap() {
+ Error::Uninitialized => {}
+ _ => panic!("unexpected error"),
+ }
+ assert_eq!(er.is_empty(), true);
+ assert_eq!(er.is_full().unwrap(), false);
+ }
+
+ #[test]
+ fn test_event_ring() {
+ let trb_size = size_of::<Trb>() as u64;
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut er = EventRing::new(gm.clone());
+ let mut st_entries = [EventRingSegmentTableEntry::new(); 3];
+ st_entries[0].set_ring_segment_base_address(0x100);
+ st_entries[0].set_ring_segment_size(3);
+ st_entries[1].set_ring_segment_base_address(0x200);
+ st_entries[1].set_ring_segment_size(3);
+ st_entries[2].set_ring_segment_base_address(0x300);
+ st_entries[2].set_ring_segment_size(3);
+ gm.write_obj_at_addr(st_entries[0], GuestAddress(0x8))
+ .unwrap();
+ gm.write_obj_at_addr(
+ st_entries[1],
+ GuestAddress(0x8 + size_of::<EventRingSegmentTableEntry>() as u64),
+ )
+ .unwrap();
+ gm.write_obj_at_addr(
+ st_entries[2],
+ GuestAddress(0x8 + 2 * size_of::<EventRingSegmentTableEntry>() as u64),
+ )
+ .unwrap();
+ // Init event ring. Must init after segment tables writting.
+ er.set_seg_table_size(3).unwrap();
+ er.set_seg_table_base_addr(GuestAddress(0x8)).unwrap();
+ er.set_dequeue_pointer(GuestAddress(0x100));
+
+ let mut trb = Trb::new();
+
+ // Fill first table.
+ trb.set_control(1);
+ assert_eq!(er.is_empty(), true);
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm.read_obj_from_addr(GuestAddress(0x100)).unwrap();
+ assert_eq!(t.get_control(), 1);
+ assert_eq!(t.get_cycle(), true);
+
+ trb.set_control(2);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm
+ .read_obj_from_addr(GuestAddress(0x100 + trb_size))
+ .unwrap();
+ assert_eq!(t.get_control(), 2);
+ assert_eq!(t.get_cycle(), true);
+
+ trb.set_control(3);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm
+ .read_obj_from_addr(GuestAddress(0x100 + 2 * trb_size))
+ .unwrap();
+ assert_eq!(t.get_control(), 3);
+ assert_eq!(t.get_cycle(), true);
+
+ // Fill second table.
+ trb.set_control(4);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm.read_obj_from_addr(GuestAddress(0x200)).unwrap();
+ assert_eq!(t.get_control(), 4);
+ assert_eq!(t.get_cycle(), true);
+
+ trb.set_control(5);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm
+ .read_obj_from_addr(GuestAddress(0x200 + trb_size))
+ .unwrap();
+ assert_eq!(t.get_control(), 5);
+ assert_eq!(t.get_cycle(), true);
+
+ trb.set_control(6);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm
+ .read_obj_from_addr(GuestAddress(0x200 + 2 * trb_size as u64))
+ .unwrap();
+ assert_eq!(t.get_control(), 6);
+ assert_eq!(t.get_cycle(), true);
+
+ // Fill third table.
+ trb.set_control(7);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm.read_obj_from_addr(GuestAddress(0x300)).unwrap();
+ assert_eq!(t.get_control(), 7);
+ assert_eq!(t.get_cycle(), true);
+
+ trb.set_control(8);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ // There is only one last trb. Considered full.
+ assert_eq!(er.is_full().unwrap(), true);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm
+ .read_obj_from_addr(GuestAddress(0x300 + trb_size))
+ .unwrap();
+ assert_eq!(t.get_control(), 8);
+ assert_eq!(t.get_cycle(), true);
+
+ // Add the last trb will result in error.
+ match er.add_event(trb.clone()) {
+ Err(Error::EventRingFull) => {}
+ _ => panic!("er should be full"),
+ };
+
+ // Dequeue one trb.
+ er.set_dequeue_pointer(GuestAddress(0x100 + trb_size));
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+
+ // Fill the last trb of the third table.
+ trb.set_control(9);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ // There is only one last trb. Considered full.
+ assert_eq!(er.is_full().unwrap(), true);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm
+ .read_obj_from_addr(GuestAddress(0x300 + trb_size))
+ .unwrap();
+ assert_eq!(t.get_control(), 8);
+ assert_eq!(t.get_cycle(), true);
+
+ // Add the last trb will result in error.
+ match er.add_event(trb.clone()) {
+ Err(Error::EventRingFull) => {}
+ _ => panic!("er should be full"),
+ };
+
+ // Dequeue until empty.
+ er.set_dequeue_pointer(GuestAddress(0x100));
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), true);
+
+ // Fill first table again.
+ trb.set_control(10);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm.read_obj_from_addr(GuestAddress(0x100)).unwrap();
+ assert_eq!(t.get_control(), 10);
+ // cycle bit should be reversed.
+ assert_eq!(t.get_cycle(), false);
+
+ trb.set_control(11);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm
+ .read_obj_from_addr(GuestAddress(0x100 + trb_size))
+ .unwrap();
+ assert_eq!(t.get_control(), 11);
+ assert_eq!(t.get_cycle(), false);
+
+ trb.set_control(12);
+ assert_eq!(er.add_event(trb.clone()).unwrap(), ());
+ assert_eq!(er.is_full().unwrap(), false);
+ assert_eq!(er.is_empty(), false);
+ let t: Trb = gm
+ .read_obj_from_addr(GuestAddress(0x100 + 2 * trb_size))
+ .unwrap();
+ assert_eq!(t.get_control(), 12);
+ assert_eq!(t.get_cycle(), false);
+ }
+}
diff --git a/devices/src/usb/xhci/interrupter.rs b/devices/src/usb/xhci/interrupter.rs
new file mode 100644
index 0000000..c58ae59
--- /dev/null
+++ b/devices/src/usb/xhci/interrupter.rs
@@ -0,0 +1,202 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::event_ring::{Error as EventRingError, EventRing};
+use super::xhci_abi::{
+ CommandCompletionEventTrb, Error as TrbError, PortStatusChangeEventTrb, TransferEventTrb, Trb,
+ TrbCast, TrbCompletionCode, TrbType,
+};
+use super::xhci_regs::*;
+use crate::register_space::Register;
+use std::fmt::{self, Display};
+use sys_util::{Error as SysError, EventFd, GuestAddress, GuestMemory};
+
+#[derive(Debug)]
+pub enum Error {
+ CastTrb(TrbError),
+ AddEvent(EventRingError),
+ SetSegTableSize(EventRingError),
+ SetSegTableBaseAddr(EventRingError),
+ SendInterrupt(SysError),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+ match self {
+ CastTrb(e) => write!(f, "cannot cast trb: {}", e),
+ AddEvent(e) => write!(f, "cannot add event: {}", e),
+ SetSegTableSize(e) => write!(f, "cannot set seg table size: {}", e),
+ SetSegTableBaseAddr(e) => write!(f, "cannot set seg table base addr: {}", e),
+ SendInterrupt(e) => write!(f, "cannot send interrupt: {}", e),
+ }
+ }
+}
+
+/// See spec 4.17 for interrupters. Controller can send an event back to guest kernel driver
+/// through interrupter.
+pub struct Interrupter {
+ interrupt_fd: EventFd,
+ usbsts: Register<u32>,
+ iman: Register<u32>,
+ erdp: Register<u64>,
+ event_handler_busy: bool,
+ enabled: bool,
+ pending: bool,
+ moderation_interval: u16,
+ moderation_counter: u16,
+ event_ring: EventRing,
+}
+
+impl Interrupter {
+ /// Create a new interrupter.
+ pub fn new(mem: GuestMemory, irq_evt: EventFd, regs: &XhciRegs) -> Self {
+ Interrupter {
+ interrupt_fd: irq_evt,
+ usbsts: regs.usbsts.clone(),
+ iman: regs.iman.clone(),
+ erdp: regs.erdp.clone(),
+ event_handler_busy: false,
+ enabled: false,
+ pending: false,
+ moderation_interval: 0,
+ moderation_counter: 0,
+ event_ring: EventRing::new(mem),
+ }
+ }
+
+ /// Returns true if event ring is empty.
+ pub fn event_ring_is_empty(&self) -> bool {
+ self.event_ring.is_empty()
+ }
+
+ /// Add event to event ring.
+ fn add_event(&mut self, trb: Trb) -> Result<()> {
+ self.event_ring.add_event(trb).map_err(Error::AddEvent)?;
+ self.pending = true;
+ self.interrupt_if_needed()
+ }
+
+ /// Send port status change trb for port.
+ pub fn send_port_status_change_trb(&mut self, port_id: u8) -> Result<()> {
+ let mut trb = Trb::new();
+ let psctrb = trb
+ .cast_mut::<PortStatusChangeEventTrb>()
+ .map_err(Error::CastTrb)?;
+ psctrb.set_port_id(port_id);
+ psctrb.set_completion_code(TrbCompletionCode::Success);
+ psctrb.set_trb_type(TrbType::PortStatusChangeEvent);
+ self.add_event(trb)
+ }
+
+ /// Send command completion trb.
+ pub fn send_command_completion_trb(
+ &mut self,
+ completion_code: TrbCompletionCode,
+ slot_id: u8,
+ trb_addr: GuestAddress,
+ ) -> Result<()> {
+ let mut trb = Trb::new();
+ let ctrb = trb
+ .cast_mut::<CommandCompletionEventTrb>()
+ .map_err(Error::CastTrb)?;
+ ctrb.set_trb_pointer(trb_addr.0);
+ ctrb.set_command_completion_parameter(0);
+ ctrb.set_completion_code(completion_code);
+ ctrb.set_trb_type(TrbType::CommandCompletionEvent);
+ ctrb.set_vf_id(0);
+ ctrb.set_slot_id(slot_id);
+ self.add_event(trb)
+ }
+
+ /// Send transfer event trb.
+ pub fn send_transfer_event_trb(
+ &mut self,
+ completion_code: TrbCompletionCode,
+ trb_pointer: u64,
+ transfer_length: u32,
+ event_data: bool,
+ slot_id: u8,
+ endpoint_id: u8,
+ ) -> Result<()> {
+ let mut trb = Trb::new();
+ let event_trb = trb.cast_mut::<TransferEventTrb>().map_err(Error::CastTrb)?;
+ event_trb.set_trb_pointer(trb_pointer);
+ event_trb.set_trb_transfer_length(transfer_length);
+ event_trb.set_completion_code(completion_code);
+ event_trb.set_event_data(event_data.into());
+ event_trb.set_trb_type(TrbType::TransferEvent);
+ event_trb.set_endpoint_id(endpoint_id);
+ event_trb.set_slot_id(slot_id);
+ self.add_event(trb)
+ }
+
+ /// Enable/Disable this interrupter.
+ pub fn set_enabled(&mut self, enabled: bool) -> Result<()> {
+ usb_debug!("interrupter set enabled {}", enabled);
+ self.enabled = enabled;
+ self.interrupt_if_needed()
+ }
+
+ /// Set interrupt moderation.
+ pub fn set_moderation(&mut self, interval: u16, counter: u16) -> Result<()> {
+ // TODO(jkwang) Moderation is not implemented yet.
+ self.moderation_interval = interval;
+ self.moderation_counter = counter;
+ self.interrupt_if_needed()
+ }
+
+ /// Set event ring seg table size.
+ pub fn set_event_ring_seg_table_size(&mut self, size: u16) -> Result<()> {
+ usb_debug!("interrupter set seg table size {}", size);
+ self.event_ring
+ .set_seg_table_size(size)
+ .map_err(Error::SetSegTableSize)
+ }
+
+ /// Set event ring segment table base address.
+ pub fn set_event_ring_seg_table_base_addr(&mut self, addr: GuestAddress) -> Result<()> {
+ usb_debug!("interrupter set table base addr {:#x}", addr.0);
+ self.event_ring
+ .set_seg_table_base_addr(addr)
+ .map_err(Error::SetSegTableBaseAddr)
+ }
+
+ /// Set event ring dequeue pointer.
+ pub fn set_event_ring_dequeue_pointer(&mut self, addr: GuestAddress) -> Result<()> {
+ usb_debug!("interrupter set dequeue ptr addr {:#x}", addr.0);
+ self.event_ring.set_dequeue_pointer(addr);
+ if addr == self.event_ring.get_enqueue_pointer() {
+ self.pending = false;
+ }
+ self.interrupt_if_needed()
+ }
+
+ /// Set event hander busy.
+ pub fn set_event_handler_busy(&mut self, busy: bool) -> Result<()> {
+ usb_debug!("set event handler busy {}", busy);
+ self.event_handler_busy = busy;
+ self.interrupt_if_needed()
+ }
+
+ /// Send and interrupt.
+ pub fn interrupt(&mut self) -> Result<()> {
+ usb_debug!("sending interrupt");
+ self.event_handler_busy = true;
+ self.pending = false;
+ self.usbsts.set_bits(USB_STS_EVENT_INTERRUPT);
+ self.iman.set_bits(IMAN_INTERRUPT_PENDING);
+ self.erdp.set_bits(ERDP_EVENT_HANDLER_BUSY);
+ self.interrupt_fd.write(1).map_err(Error::SendInterrupt)
+ }
+
+ fn interrupt_if_needed(&mut self) -> Result<()> {
+ if self.enabled && self.pending && !self.event_handler_busy {
+ self.interrupt()?;
+ }
+ Ok(())
+ }
+}
diff --git a/devices/src/usb/xhci/intr_resample_handler.rs b/devices/src/usb/xhci/intr_resample_handler.rs
new file mode 100644
index 0000000..a2a40bb
--- /dev/null
+++ b/devices/src/usb/xhci/intr_resample_handler.rs
@@ -0,0 +1,64 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::interrupter::Interrupter;
+use crate::utils::{EventHandler, EventLoop};
+use std::sync::Arc;
+use sync::Mutex;
+use sys_util::{error, EventFd, WatchingEvents};
+
+/// Interrupt Resample handler handles resample event. It will reassert interrupt if needed.
+pub struct IntrResampleHandler {
+ interrupter: Arc<Mutex<Interrupter>>,
+ resample_evt: EventFd,
+}
+
+impl IntrResampleHandler {
+ /// Start resample handler.
+ pub fn start(
+ event_loop: &EventLoop,
+ interrupter: Arc<Mutex<Interrupter>>,
+ resample_evt: EventFd,
+ ) -> Option<Arc<IntrResampleHandler>> {
+ let handler = Arc::new(IntrResampleHandler {
+ interrupter,
+ resample_evt,
+ });
+ let tmp_handler: Arc<dyn EventHandler> = handler.clone();
+ if let Err(e) = event_loop.add_event(
+ &handler.resample_evt,
+ WatchingEvents::empty().set_read(),
+ Arc::downgrade(&tmp_handler),
+ ) {
+ error!("cannot add intr resample handler to event loop: {}", e);
+ return None;
+ }
+ Some(handler)
+ }
+}
+impl EventHandler for IntrResampleHandler {
+ fn on_event(&self) -> Result<(), ()> {
+ match self.resample_evt.read() {
+ Ok(_) => {}
+ Err(e) => {
+ error!("cannot read resample evt: {}", e);
+ return Err(());
+ }
+ }
+ usb_debug!("resample triggered");
+ let mut interrupter = self.interrupter.lock();
+ if !interrupter.event_ring_is_empty() {
+ usb_debug!("irq resample re-assert irq event");
+ // There could be a race condition. When we get resample_evt and other
+ // component is sending interrupt at the same time.
+ // This might result in one more interrupt than we want. It's handled by
+ // kernel correctly.
+ if let Err(e) = interrupter.interrupt() {
+ error!("cannot send interrupt: {}", e);
+ return Err(());
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs
new file mode 100644
index 0000000..42cdef5
--- /dev/null
+++ b/devices/src/usb/xhci/mod.rs
@@ -0,0 +1,25 @@
+// Copyright 2018 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.
+
+mod command_ring_controller;
+mod device_slot;
+mod event_ring;
+mod interrupter;
+mod intr_resample_handler;
+mod ring_buffer;
+mod ring_buffer_controller;
+mod ring_buffer_stop_cb;
+mod transfer_ring_controller;
+mod xhci;
+#[allow(dead_code)]
+mod xhci_abi;
+#[allow(dead_code)]
+mod xhci_regs;
+
+pub mod scatter_gather_buffer;
+pub mod usb_hub;
+pub mod xhci_backend_device;
+pub mod xhci_backend_device_provider;
+pub mod xhci_controller;
+pub mod xhci_transfer;
diff --git a/devices/src/usb/xhci/ring_buffer.rs b/devices/src/usb/xhci/ring_buffer.rs
new file mode 100644
index 0000000..a178ebb
--- /dev/null
+++ b/devices/src/usb/xhci/ring_buffer.rs
@@ -0,0 +1,268 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::xhci_abi::{
+ AddressedTrb, Error as TrbError, LinkTrb, TransferDescriptor, Trb, TrbCast, TrbType,
+};
+use std::fmt::{self, Display};
+use std::mem::size_of;
+use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
+
+#[derive(Debug)]
+pub enum Error {
+ ReadGuestMemory(GuestMemoryError),
+ BadDequeuePointer(GuestAddress),
+ CastTrb(TrbError),
+ TrbChain(TrbError),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ ReadGuestMemory(e) => write!(f, "cannot read guest memory: {}", e),
+ BadDequeuePointer(addr) => write!(f, "bad dequeue pointer: {}", addr),
+ CastTrb(e) => write!(f, "cannot cast trb: {}", e),
+ TrbChain(e) => write!(f, "cannot get trb chain bit: {}", e),
+ }
+ }
+}
+
+/// Ring Buffer is segmented circular buffer in guest memory containing work items
+/// called transfer descriptors, each of which consists of one or more TRBs.
+/// Ring buffer logic is shared between transfer ring and command ring.
+/// Transfer Ring management is defined in xHCI spec 4.9.2.
+pub struct RingBuffer {
+ name: String,
+ mem: GuestMemory,
+ dequeue_pointer: GuestAddress,
+ // Used to check if the ring is empty. Toggled when looping back to the begining
+ // of the buffer.
+ consumer_cycle_state: bool,
+}
+
+impl Display for RingBuffer {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "RingBuffer `{}`", self.name)
+ }
+}
+
+// Public interfaces for Ring buffer.
+impl RingBuffer {
+ /// Create a new RingBuffer.
+ pub fn new(name: String, mem: GuestMemory) -> Self {
+ RingBuffer {
+ name,
+ mem,
+ dequeue_pointer: GuestAddress(0),
+ consumer_cycle_state: false,
+ }
+ }
+
+ /// Dequeue next transfer descriptor from the transfer ring.
+ pub fn dequeue_transfer_descriptor(&mut self) -> Result<Option<TransferDescriptor>> {
+ let mut td: TransferDescriptor = TransferDescriptor::new();
+ while let Some(addressed_trb) = self.get_current_trb()? {
+ if let Ok(TrbType::Link) = addressed_trb.trb.get_trb_type() {
+ let link_trb = addressed_trb
+ .trb
+ .cast::<LinkTrb>()
+ .map_err(Error::CastTrb)?;
+ self.dequeue_pointer = GuestAddress(link_trb.get_ring_segment_pointer());
+ self.consumer_cycle_state =
+ self.consumer_cycle_state != link_trb.get_toggle_cycle();
+ continue;
+ }
+
+ self.dequeue_pointer = match self.dequeue_pointer.checked_add(size_of::<Trb>() as u64) {
+ Some(addr) => addr,
+ None => {
+ return Err(Error::BadDequeuePointer(self.dequeue_pointer));
+ }
+ };
+
+ usb_debug!(
+ "{}: adding trb to td {}",
+ self.name.as_str(),
+ addressed_trb.trb
+ );
+ td.push(addressed_trb);
+ if !addressed_trb.trb.get_chain_bit().map_err(Error::TrbChain)? {
+ usb_debug!("trb chain is false returning");
+ break;
+ }
+ }
+ // A valid transfer descriptor contains at least one addressed trb and the last trb has
+ // chain bit != 0.
+ match td.last() {
+ Some(t) => {
+ if t.trb.get_chain_bit().map_err(Error::TrbChain)? {
+ return Ok(None);
+ }
+ }
+ None => return Ok(None),
+ }
+ Ok(Some(td))
+ }
+
+ /// Set dequeue pointer of the ring buffer.
+ pub fn set_dequeue_pointer(&mut self, addr: GuestAddress) {
+ usb_debug!("{}: set dequeue pointer {:x}", self.name.as_str(), addr.0);
+
+ self.dequeue_pointer = addr;
+ }
+
+ /// Set consumer cycle state of the ring buffer.
+ pub fn set_consumer_cycle_state(&mut self, state: bool) {
+ usb_debug!("{}: set consumer cycle state {}", self.name.as_str(), state);
+ self.consumer_cycle_state = state;
+ }
+
+ // Read trb pointed by dequeue pointer. Does not proceed dequeue pointer.
+ fn get_current_trb(&self) -> Result<Option<AddressedTrb>> {
+ let trb: Trb = self
+ .mem
+ .read_obj_from_addr(self.dequeue_pointer)
+ .map_err(Error::ReadGuestMemory)?;
+ usb_debug!("{}: trb read from memory {:?}", self.name.as_str(), trb);
+ // If cycle bit of trb does not equal consumer cycle state, the ring is empty.
+ // This trb is invalid.
+ if trb.get_cycle() != self.consumer_cycle_state {
+ usb_debug!(
+ "cycle bit does not match, self cycle {}",
+ self.consumer_cycle_state
+ );
+ Ok(None)
+ } else {
+ Ok(Some(AddressedTrb {
+ trb,
+ gpa: self.dequeue_pointer.0,
+ }))
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::usb::xhci::xhci_abi::*;
+
+ #[test]
+ fn ring_test_dequeue() {
+ let trb_size = size_of::<Trb>() as u64;
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut transfer_ring = RingBuffer::new(String::new(), gm.clone());
+
+ // Structure of ring buffer:
+ // 0x100 --> 0x200 --> 0x300
+ // trb 1 | trb 3 | trb 5
+ // trb 2 | trb 4 | trb 6
+ // l trb - l trb - l trb to 0x100
+ let mut trb = NormalTrb::new();
+ trb.set_trb_type(TrbType::Normal);
+ trb.set_data_buffer(1);
+ trb.set_chain(true);
+ gm.write_obj_at_addr(trb.clone(), GuestAddress(0x100))
+ .unwrap();
+
+ trb.set_data_buffer(2);
+ gm.write_obj_at_addr(trb, GuestAddress(0x100 + trb_size))
+ .unwrap();
+
+ let mut ltrb = LinkTrb::new();
+ ltrb.set_trb_type(TrbType::Link);
+ ltrb.set_ring_segment_pointer(0x200);
+ gm.write_obj_at_addr(ltrb, GuestAddress(0x100 + 2 * trb_size))
+ .unwrap();
+
+ trb.set_data_buffer(3);
+ gm.write_obj_at_addr(trb, GuestAddress(0x200)).unwrap();
+
+ // Chain bit is false.
+ trb.set_data_buffer(4);
+ trb.set_chain(false);
+ gm.write_obj_at_addr(trb, GuestAddress(0x200 + 1 * trb_size))
+ .unwrap();
+
+ ltrb.set_ring_segment_pointer(0x300);
+ gm.write_obj_at_addr(ltrb, GuestAddress(0x200 + 2 * trb_size))
+ .unwrap();
+
+ trb.set_data_buffer(5);
+ trb.set_chain(true);
+ gm.write_obj_at_addr(trb, GuestAddress(0x300)).unwrap();
+
+ // Chain bit is false.
+ trb.set_data_buffer(6);
+ trb.set_chain(false);
+ gm.write_obj_at_addr(trb, GuestAddress(0x300 + 1 * trb_size))
+ .unwrap();
+
+ ltrb.set_ring_segment_pointer(0x100);
+ gm.write_obj_at_addr(ltrb, GuestAddress(0x300 + 2 * trb_size))
+ .unwrap();
+
+ transfer_ring.set_dequeue_pointer(GuestAddress(0x100));
+ transfer_ring.set_consumer_cycle_state(false);
+
+ // Read first transfer descriptor.
+ let descriptor = transfer_ring
+ .dequeue_transfer_descriptor()
+ .unwrap()
+ .unwrap();
+ assert_eq!(descriptor.len(), 4);
+ assert_eq!(descriptor[0].trb.get_parameter(), 1);
+ assert_eq!(descriptor[1].trb.get_parameter(), 2);
+ assert_eq!(descriptor[2].trb.get_parameter(), 3);
+ assert_eq!(descriptor[3].trb.get_parameter(), 4);
+
+ // Read second transfer descriptor.
+ let descriptor = transfer_ring
+ .dequeue_transfer_descriptor()
+ .unwrap()
+ .unwrap();
+ assert_eq!(descriptor.len(), 2);
+ assert_eq!(descriptor[0].trb.get_parameter(), 5);
+ assert_eq!(descriptor[1].trb.get_parameter(), 6);
+ }
+
+ #[test]
+ fn transfer_ring_test_dequeue_failure() {
+ let trb_size = size_of::<Trb>() as u64;
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut transfer_ring = RingBuffer::new(String::new(), gm.clone());
+
+ let mut trb = NormalTrb::new();
+ trb.set_trb_type(TrbType::Normal);
+ trb.set_data_buffer(1);
+ trb.set_chain(true);
+ gm.write_obj_at_addr(trb.clone(), GuestAddress(0x100))
+ .unwrap();
+
+ trb.set_data_buffer(2);
+ gm.write_obj_at_addr(trb, GuestAddress(0x100 + trb_size))
+ .unwrap();
+
+ let mut ltrb = LinkTrb::new();
+ ltrb.set_trb_type(TrbType::Link);
+ ltrb.set_ring_segment_pointer(0x200);
+ ltrb.set_toggle_cycle(true);
+ gm.write_obj_at_addr(ltrb, GuestAddress(0x100 + 2 * trb_size))
+ .unwrap();
+
+ trb.set_data_buffer(3);
+ gm.write_obj_at_addr(trb, GuestAddress(0x200)).unwrap();
+
+ transfer_ring.set_dequeue_pointer(GuestAddress(0x100));
+ transfer_ring.set_consumer_cycle_state(false);
+
+ // Read first transfer descriptor.
+ let descriptor = transfer_ring.dequeue_transfer_descriptor().unwrap();
+ assert_eq!(descriptor.is_none(), true);
+ }
+
+}
diff --git a/devices/src/usb/xhci/ring_buffer_controller.rs b/devices/src/usb/xhci/ring_buffer_controller.rs
new file mode 100644
index 0000000..8dd5426
--- /dev/null
+++ b/devices/src/usb/xhci/ring_buffer_controller.rs
@@ -0,0 +1,343 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::ring_buffer_stop_cb::RingBufferStopCallback;
+use super::xhci_abi::*;
+use crate::utils::{self, EventHandler, EventLoop};
+use std::fmt::{self, Display};
+use std::sync::{Arc, MutexGuard};
+use sync::Mutex;
+
+use sys_util::{error, Error as SysError, EventFd, GuestAddress, GuestMemory, WatchingEvents};
+
+use super::ring_buffer::RingBuffer;
+
+#[derive(Debug)]
+pub enum Error {
+ AddEvent(utils::Error),
+ CreateEventFd(SysError),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ AddEvent(e) => write!(f, "failed to add event to event loop: {}", e),
+ CreateEventFd(e) => write!(f, "failed to create event fd: {}", e),
+ }
+ }
+}
+
+#[derive(PartialEq, Copy, Clone)]
+enum RingBufferState {
+ /// Running: RingBuffer is running, consuming transfer descriptor.
+ Running,
+ /// Stopping: Some thread requested RingBuffer stop. It will stop when current descriptor is
+ /// handled.
+ Stopping,
+ /// Stopped: RingBuffer already stopped.
+ Stopped,
+}
+
+/// TransferDescriptorHandler handles transfer descriptor. User should implement this trait and
+/// build a ring buffer controller with the struct.
+pub trait TransferDescriptorHandler {
+ /// Process descriptor asynchronously, write complete_event when done.
+ fn handle_transfer_descriptor(
+ &self,
+ descriptor: TransferDescriptor,
+ complete_event: EventFd,
+ ) -> std::result::Result<(), ()>;
+ /// Stop is called when trying to stop ring buffer controller. Returns true when stop must be
+ /// performed asynchronously. This happens because the handler is handling some descriptor
+ /// asynchronously, the stop callback of ring buffer controller must be called after the
+ /// `async` part is handled or canceled. If the TransferDescriptorHandler decide it could stop
+ /// immediately, it could return false.
+ /// For example, if a handler submitted a transfer but the transfer has not yet finished. Then
+ /// guest kernel requests to stop the ring buffer controller. Transfer descriptor handler will
+ /// return true, thus RingBufferController would transfer to Stopping state. It will be stopped
+ /// when all pending transfer completed.
+ /// On the other hand, if hander does not have any pending transfers, it would return false.
+ fn stop(&self) -> bool {
+ true
+ }
+}
+
+/// RingBufferController owns a ring buffer. It lives on a event_loop. It will pop out transfer
+/// descriptor and let TransferDescriptorHandler handle it.
+pub struct RingBufferController<T: 'static + TransferDescriptorHandler> {
+ name: String,
+ state: Mutex<RingBufferState>,
+ stop_callback: Mutex<Vec<RingBufferStopCallback>>,
+ ring_buffer: Mutex<RingBuffer>,
+ handler: Mutex<T>,
+ event_loop: Arc<EventLoop>,
+ event: EventFd,
+}
+
+impl<T: 'static + TransferDescriptorHandler> Display for RingBufferController<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "RingBufferController `{}`", self.name)
+ }
+}
+
+impl<T: Send> RingBufferController<T>
+where
+ T: 'static + TransferDescriptorHandler,
+{
+ /// Create a ring buffer controller and add it to event loop.
+ pub fn new_with_handler(
+ name: String,
+ mem: GuestMemory,
+ event_loop: Arc<EventLoop>,
+ handler: T,
+ ) -> Result<Arc<RingBufferController<T>>> {
+ let evt = EventFd::new().map_err(Error::CreateEventFd)?;
+ let controller = Arc::new(RingBufferController {
+ name: name.clone(),
+ state: Mutex::new(RingBufferState::Stopped),
+ stop_callback: Mutex::new(Vec::new()),
+ ring_buffer: Mutex::new(RingBuffer::new(name.clone(), mem)),
+ handler: Mutex::new(handler),
+ event_loop: event_loop.clone(),
+ event: evt,
+ });
+ let event_handler: Arc<dyn EventHandler> = controller.clone();
+ event_loop
+ .add_event(
+ &controller.event,
+ WatchingEvents::empty().set_read(),
+ Arc::downgrade(&event_handler),
+ )
+ .map_err(Error::AddEvent)?;
+ Ok(controller)
+ }
+
+ fn lock_ring_buffer(&self) -> MutexGuard<RingBuffer> {
+ self.ring_buffer.lock()
+ }
+
+ /// Set dequeue pointer of the internal ring buffer.
+ pub fn set_dequeue_pointer(&self, ptr: GuestAddress) {
+ usb_debug!("{}: set dequeue pointer: {:x}", self.name, ptr.0);
+ // Fast because this should only happen during xhci setup.
+ self.lock_ring_buffer().set_dequeue_pointer(ptr);
+ }
+
+ /// Set consumer cycle state.
+ pub fn set_consumer_cycle_state(&self, state: bool) {
+ usb_debug!("{}: set consumer cycle state: {}", self.name, state);
+ // Fast because this should only happen during xhci setup.
+ self.lock_ring_buffer().set_consumer_cycle_state(state);
+ }
+
+ /// Start the ring buffer.
+ pub fn start(&self) {
+ usb_debug!("{} started", self.name);
+ let mut state = self.state.lock();
+ if *state != RingBufferState::Running {
+ *state = RingBufferState::Running;
+ if let Err(e) = self.event.write(1) {
+ error!("cannot start event ring: {}", e);
+ }
+ }
+ }
+
+ /// Stop the ring buffer asynchronously.
+ pub fn stop(&self, callback: RingBufferStopCallback) {
+ usb_debug!("{} being stopped", self.name);
+ let mut state = self.state.lock();
+ if *state == RingBufferState::Stopped {
+ usb_debug!("{} is already stopped", self.name);
+ return;
+ }
+ if self.handler.lock().stop() {
+ *state = RingBufferState::Stopping;
+ self.stop_callback.lock().push(callback);
+ } else {
+ *state = RingBufferState::Stopped;
+ }
+ }
+}
+
+impl<T> Drop for RingBufferController<T>
+where
+ T: 'static + TransferDescriptorHandler,
+{
+ fn drop(&mut self) {
+ // Remove self from the event loop.
+ if let Err(e) = self.event_loop.remove_event_for_fd(&self.event) {
+ error!(
+ "cannot remove ring buffer controller from event loop: {}",
+ e
+ );
+ }
+ }
+}
+
+impl<T> EventHandler for RingBufferController<T>
+where
+ T: 'static + TransferDescriptorHandler + Send,
+{
+ fn on_event(&self) -> std::result::Result<(), ()> {
+ // `self.event` triggers ring buffer controller to run, the value read is not important.
+ match self.event.read() {
+ Ok(_) => {}
+ Err(e) => {
+ error!("cannot read from event fd: {}", e);
+ return Err(());
+ }
+ }
+ let mut state = self.state.lock();
+
+ match *state {
+ RingBufferState::Stopped => return Ok(()),
+ RingBufferState::Stopping => {
+ usb_debug!("{}: stopping ring buffer controller", self.name);
+ *state = RingBufferState::Stopped;
+ self.stop_callback.lock().clear();
+ return Ok(());
+ }
+ RingBufferState::Running => {}
+ }
+
+ let transfer_descriptor = match self.lock_ring_buffer().dequeue_transfer_descriptor() {
+ Ok(t) => t,
+ Err(e) => {
+ error!("cannot dequeue transfer descriptor: {}", e);
+ return Err(());
+ }
+ };
+
+ let transfer_descriptor = match transfer_descriptor {
+ Some(t) => t,
+ None => {
+ *state = RingBufferState::Stopped;
+ self.stop_callback.lock().clear();
+ return Ok(());
+ }
+ };
+
+ let event = match self.event.try_clone() {
+ Ok(evt) => evt,
+ Err(e) => {
+ error!("cannot clone event fd: {}", e);
+ return Err(());
+ }
+ };
+ self.handler
+ .lock()
+ .handle_transfer_descriptor(transfer_descriptor, event)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::mem::size_of;
+ use std::sync::mpsc::{channel, Sender};
+
+ struct TestHandler {
+ sender: Sender<i32>,
+ }
+
+ impl TransferDescriptorHandler for TestHandler {
+ fn handle_transfer_descriptor(
+ &self,
+ descriptor: TransferDescriptor,
+ complete_event: EventFd,
+ ) -> std::result::Result<(), ()> {
+ for atrb in descriptor {
+ assert_eq!(atrb.trb.get_trb_type().unwrap(), TrbType::Normal);
+ self.sender.send(atrb.trb.get_parameter() as i32).unwrap();
+ }
+ complete_event.write(1).unwrap();
+ Ok(())
+ }
+ }
+
+ fn setup_mem() -> GuestMemory {
+ let trb_size = size_of::<Trb>() as u64;
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Structure of ring buffer:
+ // 0x100 --> 0x200 --> 0x300
+ // trb 1 | trb 3 | trb 5
+ // trb 2 | trb 4 | trb 6
+ // l trb - l trb - l trb to 0x100
+ let mut trb = NormalTrb::new();
+ trb.set_trb_type(TrbType::Normal);
+ trb.set_data_buffer(1);
+ trb.set_chain(true);
+ gm.write_obj_at_addr(trb.clone(), GuestAddress(0x100))
+ .unwrap();
+
+ trb.set_data_buffer(2);
+ gm.write_obj_at_addr(trb, GuestAddress(0x100 + trb_size))
+ .unwrap();
+
+ let mut ltrb = LinkTrb::new();
+ ltrb.set_trb_type(TrbType::Link);
+ ltrb.set_ring_segment_pointer(0x200);
+ gm.write_obj_at_addr(ltrb, GuestAddress(0x100 + 2 * trb_size))
+ .unwrap();
+
+ trb.set_data_buffer(3);
+ gm.write_obj_at_addr(trb, GuestAddress(0x200)).unwrap();
+
+ // Chain bit is false.
+ trb.set_data_buffer(4);
+ trb.set_chain(false);
+ gm.write_obj_at_addr(trb, GuestAddress(0x200 + 1 * trb_size))
+ .unwrap();
+
+ ltrb.set_ring_segment_pointer(0x300);
+ gm.write_obj_at_addr(ltrb, GuestAddress(0x200 + 2 * trb_size))
+ .unwrap();
+
+ trb.set_data_buffer(5);
+ trb.set_chain(true);
+ gm.write_obj_at_addr(trb, GuestAddress(0x300)).unwrap();
+
+ // Chain bit is false.
+ trb.set_data_buffer(6);
+ trb.set_chain(false);
+ gm.write_obj_at_addr(trb, GuestAddress(0x300 + 1 * trb_size))
+ .unwrap();
+
+ ltrb.set_ring_segment_pointer(0x100);
+ gm.write_obj_at_addr(ltrb, GuestAddress(0x300 + 2 * trb_size))
+ .unwrap();
+ gm
+ }
+
+ #[test]
+ fn test_ring_buffer_controller() {
+ let (tx, rx) = channel();
+ let mem = setup_mem();
+ let (l, j) = EventLoop::start("test".to_string(), None).unwrap();
+ let l = Arc::new(l);
+ let controller = RingBufferController::new_with_handler(
+ "".to_string(),
+ mem,
+ l.clone(),
+ TestHandler { sender: tx },
+ )
+ .unwrap();
+ controller.set_dequeue_pointer(GuestAddress(0x100));
+ controller.set_consumer_cycle_state(false);
+ controller.start();
+ assert_eq!(rx.recv().unwrap(), 1);
+ assert_eq!(rx.recv().unwrap(), 2);
+ assert_eq!(rx.recv().unwrap(), 3);
+ assert_eq!(rx.recv().unwrap(), 4);
+ assert_eq!(rx.recv().unwrap(), 5);
+ assert_eq!(rx.recv().unwrap(), 6);
+ l.stop();
+ j.join().unwrap();
+ }
+}
diff --git a/devices/src/usb/xhci/ring_buffer_stop_cb.rs b/devices/src/usb/xhci/ring_buffer_stop_cb.rs
new file mode 100644
index 0000000..3eca928
--- /dev/null
+++ b/devices/src/usb/xhci/ring_buffer_stop_cb.rs
@@ -0,0 +1,73 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::utils::FailHandle;
+use std::sync::{Arc, Mutex};
+use sys_util::error;
+
+/// RingBufferStopCallback wraps a callback. The callback will be invoked when last instance of
+/// RingBufferStopCallback and its clones is dropped.
+///
+/// The callback might not be invoked in certain cases. Don't depend this for safety.
+#[derive(Clone)]
+pub struct RingBufferStopCallback {
+ inner: Arc<Mutex<RingBufferStopCallbackInner>>,
+}
+
+impl RingBufferStopCallback {
+ /// Create new callback from closure.
+ pub fn new<C: 'static + FnMut() + Send>(cb: C) -> RingBufferStopCallback {
+ RingBufferStopCallback {
+ inner: Arc::new(Mutex::new(RingBufferStopCallbackInner {
+ callback: Box::new(cb),
+ })),
+ }
+ }
+}
+
+struct RingBufferStopCallbackInner {
+ callback: Box<dyn FnMut() + Send>,
+}
+
+impl Drop for RingBufferStopCallbackInner {
+ fn drop(&mut self) {
+ (self.callback)();
+ }
+}
+
+/// Helper function to wrap up a closure with fail handle. The fail handle will be triggered if the
+/// closure returns an error.
+pub fn fallible_closure<E: std::fmt::Display, C: FnMut() -> Result<(), E> + 'static + Send>(
+ fail_handle: Arc<dyn FailHandle>,
+ mut callback: C,
+) -> impl FnMut() + 'static + Send {
+ move || match callback() {
+ Ok(()) => {}
+ Err(e) => {
+ error!("callback failed {}", e);
+ fail_handle.fail();
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::sync::{Arc, Mutex};
+
+ fn task(_: RingBufferStopCallback) {}
+
+ #[test]
+ fn simple_raii_callback() {
+ let a = Arc::new(Mutex::new(0));
+ let ac = a.clone();
+ let cb = RingBufferStopCallback::new(move || {
+ *ac.lock().unwrap() = 1;
+ });
+ task(cb.clone());
+ task(cb.clone());
+ task(cb);
+ assert_eq!(*a.lock().unwrap(), 1);
+ }
+}
diff --git a/devices/src/usb/xhci/scatter_gather_buffer.rs b/devices/src/usb/xhci/scatter_gather_buffer.rs
new file mode 100644
index 0000000..0150e4f
--- /dev/null
+++ b/devices/src/usb/xhci/scatter_gather_buffer.rs
@@ -0,0 +1,179 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::xhci_abi::{Error as TrbError, NormalTrb, TransferDescriptor, TrbCast, TrbType};
+use bit_field::Error as BitFieldError;
+use std::fmt::{self, Display};
+use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
+
+#[derive(Debug)]
+pub enum Error {
+ ReadGuestMemory(GuestMemoryError),
+ WriteGuestMemory(GuestMemoryError),
+ UnknownTrbType(BitFieldError),
+ CastTrb(TrbError),
+ BadTrbType(TrbType),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ ReadGuestMemory(e) => write!(f, "cannot read guest memory: {}", e),
+ WriteGuestMemory(e) => write!(f, "cannot write guest memory: {}", e),
+ UnknownTrbType(e) => write!(f, "unknown trb type: {}", e),
+ CastTrb(e) => write!(f, "cannot cast trb: {}", e),
+ BadTrbType(t) => write!(f, "should not build buffer from trb type: {:?}", t),
+ }
+ }
+}
+
+/// See xHCI spec 3.2.8 for scatter/gather transfer. It's used in bulk/interrupt transfers. See
+/// 3.2.10 for details.
+pub struct ScatterGatherBuffer {
+ mem: GuestMemory,
+ td: TransferDescriptor,
+}
+
+impl ScatterGatherBuffer {
+ /// Create a new buffer from transfer descriptor.
+ pub fn new(mem: GuestMemory, td: TransferDescriptor) -> Result<ScatterGatherBuffer> {
+ for atrb in &td {
+ let trb_type = atrb.trb.get_trb_type().map_err(Error::UnknownTrbType)?;
+ if trb_type != TrbType::Normal
+ && trb_type != TrbType::DataStage
+ && trb_type != TrbType::Isoch
+ {
+ return Err(Error::BadTrbType(trb_type));
+ }
+ }
+ Ok(ScatterGatherBuffer { mem, td })
+ }
+
+ /// Total len of this buffer.
+ pub fn len(&self) -> Result<usize> {
+ let mut total_len = 0usize;
+ for atrb in &self.td {
+ total_len += atrb
+ .trb
+ .cast::<NormalTrb>()
+ .map_err(Error::CastTrb)?
+ .get_trb_transfer_length() as usize;
+ }
+ Ok(total_len)
+ }
+
+ /// Read content to buffer, return number of bytes read.
+ pub fn read(&self, buffer: &mut [u8]) -> Result<usize> {
+ let mut total_size = 0usize;
+ let mut offset = 0;
+ for atrb in &self.td {
+ let normal_trb = atrb.trb.cast::<NormalTrb>().map_err(Error::CastTrb)?;
+ let len = normal_trb.get_trb_transfer_length() as usize;
+ let buffer_len = {
+ if offset == buffer.len() {
+ return Ok(total_size);
+ }
+ if buffer.len() > offset + len {
+ len
+ } else {
+ buffer.len() - offset
+ }
+ };
+ let buffer_end = offset + buffer_len;
+ let cur_buffer = &mut buffer[offset..buffer_end];
+ offset = buffer_end;
+ total_size += self
+ .mem
+ .read_at_addr(cur_buffer, GuestAddress(normal_trb.get_data_buffer()))
+ .map_err(Error::ReadGuestMemory)?;
+ }
+ Ok(total_size)
+ }
+
+ /// Write content from buffer, return number of bytes written.
+ pub fn write(&self, buffer: &[u8]) -> Result<usize> {
+ let mut total_size = 0usize;
+ let mut offset = 0;
+ for atrb in &self.td {
+ let normal_trb = atrb.trb.cast::<NormalTrb>().map_err(Error::CastTrb)?;
+ let len = normal_trb.get_trb_transfer_length() as usize;
+ let buffer_len = {
+ if offset == buffer.len() {
+ return Ok(total_size);
+ }
+ if buffer.len() > offset + len {
+ len
+ } else {
+ buffer.len() - offset
+ }
+ };
+ let buffer_end = offset + buffer_len;
+ let cur_buffer = &buffer[offset..buffer_end];
+ offset = buffer_end;
+ total_size += self
+ .mem
+ .write_at_addr(cur_buffer, GuestAddress(normal_trb.get_data_buffer()))
+ .map_err(Error::WriteGuestMemory)?;
+ }
+ Ok(total_size)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::usb::xhci::xhci_abi::{AddressedTrb, Trb};
+
+ #[test]
+ fn scatter_gather_buffer_test() {
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut td = TransferDescriptor::new();
+
+ // In this td, we are going to have scatter buffer at 0x100, length 4, 0x200 length 2 and
+ // 0x300 length 1.
+
+ let mut trb = Trb::new();
+ let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
+ ntrb.set_trb_type(TrbType::Normal);
+ ntrb.set_data_buffer(0x100);
+ ntrb.set_trb_transfer_length(4);
+ td.push(AddressedTrb { trb, gpa: 0 });
+
+ let mut trb = Trb::new();
+ let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
+ ntrb.set_trb_type(TrbType::Normal);
+ ntrb.set_data_buffer(0x200);
+ ntrb.set_trb_transfer_length(2);
+ td.push(AddressedTrb { trb, gpa: 0 });
+
+ let mut trb = Trb::new();
+ let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
+ ntrb.set_trb_type(TrbType::Normal);
+ ntrb.set_data_buffer(0x300);
+ ntrb.set_trb_transfer_length(1);
+ td.push(AddressedTrb { trb, gpa: 0 });
+
+ let buffer = ScatterGatherBuffer::new(gm.clone(), td).unwrap();
+
+ assert_eq!(buffer.len().unwrap(), 7);
+ let data_to_write: [u8; 7] = [7, 6, 5, 4, 3, 2, 1];
+ buffer.write(&data_to_write).unwrap();
+
+ let mut d = [0; 4];
+ gm.read_exact_at_addr(&mut d, GuestAddress(0x100)).unwrap();
+ assert_eq!(d, [7, 6, 5, 4]);;
+ gm.read_exact_at_addr(&mut d, GuestAddress(0x200)).unwrap();
+ assert_eq!(d, [3, 2, 0, 0]);;
+ gm.read_exact_at_addr(&mut d, GuestAddress(0x300)).unwrap();
+ assert_eq!(d, [1, 0, 0, 0]);;
+
+ let mut data_read = [0; 7];
+ buffer.read(&mut data_read).unwrap();
+ assert_eq!(data_to_write, data_read);
+ }
+}
diff --git a/devices/src/usb/xhci/transfer_ring_controller.rs b/devices/src/usb/xhci/transfer_ring_controller.rs
new file mode 100644
index 0000000..0b5d3b6
--- /dev/null
+++ b/devices/src/usb/xhci/transfer_ring_controller.rs
@@ -0,0 +1,87 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::usb::xhci::ring_buffer_controller::{
+ Error as RingBufferControllerError, RingBufferController, TransferDescriptorHandler,
+};
+use crate::utils::EventLoop;
+use std::sync::Arc;
+use sync::Mutex;
+use sys_util::{error, EventFd, GuestMemory};
+
+use super::interrupter::Interrupter;
+use super::usb_hub::UsbPort;
+use super::xhci_abi::TransferDescriptor;
+use super::xhci_transfer::XhciTransferManager;
+
+/// Transfer ring controller manages transfer ring.
+pub type TransferRingController = RingBufferController<TransferRingTrbHandler>;
+
+pub type TransferRingControllerError = RingBufferControllerError;
+
+/// TransferRingTrbHandler handles trbs on transfer ring.
+pub struct TransferRingTrbHandler {
+ mem: GuestMemory,
+ port: Arc<UsbPort>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ slot_id: u8,
+ endpoint_id: u8,
+ transfer_manager: XhciTransferManager,
+}
+
+impl TransferDescriptorHandler for TransferRingTrbHandler {
+ fn handle_transfer_descriptor(
+ &self,
+ descriptor: TransferDescriptor,
+ completion_event: EventFd,
+ ) -> Result<(), ()> {
+ let xhci_transfer = self.transfer_manager.create_transfer(
+ self.mem.clone(),
+ self.port.clone(),
+ self.interrupter.clone(),
+ self.slot_id,
+ self.endpoint_id,
+ descriptor,
+ completion_event,
+ );
+ xhci_transfer.send_to_backend_if_valid().map_err(|e| {
+ error!("failed to send transfer to backend: {}", e);
+ })
+ }
+
+ fn stop(&self) -> bool {
+ let backend = self.port.get_backend_device();
+ if backend.is_some() {
+ self.transfer_manager.cancel_all();
+ true
+ } else {
+ false
+ }
+ }
+}
+
+impl TransferRingController {
+ pub fn new(
+ mem: GuestMemory,
+ port: Arc<UsbPort>,
+ event_loop: Arc<EventLoop>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ slot_id: u8,
+ endpoint_id: u8,
+ ) -> Result<Arc<TransferRingController>, TransferRingControllerError> {
+ RingBufferController::new_with_handler(
+ format!("transfer ring slot_{} ep_{}", slot_id, endpoint_id),
+ mem.clone(),
+ event_loop,
+ TransferRingTrbHandler {
+ mem,
+ port,
+ interrupter,
+ slot_id,
+ endpoint_id,
+ transfer_manager: XhciTransferManager::new(),
+ },
+ )
+ }
+}
diff --git a/devices/src/usb/xhci/usb_hub.rs b/devices/src/usb/xhci/usb_hub.rs
new file mode 100644
index 0000000..28c7f19
--- /dev/null
+++ b/devices/src/usb/xhci/usb_hub.rs
@@ -0,0 +1,290 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::interrupter::{Error as InterrupterError, Interrupter};
+use super::xhci_backend_device::{BackendType, XhciBackendDevice};
+use super::xhci_regs::{
+ XhciRegs, MAX_PORTS, PORTSC_CONNECT_STATUS_CHANGE, PORTSC_CURRENT_CONNECT_STATUS,
+ PORTSC_PORT_ENABLED, PORTSC_PORT_ENABLED_DISABLED_CHANGE, USB2_PORTS_END, USB2_PORTS_START,
+ USB3_PORTS_END, USB3_PORTS_START, USB_STS_PORT_CHANGE_DETECT,
+};
+use crate::register_space::Register;
+use std::fmt::{self, Display};
+use std::sync::{Arc, MutexGuard};
+use sync::Mutex;
+
+#[derive(Debug)]
+pub enum Error {
+ AllPortsAttached,
+ AlreadyDetached(u8),
+ Attach {
+ port_id: u8,
+ reason: InterrupterError,
+ },
+ Detach {
+ port_id: u8,
+ reason: InterrupterError,
+ },
+ NoSuchDevice {
+ bus: u8,
+ addr: u8,
+ vid: u16,
+ pid: u16,
+ },
+ NoSuchPort(u8),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ AllPortsAttached => write!(f, "all suitable ports already attached"),
+ AlreadyDetached(port_id) => write!(f, "device already detached from port {}", port_id),
+ Attach { port_id, reason } => {
+ write!(f, "failed to attach device to port {}: {}", port_id, reason)
+ }
+ Detach { port_id, reason } => write!(
+ f,
+ "failed to detach device from port {}: {}",
+ port_id, reason
+ ),
+ NoSuchDevice {
+ bus,
+ addr,
+ vid,
+ pid,
+ } => write!(
+ f,
+ "device {}:{}:{:04x}:{:04x} is not attached",
+ bus, addr, vid, pid
+ ),
+ NoSuchPort(port_id) => write!(f, "port {} does not exist", port_id),
+ }
+ }
+}
+
+/// A port on usb hub. It could have a device connected to it.
+pub struct UsbPort {
+ ty: BackendType,
+ port_id: u8,
+ portsc: Register<u32>,
+ usbsts: Register<u32>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ backend_device: Mutex<Option<Box<dyn XhciBackendDevice>>>,
+}
+
+impl UsbPort {
+ /// Create a new usb port that has nothing connected to it.
+ pub fn new(
+ ty: BackendType,
+ port_id: u8,
+ portsc: Register<u32>,
+ usbsts: Register<u32>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ ) -> UsbPort {
+ UsbPort {
+ ty,
+ port_id,
+ portsc,
+ usbsts,
+ interrupter,
+ backend_device: Mutex::new(None),
+ }
+ }
+
+ fn port_id(&self) -> u8 {
+ self.port_id
+ }
+
+ /// Detach current connected backend. Returns false when there is no backend connected.
+ pub fn detach(&self) -> Result<()> {
+ let mut locked = self.backend_device.lock();
+ if locked.is_none() {
+ return Err(Error::AlreadyDetached(self.port_id));
+ }
+ usb_debug!("device detached from port {}", self.port_id);
+ *locked = None;
+ self.send_device_disconnected_event()
+ .map_err(|reason| Error::Detach {
+ port_id: self.port_id,
+ reason,
+ })
+ }
+
+ /// Get current connected backend.
+ pub fn get_backend_device(&self) -> MutexGuard<Option<Box<dyn XhciBackendDevice>>> {
+ self.backend_device.lock()
+ }
+
+ fn is_attached(&self) -> bool {
+ self.backend_device.lock().is_some()
+ }
+
+ fn reset(&self) -> std::result::Result<(), InterrupterError> {
+ if self.is_attached() {
+ self.send_device_connected_event()?;
+ }
+ Ok(())
+ }
+
+ fn attach(
+ &self,
+ device: Box<dyn XhciBackendDevice>,
+ ) -> std::result::Result<(), InterrupterError> {
+ usb_debug!("A backend is connected to port {}", self.port_id);
+ let mut locked = self.backend_device.lock();
+ assert!(locked.is_none());
+ *locked = Some(device);
+ self.send_device_connected_event()
+ }
+
+ /// Inform the guest kernel there is device connected to this port. It combines first few steps
+ /// of USB device initialization process in xHCI spec 4.3.
+ pub fn send_device_connected_event(&self) -> std::result::Result<(), InterrupterError> {
+ // xHCI spec 4.3.
+ self.portsc.set_bits(
+ PORTSC_CURRENT_CONNECT_STATUS
+ | PORTSC_PORT_ENABLED
+ | PORTSC_CONNECT_STATUS_CHANGE
+ | PORTSC_PORT_ENABLED_DISABLED_CHANGE,
+ );
+ self.usbsts.set_bits(USB_STS_PORT_CHANGE_DETECT);
+ self.interrupter
+ .lock()
+ .send_port_status_change_trb(self.port_id)
+ }
+
+ /// Inform the guest kernel that device has been detached.
+ pub fn send_device_disconnected_event(&self) -> std::result::Result<(), InterrupterError> {
+ // xHCI spec 4.3.
+ self.portsc
+ .set_bits(PORTSC_CONNECT_STATUS_CHANGE | PORTSC_PORT_ENABLED_DISABLED_CHANGE);
+ self.portsc.clear_bits(PORTSC_CURRENT_CONNECT_STATUS);
+ self.usbsts.set_bits(USB_STS_PORT_CHANGE_DETECT);
+ self.interrupter
+ .lock()
+ .send_port_status_change_trb(self.port_id)
+ }
+}
+
+/// UsbHub is a set of usb ports.
+pub struct UsbHub {
+ ports: Vec<Arc<UsbPort>>,
+}
+
+impl UsbHub {
+ /// Create usb hub with no device attached.
+ pub fn new(regs: &XhciRegs, interrupter: Arc<Mutex<Interrupter>>) -> UsbHub {
+ let mut ports = Vec::new();
+ // Each port should have a portsc register.
+ assert_eq!(MAX_PORTS as usize, regs.portsc.len());
+
+ for i in USB2_PORTS_START..USB2_PORTS_END {
+ ports.push(Arc::new(UsbPort::new(
+ BackendType::Usb2,
+ i + 1,
+ regs.portsc[i as usize].clone(),
+ regs.usbsts.clone(),
+ interrupter.clone(),
+ )));
+ }
+
+ for i in USB3_PORTS_START..USB3_PORTS_END {
+ ports.push(Arc::new(UsbPort::new(
+ BackendType::Usb3,
+ i + 1,
+ regs.portsc[i as usize].clone(),
+ regs.usbsts.clone(),
+ interrupter.clone(),
+ )));
+ }
+ UsbHub { ports }
+ }
+
+ /// Try to detach device of bus, addr, vid, pid
+ pub fn try_detach(&self, bus: u8, addr: u8, vid: u16, pid: u16) -> Result<()> {
+ for port in &self.ports {
+ // This block exists so that we only hold the backend device
+ // lock while checking the address. It needs to be dropped before
+ // calling port.detach(), because that acquires the backend
+ // device lock again.
+ {
+ let backend_device = port.get_backend_device();
+
+ let d = match backend_device.as_ref() {
+ None => continue,
+ Some(d) => d,
+ };
+
+ if d.host_bus() != bus
+ || d.host_address() != addr
+ || d.get_vid() != vid
+ || d.get_pid() != pid
+ {
+ continue;
+ }
+ }
+
+ return port.detach();
+ }
+
+ Err(Error::NoSuchDevice {
+ bus,
+ addr,
+ vid,
+ pid,
+ })
+ }
+
+ /// Reset all ports.
+ pub fn reset(&self) -> Result<()> {
+ usb_debug!("reseting usb hub");
+ for p in &self.ports {
+ p.reset().map_err(|reason| Error::Detach {
+ port_id: p.port_id(),
+ reason,
+ })?;
+ }
+ Ok(())
+ }
+
+ /// Get a specific port of the hub.
+ pub fn get_port(&self, port_id: u8) -> Option<Arc<UsbPort>> {
+ if port_id == 0 || port_id > MAX_PORTS {
+ return None;
+ }
+ let port_index = (port_id - 1) as usize;
+ Some(self.ports.get(port_index)?.clone())
+ }
+
+ /// Connect backend to next empty port.
+ pub fn connect_backend(&self, backend: Box<dyn XhciBackendDevice>) -> Result<u8> {
+ usb_debug!("Trying to connect backend to hub");
+ for port in &self.ports {
+ if port.is_attached() {
+ continue;
+ }
+ if port.ty != backend.get_backend_type() {
+ continue;
+ }
+ let port_id = port.port_id();
+ port.attach(backend)
+ .map_err(|reason| Error::Attach { port_id, reason })?;
+ return Ok(port_id);
+ }
+ Err(Error::AllPortsAttached)
+ }
+
+ /// Disconnect device from port. Returns false if port id is not valid or could not be
+ /// disonnected.
+ pub fn disconnect_port(&self, port_id: u8) -> Result<()> {
+ match self.get_port(port_id) {
+ Some(port) => port.detach(),
+ None => Err(Error::NoSuchPort(port_id)),
+ }
+ }
+}
diff --git a/devices/src/usb/xhci/xhci.rs b/devices/src/usb/xhci/xhci.rs
new file mode 100644
index 0000000..47ebe85
--- /dev/null
+++ b/devices/src/usb/xhci/xhci.rs
@@ -0,0 +1,392 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::command_ring_controller::{CommandRingController, CommandRingControllerError};
+use super::device_slot::{DeviceSlots, Error as DeviceSlotError};
+use super::interrupter::{Error as InterrupterError, Interrupter};
+use super::intr_resample_handler::IntrResampleHandler;
+use super::ring_buffer_stop_cb::RingBufferStopCallback;
+use super::usb_hub::UsbHub;
+use super::xhci_backend_device_provider::XhciBackendDeviceProvider;
+use super::xhci_regs::*;
+use crate::usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider;
+use crate::utils::{Error as UtilsError, EventLoop, FailHandle};
+use std::fmt::{self, Display};
+use std::sync::Arc;
+use sync::Mutex;
+use sys_util::{error, EventFd, GuestAddress, GuestMemory};
+
+#[derive(Debug)]
+pub enum Error {
+ StartEventLoop(UtilsError),
+ GetDeviceSlot(u8),
+ StartResampleHandler,
+ SendInterrupt(InterrupterError),
+ EnableInterrupter(InterrupterError),
+ SetModeration(InterrupterError),
+ SetupEventRing(InterrupterError),
+ SetEventHandlerBusy(InterrupterError),
+ StartProvider,
+ RingDoorbell(DeviceSlotError),
+ CreateCommandRingController(CommandRingControllerError),
+ ResetPort,
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ StartEventLoop(e) => write!(f, "failed to start event loop: {}", e),
+ GetDeviceSlot(i) => write!(f, "failed to get device slot: {}", i),
+ StartResampleHandler => write!(f, "failed to start resample handler"),
+ SendInterrupt(e) => write!(f, "failed to send interrupter: {}", e),
+ EnableInterrupter(e) => write!(f, "failed to enable interrupter: {}", e),
+ SetModeration(e) => write!(f, "failed to set interrupter moderation: {}", e),
+ SetupEventRing(e) => write!(f, "failed to setup event ring: {}", e),
+ SetEventHandlerBusy(e) => write!(f, "failed to set event handler busy: {}", e),
+ StartProvider => write!(f, "failed to start backend provider"),
+ RingDoorbell(e) => write!(f, "failed to ring doorbell: {}", e),
+ CreateCommandRingController(e) => {
+ write!(f, "failed to create command ring controller: {}", e)
+ }
+ ResetPort => write!(f, "failed to reset port"),
+ }
+ }
+}
+
+/// xHCI controller implementation.
+pub struct Xhci {
+ fail_handle: Arc<dyn FailHandle>,
+ regs: XhciRegs,
+ interrupter: Arc<Mutex<Interrupter>>,
+ command_ring_controller: Arc<CommandRingController>,
+ device_slots: DeviceSlots,
+ // resample handler and device provider only lives on EventLoop to handle corresponding events.
+ // By design, event loop only hold weak reference. We need to keep a strong reference here to
+ // keep it alive.
+ #[allow(dead_code)]
+ intr_resample_handler: Arc<IntrResampleHandler>,
+ #[allow(dead_code)]
+ device_provider: HostBackendDeviceProvider,
+}
+
+impl Xhci {
+ /// Create a new xHCI controller.
+ pub fn new(
+ fail_handle: Arc<dyn FailHandle>,
+ mem: GuestMemory,
+ device_provider: HostBackendDeviceProvider,
+ irq_evt: EventFd,
+ irq_resample_evt: EventFd,
+ regs: XhciRegs,
+ ) -> Result<Arc<Self>> {
+ let (event_loop, _join_handle) =
+ EventLoop::start("xhci".to_string(), Some(fail_handle.clone()))
+ .map_err(Error::StartEventLoop)?;
+ let interrupter = Arc::new(Mutex::new(Interrupter::new(mem.clone(), irq_evt, ®s)));
+ let event_loop = Arc::new(event_loop);
+ let intr_resample_handler =
+ IntrResampleHandler::start(&event_loop, interrupter.clone(), irq_resample_evt)
+ .ok_or(Error::StartResampleHandler)?;
+ let hub = Arc::new(UsbHub::new(®s, interrupter.clone()));
+
+ let mut device_provider = device_provider;
+ device_provider
+ .start(fail_handle.clone(), event_loop.clone(), hub.clone())
+ .map_err(|_| Error::StartProvider)?;
+
+ let device_slots = DeviceSlots::new(
+ fail_handle.clone(),
+ regs.dcbaap.clone(),
+ hub.clone(),
+ interrupter.clone(),
+ event_loop.clone(),
+ mem.clone(),
+ );
+ let command_ring_controller = CommandRingController::new(
+ mem.clone(),
+ event_loop.clone(),
+ device_slots.clone(),
+ interrupter.clone(),
+ )
+ .map_err(Error::CreateCommandRingController)?;
+ let xhci = Arc::new(Xhci {
+ fail_handle,
+ regs,
+ intr_resample_handler,
+ interrupter,
+ command_ring_controller,
+ device_slots,
+ device_provider,
+ });
+ Self::init_reg_callbacks(&xhci);
+ Ok(xhci)
+ }
+
+ fn init_reg_callbacks(xhci: &Arc<Xhci>) {
+ // All the callbacks will hold a weak reference to avoid memory leak. Thos weak upgrade
+ // should never fail.
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.usbcmd.set_write_cb(move |val: u32| {
+ // All the weak reference upgrade should never fail. xhci hold reference to the
+ // registers, callback won't be invoked if xhci is gone.
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.usbcmd_callback(val);
+ xhci.handle_register_callback_result(r, 0)
+ });
+
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.crcr.set_write_cb(move |val: u64| {
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.crcr_callback(val);
+ xhci.handle_register_callback_result(r, 0)
+ });
+
+ for i in 0..xhci.regs.portsc.len() {
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.portsc[i].set_write_cb(move |val: u32| {
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.portsc_callback(i as u32, val);
+ xhci.handle_register_callback_result(r, 0)
+ });
+ }
+
+ for i in 0..xhci.regs.doorbells.len() {
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.doorbells[i].set_write_cb(move |val: u32| {
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.doorbell_callback(i as u32, val);
+ xhci.handle_register_callback_result(r, ());
+ val
+ });
+ }
+
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.iman.set_write_cb(move |val: u32| {
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.iman_callback(val);
+ xhci.handle_register_callback_result(r, ());
+ val
+ });
+
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.imod.set_write_cb(move |val: u32| {
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.imod_callback(val);
+ xhci.handle_register_callback_result(r, ());
+ val
+ });
+
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.erstsz.set_write_cb(move |val: u32| {
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.erstsz_callback(val);
+ xhci.handle_register_callback_result(r, ());
+ val
+ });
+
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.erstba.set_write_cb(move |val: u64| {
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.erstba_callback(val);
+ xhci.handle_register_callback_result(r, ());
+ val
+ });
+
+ let xhci_weak = Arc::downgrade(xhci);
+ xhci.regs.erdp.set_write_cb(move |val: u64| {
+ let xhci = xhci_weak.upgrade().unwrap();
+ let r = xhci.erdp_callback(val);
+ xhci.handle_register_callback_result(r, ());
+ val
+ });
+ }
+
+ fn handle_register_callback_result<T>(&self, r: Result<T>, t: T) -> T {
+ match r {
+ Ok(v) => v,
+ Err(e) => {
+ error!("xhci controller failed: {}", e);
+ self.fail_handle.fail();
+ t
+ }
+ }
+ }
+
+ // Callback for usbcmd register write.
+ fn usbcmd_callback(&self, value: u32) -> Result<u32> {
+ if (value & USB_CMD_RESET) > 0 {
+ usb_debug!("xhci_controller: reset controller");
+ self.reset()?;
+ return Ok(value & (!USB_CMD_RESET));
+ }
+
+ if (value & USB_CMD_RUNSTOP) > 0 {
+ usb_debug!("xhci_controller: clear halt bits");
+ self.regs.usbsts.clear_bits(USB_STS_HALTED);
+ } else {
+ usb_debug!("xhci_controller: halt device");
+ self.halt()?;
+ self.regs.crcr.clear_bits(CRCR_COMMAND_RING_RUNNING);
+ }
+
+ // Enable interrupter if needed.
+ let enabled = (value & USB_CMD_INTERRUPTER_ENABLE) > 0
+ && (self.regs.iman.get_value() & IMAN_INTERRUPT_ENABLE) > 0;
+ usb_debug!("xhci_controller: interrupter enable?: {}", enabled);
+ self.interrupter
+ .lock()
+ .set_enabled(enabled)
+ .map_err(Error::EnableInterrupter)?;
+ Ok(value)
+ }
+
+ // Callback for crcr register write.
+ fn crcr_callback(&self, value: u64) -> Result<u64> {
+ usb_debug!("xhci_controller: write to crcr {:x}", value);
+ let value = if (self.regs.crcr.get_value() & CRCR_COMMAND_RING_RUNNING) == 0 {
+ self.command_ring_controller
+ .set_dequeue_pointer(GuestAddress(value & CRCR_COMMAND_RING_POINTER));
+ self.command_ring_controller
+ .set_consumer_cycle_state((value & CRCR_RING_CYCLE_STATE) > 0);
+ value
+ } else {
+ error!("Write to crcr while command ring is running");
+ self.regs.crcr.get_value()
+ };
+ Ok(value)
+ }
+
+ // Callback for portsc register write.
+ fn portsc_callback(&self, index: u32, value: u32) -> Result<u32> {
+ let mut value = value;
+ usb_debug!(
+ "xhci_controller: write to portsc index {} value {:x}",
+ index,
+ value
+ );
+ let port_id = (index + 1) as u8;
+ // xHCI spec 4.19.5. Note: we might want to change this logic if we support USB 3.0.
+ if (value & PORTSC_PORT_RESET) > 0 || (value & PORTSC_WARM_PORT_RESET) > 0 {
+ self.device_slots
+ .reset_port(port_id)
+ .map_err(|_| Error::ResetPort)?;
+ value &= !PORTSC_PORT_LINK_STATE_MASK;
+ value &= !PORTSC_PORT_RESET;
+ value |= PORTSC_PORT_ENABLED;
+ value |= PORTSC_PORT_RESET_CHANGE;
+ self.interrupter
+ .lock()
+ .send_port_status_change_trb(port_id)
+ .map_err(Error::SendInterrupt)?;
+ }
+ Ok(value)
+ }
+
+ // Callback for doorbell register write.
+ fn doorbell_callback(&self, index: u32, value: u32) -> Result<()> {
+ usb_debug!(
+ "xhci_controller: write to doorbell index {} value {:x}",
+ index,
+ value
+ );
+ let target = (value & DOORBELL_TARGET) as u8;
+ let stream_id: u16 = (value >> DOORBELL_STREAM_ID_OFFSET) as u16;
+ if (self.regs.usbcmd.get_value() & USB_CMD_RUNSTOP) > 0 {
+ // First doorbell is for command ring.
+ if index == 0 {
+ if target != 0 || stream_id != 0 {
+ return Ok(());
+ }
+ usb_debug!("doorbell to command ring");
+ self.regs.crcr.set_bits(CRCR_COMMAND_RING_RUNNING);
+ self.command_ring_controller.start();
+ } else {
+ usb_debug!("doorbell to device slot");
+ self.device_slots
+ .slot(index as u8)
+ .ok_or(Error::GetDeviceSlot(index as u8))?
+ .ring_doorbell(target, stream_id)
+ .map_err(Error::RingDoorbell)?;
+ }
+ }
+ Ok(())
+ }
+
+ // Callback for iman register write.
+ fn iman_callback(&self, value: u32) -> Result<()> {
+ usb_debug!("xhci_controller: write to iman {:x}", value);
+ let enabled = ((value & IMAN_INTERRUPT_ENABLE) > 0)
+ && ((self.regs.usbcmd.get_value() & USB_CMD_INTERRUPTER_ENABLE) > 0);
+ self.interrupter
+ .lock()
+ .set_enabled(enabled)
+ .map_err(Error::EnableInterrupter)
+ }
+
+ // Callback for imod register write.
+ fn imod_callback(&self, value: u32) -> Result<()> {
+ usb_debug!("xhci_controller: write to imod {:x}", value);
+ self.interrupter
+ .lock()
+ .set_moderation(
+ (value & IMOD_INTERRUPT_MODERATION_INTERVAL) as u16,
+ (value >> IMOD_INTERRUPT_MODERATION_COUNTER_OFFSET) as u16,
+ )
+ .map_err(Error::SetModeration)
+ }
+
+ // Callback for erstsz register write.
+ fn erstsz_callback(&self, value: u32) -> Result<()> {
+ usb_debug!("xhci_controller: write to erstz {:x}", value);
+ self.interrupter
+ .lock()
+ .set_event_ring_seg_table_size((value & ERSTSZ_SEGMENT_TABLE_SIZE) as u16)
+ .map_err(Error::SetupEventRing)
+ }
+
+ // Callback for erstba register write.
+ fn erstba_callback(&self, value: u64) -> Result<()> {
+ usb_debug!("xhci_controller: write to erstba {:x}", value);
+ self.interrupter
+ .lock()
+ .set_event_ring_seg_table_base_addr(GuestAddress(
+ value & ERSTBA_SEGMENT_TABLE_BASE_ADDRESS,
+ ))
+ .map_err(Error::SetupEventRing)
+ }
+
+ // Callback for erdp register write.
+ fn erdp_callback(&self, value: u64) -> Result<()> {
+ usb_debug!("xhci_controller: write to erdp {:x}", value);
+ let mut interrupter = self.interrupter.lock();
+ interrupter
+ .set_event_ring_dequeue_pointer(GuestAddress(value & ERDP_EVENT_RING_DEQUEUE_POINTER))
+ .map_err(Error::SetupEventRing)?;
+ interrupter
+ .set_event_handler_busy((value & ERDP_EVENT_HANDLER_BUSY) > 0)
+ .map_err(Error::SetEventHandlerBusy)
+ }
+
+ fn reset(&self) -> Result<()> {
+ self.regs.usbsts.set_bits(USB_STS_CONTROLLER_NOT_READY);
+ let usbsts = self.regs.usbsts.clone();
+ self.device_slots.stop_all_and_reset(move || {
+ usbsts.clear_bits(USB_STS_CONTROLLER_NOT_READY);
+ });
+ Ok(())
+ }
+
+ fn halt(&self) -> Result<()> {
+ let usbsts = self.regs.usbsts.clone();
+ self.device_slots
+ .stop_all(RingBufferStopCallback::new(move || {
+ usbsts.set_bits(USB_STS_HALTED);
+ }));
+ Ok(())
+ }
+}
diff --git a/devices/src/usb/xhci/xhci_abi.rs b/devices/src/usb/xhci/xhci_abi.rs
new file mode 100644
index 0000000..5a8748e
--- /dev/null
+++ b/devices/src/usb/xhci/xhci_abi.rs
@@ -0,0 +1,929 @@
+// Copyright 2018 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 bit_field::Error as BitFieldError;
+use bit_field::*;
+use data_model::DataInit;
+use std::fmt::{self, Display};
+use sys_util::GuestAddress;
+
+use std;
+
+#[derive(Debug)]
+pub enum Error {
+ UnknownTrbType(BitFieldError),
+ CannotCastTrb,
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ UnknownTrbType(e) => write!(f, "we got an unknown trb type value: {}", e),
+ CannotCastTrb => write!(f, "cannot cast trb from raw memory"),
+ }
+ }
+}
+
+// Fixed size of all TRB types.
+const TRB_SIZE: usize = 16;
+
+// Size of segment table.
+const SEGMENT_TABLE_SIZE: usize = 16;
+
+/// All kinds of trb.
+#[bitfield]
+#[bits = 6]
+#[derive(PartialEq, Debug, Clone, Copy)]
+pub enum TrbType {
+ Reserved = 0,
+ Normal = 1,
+ SetupStage = 2,
+ DataStage = 3,
+ StatusStage = 4,
+ Isoch = 5,
+ Link = 6,
+ EventData = 7,
+ Noop = 8,
+ EnableSlotCommand = 9,
+ DisableSlotCommand = 10,
+ AddressDeviceCommand = 11,
+ ConfigureEndpointCommand = 12,
+ EvaluateContextCommand = 13,
+ ResetEndpointCommand = 14,
+ StopEndpointCommand = 15,
+ SetTRDequeuePointerCommand = 16,
+ ResetDeviceCommand = 17,
+ NoopCommand = 23,
+ TransferEvent = 32,
+ CommandCompletionEvent = 33,
+ PortStatusChangeEvent = 34,
+}
+
+/// Completion code of trb types.
+#[bitfield]
+#[bits = 8]
+#[derive(PartialEq, Debug)]
+pub enum TrbCompletionCode {
+ Success = 1,
+ TransactionError = 4,
+ TrbError = 5,
+ NoSlotsAvailableError = 9,
+ SlotNotEnabledError = 11,
+ ShortPacket = 13,
+ ContextStateError = 19,
+}
+
+/// State of device slot.
+#[bitfield]
+#[bits = 5]
+#[derive(PartialEq, Debug)]
+pub enum DeviceSlotState {
+ // The same value (0) is used for both the enabled and disabled states. See
+ // xhci spec table 60.
+ DisabledOrEnabled = 0,
+ Default = 1,
+ Addressed = 2,
+ Configured = 3,
+}
+
+/// State of endpoint.
+#[bitfield]
+#[bits = 3]
+#[derive(PartialEq, Debug)]
+pub enum EndpointState {
+ Disabled = 0,
+ Running = 1,
+}
+
+#[bitfield]
+#[bits = 60]
+#[derive(PartialEq, Debug)]
+pub struct DequeuePtr(u64);
+
+impl DequeuePtr {
+ pub fn new(addr: GuestAddress) -> Self {
+ DequeuePtr(addr.0 >> 4)
+ }
+
+ // Get the guest physical address.
+ pub fn get_gpa(&self) -> GuestAddress {
+ GuestAddress(self.0 << 4)
+ }
+}
+
+// Generic TRB struct containing only fields common to all types.
+#[bitfield]
+#[derive(Clone, Copy, PartialEq)]
+pub struct Trb {
+ parameter: B64,
+ status: B32,
+ cycle: bool,
+ flags: B9,
+ trb_type: TrbType,
+ control: B16,
+}
+
+impl Trb {
+ fn fmt_helper(&self, f: &mut fmt::Formatter) -> Result<fmt::Result> {
+ match self.get_trb_type().map_err(Error::UnknownTrbType)? {
+ TrbType::Reserved => Ok(write!(f, "reserved trb type")),
+ TrbType::Normal => {
+ let t = self.cast::<NormalTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::SetupStage => {
+ let t = self.cast::<SetupStageTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::DataStage => {
+ let t = self.cast::<DataStageTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::StatusStage => {
+ let t = self.cast::<StatusStageTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::Isoch => {
+ let t = self.cast::<IsochTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::Link => {
+ let t = self.cast::<LinkTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::EventData => {
+ let t = self.cast::<EventDataTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::Noop => {
+ let t = self.cast::<NoopTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::EnableSlotCommand => Ok(write!(f, "trb: enable slot command {:?}", self)),
+ TrbType::DisableSlotCommand => {
+ let t = self.cast::<DisableSlotCommandTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::AddressDeviceCommand => {
+ let t = self.cast::<AddressDeviceCommandTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::ConfigureEndpointCommand => {
+ let t = self.cast::<ConfigureEndpointCommandTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::EvaluateContextCommand => {
+ let t = self.cast::<EvaluateContextCommandTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::ResetEndpointCommand => {
+ let t = self.cast::<ResetEndpointCommandTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::StopEndpointCommand => {
+ let t = self.cast::<StopEndpointCommandTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::SetTRDequeuePointerCommand => {
+ let t = self.cast::<SetTRDequeuePointerCommandTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::ResetDeviceCommand => {
+ let t = self.cast::<ResetDeviceCommandTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::NoopCommand => Ok(write!(f, "trb: noop command {:?}", self)),
+ TrbType::TransferEvent => {
+ let t = self.cast::<TransferEventTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::CommandCompletionEvent => {
+ let t = self.cast::<CommandCompletionEventTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ TrbType::PortStatusChangeEvent => {
+ let t = self.cast::<PortStatusChangeEventTrb>()?;
+ Ok(write!(f, "trb: {:?}", t))
+ }
+ }
+ }
+}
+
+impl Display for Trb {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.fmt_helper(f) {
+ Ok(f) => f,
+ Err(e) => write!(f, "fail to format trb {}", e),
+ }
+ }
+}
+
+impl Trb {
+ /// Get chain bit.
+ pub fn get_chain_bit(&self) -> Result<bool> {
+ Ok(match self.get_trb_type() {
+ Ok(TrbType::Normal) => self.cast::<NormalTrb>()?.get_chain(),
+ Ok(TrbType::DataStage) => self.cast::<DataStageTrb>()?.get_chain(),
+ Ok(TrbType::StatusStage) => self.cast::<StatusStageTrb>()?.get_chain(),
+ Ok(TrbType::Isoch) => self.cast::<IsochTrb>()?.get_chain(),
+ Ok(TrbType::Noop) => self.cast::<NoopTrb>()?.get_chain(),
+ Ok(TrbType::Link) => self.cast::<LinkTrb>()?.get_chain(),
+ Ok(TrbType::EventData) => self.cast::<EventDataTrb>()?.get_chain(),
+ _ => false,
+ })
+ }
+
+ /// Get interrupt target.
+ pub fn interrupter_target(&self) -> u8 {
+ const STATUS_INTERRUPTER_TARGET_OFFSET: u8 = 22;
+ (self.get_status() >> STATUS_INTERRUPTER_TARGET_OFFSET) as u8
+ }
+
+ /// Only some of trb types could appear in transfer ring.
+ pub fn can_be_in_transfer_ring(&self) -> Result<bool> {
+ match self.get_trb_type().map_err(Error::UnknownTrbType)? {
+ TrbType::Normal
+ | TrbType::SetupStage
+ | TrbType::DataStage
+ | TrbType::StatusStage
+ | TrbType::Isoch
+ | TrbType::Link
+ | TrbType::EventData
+ | TrbType::Noop => Ok(true),
+ _ => Ok(false),
+ }
+ }
+
+ /// Length of this transfer.
+ pub fn transfer_length(&self) -> Result<u32> {
+ const STATUS_TRANSFER_LENGTH_MASK: u32 = 0x1ffff;
+ match self.get_trb_type().map_err(Error::UnknownTrbType)? {
+ TrbType::Normal | TrbType::SetupStage | TrbType::DataStage | TrbType::Isoch => {
+ Ok(self.get_status() & STATUS_TRANSFER_LENGTH_MASK)
+ }
+ _ => Ok(0),
+ }
+ }
+
+ /// Returns true if interrupt is required on completion.
+ pub fn interrupt_on_completion(&self) -> bool {
+ const FLAGS_INTERRUPT_ON_COMPLETION_MASK: u16 = 0x10;
+ (self.get_flags() & FLAGS_INTERRUPT_ON_COMPLETION_MASK) > 0
+ }
+
+ /// Returns true if interrupt is required on transfer of short packet.
+ pub fn interrupt_on_short_packet(&self) -> bool {
+ const FLAGS_INTERRUPT_ON_SHORT_PACKET: u16 = 0x2;
+ (self.get_flags() & FLAGS_INTERRUPT_ON_SHORT_PACKET) > 0
+ }
+
+ /// Returns true if this trb is immediate data.
+ pub fn immediate_data(&self) -> Result<bool> {
+ const FLAGS_IMMEDIATE_DATA_MASK: u16 = 0x20;
+ match self.get_trb_type().map_err(Error::UnknownTrbType)? {
+ TrbType::Normal | TrbType::SetupStage | TrbType::DataStage | TrbType::Isoch => {
+ Ok((self.get_flags() & FLAGS_IMMEDIATE_DATA_MASK) != 0)
+ }
+ _ => Ok(false),
+ }
+ }
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct NormalTrb {
+ data_buffer: B64,
+ trb_transfer_length: B17,
+ td_size: B5,
+ interrupter_target: B10,
+ cycle: bool,
+ evaluate_next_trb: B1,
+ interrupt_on_short_packet: B1,
+ no_snoop: B1,
+ chain: bool,
+ interrupt_on_completion: B1,
+ immediate_data: B1,
+ reserved: B2,
+ block_event_interrupt: B1,
+ trb_type: TrbType,
+ reserved1: B16,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct SetupStageTrb {
+ request_type: B8,
+ request: B8,
+ value: B16,
+ index: B16,
+ length: B16,
+ trb_transfer_length: B17,
+ reserved0: B5,
+ interrupter_target: B10,
+ cycle: bool,
+ reserved1: B4,
+ interrupt_on_completion: B1,
+ immediate_data: B1,
+ reserved2: B3,
+ trb_type: TrbType,
+ transfer_type: B2,
+ reserved3: B14,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct DataStageTrb {
+ data_buffer_pointer: B64,
+ trb_transfer_length: B17,
+ td_size: B5,
+ interrupter_target: B10,
+ cycle: bool,
+ evaluate_next_trb: B1,
+ interrupt_on_short_packet: B1,
+ no_snoop: B1,
+ chain: bool,
+ interrupt_on_completion: B1,
+ immediate_data: B1,
+ reserved0: B3,
+ trb_type: TrbType,
+ direction: B1,
+ reserved1: B15,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct StatusStageTrb {
+ reserved0: B64,
+ reserved1: B22,
+ interrupter_target: B10,
+ cycle: bool,
+ evaluate_next_trb: B1,
+ reserved2: B2,
+ chain: bool,
+ interrupt_on_completion: B1,
+ reserved3: B4,
+ trb_type: TrbType,
+ direction: B1,
+ reserved4: B15,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct IsochTrb {
+ data_buffer_pointer: B64,
+ trb_transfer_length: B17,
+ td_size: B5,
+ interrupter_target: B10,
+ cycle: bool,
+ evaulate_nex_trb: B1,
+ interrupt_on_short_packet: B1,
+ no_snoop: B1,
+ chain: bool,
+ interrupt_on_completion: B1,
+ immediate_data: B1,
+ transfer_burst_count: B2,
+ block_event_interrupt: B1,
+ trb_type: TrbType,
+ tlbpc: B4,
+ frame_id: B11,
+ sia: B1,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct LinkTrb {
+ ring_segment_pointer: B64,
+ reserved0: B22,
+ interrupter_target: B10,
+ cycle: bool,
+ toggle_cycle: bool,
+ reserved1: B2,
+ chain: bool,
+ interrupt_on_completion: bool,
+ reserved2: B4,
+ trb_type: TrbType,
+ reserved3: B16,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct EventDataTrb {
+ event_data: B64,
+ reserved0: B22,
+ interrupter_target: B10,
+ cycle: bool,
+ evaluate_next_trb: B1,
+ reserved1: B2,
+ chain: bool,
+ interrupt_on_completion: B1,
+ reserved2: B3,
+ block_event_interrupt: B1,
+ trb_type: TrbType,
+ reserved3: B16,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct NoopTrb {
+ reserved0: B64,
+ reserved1: B22,
+ interrupter_target: B10,
+ cycle: bool,
+ evaluate_next_trb: B1,
+ reserved2: B2,
+ chain: bool,
+ interrupt_on_completion: B1,
+ reserved3: B4,
+ trb_type: TrbType,
+ reserved4: B16,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct DisableSlotCommandTrb {
+ reserved0: B32,
+ reserved1: B32,
+ reserved2: B32,
+ cycle: bool,
+ reserved3: B9,
+ trb_type: TrbType,
+ reserved4: B8,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct AddressDeviceCommandTrb {
+ input_context_pointer: B64,
+ reserved: B32,
+ cycle: bool,
+ reserved2: B8,
+ block_set_address_request: bool,
+ trb_type: TrbType,
+ reserved3: B8,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct ConfigureEndpointCommandTrb {
+ input_context_pointer: B64,
+ reserved0: B32,
+ cycle: bool,
+ reserved1: B8,
+ deconfigure: bool,
+ trb_type: TrbType,
+ reserved2: B8,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct EvaluateContextCommandTrb {
+ input_context_pointer: B64,
+ reserved0: B32,
+ cycle: bool,
+ reserved1: B9,
+ trb_type: TrbType,
+ reserved2: B8,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct ResetEndpointCommandTrb {
+ reserved0: B32,
+ reserved1: B32,
+ reserved2: B32,
+ cycle: bool,
+ reserved3: B8,
+ transfer_state_preserve: B1,
+ trb_type: TrbType,
+ endpoint_id: B5,
+ reserved4: B3,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct StopEndpointCommandTrb {
+ reserved0: B32,
+ reserved1: B32,
+ reserved2: B32,
+ cycle: bool,
+ reserved3: B9,
+ trb_type: TrbType,
+ endpoint_id: B5,
+ reserved4: B2,
+ suspend: B1,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct SetTRDequeuePointerCommandTrb {
+ dequeue_cycle_state: bool,
+ stream_context_type: B3,
+ dequeue_ptr: DequeuePtr,
+ reserved0: B16,
+ stream_id: B16,
+ cycle: bool,
+ reserved1: B9,
+ trb_type: TrbType,
+ endpoint_id: B5,
+ reserved3: B2,
+ suspend: B1,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct ResetDeviceCommandTrb {
+ reserved0: B32,
+ reserved1: B32,
+ reserved2: B32,
+ cycle: bool,
+ reserved3: B9,
+ trb_type: TrbType,
+ reserved4: B8,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct TransferEventTrb {
+ trb_pointer: B64,
+ trb_transfer_length: B24,
+ completion_code: TrbCompletionCode,
+ cycle: bool,
+ reserved0: B1,
+ event_data: B1,
+ reserved1: B7,
+ trb_type: TrbType,
+ endpoint_id: B5,
+ reserved2: B3,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct CommandCompletionEventTrb {
+ trb_pointer: B64,
+ command_completion_parameter: B24,
+ completion_code: TrbCompletionCode,
+ cycle: bool,
+ reserved: B9,
+ trb_type: TrbType,
+ vf_id: B8,
+ slot_id: B8,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct PortStatusChangeEventTrb {
+ reserved0: B24,
+ port_id: B8,
+ reserved1: B32,
+ reserved2: B24,
+ completion_code: TrbCompletionCode,
+ cycle: bool,
+ reserved3: B9,
+ trb_type: TrbType,
+ reserved4: B16,
+}
+
+/// Associate real type of trb.
+pub trait TypedTrb {
+ const TY: TrbType;
+}
+
+impl TypedTrb for Trb {
+ const TY: TrbType = TrbType::Reserved;
+}
+
+impl TypedTrb for NormalTrb {
+ const TY: TrbType = TrbType::Normal;
+}
+
+impl TypedTrb for SetupStageTrb {
+ const TY: TrbType = TrbType::SetupStage;
+}
+
+impl TypedTrb for DataStageTrb {
+ const TY: TrbType = TrbType::DataStage;
+}
+
+impl TypedTrb for StatusStageTrb {
+ const TY: TrbType = TrbType::StatusStage;
+}
+
+impl TypedTrb for IsochTrb {
+ const TY: TrbType = TrbType::Isoch;
+}
+
+impl TypedTrb for LinkTrb {
+ const TY: TrbType = TrbType::Link;
+}
+
+impl TypedTrb for EventDataTrb {
+ const TY: TrbType = TrbType::EventData;
+}
+
+impl TypedTrb for NoopTrb {
+ const TY: TrbType = TrbType::Noop;
+}
+
+impl TypedTrb for DisableSlotCommandTrb {
+ const TY: TrbType = TrbType::DisableSlotCommand;
+}
+
+impl TypedTrb for AddressDeviceCommandTrb {
+ const TY: TrbType = TrbType::AddressDeviceCommand;
+}
+
+impl TypedTrb for ConfigureEndpointCommandTrb {
+ const TY: TrbType = TrbType::ConfigureEndpointCommand;
+}
+
+impl TypedTrb for EvaluateContextCommandTrb {
+ const TY: TrbType = TrbType::EvaluateContextCommand;
+}
+
+impl TypedTrb for ResetEndpointCommandTrb {
+ const TY: TrbType = TrbType::ResetEndpointCommand;
+}
+
+impl TypedTrb for StopEndpointCommandTrb {
+ const TY: TrbType = TrbType::StopEndpointCommand;
+}
+
+impl TypedTrb for SetTRDequeuePointerCommandTrb {
+ const TY: TrbType = TrbType::SetTRDequeuePointerCommand;
+}
+
+impl TypedTrb for ResetDeviceCommandTrb {
+ const TY: TrbType = TrbType::ResetDeviceCommand;
+}
+
+impl TypedTrb for TransferEventTrb {
+ const TY: TrbType = TrbType::TransferEvent;
+}
+
+impl TypedTrb for CommandCompletionEventTrb {
+ const TY: TrbType = TrbType::CommandCompletionEvent;
+}
+
+impl TypedTrb for PortStatusChangeEventTrb {
+ const TY: TrbType = TrbType::PortStatusChangeEvent;
+}
+
+/// All trb structs have the same size. One trb could be safely casted to another, though the
+/// values might be invalid.
+pub unsafe trait TrbCast: DataInit + TypedTrb {
+ fn cast<T: TrbCast>(&self) -> Result<&T> {
+ T::from_slice(self.as_slice()).ok_or(Error::CannotCastTrb)
+ }
+
+ fn cast_mut<T: TrbCast>(&mut self) -> Result<&mut T> {
+ T::from_mut_slice(self.as_mut_slice()).ok_or(Error::CannotCastTrb)
+ }
+
+ fn checked_cast<T: TrbCast>(&self) -> Result<&T> {
+ if Trb::from_slice(self.as_slice())
+ .ok_or(Error::CannotCastTrb)?
+ .get_trb_type()
+ .map_err(Error::UnknownTrbType)?
+ != T::TY
+ {
+ return Err(Error::CannotCastTrb);
+ }
+ T::from_slice(self.as_slice()).ok_or(Error::CannotCastTrb)
+ }
+
+ fn checked_mut_cast<T: TrbCast>(&mut self) -> Result<&mut T> {
+ if Trb::from_slice(self.as_slice())
+ .ok_or(Error::CannotCastTrb)?
+ .get_trb_type()
+ .map_err(Error::UnknownTrbType)?
+ != T::TY
+ {
+ return Err(Error::CannotCastTrb);
+ }
+ T::from_mut_slice(self.as_mut_slice()).ok_or(Error::CannotCastTrb)
+ }
+}
+
+unsafe impl DataInit for Trb {}
+unsafe impl DataInit for NormalTrb {}
+unsafe impl DataInit for SetupStageTrb {}
+unsafe impl DataInit for DataStageTrb {}
+unsafe impl DataInit for StatusStageTrb {}
+unsafe impl DataInit for IsochTrb {}
+unsafe impl DataInit for LinkTrb {}
+unsafe impl DataInit for EventDataTrb {}
+unsafe impl DataInit for NoopTrb {}
+unsafe impl DataInit for DisableSlotCommandTrb {}
+unsafe impl DataInit for AddressDeviceCommandTrb {}
+unsafe impl DataInit for ConfigureEndpointCommandTrb {}
+unsafe impl DataInit for EvaluateContextCommandTrb {}
+unsafe impl DataInit for ResetEndpointCommandTrb {}
+unsafe impl DataInit for StopEndpointCommandTrb {}
+unsafe impl DataInit for SetTRDequeuePointerCommandTrb {}
+unsafe impl DataInit for ResetDeviceCommandTrb {}
+unsafe impl DataInit for TransferEventTrb {}
+unsafe impl DataInit for CommandCompletionEventTrb {}
+unsafe impl DataInit for PortStatusChangeEventTrb {}
+unsafe impl DataInit for EventRingSegmentTableEntry {}
+unsafe impl DataInit for InputControlContext {}
+unsafe impl DataInit for SlotContext {}
+unsafe impl DataInit for EndpointContext {}
+
+unsafe impl DataInit for DeviceContext {}
+unsafe impl DataInit for AddressedTrb {}
+
+unsafe impl TrbCast for Trb {}
+unsafe impl TrbCast for NormalTrb {}
+unsafe impl TrbCast for SetupStageTrb {}
+unsafe impl TrbCast for DataStageTrb {}
+unsafe impl TrbCast for StatusStageTrb {}
+unsafe impl TrbCast for IsochTrb {}
+unsafe impl TrbCast for LinkTrb {}
+unsafe impl TrbCast for EventDataTrb {}
+unsafe impl TrbCast for NoopTrb {}
+unsafe impl TrbCast for DisableSlotCommandTrb {}
+unsafe impl TrbCast for AddressDeviceCommandTrb {}
+unsafe impl TrbCast for ConfigureEndpointCommandTrb {}
+unsafe impl TrbCast for EvaluateContextCommandTrb {}
+unsafe impl TrbCast for ResetEndpointCommandTrb {}
+unsafe impl TrbCast for StopEndpointCommandTrb {}
+unsafe impl TrbCast for SetTRDequeuePointerCommandTrb {}
+unsafe impl TrbCast for ResetDeviceCommandTrb {}
+unsafe impl TrbCast for TransferEventTrb {}
+unsafe impl TrbCast for CommandCompletionEventTrb {}
+unsafe impl TrbCast for PortStatusChangeEventTrb {}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct EventRingSegmentTableEntry {
+ ring_segment_base_address: B64,
+ ring_segment_size: B16,
+ reserved2: B48,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct InputControlContext {
+ // Xhci spec 6.2.5.1.
+ drop_context_flags: B32,
+ add_context_flags: B32,
+ reserved0: B32,
+ reserved1: B32,
+ reserved2: B32,
+ reserved3: B32,
+ reserved4: B32,
+ configuration_value: B8,
+ interface_number: B8,
+ alternate_setting: B8,
+ reserved5: B8,
+}
+
+impl InputControlContext {
+ /// Get drop context flag.
+ pub fn drop_context_flag(&self, idx: u8) -> bool {
+ (self.get_drop_context_flags() & (1 << idx)) != 0
+ }
+
+ /// Get add context flag.
+ pub fn add_context_flag(&self, idx: u8) -> bool {
+ (self.get_add_context_flags() & (1 << idx)) != 0
+ }
+}
+
+// Size of device context entries (SlotContext and EndpointContext).
+pub const DEVICE_CONTEXT_ENTRY_SIZE: usize = 32usize;
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct SlotContext {
+ route_string: B20,
+ speed: B4,
+ reserved1: B1,
+ mtt: B1,
+ hub: B1,
+ context_entries: B5,
+ max_exit_latency: B16,
+ root_hub_port_number: B8,
+ num_ports: B8,
+ tt_hub_slot_id: B8,
+ tt_port_number: B8,
+ tt_think_time: B2,
+ reserved2: B4,
+ interrupter_target: B10,
+ usb_device_address: B8,
+ reserved3: B19,
+ slot_state: DeviceSlotState,
+ reserved4: B32,
+ reserved5: B32,
+ reserved6: B32,
+ reserved7: B32,
+}
+
+#[bitfield]
+#[derive(Clone, Copy)]
+pub struct EndpointContext {
+ endpoint_state: EndpointState,
+ reserved1: B5,
+ mult: B2,
+ max_primary_streams: B5,
+ linear_stream_array: B1,
+ interval: B8,
+ max_esit_payload_hi: B8,
+ reserved2: B1,
+ error_count: B2,
+ endpoint_type: B3,
+ reserved3: B1,
+ host_initiate_disable: B1,
+ max_burst_size: B8,
+ max_packet_size: B16,
+ dequeue_cycle_state: bool,
+ reserved4: B3,
+ tr_dequeue_pointer: DequeuePtr,
+ average_trb_length: B16,
+ max_esit_payload_lo: B16,
+ reserved5: B32,
+ reserved6: B32,
+ reserved7: B32,
+}
+
+/// Device context.
+#[derive(Clone, Copy, Debug)]
+pub struct DeviceContext {
+ pub slot_context: SlotContext,
+ pub endpoint_context: [EndpointContext; 31],
+}
+
+/// POD struct associates a TRB with its address in guest memory. This is
+/// useful because transfer and command completion event TRBs must contain
+/// pointers to the original TRB that generated the event.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct AddressedTrb {
+ pub trb: Trb,
+ pub gpa: u64,
+}
+
+pub type TransferDescriptor = Vec<AddressedTrb>;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn check_struct_sizes() {
+ assert_eq!(std::mem::size_of::<Trb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<NormalTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<SetupStageTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<DataStageTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<StatusStageTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<IsochTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<LinkTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<EventDataTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<NoopTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<DisableSlotCommandTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<AddressDeviceCommandTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<ConfigureEndpointCommandTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<EvaluateContextCommandTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<ResetEndpointCommandTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<StopEndpointCommandTrb>(), TRB_SIZE);
+ assert_eq!(
+ std::mem::size_of::<SetTRDequeuePointerCommandTrb>(),
+ TRB_SIZE
+ );
+ assert_eq!(std::mem::size_of::<ResetDeviceCommandTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<TransferEventTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<CommandCompletionEventTrb>(), TRB_SIZE);
+ assert_eq!(std::mem::size_of::<PortStatusChangeEventTrb>(), TRB_SIZE);
+
+ assert_eq!(
+ std::mem::size_of::<EventRingSegmentTableEntry>(),
+ SEGMENT_TABLE_SIZE
+ );
+ assert_eq!(std::mem::size_of::<InputControlContext>(), 32);
+ assert_eq!(
+ std::mem::size_of::<SlotContext>(),
+ DEVICE_CONTEXT_ENTRY_SIZE
+ );
+ assert_eq!(
+ std::mem::size_of::<EndpointContext>(),
+ DEVICE_CONTEXT_ENTRY_SIZE
+ );
+ assert_eq!(
+ std::mem::size_of::<DeviceContext>(),
+ 32 * DEVICE_CONTEXT_ENTRY_SIZE
+ );
+ }
+}
diff --git a/devices/src/usb/xhci/xhci_backend_device.rs b/devices/src/usb/xhci/xhci_backend_device.rs
new file mode 100644
index 0000000..e104cdc
--- /dev/null
+++ b/devices/src/usb/xhci/xhci_backend_device.rs
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::xhci_transfer::XhciTransfer;
+
+/// Address of this usb device, as in Set Address standard usb device request.
+pub type UsbDeviceAddress = u32;
+
+/// The type USB device provided by the backend device.
+#[derive(PartialEq, Eq)]
+pub enum BackendType {
+ Usb2,
+ Usb3,
+}
+
+/// Xhci backend device is a virtual device connected to xHCI controller. It handles xhci transfers.
+pub trait XhciBackendDevice: Send {
+ /// Returns the type of USB device provided by this device.
+ fn get_backend_type(&self) -> BackendType;
+ /// Returns host bus number of this device.
+ fn host_bus(&self) -> u8;
+ /// Returns host address of this device.
+ fn host_address(&self) -> u8;
+ /// Get vendor id of this device.
+ fn get_vid(&self) -> u16;
+ /// Get product id of this device.
+ fn get_pid(&self) -> u16;
+ /// Submit a xhci transfer to backend.
+ fn submit_transfer(&mut self, transfer: XhciTransfer) -> std::result::Result<(), ()>;
+ /// Set address of this backend.
+ fn set_address(&mut self, address: UsbDeviceAddress);
+ /// Reset the backend device.
+ fn reset(&mut self) -> std::result::Result<(), ()>;
+}
diff --git a/devices/src/usb/xhci/xhci_backend_device_provider.rs b/devices/src/usb/xhci/xhci_backend_device_provider.rs
new file mode 100644
index 0000000..c014d77
--- /dev/null
+++ b/devices/src/usb/xhci/xhci_backend_device_provider.rs
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::usb_hub::UsbHub;
+use crate::utils::{EventLoop, FailHandle};
+use std::os::unix::io::RawFd;
+use std::sync::Arc;
+
+/// Xhci backend provider will run on an EventLoop and connect new devices to usb ports.
+pub trait XhciBackendDeviceProvider: Send {
+ /// Start the provider on EventLoop.
+ fn start(
+ &mut self,
+ fail_handle: Arc<dyn FailHandle>,
+ event_loop: Arc<EventLoop>,
+ hub: Arc<UsbHub>,
+ ) -> std::result::Result<(), ()>;
+
+ /// Keep fds that should be kept open.
+ fn keep_fds(&self) -> Vec<RawFd>;
+}
diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs
new file mode 100644
index 0000000..76e1d4a
--- /dev/null
+++ b/devices/src/usb/xhci/xhci_controller.rs
@@ -0,0 +1,283 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::pci::{
+ PciBarConfiguration, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType,
+ PciInterruptPin, PciProgrammingInterface, PciSerialBusSubClass,
+};
+use crate::register_space::{Register, RegisterSpace};
+use crate::usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider;
+use crate::usb::xhci::xhci::Xhci;
+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 resources::{Alloc, SystemAllocator};
+use std::mem;
+use std::os::unix::io::RawFd;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+use sys_util::{error, EventFd, GuestMemory};
+
+const XHCI_BAR0_SIZE: u64 = 0x10000;
+
+#[derive(Clone, Copy)]
+enum UsbControllerProgrammingInterface {
+ Usb3HostController = 0x30,
+}
+
+impl PciProgrammingInterface for UsbControllerProgrammingInterface {
+ fn get_register_value(&self) -> u8 {
+ *self as u8
+ }
+}
+
+/// Use this handle to fail xhci controller.
+pub struct XhciFailHandle {
+ usbcmd: Register<u32>,
+ usbsts: Register<u32>,
+ xhci_failed: AtomicBool,
+}
+
+impl XhciFailHandle {
+ pub fn new(regs: &XhciRegs) -> XhciFailHandle {
+ XhciFailHandle {
+ usbcmd: regs.usbcmd.clone(),
+ usbsts: regs.usbsts.clone(),
+ xhci_failed: AtomicBool::new(false),
+ }
+ }
+}
+
+impl FailHandle for XhciFailHandle {
+ /// Fail this controller. Will set related registers and flip failed bool.
+ fn fail(&self) {
+ // set run/stop to stop.
+ const USBCMD_STOPPED: u32 = 0;
+ // Set host system error bit.
+ const USBSTS_HSE: u32 = 1 << 2;
+ self.usbcmd.set_value(USBCMD_STOPPED);
+ self.usbsts.set_value(USBSTS_HSE);
+
+ self.xhci_failed.store(true, Ordering::SeqCst);
+ error!("xhci controller stopped working");
+ }
+
+ /// Returns true if xhci is already failed.
+ fn failed(&self) -> bool {
+ self.xhci_failed.load(Ordering::SeqCst)
+ }
+}
+
+// Xhci controller should be created with backend device provider. Then irq should be assigned
+// before initialized. We are not making `failed` as a state here to optimize performance. Cause we
+// need to set failed in other threads.
+enum XhciControllerState {
+ Unknown,
+ Created {
+ device_provider: HostBackendDeviceProvider,
+ },
+ IrqAssigned {
+ device_provider: HostBackendDeviceProvider,
+ irq_evt: EventFd,
+ irq_resample_evt: EventFd,
+ },
+ Initialized {
+ mmio: RegisterSpace,
+ // Xhci init could fail.
+ #[allow(dead_code)]
+ xhci: Option<Arc<Xhci>>,
+ fail_handle: Arc<dyn FailHandle>,
+ },
+}
+
+/// xHCI PCI interface implementation.
+pub struct XhciController {
+ config_regs: PciConfiguration,
+ pci_bus_dev: Option<(u8, u8)>,
+ mem: GuestMemory,
+ bar0: u64, // bar0 in config_regs will be changed by guest. Not sure why.
+ state: XhciControllerState,
+}
+
+impl XhciController {
+ /// Create new xhci controller.
+ pub fn new(mem: GuestMemory, usb_provider: HostBackendDeviceProvider) -> Self {
+ let config_regs = PciConfiguration::new(
+ 0x01b73, // fresco logic, (google = 0x1ae0)
+ 0x1000, // fresco logic pdk. This chip has broken msi. See kernel xhci-pci.c
+ PciClassCode::SerialBusController,
+ &PciSerialBusSubClass::USB,
+ Some(&UsbControllerProgrammingInterface::Usb3HostController),
+ PciHeaderType::Device,
+ 0,
+ 0,
+ );
+ XhciController {
+ config_regs,
+ pci_bus_dev: None,
+ mem,
+ bar0: 0,
+ state: XhciControllerState::Created {
+ device_provider: usb_provider,
+ },
+ }
+ }
+
+ /// Init xhci controller when it's forked.
+ pub fn init_when_forked(&mut self) {
+ match mem::replace(&mut self.state, XhciControllerState::Unknown) {
+ XhciControllerState::IrqAssigned {
+ device_provider,
+ irq_evt,
+ irq_resample_evt,
+ } => {
+ let (mmio, regs) = init_xhci_mmio_space_and_regs();
+ let fail_handle: Arc<dyn FailHandle> = Arc::new(XhciFailHandle::new(®s));
+ let xhci = match Xhci::new(
+ fail_handle.clone(),
+ self.mem.clone(),
+ device_provider,
+ irq_evt,
+ irq_resample_evt,
+ regs,
+ ) {
+ Ok(xhci) => Some(xhci),
+ Err(_) => {
+ error!("fail to init xhci");
+ fail_handle.fail();
+ return;
+ }
+ };
+
+ self.state = XhciControllerState::Initialized {
+ mmio,
+ xhci,
+ fail_handle,
+ }
+ }
+ _ => {
+ error!("xhci controller is in a wrong state");
+ return;
+ }
+ }
+ }
+}
+
+impl PciDevice for XhciController {
+ fn debug_label(&self) -> String {
+ "xhci controller".to_owned()
+ }
+
+ fn assign_bus_dev(&mut self, bus: u8, device: u8) {
+ self.pci_bus_dev = Some((bus, device));
+ }
+
+ fn keep_fds(&self) -> Vec<RawFd> {
+ match &self.state {
+ XhciControllerState::Created { device_provider } => device_provider.keep_fds(),
+ _ => {
+ error!("xhci controller is in a wrong state");
+ vec![]
+ }
+ }
+ }
+
+ fn assign_irq(
+ &mut self,
+ irq_evt: EventFd,
+ irq_resample_evt: EventFd,
+ irq_num: u32,
+ irq_pin: PciInterruptPin,
+ ) {
+ match mem::replace(&mut self.state, XhciControllerState::Unknown) {
+ XhciControllerState::Created { device_provider } => {
+ self.config_regs.set_irq(irq_num as u8, irq_pin);
+ self.state = XhciControllerState::IrqAssigned {
+ device_provider,
+ irq_evt,
+ irq_resample_evt,
+ }
+ }
+ _ => {
+ error!("xhci controller is in a wrong state");
+ return;
+ }
+ }
+ }
+
+ fn allocate_io_bars(
+ &mut self,
+ resources: &mut SystemAllocator,
+ ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> {
+ let (bus, dev) = self
+ .pci_bus_dev
+ .expect("assign_bus_dev must be called prior to allocate_io_bars");
+ // xHCI spec 5.2.1.
+ let bar0_addr = resources
+ .mmio_allocator()
+ .allocate(
+ XHCI_BAR0_SIZE,
+ Alloc::PciBar { bus, dev, bar: 0 },
+ "xhci_bar0".to_string(),
+ )
+ .map_err(|e| PciDeviceError::IoAllocationFailed(XHCI_BAR0_SIZE, e))?;
+ let bar0_config = PciBarConfiguration::default()
+ .set_register_index(0)
+ .set_address(bar0_addr)
+ .set_size(XHCI_BAR0_SIZE);
+ self.config_regs
+ .add_pci_bar(&bar0_config)
+ .map_err(|e| PciDeviceError::IoRegistrationFailed(bar0_addr, e))?;
+ self.bar0 = bar0_addr;
+ Ok(vec![(bar0_addr, XHCI_BAR0_SIZE)])
+ }
+
+ fn config_registers(&self) -> &PciConfiguration {
+ &self.config_regs
+ }
+
+ fn config_registers_mut(&mut self) -> &mut PciConfiguration {
+ &mut self.config_regs
+ }
+
+ fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
+ let bar0 = self.bar0;
+ if addr < bar0 || addr > bar0 + XHCI_BAR0_SIZE {
+ return;
+ }
+ match &self.state {
+ XhciControllerState::Initialized { mmio, .. } => {
+ // Read bar would still work even if it's already failed.
+ mmio.read(addr - bar0, data);
+ }
+ _ => {
+ error!("xhci controller is in a wrong state");
+ return;
+ }
+ }
+ }
+
+ fn write_bar(&mut self, addr: u64, data: &[u8]) {
+ let bar0 = self.bar0;
+ if addr < bar0 || addr > bar0 + XHCI_BAR0_SIZE {
+ return;
+ }
+ match &self.state {
+ XhciControllerState::Initialized {
+ mmio, fail_handle, ..
+ } => {
+ if !fail_handle.failed() {
+ mmio.write(addr - bar0, data);
+ }
+ }
+ _ => {
+ error!("xhci controller is in a wrong state");
+ return;
+ }
+ }
+ }
+ fn on_device_sandboxed(&mut self) {
+ self.init_when_forked();
+ }
+}
diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs
new file mode 100644
index 0000000..810c44e
--- /dev/null
+++ b/devices/src/usb/xhci/xhci_regs.rs
@@ -0,0 +1,532 @@
+// Copyright 2018 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::register_space::{Register, RegisterSpace};
+
+/// Max interrupter number.
+pub const MAX_INTERRUPTER: u8 = 1;
+/// For port configuration, see register HCSPARAMS1, spcap1.3 and spcap2.3.
+pub const MAX_SLOTS: u8 = 16;
+
+/// Usb 2 ports start from port number 0.
+pub const USB2_PORTS_START: u8 = 0;
+/// Last usb 2 ports is 7.
+pub const USB2_PORTS_END: u8 = 8;
+/// Usb 3 ports start from port number 8.
+pub const USB3_PORTS_START: u8 = 8;
+/// Last usb 3 port is 15.
+pub const USB3_PORTS_END: u8 = 16;
+
+/// Max port number. Review the following before changing this:
+/// HCSPARAMS1, portsc, spcap1.3 and spcap2.3.
+pub const MAX_PORTS: u8 = USB3_PORTS_END;
+
+/// Cap register length.
+pub const XHCI_CAPLENGTH: u8 = 0x20;
+/// Offset for doorbell register.
+pub const XHCI_DBOFF: u32 = 0x00002000;
+/// Offset for RTs.
+pub const XHCI_RTSOFF: u32 = 0x00003000;
+
+/// Bitmask for the usbcmd register, see spec 5.4.1.
+pub const USB_CMD_RUNSTOP: u32 = 1u32 << 0;
+/// Bitmask for the usbcmd register, see spec 5.4.1.
+pub const USB_CMD_RESET: u32 = 1u32 << 1;
+/// Bitmask for the usbcmd register, see spec 5.4.1.
+pub const USB_CMD_INTERRUPTER_ENABLE: u32 = 1u32 << 2;
+
+/// Bitmask for the usbsts register, see spec 5.4.2.
+pub const USB_STS_HALTED: u32 = 1u32 << 0;
+/// Bitmask for the usbsts register, see spec 5.4.2.
+pub const USB_STS_EVENT_INTERRUPT: u32 = 1u32 << 3;
+/// Bitmask for the usbsts register, see spec 5.4.2.
+pub const USB_STS_PORT_CHANGE_DETECT: u32 = 1u32 << 4;
+/// Bitmask for the usbsts register, see spec 5.4.2.
+pub const USB_STS_CONTROLLER_NOT_READY: u32 = 1u32 << 11;
+/// Bitmask for the usbsts register, see spec 5.4.2.
+pub const USB_STS_SET_TO_CLEAR_MASK: u32 = 0x0000041C;
+
+/// Bitmask for the crcr register, see spec 5.4.5.
+pub const CRCR_RING_CYCLE_STATE: u64 = 1u64 << 0;
+/// Bitmask for the crcr register, see spec 5.4.5.
+pub const CRCR_COMMAND_STOP: u64 = 1u64 << 1;
+/// Bitmask for the crcr register, see spec 5.4.5.
+pub const CRCR_COMMAND_ABORT: u64 = 1u64 << 2;
+/// Bitmask for the crcr register, see spec 5.4.5.
+pub const CRCR_COMMAND_RING_RUNNING: u64 = 1u64 << 3;
+/// Bitmask for the crcr register, see spec 5.4.5.
+pub const CRCR_COMMAND_RING_POINTER: u64 = 0xFFFFFFFFFFFFFFC0;
+
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_CURRENT_CONNECT_STATUS: u32 = 1u32 << 0;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_PORT_ENABLED: u32 = 1u32 << 1;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_PORT_RESET: u32 = 1u32 << 4;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_PORT_LINK_STATE_MASK: u32 = 0x000001E0;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_PORT_POWER: u32 = 1u32 << 9;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_CONNECT_STATUS_CHANGE: u32 = 1u32 << 17;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_PORT_ENABLED_DISABLED_CHANGE: u32 = 1u32 << 18;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_PORT_RESET_CHANGE: u32 = 1u32 << 21;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_WARM_PORT_RESET: u32 = 1u32 << 31;
+/// Bitmask for portsc register, see spec 5.4.8.
+pub const PORTSC_SET_TO_CLEAR_MASK: u32 = 0x00FE0002;
+
+/// Bitmask for iman registers, see spec 5.5.2.1.
+pub const IMAN_INTERRUPT_PENDING: u32 = 1u32 << 0;
+/// Bitmask for iman registers, see spec 5.5.2.1.
+pub const IMAN_INTERRUPT_ENABLE: u32 = 1u32 << 1;
+/// Bitmask for iman registers, see spec 5.5.2.1.
+pub const IMAN_SET_TO_CLEAR_MASK: u32 = 0x00000001;
+
+/// Bitmask for imod registers, see spec 5.5.2.2.
+pub const IMOD_INTERRUPT_MODERATION_INTERVAL: u32 = 0xFFFF;
+/// Bitmask for imod registers, see spec 5.5.2.2.
+pub const IMOD_INTERRUPT_MODERATION_COUNTER_OFFSET: u8 = 16;
+
+/// Bitmask for erstsz registers, see 5.5.2.3.
+pub const ERSTSZ_SEGMENT_TABLE_SIZE: u32 = 0xFFFF;
+
+/// Bitmask for erstba registers, see 5.5.2.3.
+pub const ERSTBA_SEGMENT_TABLE_BASE_ADDRESS: u64 = 0xFFFFFFFFFFFFFFC0;
+
+/// Bitmask for erdp registers, see 5.5.2.3.
+pub const ERDP_EVENT_HANDLER_BUSY: u64 = 1u64 << 3;
+/// Bitmask for erdp registers, see 5.5.2.3.
+pub const ERDP_EVENT_RING_DEQUEUE_POINTER: u64 = 0xFFFFFFFFFFFFFFF0;
+/// Bitmask for erdp registers, see 5.5.2.3.
+pub const ERDP_SET_TO_CLEAR_MASK: u64 = 0x0000000000000008;
+
+/// Bitmask for doorbell registers.
+pub const DOORBELL_TARGET: u32 = 0xFF;
+/// Offset of stream id.
+pub const DOORBELL_STREAM_ID_OFFSET: u32 = 16;
+
+/// Bitmask for structural parameter registers.
+pub const HCSPARAMS1_MAX_INTERRUPTERS_MASK: u32 = 0x7FF00;
+/// Offset of max interrupters.
+pub const HCSPARAMS1_MAX_INTERRUPTERS_OFFSET: u32 = 8;
+/// Mask to get max slots.
+pub const HCSPARAMS1_MAX_SLOTS_MASK: u32 = 0xFF;
+
+/// Bitmask for extended capabilities registers.
+pub const SPCAP_PORT_COUNT_MASK: u32 = 0xFF00;
+/// Offset of port count.
+pub const SPCAP_PORT_COUNT_OFFSET: u32 = 8;
+
+/// Helper function for validating slot_id.
+pub fn valid_slot_id(slot_id: u8) -> bool {
+ // slot id count from 1.
+ slot_id > 0 && slot_id <= MAX_SLOTS
+}
+
+/// XhciRegs hold all xhci registers.
+pub struct XhciRegs {
+ pub usbcmd: Register<u32>,
+ pub usbsts: Register<u32>,
+ pub dnctrl: Register<u32>,
+ pub crcr: Register<u64>,
+ pub dcbaap: Register<u64>,
+ pub config: Register<u64>,
+ pub portsc: Vec<Register<u32>>,
+ pub doorbells: Vec<Register<u32>>,
+ pub iman: Register<u32>,
+ pub imod: Register<u32>,
+ pub erstsz: Register<u32>,
+ pub erstba: Register<u64>,
+ pub erdp: Register<u64>,
+}
+
+/// This function returns mmio space definition for xhci. See Xhci spec chapter 5
+/// for details.
+pub fn init_xhci_mmio_space_and_regs() -> (RegisterSpace, XhciRegs) {
+ let mut mmio = RegisterSpace::new();
+
+ /* Host Controller Capability Registers */
+ mmio.add_register(
+ // CAPLENGTH
+ static_register!(
+ ty: u8,
+ offset: 0x00,
+ value: XHCI_CAPLENGTH, // Operation register start at offset 0x20
+ ),
+ );
+ mmio.add_register(
+ // HCIVERSION
+ static_register!(
+ ty: u16,
+ offset: 0x02,
+ value: 0x0110,// Revision 1.1
+ ),
+ );
+ mmio.add_register(
+ // HCSPARAMS1
+ static_register!(
+ ty: u32,
+ offset: 0x04,
+ value: 0x10000110, // max_slots = 16, max_interrupters = 1, max_ports = 16
+ ),
+ );
+
+ mmio.add_register(
+ // HCSPARAMS2
+ static_register!(
+ ty: u32,
+ offset: 0x08,
+ // Maximum number of event ring segment table entries = 32k
+ // No scratchpad buffers.
+ value: 0xf0,
+ ),
+ );
+
+ mmio.add_register(
+ // HCSPARAM3
+ static_register!(
+ ty: u32,
+ offset: 0x0c,
+
+ // Exit latencies for U1 (standby with fast exit) and U2 (standby with
+ // slower exit) power states. We use the max values:
+ // - U1 to U0: < 10 us
+ // - U2 to U1: < 2047 us
+ value: 0x07FF000A,
+ ),
+ );
+
+ mmio.add_register(
+ // HCCPARAMS1
+ static_register!(
+ ty: u32,
+ offset: 0x10,
+ // Supports 64 bit addressing
+ // Max primary stream array size = 0 (streams not supported).
+ // Extended capabilities pointer = 0xC000 offset from base.
+ value: 0x30000501,
+ ),
+ );
+ mmio.add_register(
+ // DBOFF
+ static_register!(
+ ty: u32,
+ offset: 0x14,
+ value: XHCI_DBOFF, // Doorbell array offset 0x2000 from base.
+ ),
+ );
+
+ mmio.add_register(
+ // RTSOFF
+ static_register!(
+ ty: u32,
+ offset: 0x18,
+ value: XHCI_RTSOFF, // Runtime registers offset 0x3000 from base.
+ ),
+ );
+
+ mmio.add_register(
+ // HCCPARAMS2
+ static_register!(
+ ty: u32,
+ offset: 0x1c,
+ value: 0,
+ ),
+ );
+ /* End of Host Controller Capability Registers */
+
+ /* Host Controller Operational Registers */
+ let usbcmd = register!(
+ name: "usbcmd",
+ ty: u32,
+ offset: 0x20,
+ reset_value: 0,
+ guest_writeable_mask: 0x00002F0F,
+ guest_write_1_to_clear_mask: 0,
+ );
+ mmio.add_register(usbcmd.clone());
+
+ let usbsts = register!(
+ name: "usbsts",
+ ty: u32,
+ offset: 0x24,
+ reset_value: 0x00000001,
+ guest_writeable_mask: 0x0000041C,
+ guest_write_1_to_clear_mask: 0x0000041C,
+ );
+ mmio.add_register(usbsts.clone());
+
+ mmio.add_register(
+ // Pagesize
+ static_register!(
+ ty: u32,
+ offset: 0x28,
+ value: 0x00000001,
+ ),
+ );
+
+ let dnctrl = register!(
+ name: "dnctrl",
+ ty: u32,
+ offset: 0x34,
+ reset_value: 0,
+ guest_writeable_mask: 0x0000FFFF,
+ guest_write_1_to_clear_mask: 0,
+ );
+ mmio.add_register(dnctrl.clone());
+
+ let crcr = register!(
+ name: "crcr",
+ ty: u64,
+ offset: 0x38,
+ reset_value: 9,
+ guest_writeable_mask: 0xFFFFFFFFFFFFFFC7,
+ guest_write_1_to_clear_mask: 0,
+ );
+ mmio.add_register(crcr.clone());
+
+ let dcbaap = register!(
+ name: "dcbaap",
+ ty: u64,
+ offset: 0x50,
+ reset_value: 0x0,
+ guest_writeable_mask: 0xFFFFFFFFFFFFFFC0,
+ guest_write_1_to_clear_mask: 0,
+ );
+ mmio.add_register(dcbaap.clone());
+
+ let config = register!(
+ name: "config",
+ ty: u64,
+ offset: 0x58,
+ reset_value: 0,
+ guest_writeable_mask: 0x0000003F,
+ guest_write_1_to_clear_mask: 0,
+ );
+ mmio.add_register(config.clone());
+
+ let portsc = register_array!(
+ name: "portsc",
+ ty: u32,
+ cnt: MAX_PORTS,
+ base_offset: 0x420,
+ stride: 16,
+ reset_value: 0x000002A0,
+ guest_writeable_mask: 0x8EFFC3F2,
+ guest_write_1_to_clear_mask: 0x00FE0002,);
+ mmio.add_register_array(&portsc);
+
+ // Portpmsc.
+ mmio.add_register_array(®ister_array!(
+ name: "portpmsc",
+ ty: u32,
+ cnt: MAX_PORTS,
+ base_offset: 0x424,
+ stride: 16,
+ reset_value: 0,
+ guest_writeable_mask: 0x0001FFFF,
+ guest_write_1_to_clear_mask: 0,));
+
+ // Portli
+ mmio.add_register_array(®ister_array!(
+ name: "portli",
+ ty: u32,
+ cnt: MAX_PORTS,
+ base_offset: 0x428,
+ stride: 16,
+ reset_value: 0,
+ guest_writeable_mask: 0,
+ guest_write_1_to_clear_mask: 0,));
+
+ // Porthlpmc
+ mmio.add_register_array(®ister_array!(
+ name: "porthlpmc",
+ ty: u32,
+ cnt: MAX_PORTS,
+ base_offset: 0x42c,
+ stride: 16,
+ reset_value: 0,
+ guest_writeable_mask: 0x00003FFF,
+ guest_write_1_to_clear_mask: 0,));
+
+ let doorbells = register_array!(
+ name: "doorbell",
+ ty: u32,
+ cnt: MAX_SLOTS + 1, // Must be equal to max_slots + 1
+ base_offset: 0x2000,
+ stride: 4,
+ reset_value: 0,
+ guest_writeable_mask: 0xFFFF00FF,
+ guest_write_1_to_clear_mask: 0,);
+ mmio.add_register_array(&doorbells);
+
+ /*Runtime Registers */
+
+ mmio.add_register(
+ // mfindex
+ static_register!(
+ ty: u32,
+ offset: 0x3000,
+ value: 0, // 4 ports starting at port 5
+ ),
+ );
+
+ /* Reg Array for interrupters */
+ // Although the following should be register arrays, we only have one interrupter.
+ let iman = register!(
+ name: "iman",
+ ty: u32,
+ offset: 0x3020,
+ reset_value: 0,
+ guest_writeable_mask: 0x00000003,
+ guest_write_1_to_clear_mask: 0x00000001,);
+ mmio.add_register(iman.clone());
+
+ let imod = register!(
+ name: "imod",
+ ty: u32,
+ offset: 0x3024,
+ reset_value: 0x00000FA0,
+ guest_writeable_mask: 0xFFFFFFFF,
+ guest_write_1_to_clear_mask: 0,);
+ mmio.add_register(imod.clone());
+
+ let erstsz = register!(
+ name: "erstsz",
+ ty: u32,
+ offset: 0x3028,
+ reset_value: 0,
+ guest_writeable_mask: 0x0000FFFF,
+ guest_write_1_to_clear_mask: 0,);
+ mmio.add_register(erstsz.clone());
+
+ let erstba = register!(
+ name: "erstba",
+ ty: u64,
+ offset: 0x3030,
+ reset_value: 0,
+ guest_writeable_mask: 0xFFFFFFFFFFFFFFC0,
+ guest_write_1_to_clear_mask: 0,);
+ mmio.add_register(erstba.clone());
+
+ let erdp = register!(
+ name: "erdp",
+ ty: u64,
+ offset: 0x3038,
+ reset_value: 0,
+ guest_writeable_mask: 0xFFFFFFFFFFFFFFFF,
+ guest_write_1_to_clear_mask: 0x0000000000000008,);
+ mmio.add_register(erdp.clone());
+
+ /* End of Runtime Registers */
+
+ let xhci_regs = XhciRegs {
+ usbcmd,
+ usbsts,
+ dnctrl,
+ crcr,
+ dcbaap,
+ config,
+ portsc,
+ doorbells,
+ iman,
+ imod,
+ erstsz,
+ erstba,
+ erdp,
+ };
+
+ /* End of Host Controller Operational Registers */
+
+ /* Extended Capability Registers */
+
+ // Extended capability registers. Base offset defined by hccparams1.
+ // Each set of 4 registers represents a "Supported Protocol" extended
+ // capability. The first capability indicates that ports 1-8 are USB 2.0. There is no USB 3.0
+ // port for now. See xHCI spec 7.1 & 7.2 for more details.
+ mmio.add_register(
+ // spcap 1.1
+ static_register!(
+ ty: u32,
+ offset: 0xc000,
+ // "Supported Protocol" capability.
+ // Next capability starts after 0x40 dwords.
+ // USB 2.0. Revision 2.0.
+ value: 0x02004002,
+ ),
+ );
+ mmio.add_register(
+ // spcap 1.2
+ static_register!(
+ ty: u32,
+ offset: 0xc004,
+ value: 0x20425355, // Name string = "USB "
+ ),
+ );
+ mmio.add_register(
+ // spcap 1.3
+ static_register!(
+ ty: u32,
+ offset: 0xc008,
+ value: 0x00000801, // 8 ports starting at port 1. See USB2_PORTS_START and USB2_PORTS_END.
+ ),
+ );
+
+ mmio.add_register(
+ // spcap 1.4
+ static_register!(
+ ty: u32,
+ offset: 0xc00c,
+ // The specification says that this shall be set to 0.
+ // Section 7.2.2.1.4.
+ value: 0,
+ ),
+ );
+
+ mmio.add_register(
+ // spcap 2.1
+ static_register!(
+ ty: u32,
+ offset: 0xc100,
+ // "Supported Protocol" capability.
+ // Not next capability.
+ // USB 3.0. Revision 2.0.
+ value: 0x03000002,
+ ),
+ );
+ mmio.add_register(
+ // spcap 2.2
+ static_register!(
+ ty: u32,
+ offset: 0xc104,
+ value: 0x20425355, // Name string = "USB "
+ ),
+ );
+ mmio.add_register(
+ // spcap 2.3
+ static_register!(
+ ty: u32,
+ offset: 0xc108,
+ value: 0x00000809, // 8 ports starting at port 9. See USB3_PORTS_START and USB3_PORTS_END.
+ ),
+ );
+
+ mmio.add_register(
+ // spcap 2.4
+ static_register!(
+ ty: u32,
+ offset: 0xc10c,
+ // The specification says that this shall be set to 0.
+ // Section 7.2.2.1.4.
+ value: 0,
+ ),
+ );
+
+ /* End of Host Controller Operational Registers */
+
+ (mmio, xhci_regs)
+}
diff --git a/devices/src/usb/xhci/xhci_transfer.rs b/devices/src/usb/xhci/xhci_transfer.rs
new file mode 100644
index 0000000..e166c1a
--- /dev/null
+++ b/devices/src/usb/xhci/xhci_transfer.rs
@@ -0,0 +1,444 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::interrupter::{Error as InterrupterError, Interrupter};
+use super::scatter_gather_buffer::{Error as BufferError, ScatterGatherBuffer};
+use super::usb_hub::{Error as HubError, UsbPort};
+use super::xhci_abi::{
+ AddressedTrb, Error as TrbError, EventDataTrb, SetupStageTrb, TransferDescriptor, TrbCast,
+ TrbCompletionCode, TrbType,
+};
+use super::xhci_regs::MAX_INTERRUPTER;
+use bit_field::Error as BitFieldError;
+use std::cmp::min;
+use std::fmt::{self, Display};
+use std::mem;
+use std::sync::{Arc, Weak};
+use sync::Mutex;
+use sys_util::{error, Error as SysError, EventFd, GuestMemory};
+use usb_util::types::UsbRequestSetup;
+use usb_util::usb_transfer::TransferStatus;
+
+#[derive(Debug)]
+pub enum Error {
+ TrbType(BitFieldError),
+ CastTrb(TrbError),
+ TransferLength(TrbError),
+ BadTrbType(TrbType),
+ WriteCompletionEvent(SysError),
+ CreateBuffer(BufferError),
+ DetachPort(HubError),
+ SendInterrupt(InterrupterError),
+ SubmitTransfer,
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ TrbType(e) => write!(f, "cannot get trb type: {}", e),
+ CastTrb(e) => write!(f, "cannot cast trb: {}", e),
+ TransferLength(e) => write!(f, "cannot get transfer length: {}", e),
+ BadTrbType(t) => write!(f, "unexpected trb type: {:?}", t),
+ WriteCompletionEvent(e) => write!(f, "cannot write completion event: {}", e),
+ CreateBuffer(e) => write!(f, "cannot create transfer buffer: {}", e),
+ DetachPort(e) => write!(f, "cannot detach from port: {}", e),
+ SendInterrupt(e) => write!(f, "cannot send interrupter: {}", e),
+ SubmitTransfer => write!(f, "failed to submit transfer to backend"),
+ }
+ }
+}
+
+/// Type of usb endpoints.
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub enum TransferDirection {
+ In,
+ Out,
+ Control,
+}
+
+/// Current state of xhci transfer.
+pub enum XhciTransferState {
+ Created,
+ /// When transfer is submitted, it will contain a transfer callback, which should be invoked
+ /// when the transfer is cancelled.
+ Submitted {
+ cancel_callback: Box<dyn FnMut() + Send>,
+ },
+ Cancelling,
+ Cancelled,
+ Completed,
+}
+
+impl XhciTransferState {
+ /// Try to cancel this transfer, if it's possible.
+ pub fn try_cancel(&mut self) {
+ match mem::replace(self, XhciTransferState::Created) {
+ XhciTransferState::Submitted {
+ mut cancel_callback,
+ } => {
+ *self = XhciTransferState::Cancelling;
+ cancel_callback();
+ }
+ XhciTransferState::Cancelling => {
+ error!("Another cancellation is already issued.");
+ }
+ _ => {
+ *self = XhciTransferState::Cancelled;
+ }
+ }
+ }
+}
+
+/// Type of a transfer received handled by transfer ring.
+pub enum XhciTransferType {
+ // Normal means bulk transfer or interrupt transfer, depending on endpoint type.
+ // See spec 4.11.2.1.
+ Normal(ScatterGatherBuffer),
+ // See usb spec for setup stage, data stage and status stage,
+ // see xHCI spec 4.11.2.2 for corresponding trbs.
+ SetupStage(UsbRequestSetup),
+ DataStage(ScatterGatherBuffer),
+ StatusStage,
+ // See xHCI spec 4.11.2.3.
+ Isochronous(ScatterGatherBuffer),
+ // See xHCI spec 6.4.1.4.
+ Noop,
+}
+
+impl XhciTransferType {
+ /// Analyze transfer descriptor and return transfer type.
+ pub fn new(mem: GuestMemory, td: TransferDescriptor) -> Result<XhciTransferType> {
+ // We can figure out transfer type from the first trb.
+ // See transfer descriptor description in xhci spec for more details.
+ match td[0].trb.get_trb_type().map_err(Error::TrbType)? {
+ TrbType::Normal => {
+ let buffer = ScatterGatherBuffer::new(mem, td).map_err(Error::CreateBuffer)?;
+ Ok(XhciTransferType::Normal(buffer))
+ }
+ TrbType::SetupStage => {
+ let trb = td[0].trb.cast::<SetupStageTrb>().map_err(Error::CastTrb)?;
+ Ok(XhciTransferType::SetupStage(UsbRequestSetup::new(
+ trb.get_request_type(),
+ trb.get_request(),
+ trb.get_value(),
+ trb.get_index(),
+ trb.get_length(),
+ )))
+ }
+ TrbType::DataStage => {
+ let buffer = ScatterGatherBuffer::new(mem, td).map_err(Error::CreateBuffer)?;
+ Ok(XhciTransferType::DataStage(buffer))
+ }
+ TrbType::StatusStage => Ok(XhciTransferType::StatusStage),
+ TrbType::Isoch => {
+ let buffer = ScatterGatherBuffer::new(mem, td).map_err(Error::CreateBuffer)?;
+ Ok(XhciTransferType::Isochronous(buffer))
+ }
+ TrbType::Noop => Ok(XhciTransferType::Noop),
+ t => Err(Error::BadTrbType(t)),
+ }
+ }
+}
+
+/// Xhci Transfer manager holds reference to all ongoing transfers. Can cancel them all if
+/// needed.
+#[derive(Clone)]
+pub struct XhciTransferManager {
+ transfers: Arc<Mutex<Vec<Weak<Mutex<XhciTransferState>>>>>,
+}
+
+impl XhciTransferManager {
+ /// Create a new manager.
+ pub fn new() -> XhciTransferManager {
+ XhciTransferManager {
+ transfers: Arc::new(Mutex::new(Vec::new())),
+ }
+ }
+
+ /// Build a new XhciTransfer. Endpoint id is the id in xHCI device slot.
+ pub fn create_transfer(
+ &self,
+ mem: GuestMemory,
+ port: Arc<UsbPort>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ slot_id: u8,
+ endpoint_id: u8,
+ transfer_trbs: TransferDescriptor,
+ completion_event: EventFd,
+ ) -> XhciTransfer {
+ assert!(!transfer_trbs.is_empty());
+ let transfer_dir = {
+ if endpoint_id == 0 {
+ TransferDirection::Control
+ } else if (endpoint_id % 2) == 0 {
+ TransferDirection::Out
+ } else {
+ TransferDirection::In
+ }
+ };
+ let t = XhciTransfer {
+ manager: self.clone(),
+ state: Arc::new(Mutex::new(XhciTransferState::Created)),
+ mem,
+ port,
+ interrupter,
+ transfer_completion_event: completion_event,
+ slot_id,
+ endpoint_id,
+ transfer_dir,
+ transfer_trbs,
+ };
+ self.transfers.lock().push(Arc::downgrade(&t.state));
+ t
+ }
+
+ /// Cancel all current transfers.
+ pub fn cancel_all(&self) {
+ self.transfers.lock().iter().for_each(|t| {
+ let state = match t.upgrade() {
+ Some(state) => state,
+ None => {
+ error!("transfer is already cancelled or finished");
+ return;
+ }
+ };
+ state.lock().try_cancel();
+ });
+ }
+
+ fn remove_transfer(&self, t: &Arc<Mutex<XhciTransferState>>) {
+ let mut transfers = self.transfers.lock();
+ match transfers.iter().position(|wt| match wt.upgrade() {
+ Some(wt) => Arc::ptr_eq(&wt, t),
+ None => false,
+ }) {
+ None => error!("attempted to remove unknow transfer"),
+ Some(i) => {
+ transfers.swap_remove(i);
+ }
+ }
+ }
+}
+
+/// Xhci transfer denotes a transfer initiated by guest os driver. It will be submitted to a
+/// XhciBackendDevice.
+pub struct XhciTransfer {
+ manager: XhciTransferManager,
+ state: Arc<Mutex<XhciTransferState>>,
+ mem: GuestMemory,
+ port: Arc<UsbPort>,
+ interrupter: Arc<Mutex<Interrupter>>,
+ slot_id: u8,
+ // id of endpoint in device slot.
+ endpoint_id: u8,
+ transfer_dir: TransferDirection,
+ transfer_trbs: TransferDescriptor,
+ transfer_completion_event: EventFd,
+}
+
+impl Drop for XhciTransfer {
+ fn drop(&mut self) {
+ self.manager.remove_transfer(&self.state);
+ }
+}
+
+impl fmt::Debug for XhciTransfer {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "xhci_transfer slot id: {}, endpoint id {}, transfer_dir {:?}, transfer_trbs {:?}",
+ self.slot_id, self.endpoint_id, self.transfer_dir, self.transfer_trbs
+ )
+ }
+}
+
+impl XhciTransfer {
+ /// Get state of this transfer.
+ pub fn state(&self) -> &Arc<Mutex<XhciTransferState>> {
+ &self.state
+ }
+
+ /// Get transfer type.
+ pub fn get_transfer_type(&self) -> Result<XhciTransferType> {
+ XhciTransferType::new(self.mem.clone(), self.transfer_trbs.clone())
+ }
+
+ /// Get endpoint number.
+ pub fn get_endpoint_number(&self) -> u8 {
+ // See spec 4.5.1 for dci.
+ self.endpoint_id / 2
+ }
+
+ /// get transfer direction.
+ pub fn get_transfer_dir(&self) -> TransferDirection {
+ self.transfer_dir
+ }
+
+ /// This functions should be invoked when transfer is completed (or failed).
+ pub fn on_transfer_complete(
+ &self,
+ status: &TransferStatus,
+ bytes_transferred: u32,
+ ) -> Result<()> {
+ match status {
+ TransferStatus::NoDevice => {
+ usb_debug!("device disconnected, detaching from port");
+ // If the device is gone, we don't need to send transfer completion event, cause we
+ // are going to destroy everything related to this device anyway.
+ self.port.detach().map_err(Error::DetachPort)?;
+ return Ok(());
+ }
+ TransferStatus::Cancelled => {
+ // TODO(jkwang) According to the spec, we should send a stopped event here. But
+ // kernel driver does not do anything meaningful when it sees a stopped event.
+ return self
+ .transfer_completion_event
+ .write(1)
+ .map_err(Error::WriteCompletionEvent);
+ }
+ TransferStatus::Completed => {
+ self.transfer_completion_event
+ .write(1)
+ .map_err(Error::WriteCompletionEvent)?;
+ }
+ _ => {
+ // Transfer failed, we are not handling this correctly yet. Guest kernel might see
+ // short packets for in transfer and might think control transfer is successful. It
+ // will eventually find out device is in a wrong state.
+ self.transfer_completion_event
+ .write(1)
+ .map_err(Error::WriteCompletionEvent)?;
+ }
+ }
+
+ let mut edtla: u32 = 0;
+ // As noted in xHCI spec 4.11.3.1
+ // Transfer Event TRB only occurs under the following conditions:
+ // 1. If the Interrupt On Completion flag is set.
+ // 2. When a short transfer occurs during the execution of a Transfer TRB and the
+ // Interrupt-on-Short Packet flag is set.
+ // 3. If an error occurs during the execution of a Transfer TRB.
+ // Errors are handled above, so just check for the two flags.
+ for atrb in &self.transfer_trbs {
+ edtla += atrb.trb.transfer_length().map_err(Error::TransferLength)?;
+ if atrb.trb.interrupt_on_completion()
+ || (atrb.trb.interrupt_on_short_packet() && edtla > bytes_transferred)
+ {
+ // For details about event data trb and EDTLA, see spec 4.11.5.2.
+ if atrb.trb.get_trb_type().map_err(Error::TrbType)? == TrbType::EventData {
+ let tlength = min(edtla, bytes_transferred);
+ self.interrupter
+ .lock()
+ .send_transfer_event_trb(
+ TrbCompletionCode::Success,
+ atrb.trb
+ .cast::<EventDataTrb>()
+ .map_err(Error::CastTrb)?
+ .get_event_data(),
+ tlength,
+ true,
+ self.slot_id,
+ self.endpoint_id,
+ )
+ .map_err(Error::SendInterrupt)?;
+ } else {
+ // For Short Transfer details, see xHCI spec 4.10.1.1.
+ if edtla > bytes_transferred {
+ usb_debug!("on transfer complete short packet");
+ let residual_transfer_length = edtla - bytes_transferred;
+ self.interrupter
+ .lock()
+ .send_transfer_event_trb(
+ TrbCompletionCode::ShortPacket,
+ atrb.gpa,
+ residual_transfer_length,
+ true,
+ self.slot_id,
+ self.endpoint_id,
+ )
+ .map_err(Error::SendInterrupt)?;
+ } else {
+ usb_debug!("on transfer complete success");
+ self.interrupter
+ .lock()
+ .send_transfer_event_trb(
+ TrbCompletionCode::Success,
+ atrb.gpa,
+ 0, // transfer length
+ true,
+ self.slot_id,
+ self.endpoint_id,
+ )
+ .map_err(Error::SendInterrupt)?;
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Send this transfer to backend if it's a valid transfer.
+ pub fn send_to_backend_if_valid(self) -> Result<()> {
+ if self.validate_transfer()? {
+ // Backend should invoke on transfer complete when transfer is completed.
+ let port = self.port.clone();
+ let mut backend = port.get_backend_device();
+ match &mut *backend {
+ Some(backend) => backend
+ .submit_transfer(self)
+ .map_err(|_| Error::SubmitTransfer)?,
+ None => {
+ error!("backend is already disconnected");
+ self.transfer_completion_event
+ .write(1)
+ .map_err(Error::WriteCompletionEvent)?;
+ }
+ }
+ } else {
+ error!("invalid td on transfer ring");
+ self.transfer_completion_event
+ .write(1)
+ .map_err(Error::WriteCompletionEvent)?;
+ }
+ Ok(())
+ }
+
+ // Check each trb in the transfer descriptor for invalid or out of bounds
+ // parameters. Returns true iff the transfer descriptor is valid.
+ fn validate_transfer(&self) -> Result<bool> {
+ let mut valid = true;
+ for atrb in &self.transfer_trbs {
+ if !trb_is_valid(&atrb) {
+ self.interrupter
+ .lock()
+ .send_transfer_event_trb(
+ TrbCompletionCode::TrbError,
+ atrb.gpa,
+ 0,
+ false,
+ self.slot_id,
+ self.endpoint_id,
+ )
+ .map_err(Error::SendInterrupt)?;
+ valid = false;
+ }
+ }
+ Ok(valid)
+ }
+}
+
+fn trb_is_valid(atrb: &AddressedTrb) -> bool {
+ let can_be_in_transfer_ring = match atrb.trb.can_be_in_transfer_ring() {
+ Ok(v) => v,
+ Err(e) => {
+ error!("unknown error {:?}", e);
+ return false;
+ }
+ };
+ can_be_in_transfer_ring && (atrb.trb.interrupter_target() < MAX_INTERRUPTER)
+}
diff --git a/devices/src/utils/async_job_queue.rs b/devices/src/utils/async_job_queue.rs
new file mode 100644
index 0000000..76ae8c7
--- /dev/null
+++ b/devices/src/utils/async_job_queue.rs
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::{Error, Result};
+use super::{EventHandler, EventLoop};
+use std::mem;
+use std::sync::Arc;
+use sync::Mutex;
+use sys_util::{error, EventFd, WatchingEvents};
+
+/// Async Job Queue can schedule async jobs.
+pub struct AsyncJobQueue {
+ jobs: Mutex<Vec<Box<dyn FnMut() + Send>>>,
+ evt: EventFd,
+}
+
+impl AsyncJobQueue {
+ /// Init job queue on event loop.
+ pub fn init(event_loop: &EventLoop) -> Result<Arc<AsyncJobQueue>> {
+ let evt = EventFd::new().map_err(Error::CreateEventFd)?;
+ let queue = Arc::new(AsyncJobQueue {
+ jobs: Mutex::new(Vec::new()),
+ evt,
+ });
+ let handler: Arc<dyn EventHandler> = queue.clone();
+ event_loop.add_event(
+ &queue.evt,
+ WatchingEvents::empty().set_read(),
+ Arc::downgrade(&handler),
+ )?;
+ Ok(queue)
+ }
+
+ /// Queue a new job. It will be invoked on event loop.
+ pub fn queue_job<T: Fn() + 'static + Send>(&self, cb: T) -> Result<()> {
+ self.jobs.lock().push(Box::new(cb));
+ self.evt.write(1).map_err(Error::WriteEventFd)
+ }
+}
+
+impl EventHandler for AsyncJobQueue {
+ fn on_event(&self) -> std::result::Result<(), ()> {
+ // We want to read out the event, but the value is not important.
+ match self.evt.read() {
+ Ok(_) => {}
+ Err(e) => {
+ error!("read event fd failed {}", e);
+ return Err(());
+ }
+ }
+
+ let jobs = mem::replace(&mut *self.jobs.lock(), Vec::new());
+ for mut cb in jobs {
+ cb();
+ }
+ Ok(())
+ }
+}
diff --git a/devices/src/utils/error.rs b/devices/src/utils/error.rs
new file mode 100644
index 0000000..024d3f7
--- /dev/null
+++ b/devices/src/utils/error.rs
@@ -0,0 +1,37 @@
+// Copyright 2018 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::fmt::{self, Display};
+use sys_util::Error as SysError;
+
+#[derive(Debug)]
+pub enum Error {
+ EventLoopAlreadyFailed,
+ CreateEventFd(SysError),
+ ReadEventFd(SysError),
+ WriteEventFd(SysError),
+ CreatePollContext(SysError),
+ PollContextAddFd(SysError),
+ PollContextDeleteFd(SysError),
+ StartThread(std::io::Error),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ EventLoopAlreadyFailed => write!(f, "event loop already failed due to previous errors"),
+ CreateEventFd(e) => write!(f, "failed to create event fd: {}", e),
+ ReadEventFd(e) => write!(f, "failed to read event fd: {}", e),
+ WriteEventFd(e) => write!(f, "failed to write event fd: {}", 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),
+ StartThread(e) => write!(f, "failed to start thread: {}", e),
+ }
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
diff --git a/devices/src/utils/event_loop.rs b/devices/src/utils/event_loop.rs
new file mode 100644
index 0000000..405c543
--- /dev/null
+++ b/devices/src/utils/event_loop.rs
@@ -0,0 +1,252 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::error::{Error, Result};
+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;
+use sys_util::{error, warn, EpollContext, EpollEvents, EventFd, PollToken, WatchingEvents};
+
+/// A fail handle will do the clean up when we cannot recover from some error.
+pub trait FailHandle: Send + Sync {
+ /// Fail the code.
+ fn fail(&self);
+ /// Returns true if already failed.
+ fn failed(&self) -> bool;
+}
+
+impl FailHandle for Option<Arc<dyn FailHandle>> {
+ fn fail(&self) {
+ match self {
+ Some(handle) => handle.fail(),
+ None => error!("event loop trying to fail without a fail handle"),
+ }
+ }
+
+ fn failed(&self) -> bool {
+ match self {
+ Some(handle) => handle.failed(),
+ None => false,
+ }
+ }
+}
+
+/// 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>>>>,
+ stop_evt: EventFd,
+}
+
+/// Interface for event handler.
+pub trait EventHandler: Send + Sync {
+ fn on_event(&self) -> std::result::Result<(), ()>;
+}
+
+impl EventLoop {
+ /// Start an event loop. An optional fail handle could be passed to the event loop.
+ pub fn start(
+ name: String,
+ fail_handle: Option<Arc<dyn FailHandle>>,
+ ) -> Result<(EventLoop, thread::JoinHandle<()>)> {
+ let (self_stop_evt, stop_evt) = EventFd::new()
+ .and_then(|e| Ok((e.try_clone()?, e)))
+ .map_err(Error::CreateEventFd)?;
+
+ let fd_callbacks: Arc<Mutex<BTreeMap<RawFd, 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 = Arc::new(poll_ctx);
+ let event_loop = EventLoop {
+ fail_handle: fail_handle.clone(),
+ poll_ctx: poll_ctx.clone(),
+ handlers: fd_callbacks.clone(),
+ stop_evt: self_stop_evt,
+ };
+
+ let handle = thread::Builder::new()
+ .name(name)
+ .spawn(move || {
+ let event_loop = EpollEvents::new();
+ loop {
+ if fail_handle.failed() {
+ error!("xhci controller already failed, stopping event ring");
+ return;
+ }
+ let events = match poll_ctx.wait(&event_loop) {
+ Ok(events) => events,
+ Err(e) => {
+ error!("cannot poll {:?}", e);
+ fail_handle.fail();
+ return;
+ }
+ };
+ for event in &events {
+ if event.token().as_raw_fd() == stop_evt.as_raw_fd() {
+ return;
+ } else {
+ let fd = event.token().as_raw_fd();
+ let mut locked = fd_callbacks.lock();
+ let weak_handler = match locked.get(&fd) {
+ Some(cb) => cb.clone(),
+ None => {
+ warn!("callback for fd {} already removed", fd);
+ continue;
+ }
+ };
+ match weak_handler.upgrade() {
+ Some(handler) => {
+ // Drop lock before triggering the event.
+ drop(locked);
+ match handler.on_event() {
+ Ok(()) => {}
+ Err(_) => {
+ error!("event loop stopping due to handle event error");
+ fail_handle.fail();
+ return;
+ }
+ };
+ }
+ // If the handler is already gone, we remove the fd.
+ None => {
+ let _ = poll_ctx.delete(&Fd(fd));
+ if locked.remove(&fd).is_none() {
+ error!("fail to remove handler for file descriptor {}", fd);
+ }
+ }
+ };
+ }
+ }
+ }
+ })
+ .map_err(Error::StartThread)?;
+
+ Ok((event_loop, handle))
+ }
+
+ /// Add a new event to event loop. The event handler will be invoked when `event` happens on
+ /// `fd`.
+ ///
+ /// If the same `fd` 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,
+ 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);
+ // 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)
+ }
+
+ /// Removes event for this `fd`. 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<()> {
+ 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());
+ Ok(())
+ }
+
+ /// Stops this event loop asynchronously. Previous events might not be handled.
+ pub fn stop(&self) {
+ match self.stop_evt.write(1) {
+ Ok(_) => {}
+ Err(_) => {
+ error!("fail to send event loop stop event, it might already stopped");
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::sync::{Arc, Condvar, Mutex};
+ use sys_util::EventFd;
+
+ struct EventLoopTestHandler {
+ val: Mutex<u8>,
+ cvar: Condvar,
+ evt: EventFd,
+ }
+
+ impl EventHandler for EventLoopTestHandler {
+ fn on_event(&self) -> std::result::Result<(), ()> {
+ self.evt.read().unwrap();
+ *self.val.lock().unwrap() += 1;
+ self.cvar.notify_one();
+ Ok(())
+ }
+ }
+
+ #[test]
+ fn event_loop_test() {
+ let (l, j) = EventLoop::start("test".to_string(), None).unwrap();
+ let (self_evt, evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed creating EventFd pair: {:?}", e);
+ return;
+ }
+ };
+ let h = Arc::new(EventLoopTestHandler {
+ val: Mutex::new(0),
+ cvar: Condvar::new(),
+ evt,
+ });
+ let t: Arc<EventHandler> = h.clone();
+ l.add_event(
+ &h.evt,
+ WatchingEvents::empty().set_read(),
+ Arc::downgrade(&t),
+ )
+ .unwrap();
+ self_evt.write(1).unwrap();
+ let _ = h.cvar.wait(h.val.lock().unwrap()).unwrap();
+ l.stop();
+ j.join().unwrap();
+ assert_eq!(*(h.val.lock().unwrap()), 1);
+ }
+}
diff --git a/devices/src/utils/mod.rs b/devices/src/utils/mod.rs
new file mode 100644
index 0000000..6f35025
--- /dev/null
+++ b/devices/src/utils/mod.rs
@@ -0,0 +1,11 @@
+// Copyright 2018 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.
+
+mod async_job_queue;
+mod error;
+mod event_loop;
+
+pub use self::async_job_queue::*;
+pub use self::error::*;
+pub use self::event_loop::*;
diff --git a/devices/src/virtio/balloon.rs b/devices/src/virtio/balloon.rs
new file mode 100644
index 0000000..451421b
--- /dev/null
+++ b/devices/src/virtio/balloon.rs
@@ -0,0 +1,362 @@
+// Copyright 2017 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;
+use std::cmp;
+use std::fmt::{self, Display};
+use std::io::Write;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
+use msg_socket::MsgReceiver;
+use sys_util::{
+ self, error, info, warn, EventFd, GuestAddress, GuestMemory, PollContext, PollToken,
+};
+use vm_control::{BalloonControlCommand, BalloonControlResponseSocket};
+
+use super::{
+ DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_CONFIG_CHANGED,
+ INTERRUPT_STATUS_USED_RING, TYPE_BALLOON, VIRTIO_F_VERSION_1,
+};
+
+#[derive(Debug)]
+pub enum BalloonError {
+ /// Request to adjust memory size can't provide the number of pages requested.
+ NotEnoughPages,
+ /// Failure wriitng the config notification event.
+ WritingConfigEvent(sys_util::Error),
+}
+pub type Result<T> = std::result::Result<T, BalloonError>;
+
+impl Display for BalloonError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::BalloonError::*;
+
+ match self {
+ NotEnoughPages => write!(f, "not enough pages"),
+ WritingConfigEvent(e) => write!(f, "failed to write config event: {}", e),
+ }
+ }
+}
+
+// Balloon has three virt IO queues: Inflate, Deflate, and Stats.
+// Stats is currently not used.
+const QUEUE_SIZE: u16 = 128;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE, QUEUE_SIZE];
+
+const VIRTIO_BALLOON_PFN_SHIFT: u32 = 12;
+
+// The feature bitmap for virtio balloon
+const VIRTIO_BALLOON_F_MUST_TELL_HOST: u32 = 0; // Tell before reclaiming pages
+const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; // Deflate balloon on OOM
+
+// BalloonConfig is modified by the worker and read from the device thread.
+#[derive(Default)]
+struct BalloonConfig {
+ num_pages: AtomicUsize,
+ actual_pages: AtomicUsize,
+}
+
+struct Worker {
+ mem: GuestMemory,
+ inflate_queue: Queue,
+ deflate_queue: Queue,
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ config: Arc<BalloonConfig>,
+ command_socket: BalloonControlResponseSocket,
+}
+
+fn valid_inflate_desc(desc: &DescriptorChain) -> bool {
+ !desc.is_write_only() && desc.len % 4 == 0
+}
+
+impl Worker {
+ fn process_inflate_deflate(&mut self, inflate: bool) -> bool {
+ let queue = if inflate {
+ &mut self.inflate_queue
+ } else {
+ &mut self.deflate_queue
+ };
+
+ let mut needs_interrupt = false;
+ while let Some(avail_desc) = queue.pop(&self.mem) {
+ if inflate && valid_inflate_desc(&avail_desc) {
+ let num_addrs = avail_desc.len / 4;
+ for i in 0..num_addrs as usize {
+ let addr = match avail_desc.addr.checked_add((i * 4) as u64) {
+ Some(a) => a,
+ None => break,
+ };
+ let guest_input: u32 = match self.mem.read_obj_from_addr(addr) {
+ Ok(a) => a,
+ Err(_) => continue,
+ };
+ let guest_address =
+ GuestAddress((guest_input as u64) << VIRTIO_BALLOON_PFN_SHIFT);
+
+ if self
+ .mem
+ .remove_range(guest_address, 1 << VIRTIO_BALLOON_PFN_SHIFT)
+ .is_err()
+ {
+ warn!("Marking pages unused failed; addr={}", guest_address);
+ continue;
+ }
+ }
+ }
+
+ queue.add_used(&self.mem, avail_desc.index, 0);
+ needs_interrupt = true;
+ }
+
+ needs_interrupt
+ }
+
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ self.interrupt_evt.write(1).unwrap();
+ }
+
+ fn signal_config_changed(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_CONFIG_CHANGED as usize, Ordering::SeqCst);
+ self.interrupt_evt.write(1).unwrap();
+ }
+
+ fn run(&mut self, mut queue_evts: Vec<EventFd>, kill_evt: EventFd) {
+ #[derive(PartialEq, PollToken)]
+ enum Token {
+ Inflate,
+ Deflate,
+ CommandSocket,
+ InterruptResample,
+ Kill,
+ }
+
+ let inflate_queue_evt = queue_evts.remove(0);
+ let deflate_queue_evt = queue_evts.remove(0);
+
+ let poll_ctx: PollContext<Token> = match PollContext::new()
+ .and_then(|pc| pc.add(&inflate_queue_evt, Token::Inflate).and(Ok(pc)))
+ .and_then(|pc| pc.add(&deflate_queue_evt, Token::Deflate).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.command_socket, Token::CommandSocket)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ {
+ Ok(pc) => pc,
+ Err(e) => {
+ error!("failed creating PollContext: {}", e);
+ return;
+ }
+ };
+
+ 'poll: loop {
+ let events = match poll_ctx.wait() {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed polling for events: {}", e);
+ break;
+ }
+ };
+
+ let mut needs_interrupt = false;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::Inflate => {
+ if let Err(e) = inflate_queue_evt.read() {
+ error!("failed reading inflate queue EventFd: {}", e);
+ break 'poll;
+ }
+ needs_interrupt |= self.process_inflate_deflate(true);
+ }
+ Token::Deflate => {
+ if let Err(e) = deflate_queue_evt.read() {
+ error!("failed reading deflate queue EventFd: {}", e);
+ break 'poll;
+ }
+ needs_interrupt |= self.process_inflate_deflate(false);
+ }
+ Token::CommandSocket => {
+ if let Ok(req) = self.command_socket.recv() {
+ match req {
+ BalloonControlCommand::Adjust { num_bytes } => {
+ let num_pages =
+ (num_bytes >> VIRTIO_BALLOON_PFN_SHIFT) as usize;
+ info!("ballon config changed to consume {} pages", num_pages);
+
+ self.config.num_pages.store(num_pages, Ordering::Relaxed);
+ self.signal_config_changed();
+ }
+ };
+ }
+ }
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_evt.write(1).unwrap();
+ }
+ }
+ Token::Kill => break 'poll,
+ }
+ }
+ for event in events.iter_hungup() {
+ if event.token() == Token::CommandSocket && !event.readable() {
+ // If this call fails, the command socket was already removed from the
+ // PollContext.
+ let _ = poll_ctx.delete(&self.command_socket);
+ }
+ }
+ if needs_interrupt {
+ self.signal_used_queue();
+ }
+ }
+ }
+}
+
+/// Virtio device for memory balloon inflation/deflation.
+pub struct Balloon {
+ command_socket: Option<BalloonControlResponseSocket>,
+ config: Arc<BalloonConfig>,
+ features: u64,
+ kill_evt: Option<EventFd>,
+}
+
+impl Balloon {
+ /// Create a new virtio balloon device.
+ pub fn new(command_socket: BalloonControlResponseSocket) -> Result<Balloon> {
+ Ok(Balloon {
+ command_socket: Some(command_socket),
+ config: Arc::new(BalloonConfig {
+ num_pages: AtomicUsize::new(0),
+ actual_pages: AtomicUsize::new(0),
+ }),
+ kill_evt: None,
+ // TODO(dgreid) - Add stats queue feature.
+ features: 1 << VIRTIO_BALLOON_F_MUST_TELL_HOST | 1 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM,
+ })
+ }
+}
+
+impl Drop for Balloon {
+ 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);
+ }
+ }
+}
+
+impl VirtioDevice for Balloon {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ vec![self.command_socket.as_ref().unwrap().as_raw_fd()]
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_BALLOON
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn read_config(&self, offset: u64, mut data: &mut [u8]) {
+ if offset >= 8 {
+ return;
+ }
+ let num_pages = self.config.num_pages.load(Ordering::Relaxed) as u32;
+ let actual_pages = self.config.actual_pages.load(Ordering::Relaxed) as u32;
+ let mut config = [0u8; 8];
+ // These writes can't fail as they fit in the declared array so unwrap is fine.
+ (&mut config[0..])
+ .write_u32::<LittleEndian>(num_pages)
+ .unwrap();
+ (&mut config[4..])
+ .write_u32::<LittleEndian>(actual_pages)
+ .unwrap();
+ if let Some(end) = offset.checked_add(data.len() as u64) {
+ // This write can't fail, offset and end are checked against the length of config.
+ data.write_all(&config[offset as usize..cmp::min(end, 8) as usize])
+ .unwrap();
+ }
+ }
+
+ fn write_config(&mut self, offset: u64, mut data: &[u8]) {
+ // Only allow writing to `actual` pages from the guest.
+ if offset != 4 || data.len() != 4 {
+ return;
+ }
+ // This read can't fail as it fits in the declared array so unwrap is fine.
+ let new_actual: u32 = data.read_u32::<LittleEndian>().unwrap();
+ self.config
+ .actual_pages
+ .store(new_actual as usize, Ordering::Relaxed);
+ }
+
+ fn features(&self) -> u64 {
+ 1 << VIRTIO_BALLOON_F_MUST_TELL_HOST
+ | 1 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM
+ | 1 << VIRTIO_F_VERSION_1
+ }
+
+ fn ack_features(&mut self, value: u64) {
+ self.features &= value;
+ }
+
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != QUEUE_SIZES.len() || queue_evts.len() != QUEUE_SIZES.len() {
+ return;
+ }
+
+ let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed to create kill EventFd pair: {}", e);
+ return;
+ }
+ };
+ self.kill_evt = Some(self_kill_evt);
+
+ let config = self.config.clone();
+ let command_socket = self.command_socket.take().unwrap();
+ let worker_result = thread::Builder::new()
+ .name("virtio_balloon".to_string())
+ .spawn(move || {
+ let mut worker = Worker {
+ mem,
+ inflate_queue: queues.remove(0),
+ deflate_queue: queues.remove(0),
+ interrupt_status: status,
+ interrupt_evt,
+ interrupt_resample_evt,
+ command_socket,
+ config,
+ };
+ worker.run(queue_evts, kill_evt);
+ });
+ if let Err(e) = worker_result {
+ error!("failed to spawn virtio_balloon worker: {}", e);
+ return;
+ }
+ }
+}
diff --git a/devices/src/virtio/block.rs b/devices/src/virtio/block.rs
new file mode 100644
index 0000000..59cc51a
--- /dev/null
+++ b/devices/src/virtio/block.rs
@@ -0,0 +1,1169 @@
+// Copyright 2017 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::cmp;
+use std::fmt::{self, Display};
+use std::io::{self, Seek, SeekFrom, Write};
+use std::mem::{size_of, size_of_val};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::result;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+use std::time::Duration;
+use std::u32;
+
+use sync::Mutex;
+use sys_util::Error as SysError;
+use sys_util::Result as SysResult;
+use sys_util::{
+ error, info, warn, EventFd, FileReadWriteVolatile, FileSetLen, FileSync, GuestAddress,
+ GuestMemory, GuestMemoryError, PollContext, PollToken, PunchHole, TimerFd, WriteZeroes,
+};
+
+use data_model::{DataInit, Le16, Le32, Le64, VolatileMemory, VolatileMemoryError};
+use msg_socket::{MsgReceiver, MsgSender};
+use vm_control::{DiskControlCommand, DiskControlResponseSocket, DiskControlResult};
+
+use super::{
+ DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_CONFIG_CHANGED,
+ INTERRUPT_STATUS_USED_RING, TYPE_BLOCK, VIRTIO_F_VERSION_1,
+};
+
+const QUEUE_SIZE: u16 = 256;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
+const SECTOR_SHIFT: u8 = 9;
+const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
+const MAX_DISCARD_SECTORS: u32 = u32::MAX;
+const MAX_WRITE_ZEROES_SECTORS: u32 = u32::MAX;
+// Hard-coded to 64 KiB (in 512-byte sectors) for now,
+// but this should probably be based on cluster size for qcow.
+const DISCARD_SECTOR_ALIGNMENT: u32 = 128;
+
+const VIRTIO_BLK_T_IN: u32 = 0;
+const VIRTIO_BLK_T_OUT: u32 = 1;
+const VIRTIO_BLK_T_FLUSH: u32 = 4;
+const VIRTIO_BLK_T_DISCARD: u32 = 11;
+const VIRTIO_BLK_T_WRITE_ZEROES: u32 = 13;
+
+const VIRTIO_BLK_S_OK: u8 = 0;
+const VIRTIO_BLK_S_IOERR: u8 = 1;
+const VIRTIO_BLK_S_UNSUPP: u8 = 2;
+
+const VIRTIO_BLK_F_RO: u32 = 5;
+const VIRTIO_BLK_F_BLK_SIZE: u32 = 6;
+const VIRTIO_BLK_F_FLUSH: u32 = 9;
+const VIRTIO_BLK_F_DISCARD: u32 = 13;
+const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14;
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_blk_geometry {
+ cylinders: Le16,
+ heads: u8,
+ sectors: u8,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_blk_geometry {}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_blk_topology {
+ physical_block_exp: u8,
+ alignment_offset: u8,
+ min_io_size: Le16,
+ opt_io_size: Le32,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_blk_topology {}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_blk_config {
+ capacity: Le64,
+ size_max: Le32,
+ seg_max: Le32,
+ geometry: virtio_blk_geometry,
+ blk_size: Le32,
+ topology: virtio_blk_topology,
+ writeback: u8,
+ unused0: [u8; 3],
+ max_discard_sectors: Le32,
+ max_discard_seg: Le32,
+ discard_sector_alignment: Le32,
+ max_write_zeroes_sectors: Le32,
+ max_write_zeroes_seg: Le32,
+ write_zeroes_may_unmap: u8,
+ unused1: [u8; 3],
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_blk_config {}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_blk_discard_write_zeroes {
+ sector: Le64,
+ num_sectors: Le32,
+ flags: Le32,
+}
+
+const VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP: u32 = 1 << 0;
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_blk_discard_write_zeroes {}
+
+pub trait DiskFile:
+ FileSetLen + FileSync + FileReadWriteVolatile + PunchHole + Seek + WriteZeroes
+{
+}
+impl<D: FileSetLen + FileSync + PunchHole + FileReadWriteVolatile + Seek + WriteZeroes> DiskFile
+ for D
+{
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+enum RequestType {
+ In,
+ Out,
+ Flush,
+ Discard,
+ WriteZeroes,
+ Unsupported(u32),
+}
+
+impl Display for RequestType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::RequestType::*;
+
+ match self {
+ In => write!(f, "in"),
+ Out => write!(f, "out"),
+ Flush => write!(f, "flush"),
+ Discard => write!(f, "discard"),
+ WriteZeroes => write!(f, "write zeroes"),
+ Unsupported(n) => write!(f, "unsupported({})", n),
+ }
+ }
+}
+
+#[derive(Debug)]
+enum ParseError {
+ /// Guest gave us bad memory addresses
+ GuestMemory(GuestMemoryError),
+ /// Guest gave us offsets that would have overflowed a usize.
+ CheckedOffset(GuestAddress, u64),
+ /// Guest gave us a write only descriptor that protocol says to read from.
+ UnexpectedWriteOnlyDescriptor,
+ /// Guest gave us a read only descriptor that protocol says to write to.
+ UnexpectedReadOnlyDescriptor,
+ /// Guest gave us too few descriptors in a descriptor chain.
+ DescriptorChainTooShort,
+ /// Guest gave us a descriptor that was too short to use.
+ DescriptorLengthTooSmall,
+}
+
+impl Display for ParseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::ParseError::*;
+
+ match self {
+ GuestMemory(e) => write!(f, "bad guest memory address: {}", e),
+ CheckedOffset(addr, offset) => write!(f, "{}+{} would overflow a usize", addr, offset),
+ UnexpectedWriteOnlyDescriptor => write!(f, "unexpected write-only descriptor"),
+ UnexpectedReadOnlyDescriptor => write!(f, "unexpected read-only descriptor"),
+ DescriptorChainTooShort => write!(f, "descriptor chain too short"),
+ DescriptorLengthTooSmall => write!(f, "descriptor length too small"),
+ }
+ }
+}
+
+fn request_type(
+ mem: &GuestMemory,
+ desc_addr: GuestAddress,
+) -> result::Result<RequestType, ParseError> {
+ let type_ = mem
+ .read_obj_from_addr(desc_addr)
+ .map_err(ParseError::GuestMemory)?;
+ match type_ {
+ VIRTIO_BLK_T_IN => Ok(RequestType::In),
+ VIRTIO_BLK_T_OUT => Ok(RequestType::Out),
+ VIRTIO_BLK_T_FLUSH => Ok(RequestType::Flush),
+ VIRTIO_BLK_T_DISCARD => Ok(RequestType::Discard),
+ VIRTIO_BLK_T_WRITE_ZEROES => Ok(RequestType::WriteZeroes),
+ t => Ok(RequestType::Unsupported(t)),
+ }
+}
+
+fn sector(mem: &GuestMemory, desc_addr: GuestAddress) -> result::Result<u64, ParseError> {
+ const SECTOR_OFFSET: u64 = 8;
+ let addr = match mem.checked_offset(desc_addr, SECTOR_OFFSET) {
+ Some(v) => v,
+ None => return Err(ParseError::CheckedOffset(desc_addr, SECTOR_OFFSET)),
+ };
+
+ mem.read_obj_from_addr(addr)
+ .map_err(ParseError::GuestMemory)
+}
+
+fn discard_write_zeroes_segment(
+ mem: &GuestMemory,
+ seg_addr: GuestAddress,
+) -> result::Result<virtio_blk_discard_write_zeroes, ParseError> {
+ mem.read_obj_from_addr(seg_addr)
+ .map_err(ParseError::GuestMemory)
+}
+
+#[derive(Debug)]
+enum ExecuteError {
+ /// Error arming the flush timer.
+ Flush(io::Error),
+ ReadVolatile {
+ addr: GuestAddress,
+ length: u32,
+ sector: u64,
+ volatile_memory_error: VolatileMemoryError,
+ },
+ ReadIo {
+ addr: GuestAddress,
+ length: u32,
+ sector: u64,
+ io_error: io::Error,
+ },
+ Seek {
+ ioerr: io::Error,
+ sector: u64,
+ },
+ TimerFd(SysError),
+ WriteVolatile {
+ addr: GuestAddress,
+ length: u32,
+ sector: u64,
+ volatile_memory_error: VolatileMemoryError,
+ },
+ WriteIo {
+ addr: GuestAddress,
+ length: u32,
+ sector: u64,
+ io_error: io::Error,
+ },
+ DiscardWriteZeroes {
+ ioerr: Option<io::Error>,
+ sector: u64,
+ num_sectors: u32,
+ flags: u32,
+ },
+ ReadOnly {
+ request_type: RequestType,
+ },
+ OutOfRange,
+ Unsupported(u32),
+}
+
+impl Display for ExecuteError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::ExecuteError::*;
+
+ match self {
+ Flush(e) => write!(f, "failed to flush: {}", e),
+ ReadVolatile {
+ addr,
+ length,
+ sector,
+ volatile_memory_error,
+ } => write!(
+ f,
+ "memory error reading {} bytes from sector {} to address {}: {}",
+ length, sector, addr, volatile_memory_error,
+ ),
+ ReadIo {
+ addr,
+ length,
+ sector,
+ io_error,
+ } => write!(
+ f,
+ "io error reading {} bytes from sector {} to address {}: {}",
+ length, sector, addr, io_error,
+ ),
+ Seek { ioerr, sector } => write!(f, "failed to seek to sector {}: {}", sector, ioerr),
+ TimerFd(e) => write!(f, "{}", e),
+ WriteVolatile {
+ addr,
+ length,
+ sector,
+ volatile_memory_error,
+ } => write!(
+ f,
+ "memory error writing {} bytes from address {} to sector {}: {}",
+ length, addr, sector, volatile_memory_error,
+ ),
+ WriteIo {
+ addr,
+ length,
+ sector,
+ io_error,
+ } => write!(
+ f,
+ "io error writing {} bytes from address {} to sector {}: {}",
+ length, addr, sector, io_error,
+ ),
+ DiscardWriteZeroes {
+ ioerr: Some(ioerr),
+ sector,
+ num_sectors,
+ flags,
+ } => write!(
+ f,
+ "failed to perform discard or write zeroes; sector={} num_sectors={} flags={}; {}",
+ sector, num_sectors, flags, ioerr,
+ ),
+ DiscardWriteZeroes {
+ ioerr: None,
+ sector,
+ num_sectors,
+ flags,
+ } => write!(
+ f,
+ "failed to perform discard or write zeroes; sector={} num_sectors={} flags={}",
+ sector, num_sectors, flags,
+ ),
+ ReadOnly { request_type } => write!(f, "read only; request_type={}", request_type),
+ OutOfRange => write!(f, "out of range"),
+ Unsupported(n) => write!(f, "unsupported ({})", n),
+ }
+ }
+}
+
+impl ExecuteError {
+ fn status(&self) -> u8 {
+ match self {
+ ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
+ ExecuteError::ReadIo { .. } => VIRTIO_BLK_S_IOERR,
+ ExecuteError::ReadVolatile { .. } => VIRTIO_BLK_S_IOERR,
+ ExecuteError::Seek { .. } => VIRTIO_BLK_S_IOERR,
+ ExecuteError::TimerFd(_) => VIRTIO_BLK_S_IOERR,
+ ExecuteError::WriteIo { .. } => VIRTIO_BLK_S_IOERR,
+ ExecuteError::WriteVolatile { .. } => VIRTIO_BLK_S_IOERR,
+ ExecuteError::DiscardWriteZeroes { .. } => VIRTIO_BLK_S_IOERR,
+ ExecuteError::ReadOnly { .. } => VIRTIO_BLK_S_IOERR,
+ ExecuteError::OutOfRange { .. } => VIRTIO_BLK_S_IOERR,
+ ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
+ }
+ }
+}
+
+struct Request {
+ request_type: RequestType,
+ sector: u64,
+ data_addr: GuestAddress,
+ data_len: u32,
+ status_addr: GuestAddress,
+ discard_write_zeroes_seg: Option<virtio_blk_discard_write_zeroes>,
+}
+
+impl Request {
+ fn parse(
+ avail_desc: &DescriptorChain,
+ mem: &GuestMemory,
+ ) -> result::Result<Request, ParseError> {
+ // The head contains the request type which MUST be readable.
+ if avail_desc.is_write_only() {
+ return Err(ParseError::UnexpectedWriteOnlyDescriptor);
+ }
+
+ let req_type = request_type(&mem, avail_desc.addr)?;
+ if req_type == RequestType::Flush {
+ Request::parse_flush(avail_desc, mem)
+ } else if req_type == RequestType::Discard || req_type == RequestType::WriteZeroes {
+ Request::parse_discard_write_zeroes(avail_desc, mem, req_type)
+ } else {
+ Request::parse_read_write(avail_desc, mem, req_type)
+ }
+ }
+
+ fn parse_flush(
+ avail_desc: &DescriptorChain,
+ mem: &GuestMemory,
+ ) -> result::Result<Request, ParseError> {
+ let sector = sector(&mem, avail_desc.addr)?;
+ let status_desc = avail_desc
+ .next_descriptor()
+ .ok_or(ParseError::DescriptorChainTooShort)?;
+
+ // The status MUST always be writable
+ if !status_desc.is_write_only() {
+ return Err(ParseError::UnexpectedReadOnlyDescriptor);
+ }
+
+ if status_desc.len < 1 {
+ return Err(ParseError::DescriptorLengthTooSmall);
+ }
+
+ Ok(Request {
+ request_type: RequestType::Flush,
+ sector,
+ data_addr: GuestAddress(0),
+ data_len: 0,
+ status_addr: status_desc.addr,
+ discard_write_zeroes_seg: None,
+ })
+ }
+
+ fn parse_discard_write_zeroes(
+ avail_desc: &DescriptorChain,
+ mem: &GuestMemory,
+ req_type: RequestType,
+ ) -> result::Result<Request, ParseError> {
+ let seg_desc = avail_desc
+ .next_descriptor()
+ .ok_or(ParseError::DescriptorChainTooShort)?;
+ let status_desc = seg_desc
+ .next_descriptor()
+ .ok_or(ParseError::DescriptorChainTooShort)?;
+
+ if seg_desc.is_write_only() {
+ return Err(ParseError::UnexpectedWriteOnlyDescriptor);
+ }
+
+ // For simplicity, we currently only support a single segment
+ // for discard and write zeroes commands. This allows the
+ // request to be represented as a single Request object.
+ if seg_desc.len < size_of::<virtio_blk_discard_write_zeroes>() as u32 {
+ return Err(ParseError::DescriptorLengthTooSmall);
+ }
+
+ let seg = discard_write_zeroes_segment(&mem, seg_desc.addr)?;
+
+ // The status MUST always be writable
+ if !status_desc.is_write_only() {
+ return Err(ParseError::UnexpectedReadOnlyDescriptor);
+ }
+
+ if status_desc.len < 1 {
+ return Err(ParseError::DescriptorLengthTooSmall);
+ }
+
+ Ok(Request {
+ request_type: req_type,
+ sector: 0,
+ data_addr: GuestAddress(0),
+ data_len: 0,
+ status_addr: status_desc.addr,
+ discard_write_zeroes_seg: Some(seg),
+ })
+ }
+
+ fn parse_read_write(
+ avail_desc: &DescriptorChain,
+ mem: &GuestMemory,
+ req_type: RequestType,
+ ) -> result::Result<Request, ParseError> {
+ let sector = sector(&mem, avail_desc.addr)?;
+ let data_desc = avail_desc
+ .next_descriptor()
+ .ok_or(ParseError::DescriptorChainTooShort)?;
+ let status_desc = data_desc
+ .next_descriptor()
+ .ok_or(ParseError::DescriptorChainTooShort)?;
+
+ if data_desc.is_write_only() && req_type == RequestType::Out {
+ return Err(ParseError::UnexpectedWriteOnlyDescriptor);
+ }
+
+ if !data_desc.is_write_only() && req_type == RequestType::In {
+ return Err(ParseError::UnexpectedReadOnlyDescriptor);
+ }
+
+ // The status MUST always be writable
+ if !status_desc.is_write_only() {
+ return Err(ParseError::UnexpectedReadOnlyDescriptor);
+ }
+
+ if status_desc.len < 1 {
+ return Err(ParseError::DescriptorLengthTooSmall);
+ }
+
+ Ok(Request {
+ request_type: req_type,
+ sector,
+ data_addr: data_desc.addr,
+ data_len: data_desc.len,
+ status_addr: status_desc.addr,
+ discard_write_zeroes_seg: None,
+ })
+ }
+
+ fn execute<T: DiskFile>(
+ &self,
+ read_only: bool,
+ disk: &mut T,
+ disk_size: u64,
+ flush_timer: &mut TimerFd,
+ flush_timer_armed: &mut bool,
+ mem: &GuestMemory,
+ ) -> result::Result<u32, ExecuteError> {
+ // Delay after a write when the file is auto-flushed.
+ let flush_delay = Duration::from_secs(60);
+
+ if read_only && self.request_type != RequestType::In {
+ return Err(ExecuteError::ReadOnly {
+ request_type: self.request_type,
+ });
+ }
+
+ /// Check that a request accesses only data within the disk's current size.
+ /// All parameters are in units of bytes.
+ fn check_range(
+ io_start: u64,
+ io_length: u64,
+ disk_size: u64,
+ ) -> result::Result<(), ExecuteError> {
+ let io_end = io_start
+ .checked_add(io_length)
+ .ok_or(ExecuteError::OutOfRange)?;
+ if io_end > disk_size {
+ Err(ExecuteError::OutOfRange)
+ } else {
+ Ok(())
+ }
+ }
+
+ match self.request_type {
+ RequestType::In => {
+ let offset = self
+ .sector
+ .checked_shl(u32::from(SECTOR_SHIFT))
+ .ok_or(ExecuteError::OutOfRange)?;
+ check_range(offset, u64::from(self.data_len), disk_size)?;
+ disk.seek(SeekFrom::Start(offset))
+ .map_err(|e| ExecuteError::Seek {
+ ioerr: e,
+ sector: self.sector,
+ })?;
+ let mem_slice = mem
+ .get_slice(self.data_addr.0, self.data_len as u64)
+ .map_err(|volatile_memory_error| ExecuteError::ReadVolatile {
+ addr: self.data_addr,
+ length: self.data_len,
+ sector: self.sector,
+ volatile_memory_error,
+ })?;
+ disk.read_exact_volatile(mem_slice)
+ .map_err(|io_error| ExecuteError::ReadIo {
+ addr: self.data_addr,
+ length: self.data_len,
+ sector: self.sector,
+ io_error,
+ })?;
+ return Ok(self.data_len);
+ }
+ RequestType::Out => {
+ let offset = self
+ .sector
+ .checked_shl(u32::from(SECTOR_SHIFT))
+ .ok_or(ExecuteError::OutOfRange)?;
+ check_range(offset, u64::from(self.data_len), disk_size)?;
+ disk.seek(SeekFrom::Start(offset))
+ .map_err(|e| ExecuteError::Seek {
+ ioerr: e,
+ sector: self.sector,
+ })?;
+ let mem_slice = mem
+ .get_slice(self.data_addr.0, self.data_len as u64)
+ .map_err(|volatile_memory_error| ExecuteError::WriteVolatile {
+ addr: self.data_addr,
+ length: self.data_len,
+ sector: self.sector,
+ volatile_memory_error,
+ })?;
+ disk.write_all_volatile(mem_slice)
+ .map_err(|io_error| ExecuteError::WriteIo {
+ addr: self.data_addr,
+ length: self.data_len,
+ sector: self.sector,
+ io_error,
+ })?;
+ if !*flush_timer_armed {
+ flush_timer
+ .reset(flush_delay, None)
+ .map_err(ExecuteError::TimerFd)?;
+ *flush_timer_armed = true;
+ }
+ }
+ RequestType::Discard | RequestType::WriteZeroes => {
+ if let Some(seg) = self.discard_write_zeroes_seg {
+ let sector = seg.sector.to_native();
+ let num_sectors = seg.num_sectors.to_native();
+ let flags = seg.flags.to_native();
+
+ let valid_flags = if self.request_type == RequestType::WriteZeroes {
+ VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP
+ } else {
+ 0
+ };
+
+ if (flags & !valid_flags) != 0 {
+ return Err(ExecuteError::DiscardWriteZeroes {
+ ioerr: None,
+ sector,
+ num_sectors,
+ flags,
+ });
+ }
+
+ let offset = sector
+ .checked_shl(u32::from(SECTOR_SHIFT))
+ .ok_or(ExecuteError::OutOfRange)?;
+ let length = u64::from(num_sectors)
+ .checked_shl(u32::from(SECTOR_SHIFT))
+ .ok_or(ExecuteError::OutOfRange)?;
+ check_range(offset, length, disk_size)?;
+
+ if self.request_type == RequestType::Discard {
+ // Since Discard is just a hint and some filesystems may not implement
+ // FALLOC_FL_PUNCH_HOLE, ignore punch_hole errors.
+ let _ = disk.punch_hole(offset, length);
+ } else {
+ disk.seek(SeekFrom::Start(offset))
+ .map_err(|e| ExecuteError::Seek { ioerr: e, sector })?;
+ disk.write_zeroes(length as usize).map_err(|e| {
+ ExecuteError::DiscardWriteZeroes {
+ ioerr: Some(e),
+ sector,
+ num_sectors,
+ flags,
+ }
+ })?;
+ }
+ }
+ }
+ RequestType::Flush => {
+ disk.fsync().map_err(ExecuteError::Flush)?;
+ flush_timer.clear().map_err(ExecuteError::TimerFd)?;
+ *flush_timer_armed = false;
+ }
+ RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
+ };
+ Ok(0)
+ }
+}
+
+struct Worker<T: DiskFile> {
+ queues: Vec<Queue>,
+ mem: GuestMemory,
+ disk_image: T,
+ disk_size: Arc<Mutex<u64>>,
+ read_only: bool,
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+}
+
+impl<T: DiskFile> Worker<T> {
+ fn process_queue(
+ &mut self,
+ queue_index: usize,
+ flush_timer: &mut TimerFd,
+ flush_timer_armed: &mut bool,
+ ) -> bool {
+ let queue = &mut self.queues[queue_index];
+
+ let disk_size = self.disk_size.lock();
+
+ let mut needs_interrupt = false;
+ while let Some(avail_desc) = queue.pop(&self.mem) {
+ let len;
+ match Request::parse(&avail_desc, &self.mem) {
+ Ok(request) => {
+ let status = match request.execute(
+ self.read_only,
+ &mut self.disk_image,
+ *disk_size,
+ flush_timer,
+ flush_timer_armed,
+ &self.mem,
+ ) {
+ Ok(l) => {
+ len = l;
+ VIRTIO_BLK_S_OK
+ }
+ Err(e) => {
+ error!("failed executing disk request: {}", e);
+ len = 1; // 1 byte for the status
+ e.status()
+ }
+ };
+ // We use unwrap because the request parsing process already checked that the
+ // status_addr was valid.
+ self.mem
+ .write_obj_at_addr(status, request.status_addr)
+ .unwrap();
+ }
+ Err(e) => {
+ error!("failed processing available descriptor chain: {}", e);
+ len = 0;
+ }
+ }
+
+ queue.add_used(&self.mem, avail_desc.index, len);
+ needs_interrupt = true;
+ }
+
+ needs_interrupt
+ }
+
+ fn resize(&mut self, new_size: u64) -> DiskControlResult {
+ if self.read_only {
+ error!("Attempted to resize read-only block device");
+ return DiskControlResult::Err(SysError::new(libc::EROFS));
+ }
+
+ info!("Resizing block device to {} bytes", new_size);
+
+ if let Err(e) = self.disk_image.set_len(new_size) {
+ error!("Resizing disk failed! {}", e);
+ return DiskControlResult::Err(SysError::new(libc::EIO));
+ }
+
+ if let Ok(new_disk_size) = self.disk_image.seek(SeekFrom::End(0)) {
+ let mut disk_size = self.disk_size.lock();
+ *disk_size = new_disk_size;
+ }
+ DiskControlResult::Ok
+ }
+
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ self.interrupt_evt.write(1).unwrap();
+ }
+
+ fn signal_config_changed(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_CONFIG_CHANGED as usize, Ordering::SeqCst);
+ self.interrupt_evt.write(1).unwrap();
+ }
+
+ fn run(
+ &mut self,
+ queue_evt: EventFd,
+ kill_evt: EventFd,
+ control_socket: DiskControlResponseSocket,
+ ) {
+ #[derive(PollToken)]
+ enum Token {
+ FlushTimer,
+ QueueAvailable,
+ ControlRequest,
+ InterruptResample,
+ Kill,
+ }
+
+ let mut flush_timer = match TimerFd::new() {
+ Ok(t) => t,
+ Err(e) => {
+ error!("Failed to create the flush timer: {}", e);
+ return;
+ }
+ };
+ let mut flush_timer_armed = false;
+
+ let poll_ctx: PollContext<Token> = match PollContext::new()
+ .and_then(|pc| pc.add(&flush_timer, Token::FlushTimer).and(Ok(pc)))
+ .and_then(|pc| pc.add(&queue_evt, Token::QueueAvailable).and(Ok(pc)))
+ .and_then(|pc| pc.add(&control_socket, Token::ControlRequest).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ {
+ Ok(pc) => pc,
+ Err(e) => {
+ error!("failed creating PollContext: {}", e);
+ return;
+ }
+ };
+
+ 'poll: loop {
+ let events = match poll_ctx.wait() {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed polling for events: {}", e);
+ break;
+ }
+ };
+
+ let mut needs_interrupt = false;
+ let mut needs_config_interrupt = false;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::FlushTimer => {
+ if let Err(e) = self.disk_image.fsync() {
+ error!("Failed to flush the disk: {}", e);
+ break 'poll;
+ }
+ if let Err(e) = flush_timer.wait() {
+ error!("Failed to clear flush timer: {}", e);
+ break 'poll;
+ }
+ }
+ Token::QueueAvailable => {
+ if let Err(e) = queue_evt.read() {
+ error!("failed reading queue EventFd: {}", e);
+ break 'poll;
+ }
+ needs_interrupt |=
+ self.process_queue(0, &mut flush_timer, &mut flush_timer_armed);
+ }
+ Token::ControlRequest => {
+ let req = match control_socket.recv() {
+ Ok(req) => req,
+ Err(e) => {
+ error!("control socket failed recv: {}", e);
+ break 'poll;
+ }
+ };
+
+ let resp = match req {
+ DiskControlCommand::Resize { new_size } => {
+ needs_config_interrupt = true;
+ self.resize(new_size)
+ }
+ };
+
+ if let Err(e) = control_socket.send(&resp) {
+ error!("control socket failed send: {}", e);
+ break 'poll;
+ }
+ }
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_evt.write(1).unwrap();
+ }
+ }
+ Token::Kill => break 'poll,
+ }
+ }
+ if needs_interrupt {
+ self.signal_used_queue();
+ }
+ if needs_config_interrupt {
+ self.signal_config_changed();
+ }
+ }
+ }
+}
+
+/// Virtio device for exposing block level read/write operations on a host file.
+pub struct Block<T: DiskFile> {
+ kill_evt: Option<EventFd>,
+ disk_image: Option<T>,
+ disk_size: Arc<Mutex<u64>>,
+ avail_features: u64,
+ read_only: bool,
+ control_socket: Option<DiskControlResponseSocket>,
+}
+
+fn build_config_space(disk_size: u64) -> virtio_blk_config {
+ virtio_blk_config {
+ // If the image is not a multiple of the sector size, the tail bits are not exposed.
+ capacity: Le64::from(disk_size >> SECTOR_SHIFT),
+ blk_size: Le32::from(SECTOR_SIZE as u32),
+ max_discard_sectors: Le32::from(MAX_DISCARD_SECTORS),
+ discard_sector_alignment: Le32::from(DISCARD_SECTOR_ALIGNMENT),
+ max_write_zeroes_sectors: Le32::from(MAX_WRITE_ZEROES_SECTORS),
+ write_zeroes_may_unmap: 1,
+ // Limit number of segments to 1 - see parse_discard_write_zeroes()
+ max_discard_seg: Le32::from(1),
+ max_write_zeroes_seg: Le32::from(1),
+ ..Default::default()
+ }
+}
+
+impl<T: DiskFile> Block<T> {
+ /// Create a new virtio block device that operates on the given file.
+ ///
+ /// The given file must be seekable and sizable.
+ pub fn new(
+ mut disk_image: T,
+ read_only: bool,
+ control_socket: Option<DiskControlResponseSocket>,
+ ) -> SysResult<Block<T>> {
+ let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
+ if disk_size % SECTOR_SIZE != 0 {
+ warn!(
+ "Disk size {} is not a multiple of sector size {}; \
+ the remainder will not be visible to the guest.",
+ disk_size, SECTOR_SIZE
+ );
+ }
+
+ let mut avail_features: u64 = 1 << VIRTIO_BLK_F_FLUSH;
+ if read_only {
+ avail_features |= 1 << VIRTIO_BLK_F_RO;
+ } else {
+ avail_features |= 1 << VIRTIO_BLK_F_DISCARD;
+ avail_features |= 1 << VIRTIO_BLK_F_WRITE_ZEROES;
+ }
+ avail_features |= 1 << VIRTIO_F_VERSION_1;
+ avail_features |= 1 << VIRTIO_BLK_F_BLK_SIZE;
+
+ Ok(Block {
+ kill_evt: None,
+ disk_image: Some(disk_image),
+ disk_size: Arc::new(Mutex::new(disk_size)),
+ avail_features,
+ read_only,
+ control_socket,
+ })
+ }
+}
+
+impl<T: DiskFile> Drop for Block<T> {
+ fn drop(&mut self) {
+ if let Some(kill_evt) = self.kill_evt.take() {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = kill_evt.write(1);
+ }
+ }
+}
+
+impl<T: 'static + AsRawFd + DiskFile + Send> VirtioDevice for Block<T> {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ let mut keep_fds = Vec::new();
+
+ if let Some(disk_image) = &self.disk_image {
+ keep_fds.push(disk_image.as_raw_fd());
+ }
+
+ if let Some(control_socket) = &self.control_socket {
+ keep_fds.push(control_socket.as_raw_fd());
+ }
+
+ keep_fds
+ }
+
+ fn features(&self) -> u64 {
+ self.avail_features
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_BLOCK
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn read_config(&self, offset: u64, mut data: &mut [u8]) {
+ let config_space = {
+ let disk_size = self.disk_size.lock();
+ build_config_space(*disk_size)
+ };
+ let config_len = size_of_val(&config_space) as u64;
+ if offset >= config_len {
+ return;
+ }
+
+ if let Some(end) = offset.checked_add(data.len() as u64) {
+ let offset = offset as usize;
+ let end = cmp::min(end, config_len) as usize;
+ // This write can't fail, offset and end are checked against config_len.
+ data.write_all(&config_space.as_slice()[offset..end])
+ .unwrap();
+ }
+ }
+
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ queues: Vec<Queue>,
+ mut queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != 1 || queue_evts.len() != 1 {
+ return;
+ }
+
+ let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed creating kill EventFd pair: {}", e);
+ return;
+ }
+ };
+ self.kill_evt = Some(self_kill_evt);
+
+ let read_only = self.read_only;
+ let disk_size = self.disk_size.clone();
+ if let Some(disk_image) = self.disk_image.take() {
+ if let Some(control_socket) = self.control_socket.take() {
+ let worker_result =
+ thread::Builder::new()
+ .name("virtio_blk".to_string())
+ .spawn(move || {
+ let mut worker = Worker {
+ queues,
+ mem,
+ disk_image,
+ disk_size,
+ read_only,
+ interrupt_status: status,
+ interrupt_evt,
+ interrupt_resample_evt,
+ };
+ worker.run(queue_evts.remove(0), kill_evt, control_socket);
+ });
+
+ if let Err(e) = worker_result {
+ error!("failed to spawn virtio_blk worker: {}", e);
+ return;
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::fs::{File, OpenOptions};
+ use std::path::PathBuf;
+ use sys_util::TempDir;
+
+ use super::*;
+
+ #[test]
+ fn read_size() {
+ let tempdir = TempDir::new("/tmp/block_read_test").unwrap();
+ let mut path = PathBuf::from(tempdir.as_path().unwrap());
+ path.push("disk_image");
+ let f = File::create(&path).unwrap();
+ f.set_len(0x1000).unwrap();
+
+ let b = Block::new(f, true, None).unwrap();
+ let mut num_sectors = [0u8; 4];
+ b.read_config(0, &mut num_sectors);
+ // size is 0x1000, so num_sectors is 8 (4096/512).
+ assert_eq!([0x08, 0x00, 0x00, 0x00], num_sectors);
+ let mut msw_sectors = [0u8; 4];
+ b.read_config(4, &mut msw_sectors);
+ // size is 0x1000, so msw_sectors is 0.
+ assert_eq!([0x00, 0x00, 0x00, 0x00], msw_sectors);
+ }
+
+ #[test]
+ fn read_features() {
+ let tempdir = TempDir::new("/tmp/block_read_test").unwrap();
+ let mut path = PathBuf::from(tempdir.as_path().unwrap());
+ path.push("disk_image");
+
+ // read-write block device
+ {
+ let f = File::create(&path).unwrap();
+ let b = Block::new(f, false, None).unwrap();
+ // writable device should set VIRTIO_BLK_F_FLUSH + VIRTIO_BLK_F_DISCARD
+ // + VIRTIO_BLK_F_WRITE_ZEROES + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE
+ assert_eq!(0x100006240, b.features());
+ }
+
+ // read-only block device
+ {
+ let f = File::create(&path).unwrap();
+ let b = Block::new(f, true, None).unwrap();
+ // read-only device should set VIRTIO_BLK_F_FLUSH and VIRTIO_BLK_F_RO
+ // + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE
+ assert_eq!(0x100000260, b.features());
+ }
+ }
+
+ #[test]
+ fn read_last_sector() {
+ let tempdir = TempDir::new("/tmp/block_read_test").unwrap();
+ let mut path = PathBuf::from(tempdir.as_path().unwrap());
+ path.push("disk_image");
+ let mut f = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .open(&path)
+ .unwrap();
+ let disk_size = 0x1000;
+ f.set_len(disk_size).unwrap();
+
+ let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
+ .expect("Creating guest memory failed.");
+
+ let req = Request {
+ request_type: RequestType::In,
+ sector: 7, // Disk is 8 sectors long, so this is the last valid sector.
+ data_addr: GuestAddress(0x1000),
+ data_len: 512, // Read 1 sector of data.
+ status_addr: GuestAddress(0),
+ discard_write_zeroes_seg: None,
+ };
+
+ let mut flush_timer = TimerFd::new().expect("failed to create flush_timer");
+ let mut flush_timer_armed = false;
+
+ assert_eq!(
+ 512,
+ req.execute(
+ false,
+ &mut f,
+ disk_size,
+ &mut flush_timer,
+ &mut flush_timer_armed,
+ &mem
+ )
+ .expect("execute failed"),
+ );
+ }
+
+ #[test]
+ fn read_beyond_last_sector() {
+ let tempdir = TempDir::new("/tmp/block_read_test").unwrap();
+ let mut path = PathBuf::from(tempdir.as_path().unwrap());
+ path.push("disk_image");
+ let mut f = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .open(&path)
+ .unwrap();
+ let disk_size = 0x1000;
+ f.set_len(disk_size).unwrap();
+
+ let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
+ .expect("Creating guest memory failed.");
+
+ let req = Request {
+ request_type: RequestType::In,
+ sector: 7, // Disk is 8 sectors long, so this is the last valid sector.
+ data_addr: GuestAddress(0x1000),
+ data_len: 512 * 2, // Read 2 sectors of data (overlap the end of the disk).
+ status_addr: GuestAddress(0),
+ discard_write_zeroes_seg: None,
+ };
+
+ let mut flush_timer = TimerFd::new().expect("failed to create flush_timer");
+ let mut flush_timer_armed = false;
+
+ req.execute(
+ false,
+ &mut f,
+ disk_size,
+ &mut flush_timer,
+ &mut flush_timer_armed,
+ &mem,
+ )
+ .expect_err("execute was supposed to fail");
+ }
+}
diff --git a/devices/src/virtio/gpu/backend.rs b/devices/src/virtio/gpu/backend.rs
new file mode 100644
index 0000000..e441541
--- /dev/null
+++ b/devices/src/virtio/gpu/backend.rs
@@ -0,0 +1,1020 @@
+// Copyright 2018 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.
+
+//! Implementation for the transport agnostic virtio-gpu protocol, including display and rendering.
+
+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::usize;
+
+use data_model::*;
+
+use msg_socket::{MsgReceiver, MsgSender};
+use sys_util::{error, warn, GuestAddress, GuestMemory};
+
+use gpu_buffer::{Buffer, Device, Flags, Format};
+use gpu_display::*;
+use gpu_renderer::{
+ format_fourcc as renderer_fourcc, Box3, Context as RendererContext, Image as RendererImage,
+ Renderer, Resource as GpuRendererResource, ResourceCreateArgs, VIRGL_RES_BIND_SCANOUT,
+};
+
+use super::protocol::{
+ GpuResponse, GpuResponsePlaneInfo, VIRTIO_GPU_CAPSET_VIRGL, VIRTIO_GPU_CAPSET_VIRGL2,
+};
+use crate::virtio::resource_bridge::*;
+use vm_control::VmMemoryControlRequestSocket;
+
+const DEFAULT_WIDTH: u32 = 1280;
+const DEFAULT_HEIGHT: u32 = 1024;
+
+/// Trait for virtio-gpu resources allocated by the guest.
+trait VirglResource {
+ /// The width in pixels of this resource.
+ fn width(&self) -> u32;
+
+ /// The height in pixels of this resource.
+ fn height(&self) -> u32;
+
+ /// Associates the backing for this resource with the given guest memory.
+ fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>);
+
+ /// Removes associated memory for this resource previously made with `attach_guest_backing`.
+ fn detach_guest_backing(&mut self);
+
+ /// Returns the GPU `Buffer` for this resource, if it has one.
+ fn buffer(&self) -> Option<&Buffer> {
+ None
+ }
+
+ /// Returns the renderer's concrete `GpuRendererResource` for this resource, if it has one.
+ fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> {
+ None
+ }
+
+ /// Returns an import ID for this resource onto the given display, if successful.
+ fn import_to_display(&mut self, _display: &Rc<RefCell<GpuDisplay>>) -> Option<u32> {
+ None
+ }
+
+ /// Copies the given rectangle of pixels from guest memory, using the backing specified from a
+ /// call to `attach_guest_backing`.
+ fn write_from_guest_memory(
+ &mut self,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ src_offset: u64,
+ mem: &GuestMemory,
+ );
+
+ /// Reads from the given rectangle of pixels in the resource to the `dst` slice of memory.
+ fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice);
+}
+
+impl VirglResource for GpuRendererResource {
+ fn width(&self) -> u32 {
+ match self.get_info() {
+ Ok(info) => info.width,
+ Err(_) => 0,
+ }
+ }
+ fn height(&self) -> u32 {
+ match self.get_info() {
+ Ok(info) => info.height,
+ Err(_) => 0,
+ }
+ }
+
+ fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>) {
+ if let Err(e) = self.attach_backing(&vecs[..], mem) {
+ error!("failed to attach backing to resource: {}", e);
+ }
+ }
+
+ fn detach_guest_backing(&mut self) {
+ self.detach_backing();
+ }
+
+ fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> {
+ Some(self)
+ }
+
+ fn write_from_guest_memory(
+ &mut self,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ src_offset: u64,
+ _mem: &GuestMemory,
+ ) {
+ let res = self.transfer_write(
+ None,
+ 0,
+ 0,
+ 0,
+ Box3 {
+ x,
+ y,
+ z: 0,
+ w: width,
+ h: height,
+ d: 0,
+ },
+ src_offset,
+ );
+ if let Err(e) = res {
+ error!(
+ "failed to write to resource (x={} y={} w={} h={}, src_offset={}): {}",
+ x, y, width, height, src_offset, e
+ );
+ }
+ }
+
+ fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice) {
+ let res = GpuRendererResource::read_to_volatile(
+ self,
+ None,
+ 0,
+ 0,
+ 0,
+ Box3 {
+ x,
+ y,
+ z: 0,
+ w: width,
+ h: height,
+ d: 0,
+ },
+ 0,
+ dst,
+ );
+ if let Err(e) = res {
+ error!("failed to read from resource: {}", e);
+ }
+ }
+}
+
+/// A buffer backed with a `gpu_buffer::Buffer`.
+struct BackedBuffer {
+ display_import: Option<(Rc<RefCell<GpuDisplay>>, u32)>,
+ backing: Vec<(GuestAddress, usize)>,
+ buffer: Buffer,
+ gpu_renderer_resource: Option<GpuRendererResource>,
+ _image: Option<RendererImage>,
+}
+
+impl BackedBuffer {
+ fn new_renderer_registered(
+ buffer: Buffer,
+ gpu_renderer_resource: GpuRendererResource,
+ image: RendererImage,
+ ) -> BackedBuffer {
+ BackedBuffer {
+ display_import: None,
+ backing: Vec::new(),
+ buffer,
+ gpu_renderer_resource: Some(gpu_renderer_resource),
+ _image: Some(image),
+ }
+ }
+}
+
+impl From<Buffer> for BackedBuffer {
+ fn from(buffer: Buffer) -> BackedBuffer {
+ BackedBuffer {
+ display_import: None,
+ backing: Vec::new(),
+ buffer,
+ gpu_renderer_resource: None,
+ _image: None,
+ }
+ }
+}
+
+impl VirglResource for BackedBuffer {
+ fn width(&self) -> u32 {
+ self.buffer.width()
+ }
+
+ fn height(&self) -> u32 {
+ self.buffer.height()
+ }
+
+ fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>) {
+ self.backing = vecs.clone();
+ if let Some(resource) = &mut self.gpu_renderer_resource {
+ if let Err(e) = resource.attach_backing(&vecs[..], mem) {
+ error!("failed to attach backing to BackBuffer resource: {}", e);
+ }
+ }
+ }
+
+ fn detach_guest_backing(&mut self) {
+ if let Some(resource) = &mut self.gpu_renderer_resource {
+ resource.detach_backing();
+ }
+ self.backing.clear();
+ }
+
+ fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> {
+ self.gpu_renderer_resource.as_mut()
+ }
+
+ fn buffer(&self) -> Option<&Buffer> {
+ Some(&self.buffer)
+ }
+
+ fn import_to_display(&mut self, display: &Rc<RefCell<GpuDisplay>>) -> Option<u32> {
+ if let Some((self_display, import)) = &self.display_import {
+ if Rc::ptr_eq(self_display, display) {
+ return Some(*import);
+ }
+ }
+ let dmabuf = match self.buffer.export_plane_fd(0) {
+ Ok(dmabuf) => dmabuf,
+ Err(e) => {
+ error!("failed to get dmabuf for scanout: {}", e);
+ return None;
+ }
+ };
+
+ match display.borrow_mut().import_dmabuf(
+ dmabuf.as_raw_fd(),
+ 0, /* offset */
+ self.buffer.stride(),
+ self.buffer.format_modifier(),
+ self.buffer.width(),
+ self.buffer.height(),
+ self.buffer.format().into(),
+ ) {
+ Ok(import_id) => {
+ self.display_import = Some((display.clone(), import_id));
+ Some(import_id)
+ }
+ Err(e) => {
+ error!("failed to import dmabuf for display: {}", e);
+ None
+ }
+ }
+ }
+
+ fn write_from_guest_memory(
+ &mut self,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ src_offset: u64,
+ mem: &GuestMemory,
+ ) {
+ if src_offset >= usize::MAX as u64 {
+ error!(
+ "failed to write to resource with given offset: {}",
+ src_offset
+ );
+ return;
+ }
+ let res = self.buffer.write_from_sg(
+ x,
+ y,
+ width,
+ height,
+ 0, // plane
+ src_offset as usize,
+ self.backing
+ .iter()
+ .map(|&(addr, len)| mem.get_slice(addr.offset(), len as u64).unwrap_or_default()),
+ );
+ if let Err(e) = res {
+ error!("failed to write to resource from guest memory: {}", e)
+ }
+ }
+
+ fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice) {
+ if let Err(e) = self.buffer.read_to_volatile(x, y, width, height, 0, dst) {
+ error!("failed to copy resource: {}", e);
+ }
+ }
+}
+
+/// The virtio-gpu backend state tracker.
+///
+/// Commands from the virtio-gpu protocol can be submitted here using the methods, and they will be
+/// realized on the hardware. Most methods return a `GpuResponse` that indicate the success,
+/// failure, or requested data for the given command.
+pub struct Backend {
+ display: Rc<RefCell<GpuDisplay>>,
+ device: Device,
+ renderer: Renderer,
+ resources: Map<u32, Box<dyn VirglResource>>,
+ contexts: Map<u32, RendererContext>,
+ #[allow(dead_code)]
+ gpu_device_socket: VmMemoryControlRequestSocket,
+ scanout_surface: Option<u32>,
+ cursor_surface: Option<u32>,
+ scanout_resource: u32,
+ cursor_resource: u32,
+}
+
+impl Backend {
+ /// Creates a new backend for virtio-gpu that realizes all commands using the given `device` for
+ /// allocating buffers, `display` for showing the results, and `renderer` for submitting
+ /// rendering commands.
+ pub fn new(
+ device: Device,
+ display: GpuDisplay,
+ renderer: Renderer,
+ gpu_device_socket: VmMemoryControlRequestSocket,
+ ) -> Backend {
+ Backend {
+ display: Rc::new(RefCell::new(display)),
+ device,
+ renderer,
+ gpu_device_socket,
+ resources: Default::default(),
+ contexts: Default::default(),
+ scanout_surface: None,
+ cursor_surface: None,
+ scanout_resource: 0,
+ cursor_resource: 0,
+ }
+ }
+
+ /// Gets a reference to the display passed into `new`.
+ pub fn display(&self) -> &Rc<RefCell<GpuDisplay>> {
+ &self.display
+ }
+
+ /// Processes the internal `display` events and returns `true` if the main display was closed.
+ pub fn process_display(&mut self) -> bool {
+ let mut display = self.display.borrow_mut();
+ display.dispatch_events();
+ self.scanout_surface
+ .map(|s| display.close_requested(s))
+ .unwrap_or(false)
+ }
+
+ pub fn process_resource_bridge(&self, resource_bridge: &ResourceResponseSocket) {
+ let request = match resource_bridge.recv() {
+ Ok(msg) => msg,
+ Err(e) => {
+ error!("error receiving resource bridge request: {}", e);
+ return;
+ }
+ };
+
+ let response = match request {
+ ResourceRequest::GetResource { id } => self
+ .resources
+ .get(&id)
+ .and_then(|resource| resource.buffer())
+ .and_then(|buffer| buffer.export_plane_fd(0).ok())
+ .map(ResourceResponse::Resource)
+ .unwrap_or(ResourceResponse::Invalid),
+ };
+
+ if let Err(e) = resource_bridge.send(&response) {
+ error!("error sending resource bridge request: {}", e);
+ }
+ }
+
+ /// Gets the list of supported display resolutions as a slice of `(width, height)` tuples.
+ pub fn display_info(&self) -> &[(u32, u32)] {
+ &[(DEFAULT_WIDTH, DEFAULT_HEIGHT)]
+ }
+
+ /// Creates a 2D resource with the given properties and associated it with the given id.
+ pub fn create_resource_2d(
+ &mut self,
+ id: u32,
+ width: u32,
+ height: u32,
+ fourcc: u32,
+ ) -> GpuResponse {
+ if id == 0 {
+ return GpuResponse::ErrInvalidResourceId;
+ }
+ match self.resources.entry(id) {
+ Entry::Vacant(slot) => {
+ let res = self.device.create_buffer(
+ width,
+ height,
+ Format::from(fourcc),
+ Flags::empty().use_scanout(true).use_linear(true),
+ );
+ match res {
+ Ok(res) => {
+ slot.insert(Box::from(BackedBuffer::from(res)));
+ GpuResponse::OkNoData
+ }
+ Err(_) => {
+ error!("failed to create renderer resource {}", fourcc);
+ GpuResponse::ErrUnspec
+ }
+ }
+ }
+ Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ /// Removes the guest's reference count for the given resource id.
+ pub fn unref_resource(&mut self, id: u32) -> GpuResponse {
+ match self.resources.remove(&id) {
+ Some(_) => GpuResponse::OkNoData,
+ None => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ /// Sets the given resource id as the source of scanout to the display.
+ pub fn set_scanout(&mut self, id: u32) -> GpuResponse {
+ let mut display = self.display.borrow_mut();
+ if id == 0 {
+ if let Some(surface) = self.scanout_surface.take() {
+ display.release_surface(surface);
+ }
+ self.scanout_resource = 0;
+ if let Some(surface) = self.cursor_surface.take() {
+ display.release_surface(surface);
+ }
+ self.cursor_resource = 0;
+ GpuResponse::OkNoData
+ } else if self.resources.get_mut(&id).is_some() {
+ self.scanout_resource = id;
+
+ if self.scanout_surface.is_none() {
+ match display.create_surface(None, DEFAULT_WIDTH, DEFAULT_HEIGHT) {
+ Ok(surface) => self.scanout_surface = Some(surface),
+ Err(e) => error!("failed to create display surface: {}", e),
+ }
+ }
+ GpuResponse::OkNoData
+ } else {
+ GpuResponse::ErrInvalidResourceId
+ }
+ }
+
+ fn flush_resource_to_surface(
+ &mut self,
+ resource_id: u32,
+ surface_id: u32,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ ) -> GpuResponse {
+ let resource = match self.resources.get_mut(&resource_id) {
+ Some(r) => r,
+ None => return GpuResponse::ErrInvalidResourceId,
+ };
+
+ if let Some(import_id) = resource.import_to_display(&self.display) {
+ self.display.borrow_mut().flip_to(surface_id, import_id);
+ return GpuResponse::OkNoData;
+ }
+
+ // Import failed, fall back to a copy.
+ let display = self.display.borrow_mut();
+ // Prevent overwriting a buffer that is currently being used by the compositor.
+ if display.next_buffer_in_use(surface_id) {
+ return GpuResponse::OkNoData;
+ }
+ let fb = match display.framebuffer_memory(surface_id) {
+ Some(fb) => fb,
+ None => {
+ error!("failed to access framebuffer for surface {}", surface_id);
+ return GpuResponse::ErrUnspec;
+ }
+ };
+
+ resource.read_to_volatile(x, y, width, height, fb);
+ display.flip(surface_id);
+
+ GpuResponse::OkNoData
+ }
+
+ /// Flushes the given rectangle of pixels of the given resource to the display.
+ pub fn flush_resource(
+ &mut self,
+ id: u32,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ ) -> GpuResponse {
+ if id == 0 {
+ return GpuResponse::OkNoData;
+ }
+
+ let mut response = GpuResponse::OkNoData;
+
+ if id == self.scanout_resource {
+ if let Some(surface_id) = self.scanout_surface {
+ response = self.flush_resource_to_surface(id, surface_id, x, y, width, height);
+ }
+ }
+
+ if response != GpuResponse::OkNoData {
+ return response;
+ }
+
+ if id == self.cursor_resource {
+ if let Some(surface_id) = self.cursor_surface {
+ response = self.flush_resource_to_surface(id, surface_id, x, y, width, height);
+ }
+ }
+
+ response
+ }
+
+ /// Copes the given rectangle of pixels of the given resource's backing memory to the host side
+ /// resource.
+ pub fn transfer_to_resource_2d(
+ &mut self,
+ id: u32,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ src_offset: u64,
+ mem: &GuestMemory,
+ ) -> GpuResponse {
+ match self.resources.get_mut(&id) {
+ Some(res) => {
+ res.write_from_guest_memory(x, y, width, height, src_offset, mem);
+ GpuResponse::OkNoData
+ }
+ None => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ /// Attaches backing memory to the given resource, represented by a `Vec` of `(address, size)`
+ /// tuples in the guest's physical address space.
+ pub fn attach_backing(
+ &mut self,
+ id: u32,
+ mem: &GuestMemory,
+ vecs: Vec<(GuestAddress, usize)>,
+ ) -> GpuResponse {
+ match self.resources.get_mut(&id) {
+ Some(resource) => {
+ resource.attach_guest_backing(mem, vecs);
+ GpuResponse::OkNoData
+ }
+ None => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ /// Detaches any backing memory from the given resource, if there is any.
+ pub fn detach_backing(&mut self, id: u32) -> GpuResponse {
+ match self.resources.get_mut(&id) {
+ Some(resource) => {
+ resource.detach_guest_backing();
+ GpuResponse::OkNoData
+ }
+ None => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ /// Updates the cursor's memory to the given id, and sets its position to the given coordinates.
+ pub fn update_cursor(&mut self, id: u32, x: u32, y: u32) -> GpuResponse {
+ if id == 0 {
+ if let Some(surface) = self.cursor_surface.take() {
+ self.display.borrow_mut().release_surface(surface);
+ }
+ self.cursor_resource = 0;
+ GpuResponse::OkNoData
+ } else if let Some(resource) = self.resources.get_mut(&id) {
+ self.cursor_resource = id;
+ if self.cursor_surface.is_none() {
+ match self.display.borrow_mut().create_surface(
+ self.scanout_surface,
+ resource.width(),
+ resource.height(),
+ ) {
+ Ok(surface) => self.cursor_surface = Some(surface),
+ Err(e) => {
+ error!("failed to create cursor surface: {}", e);
+ return GpuResponse::ErrUnspec;
+ }
+ }
+ }
+
+ let cursor_surface = self.cursor_surface.unwrap();
+ self.display.borrow_mut().set_position(cursor_surface, x, y);
+
+ // Gets the resource's pixels into the display by importing the buffer.
+ if let Some(import_id) = resource.import_to_display(&self.display) {
+ self.display.borrow_mut().flip_to(cursor_surface, import_id);
+ return GpuResponse::OkNoData;
+ }
+
+ // Importing failed, so try copying the pixels into the surface's slower shared memory
+ // framebuffer.
+ if let Some(buffer) = resource.buffer() {
+ if let Some(fb) = self.display.borrow_mut().framebuffer_memory(cursor_surface) {
+ if let Err(e) =
+ buffer.read_to_volatile(0, 0, buffer.width(), buffer.height(), 0, fb)
+ {
+ error!("failed to copy resource to cursor: {}", e);
+ return GpuResponse::ErrInvalidParameter;
+ }
+ }
+ self.display.borrow_mut().flip(cursor_surface);
+ }
+ GpuResponse::OkNoData
+ } else {
+ GpuResponse::ErrInvalidResourceId
+ }
+ }
+
+ /// Moves the cursor's position to the given coordinates.
+ pub fn move_cursor(&mut self, x: u32, y: u32) -> GpuResponse {
+ if let Some(cursor_surface) = self.cursor_surface {
+ if let Some(scanout_surface) = self.scanout_surface {
+ let display = self.display.borrow_mut();
+ display.set_position(cursor_surface, x, y);
+ display.commit(scanout_surface);
+ }
+ }
+ GpuResponse::OkNoData
+ }
+
+ /// Gets the renderer's capset information associated with `index`.
+ pub fn get_capset_info(&self, index: u32) -> GpuResponse {
+ let id = match index {
+ 0 => VIRTIO_GPU_CAPSET_VIRGL,
+ 1 => VIRTIO_GPU_CAPSET_VIRGL2,
+ _ => return GpuResponse::ErrInvalidParameter,
+ };
+ let (version, size) = self.renderer.get_cap_set_info(id);
+ GpuResponse::OkCapsetInfo { id, version, size }
+ }
+
+ /// Gets the capset of `version` associated with `id`.
+ pub fn get_capset(&self, id: u32, version: u32) -> GpuResponse {
+ GpuResponse::OkCapset(self.renderer.get_cap_set(id, version))
+ }
+
+ /// Creates a fresh renderer context with the given `id`.
+ pub fn create_renderer_context(&mut self, id: u32) -> GpuResponse {
+ if id == 0 {
+ return GpuResponse::ErrInvalidContextId;
+ }
+ match self.contexts.entry(id) {
+ Entry::Occupied(_) => GpuResponse::ErrInvalidContextId,
+ Entry::Vacant(slot) => match self.renderer.create_context(id) {
+ Ok(ctx) => {
+ slot.insert(ctx);
+ GpuResponse::OkNoData
+ }
+ Err(e) => {
+ error!("failed to create renderer ctx: {}", e);
+ GpuResponse::ErrUnspec
+ }
+ },
+ }
+ }
+
+ /// Destorys the renderer context associated with `id`.
+ pub fn destroy_renderer_context(&mut self, id: u32) -> GpuResponse {
+ match self.contexts.remove(&id) {
+ Some(_) => GpuResponse::OkNoData,
+ None => GpuResponse::ErrInvalidContextId,
+ }
+ }
+
+ /// Attaches the indicated resource to the given context.
+ pub fn context_attach_resource(&mut self, ctx_id: u32, res_id: u32) -> GpuResponse {
+ match (
+ self.contexts.get_mut(&ctx_id),
+ self.resources
+ .get_mut(&res_id)
+ .and_then(|res| res.gpu_renderer_resource()),
+ ) {
+ (Some(ctx), Some(res)) => {
+ ctx.attach(res);
+ GpuResponse::OkNoData
+ }
+ (None, _) => GpuResponse::ErrInvalidContextId,
+ (_, None) => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ /// detaches the indicated resource to the given context.
+ pub fn context_detach_resource(&mut self, ctx_id: u32, res_id: u32) -> GpuResponse {
+ match (
+ self.contexts.get_mut(&ctx_id),
+ self.resources
+ .get_mut(&res_id)
+ .and_then(|res| res.gpu_renderer_resource()),
+ ) {
+ (Some(ctx), Some(res)) => {
+ ctx.detach(res);
+ GpuResponse::OkNoData
+ }
+ (None, _) => GpuResponse::ErrInvalidContextId,
+ (_, None) => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ pub fn allocate_using_minigbm(args: ResourceCreateArgs) -> bool {
+ args.bind & VIRGL_RES_BIND_SCANOUT != 0
+ && args.depth == 1
+ && args.array_size == 1
+ && args.last_level == 0
+ && args.nr_samples == 0
+ }
+
+ /// Creates a 3D resource with the given properties and associated it with the given id.
+ pub fn resource_create_3d(
+ &mut self,
+ id: u32,
+ target: u32,
+ format: u32,
+ bind: u32,
+ width: u32,
+ height: u32,
+ depth: u32,
+ array_size: u32,
+ last_level: u32,
+ nr_samples: u32,
+ flags: u32,
+ ) -> GpuResponse {
+ if id == 0 {
+ return GpuResponse::ErrInvalidResourceId;
+ }
+
+ let create_args = ResourceCreateArgs {
+ handle: id,
+ target,
+ format,
+ bind,
+ width,
+ height,
+ depth,
+ array_size,
+ last_level,
+ nr_samples,
+ flags,
+ };
+
+ match self.resources.entry(id) {
+ Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
+ Entry::Vacant(slot) => {
+ if Backend::allocate_using_minigbm(create_args) {
+ match renderer_fourcc(create_args.format) {
+ Some(fourcc) => {
+ let buffer = match self.device.create_buffer(
+ width,
+ height,
+ Format::from(fourcc),
+ Flags::empty().use_scanout(true).use_rendering(true),
+ ) {
+ Ok(buffer) => buffer,
+ Err(_) => {
+ // Attempt to allocate the buffer without scanout flag.
+ match self.device.create_buffer(
+ width,
+ height,
+ Format::from(fourcc),
+ Flags::empty().use_rendering(true),
+ ) {
+ Ok(buffer) => buffer,
+ Err(e) => {
+ error!(
+ "failed to create buffer for 3d resource {}: {}",
+ format, e
+ );
+ return GpuResponse::ErrUnspec;
+ }
+ }
+ }
+ };
+
+ let dma_buf_fd = match buffer.export_plane_fd(0) {
+ Ok(dma_buf_fd) => dma_buf_fd,
+ Err(e) => {
+ error!("failed to export plane fd: {}", e);
+ return GpuResponse::ErrUnspec;
+ }
+ };
+
+ let image = match self.renderer.image_from_dmabuf(
+ fourcc,
+ width,
+ height,
+ dma_buf_fd.as_raw_fd(),
+ buffer.plane_offset(0),
+ buffer.plane_stride(0),
+ ) {
+ Ok(image) => image,
+ Err(e) => {
+ error!("failed to create egl image: {}", e);
+ return GpuResponse::ErrUnspec;
+ }
+ };
+
+ let res = self.renderer.import_resource(create_args, &image);
+ match res {
+ Ok(res) => {
+ let format_modifier = buffer.format_modifier();
+ let mut plane_info = Vec::with_capacity(buffer.num_planes());
+ for plane_index in 0..buffer.num_planes() {
+ plane_info.push(GpuResponsePlaneInfo {
+ stride: buffer.plane_stride(plane_index),
+ offset: buffer.plane_offset(plane_index),
+ });
+ }
+ let backed =
+ BackedBuffer::new_renderer_registered(buffer, res, image);
+ slot.insert(Box::new(backed));
+ GpuResponse::OkResourcePlaneInfo {
+ format_modifier,
+ plane_info,
+ }
+ }
+ Err(e) => {
+ error!("failed to import renderer resource: {}", e);
+ GpuResponse::ErrUnspec
+ }
+ }
+ }
+ None => {
+ warn!(
+ "failed to get fourcc for minigbm 3d resource {}, falling back",
+ format
+ );
+ let res = self.renderer.create_resource(create_args);
+ match res {
+ Ok(res) => {
+ slot.insert(Box::new(res));
+ GpuResponse::OkNoData
+ }
+ Err(e) => {
+ error!("failed to create renderer resource: {}", e);
+ GpuResponse::ErrUnspec
+ }
+ }
+ }
+ }
+ } else {
+ let res = self.renderer.create_resource(create_args);
+ match res {
+ Ok(res) => {
+ slot.insert(Box::new(res));
+ GpuResponse::OkNoData
+ }
+ Err(e) => {
+ error!("failed to create renderer resource: {}", e);
+ GpuResponse::ErrUnspec
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// Copes the given 3D rectangle of pixels of the given resource's backing memory to the host
+ /// side resource.
+ pub fn transfer_to_resource_3d(
+ &mut self,
+ ctx_id: u32,
+ res_id: u32,
+ x: u32,
+ y: u32,
+ z: u32,
+ width: u32,
+ height: u32,
+ depth: u32,
+ level: u32,
+ stride: u32,
+ layer_stride: u32,
+ offset: u64,
+ ) -> GpuResponse {
+ let ctx = match ctx_id {
+ 0 => None,
+ id => match self.contexts.get(&id) {
+ None => return GpuResponse::ErrInvalidContextId,
+ ctx => ctx,
+ },
+ };
+ match self.resources.get_mut(&res_id) {
+ Some(res) => match res.gpu_renderer_resource() {
+ Some(res) => {
+ let transfer_box = Box3 {
+ x,
+ y,
+ z,
+ w: width,
+ h: height,
+ d: depth,
+ };
+ let res =
+ res.transfer_write(ctx, level, stride, layer_stride, transfer_box, offset);
+ match res {
+ Ok(_) => GpuResponse::OkNoData,
+ Err(e) => {
+ error!("failed to transfer to host: {}", e);
+ GpuResponse::ErrUnspec
+ }
+ }
+ }
+ None => GpuResponse::ErrInvalidResourceId,
+ },
+ None => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ /// Copes the given rectangle of pixels from the resource to the given resource's backing
+ /// memory.
+ pub fn transfer_from_resource_3d(
+ &mut self,
+ ctx_id: u32,
+ res_id: u32,
+ x: u32,
+ y: u32,
+ z: u32,
+ width: u32,
+ height: u32,
+ depth: u32,
+ level: u32,
+ stride: u32,
+ layer_stride: u32,
+ offset: u64,
+ ) -> GpuResponse {
+ let ctx = match ctx_id {
+ 0 => None,
+ id => match self.contexts.get(&id) {
+ None => return GpuResponse::ErrInvalidContextId,
+ ctx => ctx,
+ },
+ };
+ match self.resources.get_mut(&res_id) {
+ Some(res) => match res.gpu_renderer_resource() {
+ Some(res) => {
+ let transfer_box = Box3 {
+ x,
+ y,
+ z,
+ w: width,
+ h: height,
+ d: depth,
+ };
+ let res =
+ res.transfer_read(ctx, level, stride, layer_stride, transfer_box, offset);
+ match res {
+ Ok(_) => GpuResponse::OkNoData,
+ Err(e) => {
+ error!("failed to transfer from host: {}", e);
+ GpuResponse::ErrUnspec
+ }
+ }
+ }
+ None => GpuResponse::ErrInvalidResourceId,
+ },
+ None => GpuResponse::ErrInvalidResourceId,
+ }
+ }
+
+ /// Submits a command buffer to the given rendering context.
+ pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> GpuResponse {
+ match self.contexts.get_mut(&ctx_id) {
+ Some(ctx) => match ctx.submit(&mut commands[..]) {
+ Ok(_) => GpuResponse::OkNoData,
+ Err(e) => {
+ error!("failed to submit command buffer: {}", e);
+ GpuResponse::ErrUnspec
+ }
+ },
+ None => GpuResponse::ErrInvalidContextId,
+ }
+ }
+
+ pub fn create_fence(&mut self, ctx_id: u32, fence_id: u32) -> GpuResponse {
+ // There is a mismatch of ordering that is intentional.
+ // This create_fence matches the other functions in Backend, yet
+ // the renderer matches the virgl interface.
+ match self.renderer.create_fence(fence_id, ctx_id) {
+ Ok(_) => GpuResponse::OkNoData,
+ Err(e) => {
+ error!("failed to create fence: {}", e);
+ GpuResponse::ErrUnspec
+ }
+ }
+ }
+
+ pub fn fence_poll(&mut self) -> u32 {
+ self.renderer.poll()
+ }
+
+ pub fn force_ctx_0(&mut self) {
+ self.renderer.force_ctx_0();
+ }
+}
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
new file mode 100644
index 0000000..cc42ace
--- /dev/null
+++ b/devices/src/virtio/gpu/mod.rs
@@ -0,0 +1,833 @@
+// Copyright 2018 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.
+
+mod backend;
+mod protocol;
+
+use std::cell::RefCell;
+use std::collections::VecDeque;
+use std::i64;
+use std::mem::size_of;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::{Path, PathBuf};
+use std::rc::Rc;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+use std::time::Duration;
+
+use data_model::*;
+
+use sys_util::{
+ debug, error, warn, Error, EventFd, GuestAddress, GuestMemory, PollContext, PollToken,
+};
+
+use gpu_buffer::Device;
+use gpu_display::*;
+use gpu_renderer::{format_fourcc, Renderer};
+
+use super::{
+ resource_bridge::*, AvailIter, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_GPU,
+ VIRTIO_F_VERSION_1,
+};
+
+use self::backend::Backend;
+use self::protocol::*;
+use crate::pci::{PciBarConfiguration, PciBarPrefetchable, PciBarRegionType};
+
+use vm_control::VmMemoryControlRequestSocket;
+
+// First queue is for virtio gpu commands. Second queue is for cursor commands, which we expect
+// there to be fewer of.
+const QUEUE_SIZES: &[u16] = &[256, 16];
+const FENCE_POLL_MS: u64 = 1;
+
+struct QueueDescriptor {
+ index: u16,
+ addr: GuestAddress,
+ len: u32,
+ data: Option<(GuestAddress, u32)>,
+ ret: Option<(GuestAddress, u32)>,
+}
+
+struct ReturnDescriptor {
+ index: u16,
+ len: u32,
+}
+
+struct FenceDescriptor {
+ fence_id: u32,
+ len: u32,
+ desc: QueueDescriptor,
+}
+
+struct Frontend {
+ ctrl_descriptors: VecDeque<QueueDescriptor>,
+ cursor_descriptors: VecDeque<QueueDescriptor>,
+ return_ctrl_descriptors: VecDeque<ReturnDescriptor>,
+ return_cursor_descriptors: VecDeque<ReturnDescriptor>,
+ fence_descriptors: Vec<FenceDescriptor>,
+ backend: Backend,
+}
+
+impl Frontend {
+ fn new(backend: Backend) -> Frontend {
+ Frontend {
+ ctrl_descriptors: Default::default(),
+ cursor_descriptors: Default::default(),
+ return_ctrl_descriptors: Default::default(),
+ return_cursor_descriptors: Default::default(),
+ fence_descriptors: Default::default(),
+ backend,
+ }
+ }
+
+ fn display(&self) -> &Rc<RefCell<GpuDisplay>> {
+ self.backend.display()
+ }
+
+ fn process_display(&mut self) -> bool {
+ self.backend.process_display()
+ }
+
+ fn process_resource_bridge(&self, resource_bridge: &ResourceResponseSocket) {
+ self.backend.process_resource_bridge(resource_bridge);
+ }
+
+ fn process_gpu_command(
+ &mut self,
+ mem: &GuestMemory,
+ cmd: GpuCommand,
+ data: Option<VolatileSlice>,
+ ) -> GpuResponse {
+ self.backend.force_ctx_0();
+
+ match cmd {
+ GpuCommand::GetDisplayInfo(_) => {
+ GpuResponse::OkDisplayInfo(self.backend.display_info().to_vec())
+ }
+ GpuCommand::ResourceCreate2d(info) => {
+ let format = info.format.to_native();
+ match format_fourcc(format) {
+ Some(fourcc) => self.backend.create_resource_2d(
+ info.resource_id.to_native(),
+ info.width.to_native(),
+ info.height.to_native(),
+ fourcc,
+ ),
+ None => {
+ warn!(
+ "failed to create resource with unrecognized pipe format {}",
+ format
+ );
+ GpuResponse::ErrInvalidParameter
+ }
+ }
+ }
+ GpuCommand::ResourceUnref(info) => {
+ self.backend.unref_resource(info.resource_id.to_native())
+ }
+ GpuCommand::SetScanout(info) => self.backend.set_scanout(info.resource_id.to_native()),
+ GpuCommand::ResourceFlush(info) => self.backend.flush_resource(
+ info.resource_id.to_native(),
+ info.r.x.to_native(),
+ info.r.y.to_native(),
+ info.r.width.to_native(),
+ info.r.height.to_native(),
+ ),
+ GpuCommand::TransferToHost2d(info) => self.backend.transfer_to_resource_2d(
+ info.resource_id.to_native(),
+ info.r.x.to_native(),
+ info.r.y.to_native(),
+ info.r.width.to_native(),
+ info.r.height.to_native(),
+ info.offset.to_native(),
+ mem,
+ ),
+ GpuCommand::ResourceAttachBacking(info) if data.is_some() => {
+ let data = data.unwrap();
+ let entry_count = info.nr_entries.to_native() as usize;
+ let mut iovecs = Vec::with_capacity(entry_count);
+ for i in 0..entry_count {
+ if let Ok(entry_ref) =
+ data.get_ref((i * size_of::<virtio_gpu_mem_entry>()) as u64)
+ {
+ let entry: virtio_gpu_mem_entry = entry_ref.load();
+ let addr = GuestAddress(entry.addr.to_native());
+ let len = entry.length.to_native() as usize;
+ iovecs.push((addr, len))
+ } else {
+ return GpuResponse::ErrUnspec;
+ }
+ }
+ self.backend
+ .attach_backing(info.resource_id.to_native(), mem, iovecs)
+ }
+ GpuCommand::ResourceDetachBacking(info) => {
+ self.backend.detach_backing(info.resource_id.to_native())
+ }
+ GpuCommand::UpdateCursor(info) => self.backend.update_cursor(
+ info.resource_id.to_native(),
+ info.pos.x.into(),
+ info.pos.y.into(),
+ ),
+ GpuCommand::MoveCursor(info) => self
+ .backend
+ .move_cursor(info.pos.x.into(), info.pos.y.into()),
+ GpuCommand::GetCapsetInfo(info) => {
+ self.backend.get_capset_info(info.capset_index.to_native())
+ }
+ GpuCommand::GetCapset(info) => self
+ .backend
+ .get_capset(info.capset_id.to_native(), info.capset_version.to_native()),
+ GpuCommand::CtxCreate(info) => self
+ .backend
+ .create_renderer_context(info.hdr.ctx_id.to_native()),
+ GpuCommand::CtxDestroy(info) => self
+ .backend
+ .destroy_renderer_context(info.hdr.ctx_id.to_native()),
+ GpuCommand::CtxAttachResource(info) => self
+ .backend
+ .context_attach_resource(info.hdr.ctx_id.to_native(), info.resource_id.to_native()),
+ GpuCommand::CtxDetachResource(info) => self
+ .backend
+ .context_detach_resource(info.hdr.ctx_id.to_native(), info.resource_id.to_native()),
+ GpuCommand::ResourceCreate3d(info) => {
+ let id = info.resource_id.to_native();
+ let target = info.target.to_native();
+ let format = info.format.to_native();
+ let bind = info.bind.to_native();
+ let width = info.width.to_native();
+ let height = info.height.to_native();
+ let depth = info.depth.to_native();
+ let array_size = info.array_size.to_native();
+ let last_level = info.last_level.to_native();
+ let nr_samples = info.nr_samples.to_native();
+ let flags = info.flags.to_native();
+ self.backend.resource_create_3d(
+ id, target, format, bind, width, height, depth, array_size, last_level,
+ nr_samples, flags,
+ )
+ }
+ GpuCommand::TransferToHost3d(info) => {
+ let ctx_id = info.hdr.ctx_id.to_native();
+ let res_id = info.resource_id.to_native();
+ let x = info.box_.x.to_native();
+ let y = info.box_.y.to_native();
+ let z = info.box_.z.to_native();
+ let width = info.box_.w.to_native();
+ let height = info.box_.h.to_native();
+ let depth = info.box_.d.to_native();
+ let level = info.level.to_native();
+ let stride = info.stride.to_native();
+ let layer_stride = info.layer_stride.to_native();
+ let offset = info.offset.to_native();
+ self.backend.transfer_to_resource_3d(
+ ctx_id,
+ res_id,
+ x,
+ y,
+ z,
+ width,
+ height,
+ depth,
+ level,
+ stride,
+ layer_stride,
+ offset,
+ )
+ }
+ GpuCommand::TransferFromHost3d(info) => {
+ let ctx_id = info.hdr.ctx_id.to_native();
+ let res_id = info.resource_id.to_native();
+ let x = info.box_.x.to_native();
+ let y = info.box_.y.to_native();
+ let z = info.box_.z.to_native();
+ let width = info.box_.w.to_native();
+ let height = info.box_.h.to_native();
+ let depth = info.box_.d.to_native();
+ let level = info.level.to_native();
+ let stride = info.stride.to_native();
+ let layer_stride = info.layer_stride.to_native();
+ let offset = info.offset.to_native();
+ self.backend.transfer_from_resource_3d(
+ ctx_id,
+ res_id,
+ x,
+ y,
+ z,
+ width,
+ height,
+ depth,
+ level,
+ stride,
+ layer_stride,
+ offset,
+ )
+ }
+ GpuCommand::CmdSubmit3d(info) => {
+ if data.is_some() {
+ let data = data.unwrap(); // guarded by this match arm
+ let cmd_size = info.size.to_native() as usize;
+ match data.get_slice(0, cmd_size as u64) {
+ Ok(cmd_slice) => {
+ let mut cmd_buf = vec![0; cmd_size];
+ cmd_slice.copy_to(&mut cmd_buf[..]);
+ self.backend
+ .submit_command(info.hdr.ctx_id.to_native(), &mut cmd_buf[..])
+ }
+ Err(_) => GpuResponse::ErrInvalidParameter,
+ }
+ } else {
+ // Silently accept empty command buffers to allow for
+ // benchmarking.
+ GpuResponse::OkNoData
+ }
+ }
+ _ => {
+ error!("unhandled command {:?}", cmd);
+ GpuResponse::ErrUnspec
+ }
+ }
+ }
+
+ fn take_descriptors(
+ mem: &GuestMemory,
+ desc_iter: AvailIter,
+ descriptors: &mut VecDeque<QueueDescriptor>,
+ return_descriptors: &mut VecDeque<ReturnDescriptor>,
+ ) {
+ for desc in desc_iter {
+ if desc.len as usize >= size_of::<virtio_gpu_ctrl_hdr>() && !desc.is_write_only() {
+ let mut q_desc = QueueDescriptor {
+ index: desc.index,
+ addr: desc.addr,
+ len: desc.len,
+ data: None,
+ ret: None,
+ };
+ if let Some(extra_desc) = desc.next_descriptor() {
+ if extra_desc.is_write_only() {
+ q_desc.ret = Some((extra_desc.addr, extra_desc.len));
+ } else {
+ q_desc.data = Some((extra_desc.addr, extra_desc.len));
+ }
+ if let Some(extra_desc) = extra_desc.next_descriptor() {
+ if extra_desc.is_write_only() && q_desc.ret.is_none() {
+ q_desc.ret = Some((extra_desc.addr, extra_desc.len));
+ }
+ }
+ }
+ descriptors.push_back(q_desc);
+ } else {
+ let likely_type = mem.read_obj_from_addr(desc.addr).unwrap_or(Le32::from(0));
+ debug!(
+ "ctrl queue bad descriptor index = {} len = {} write = {} type = {}",
+ desc.index,
+ desc.len,
+ desc.is_write_only(),
+ virtio_gpu_cmd_str(likely_type.to_native())
+ );
+ return_descriptors.push_back(ReturnDescriptor {
+ index: desc.index,
+ len: 0,
+ });
+ }
+ }
+ }
+
+ fn take_ctrl_descriptors(&mut self, mem: &GuestMemory, desc_iter: AvailIter) {
+ Frontend::take_descriptors(
+ mem,
+ desc_iter,
+ &mut self.ctrl_descriptors,
+ &mut self.return_ctrl_descriptors,
+ );
+ }
+
+ fn take_cursor_descriptors(&mut self, mem: &GuestMemory, desc_iter: AvailIter) {
+ Frontend::take_descriptors(
+ mem,
+ desc_iter,
+ &mut self.cursor_descriptors,
+ &mut self.return_cursor_descriptors,
+ );
+ }
+
+ fn process_descriptor(
+ &mut self,
+ mem: &GuestMemory,
+ desc: QueueDescriptor,
+ ) -> Option<ReturnDescriptor> {
+ let mut resp = GpuResponse::ErrUnspec;
+ let mut gpu_cmd = None;
+ let mut len = 0;
+ if let Ok(desc_mem) = mem.get_slice(desc.addr.offset(), desc.len as u64) {
+ match GpuCommand::decode(desc_mem) {
+ Ok(cmd) => {
+ match desc.data {
+ Some(data_desc) => {
+ match mem.get_slice(data_desc.0.offset(), data_desc.1 as u64) {
+ Ok(data_mem) => {
+ resp = self.process_gpu_command(mem, cmd, Some(data_mem))
+ }
+ Err(e) => debug!("ctrl queue invalid data descriptor: {}", e),
+ }
+ }
+ None => resp = self.process_gpu_command(mem, cmd, None),
+ }
+ gpu_cmd = Some(cmd);
+ }
+ Err(e) => debug!("ctrl queue decode error: {}", e),
+ }
+ }
+ if resp.is_err() {
+ debug!("{:?} -> {:?}", gpu_cmd, resp);
+ }
+ if let Some(ret_desc) = desc.ret {
+ if let Ok(ret_desc_mem) = mem.get_slice(ret_desc.0.offset(), ret_desc.1 as u64) {
+ let mut fence_id = 0;
+ let mut ctx_id = 0;
+ let mut flags = 0;
+ if let Some(cmd) = gpu_cmd {
+ let ctrl_hdr = cmd.ctrl_hdr();
+ if ctrl_hdr.flags.to_native() & VIRTIO_GPU_FLAG_FENCE != 0 {
+ fence_id = ctrl_hdr.fence_id.to_native();
+ ctx_id = ctrl_hdr.ctx_id.to_native();
+ flags = VIRTIO_GPU_FLAG_FENCE;
+
+ let fence_resp = self.backend.create_fence(ctx_id, fence_id as u32);
+ if fence_resp.is_err() {
+ warn!("create_fence {} -> {:?}", fence_id, fence_resp);
+ resp = fence_resp;
+ }
+ }
+ }
+
+ // Prepare the response now, even if it is going to wait until
+ // fence is complete.
+ match resp.encode(flags, fence_id, ctx_id, ret_desc_mem) {
+ Ok(l) => len = l,
+ Err(e) => debug!("ctrl queue response encode error: {}", e),
+ }
+
+ if flags & VIRTIO_GPU_FLAG_FENCE != 0 {
+ self.fence_descriptors.push(FenceDescriptor {
+ fence_id: fence_id as u32,
+ len,
+ desc,
+ });
+
+ return None;
+ }
+
+ // No fence, respond now.
+ }
+ }
+ Some(ReturnDescriptor {
+ index: desc.index,
+ len,
+ })
+ }
+
+ fn process_ctrl(&mut self, mem: &GuestMemory) -> Option<ReturnDescriptor> {
+ self.return_ctrl_descriptors.pop_front().or_else(|| {
+ self.ctrl_descriptors
+ .pop_front()
+ .and_then(|desc| self.process_descriptor(mem, desc))
+ })
+ }
+
+ fn process_cursor(&mut self, mem: &GuestMemory) -> Option<ReturnDescriptor> {
+ self.return_cursor_descriptors.pop_front().or_else(|| {
+ self.cursor_descriptors
+ .pop_front()
+ .and_then(|desc| self.process_descriptor(mem, desc))
+ })
+ }
+
+ fn fence_poll(&mut self) {
+ let fence_id = self.backend.fence_poll();
+ let return_descs = &mut self.return_ctrl_descriptors;
+ self.fence_descriptors.retain(|f_desc| {
+ if f_desc.fence_id > fence_id {
+ true
+ } else {
+ return_descs.push_back(ReturnDescriptor {
+ index: f_desc.desc.index,
+ len: f_desc.len,
+ });
+ false
+ }
+ })
+ }
+}
+
+struct Worker {
+ exit_evt: EventFd,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ interrupt_status: Arc<AtomicUsize>,
+ ctrl_queue: Queue,
+ ctrl_evt: EventFd,
+ cursor_queue: Queue,
+ cursor_evt: EventFd,
+ resource_bridge: Option<ResourceResponseSocket>,
+ kill_evt: EventFd,
+ state: Frontend,
+}
+
+impl Worker {
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ let _ = self.interrupt_evt.write(1);
+ }
+
+ fn run(&mut self) {
+ #[derive(PollToken)]
+ enum Token {
+ CtrlQueue,
+ CursorQueue,
+ Display,
+ ResourceBridge,
+ InterruptResample,
+ Kill,
+ }
+
+ let poll_ctx: PollContext<Token> = match PollContext::new()
+ .and_then(|pc| pc.add(&self.ctrl_evt, Token::CtrlQueue).and(Ok(pc)))
+ .and_then(|pc| pc.add(&self.cursor_evt, Token::CursorQueue).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&*self.state.display().borrow(), Token::Display)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&self.kill_evt, Token::Kill).and(Ok(pc)))
+ {
+ Ok(pc) => pc,
+ Err(e) => {
+ error!("failed creating PollContext: {}", e);
+ return;
+ }
+ };
+
+ if let Some(resource_bridge) = &self.resource_bridge {
+ if let Err(e) = poll_ctx.add(resource_bridge, Token::ResourceBridge) {
+ error!("failed to add resource bridge to PollContext: {}", e);
+ }
+ }
+
+ 'poll: 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)
+ } else {
+ Duration::new(i64::MAX as u64, 0)
+ };
+
+ let events = match poll_ctx.wait_timeout(duration) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed polling for events: {}", e);
+ break;
+ }
+ };
+ let mut signal_used = false;
+ let mut process_resource_bridge = false;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::CtrlQueue => {
+ let _ = self.ctrl_evt.read();
+ self.state
+ .take_ctrl_descriptors(&self.mem, self.ctrl_queue.iter(&self.mem));
+ }
+ Token::CursorQueue => {
+ let _ = self.cursor_evt.read();
+ self.state
+ .take_cursor_descriptors(&self.mem, self.cursor_queue.iter(&self.mem));
+ }
+ Token::Display => {
+ let close_requested = self.state.process_display();
+ if close_requested {
+ let _ = self.exit_evt.write(1);
+ }
+ }
+ Token::ResourceBridge => process_resource_bridge = true,
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_evt.write(1).unwrap();
+ }
+ }
+ Token::Kill => {
+ break 'poll;
+ }
+ }
+ }
+
+ // All cursor commands go first because they have higher priority.
+ while let Some(desc) = self.state.process_cursor(&self.mem) {
+ self.cursor_queue.add_used(&self.mem, desc.index, desc.len);
+ signal_used = true;
+ }
+
+ self.state.fence_poll();
+
+ while let Some(desc) = self.state.process_ctrl(&self.mem) {
+ self.ctrl_queue.add_used(&self.mem, desc.index, desc.len);
+ signal_used = true;
+ }
+
+ // Process the entire control queue before the resource bridge in case a resource is
+ // created or destroyed by the control queue. Processing the resource bridge first may
+ // lead to a race condition.
+ if process_resource_bridge {
+ if let Some(resource_bridge) = &self.resource_bridge {
+ self.state.process_resource_bridge(resource_bridge);
+ }
+ }
+
+ if signal_used {
+ self.signal_used_queue();
+ }
+ }
+ }
+}
+
+pub struct Gpu {
+ config_event: bool,
+ exit_evt: EventFd,
+ gpu_device_socket: Option<VmMemoryControlRequestSocket>,
+ resource_bridge: Option<ResourceResponseSocket>,
+ kill_evt: Option<EventFd>,
+ wayland_socket_path: PathBuf,
+}
+
+impl Gpu {
+ pub fn new<P: AsRef<Path>>(
+ exit_evt: EventFd,
+ gpu_device_socket: Option<VmMemoryControlRequestSocket>,
+ resource_bridge: Option<ResourceResponseSocket>,
+ wayland_socket_path: P,
+ ) -> Gpu {
+ Gpu {
+ config_event: false,
+ exit_evt,
+ gpu_device_socket,
+ resource_bridge,
+ kill_evt: None,
+ wayland_socket_path: wayland_socket_path.as_ref().to_path_buf(),
+ }
+ }
+
+ fn get_config(&self) -> virtio_gpu_config {
+ let mut events_read = 0;
+ if self.config_event {
+ events_read |= VIRTIO_GPU_EVENT_DISPLAY;
+ }
+ virtio_gpu_config {
+ events_read: Le32::from(events_read),
+ events_clear: Le32::from(0),
+ num_scanouts: Le32::from(1),
+ num_capsets: Le32::from(2),
+ }
+ }
+}
+
+impl Drop for Gpu {
+ fn drop(&mut self) {
+ if let Some(kill_evt) = self.kill_evt.take() {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = kill_evt.write(1);
+ }
+ }
+}
+
+impl VirtioDevice for Gpu {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ let mut keep_fds = 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);
+ }
+
+ if let Some(ref gpu_device_socket) = self.gpu_device_socket {
+ keep_fds.push(gpu_device_socket.as_raw_fd());
+ }
+
+ keep_fds.push(self.exit_evt.as_raw_fd());
+ if let Some(resource_bridge) = &self.resource_bridge {
+ keep_fds.push(resource_bridge.as_raw_fd());
+ }
+ keep_fds
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_GPU
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn features(&self) -> u64 {
+ 1 << VIRTIO_GPU_F_VIRGL | 1 << VIRTIO_F_VERSION_1
+ }
+
+ fn ack_features(&mut self, value: u64) {
+ let _ = value;
+ }
+
+ fn read_config(&self, offset: u64, data: &mut [u8]) {
+ let offset = offset as usize;
+ let len = data.len();
+ let cfg = self.get_config();
+ let cfg_slice = cfg.as_slice();
+ if offset + len <= cfg_slice.len() {
+ data.copy_from_slice(&cfg_slice[offset..offset + len]);
+ }
+ }
+
+ fn write_config(&mut self, offset: u64, data: &[u8]) {
+ let offset = offset as usize;
+ let len = data.len();
+ let mut cfg = self.get_config();
+ let cfg_slice = cfg.as_mut_slice();
+ if offset + len <= cfg_slice.len() {
+ cfg_slice[offset..offset + len].copy_from_slice(data);
+ }
+ if (cfg.events_clear.to_native() & VIRTIO_GPU_EVENT_DISPLAY) != 0 {
+ self.config_event = false;
+ }
+ }
+
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ interrupt_status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ mut queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != QUEUE_SIZES.len() || queue_evts.len() != QUEUE_SIZES.len() {
+ return;
+ }
+
+ let exit_evt = match self.exit_evt.try_clone() {
+ Ok(e) => e,
+ Err(e) => {
+ error!("error cloning exit eventfd: {}", e);
+ return;
+ }
+ };
+
+ let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("error creating kill EventFd pair: {}", e);
+ return;
+ }
+ };
+ self.kill_evt = Some(self_kill_evt);
+
+ let resource_bridge = self.resource_bridge.take();
+
+ let ctrl_queue = queues.remove(0);
+ let ctrl_evt = queue_evts.remove(0);
+ let cursor_queue = queues.remove(0);
+ let cursor_evt = queue_evts.remove(0);
+ let socket_path = self.wayland_socket_path.clone();
+ if let Some(gpu_device_socket) = self.gpu_device_socket.take() {
+ let worker_result =
+ thread::Builder::new()
+ .name("virtio_gpu".to_string())
+ .spawn(move || {
+ const UNDESIRED_CARDS: &[&str] = &["vgem", "pvr"];
+ let drm_card = match gpu_buffer::rendernode::open_device(UNDESIRED_CARDS) {
+ Ok(f) => f,
+ Err(()) => {
+ error!("failed to open card");
+ return;
+ }
+ };
+
+ let device = match Device::new(drm_card) {
+ Ok(d) => d,
+ Err(()) => {
+ error!("failed to open device");
+ return;
+ }
+ };
+
+ let display = match GpuDisplay::new(socket_path) {
+ Ok(c) => c,
+ Err(e) => {
+ error!("failed to open display: {}", e);
+ return;
+ }
+ };
+
+ if cfg!(debug_assertions) {
+ let ret =
+ unsafe { libc::dup2(libc::STDOUT_FILENO, libc::STDERR_FILENO) };
+ if ret == -1 {
+ warn!("unable to dup2 stdout to stderr: {}", Error::last());
+ }
+ }
+
+ let renderer = match Renderer::init() {
+ Ok(r) => r,
+ Err(e) => {
+ error!("failed to initialize gpu renderer: {}", e);
+ return;
+ }
+ };
+
+ Worker {
+ exit_evt,
+ mem,
+ interrupt_evt,
+ interrupt_resample_evt,
+ interrupt_status,
+ ctrl_queue,
+ ctrl_evt,
+ cursor_queue,
+ cursor_evt,
+ resource_bridge,
+ kill_evt,
+ state: Frontend::new(Backend::new(
+ device,
+ display,
+ renderer,
+ gpu_device_socket,
+ )),
+ }
+ .run()
+ });
+
+ if let Err(e) = worker_result {
+ error!("failed to spawn virtio_gpu worker: {}", e);
+ return;
+ }
+ }
+ }
+
+ // Require 1 BAR for mapping 3D buffers
+ fn get_device_bars(&self) -> Vec<PciBarConfiguration> {
+ vec![PciBarConfiguration::new(
+ 4,
+ 1 << 33,
+ PciBarRegionType::Memory64BitRegion,
+ PciBarPrefetchable::NotPrefetchable,
+ )]
+ }
+}
diff --git a/devices/src/virtio/gpu/protocol.rs b/devices/src/virtio/gpu/protocol.rs
new file mode 100644
index 0000000..008283b
--- /dev/null
+++ b/devices/src/virtio/gpu/protocol.rs
@@ -0,0 +1,798 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![allow(dead_code)]
+#![allow(non_camel_case_types)]
+
+use std::cmp::min;
+use std::fmt::{self, Display};
+use std::marker::PhantomData;
+use std::mem::{size_of, size_of_val};
+use std::str::from_utf8;
+
+use data_model::{DataInit, Le32, Le64, VolatileMemory, VolatileMemoryError, VolatileSlice};
+
+pub const VIRTIO_GPU_F_VIRGL: u32 = 0;
+
+pub const VIRTIO_GPU_UNDEFINED: u32 = 0x0;
+
+/* 2d commands */
+pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x100;
+pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x101;
+pub const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x102;
+pub const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x103;
+pub const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x104;
+pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x105;
+pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x106;
+pub const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x107;
+pub const VIRTIO_GPU_CMD_GET_CAPSET_INFO: u32 = 0x108;
+pub const VIRTIO_GPU_CMD_GET_CAPSET: u32 = 0x109;
+
+/* 3d commands */
+pub const VIRTIO_GPU_CMD_CTX_CREATE: u32 = 0x200;
+pub const VIRTIO_GPU_CMD_CTX_DESTROY: u32 = 0x201;
+pub const VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: u32 = 0x202;
+pub const VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: u32 = 0x203;
+pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: u32 = 0x204;
+pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: u32 = 0x205;
+pub const VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: u32 = 0x206;
+pub const VIRTIO_GPU_CMD_SUBMIT_3D: u32 = 0x207;
+
+/* cursor commands */
+pub const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x300;
+pub const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x301;
+
+/* success responses */
+pub const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
+pub const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
+pub const VIRTIO_GPU_RESP_OK_CAPSET_INFO: u32 = 0x1102;
+pub const VIRTIO_GPU_RESP_OK_CAPSET: u32 = 0x1103;
+pub const VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO: u32 = 0x1104;
+
+/* error responses */
+pub const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200;
+pub const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201;
+pub const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202;
+pub const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203;
+pub const VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID: u32 = 0x1204;
+pub const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205;
+
+pub fn virtio_gpu_cmd_str(cmd: u32) -> &'static str {
+ match cmd {
+ VIRTIO_GPU_CMD_GET_DISPLAY_INFO => "VIRTIO_GPU_CMD_GET_DISPLAY_INFO",
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => "VIRTIO_GPU_CMD_RESOURCE_CREATE_2D",
+ VIRTIO_GPU_CMD_RESOURCE_UNREF => "VIRTIO_GPU_CMD_RESOURCE_UNREF",
+ VIRTIO_GPU_CMD_SET_SCANOUT => "VIRTIO_GPU_CMD_SET_SCANOUT",
+ VIRTIO_GPU_CMD_RESOURCE_FLUSH => "VIRTIO_GPU_CMD_RESOURCE_FLUSH",
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => "VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D",
+ VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => "VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING",
+ VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => "VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING",
+ VIRTIO_GPU_CMD_GET_CAPSET_INFO => "VIRTIO_GPU_CMD_GET_CAPSET_INFO",
+ VIRTIO_GPU_CMD_GET_CAPSET => "VIRTIO_GPU_CMD_GET_CAPSET",
+ VIRTIO_GPU_CMD_CTX_CREATE => "VIRTIO_GPU_CMD_CTX_CREATE",
+ VIRTIO_GPU_CMD_CTX_DESTROY => "VIRTIO_GPU_CMD_CTX_DESTROY",
+ VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE => "VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE",
+ VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE => "VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE",
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_3D => "VIRTIO_GPU_CMD_RESOURCE_CREATE_3D",
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D => "VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D",
+ VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => "VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D",
+ VIRTIO_GPU_CMD_SUBMIT_3D => "VIRTIO_GPU_CMD_SUBMIT_3D",
+ VIRTIO_GPU_CMD_UPDATE_CURSOR => "VIRTIO_GPU_CMD_UPDATE_CURSOR",
+ VIRTIO_GPU_CMD_MOVE_CURSOR => "VIRTIO_GPU_CMD_MOVE_CURSOR",
+ VIRTIO_GPU_RESP_OK_NODATA => "VIRTIO_GPU_RESP_OK_NODATA",
+ VIRTIO_GPU_RESP_OK_DISPLAY_INFO => "VIRTIO_GPU_RESP_OK_DISPLAY_INFO",
+ VIRTIO_GPU_RESP_OK_CAPSET_INFO => "VIRTIO_GPU_RESP_OK_CAPSET_INFO",
+ VIRTIO_GPU_RESP_OK_CAPSET => "VIRTIO_GPU_RESP_OK_CAPSET",
+ VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO => "VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO",
+ VIRTIO_GPU_RESP_ERR_UNSPEC => "VIRTIO_GPU_RESP_ERR_UNSPEC",
+ VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY => "VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY",
+ VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID => "VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID",
+ VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID => "VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID",
+ VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID => "VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID",
+ VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER => "VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER",
+ _ => "UNKNOWN",
+ }
+}
+
+pub const VIRTIO_GPU_FLAG_FENCE: u32 = (1 << 0);
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_ctrl_hdr {
+ pub type_: Le32,
+ pub flags: Le32,
+ pub fence_id: Le64,
+ pub ctx_id: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_ctrl_hdr {}
+
+/* data passed in the cursor vq */
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_cursor_pos {
+ pub scanout_id: Le32,
+ pub x: Le32,
+ pub y: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_cursor_pos {}
+
+/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_update_cursor {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub pos: virtio_gpu_cursor_pos, /* update & move */
+ pub resource_id: Le32, /* update only */
+ pub hot_x: Le32, /* update only */
+ pub hot_y: Le32, /* update only */
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_update_cursor {}
+
+/* data passed in the control vq, 2d related */
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct virtio_gpu_rect {
+ pub x: Le32,
+ pub y: Le32,
+ pub width: Le32,
+ pub height: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_rect {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_unref {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub resource_id: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_unref {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_create_2d {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub resource_id: Le32,
+ pub format: Le32,
+ pub width: Le32,
+ pub height: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_create_2d {}
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_set_scanout {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub r: virtio_gpu_rect,
+ pub scanout_id: Le32,
+ pub resource_id: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_set_scanout {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_flush {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub r: virtio_gpu_rect,
+ pub resource_id: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_flush {}
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_transfer_to_host_2d {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub r: virtio_gpu_rect,
+ pub offset: Le64,
+ pub resource_id: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_transfer_to_host_2d {}
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_mem_entry {
+ pub addr: Le64,
+ pub length: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_mem_entry {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_attach_backing {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub resource_id: Le32,
+ pub nr_entries: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_attach_backing {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_detach_backing {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub resource_id: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_detach_backing {}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct virtio_gpu_display_one {
+ pub r: virtio_gpu_rect,
+ pub enabled: Le32,
+ pub flags: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_display_one {}
+
+/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
+const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resp_display_info {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub pmodes: [virtio_gpu_display_one; VIRTIO_GPU_MAX_SCANOUTS],
+}
+
+unsafe impl DataInit for virtio_gpu_resp_display_info {}
+
+/* data passed in the control vq, 3d related */
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_box {
+ pub x: Le32,
+ pub y: Le32,
+ pub z: Le32,
+ pub w: Le32,
+ pub h: Le32,
+ pub d: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_box {}
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_transfer_host_3d {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub box_: virtio_gpu_box,
+ pub offset: Le64,
+ pub resource_id: Le32,
+ pub level: Le32,
+ pub stride: Le32,
+ pub layer_stride: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_transfer_host_3d {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
+pub const VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP: u32 = (1 << 0);
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_create_3d {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub resource_id: Le32,
+ pub target: Le32,
+ pub format: Le32,
+ pub bind: Le32,
+ pub width: Le32,
+ pub height: Le32,
+ pub depth: Le32,
+ pub array_size: Le32,
+ pub last_level: Le32,
+ pub nr_samples: Le32,
+ pub flags: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_create_3d {}
+
+/* VIRTIO_GPU_CMD_CTX_CREATE */
+#[derive(Copy)]
+#[repr(C)]
+pub struct virtio_gpu_ctx_create {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub nlen: Le32,
+ pub padding: Le32,
+ pub debug_name: [u8; 64],
+}
+
+unsafe impl DataInit for virtio_gpu_ctx_create {}
+
+impl Clone for virtio_gpu_ctx_create {
+ fn clone(&self) -> virtio_gpu_ctx_create {
+ *self
+ }
+}
+
+impl fmt::Debug for virtio_gpu_ctx_create {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let debug_name = from_utf8(&self.debug_name[..min(64, self.nlen.to_native() as usize)])
+ .unwrap_or("<invalid>");
+ f.debug_struct("virtio_gpu_ctx_create")
+ .field("hdr", &self.hdr)
+ .field("debug_name", &debug_name)
+ .finish()
+ }
+}
+
+/* VIRTIO_GPU_CMD_CTX_DESTROY */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_ctx_destroy {
+ pub hdr: virtio_gpu_ctrl_hdr,
+}
+
+unsafe impl DataInit for virtio_gpu_ctx_destroy {}
+
+/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_ctx_resource {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub resource_id: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_ctx_resource {}
+
+/* VIRTIO_GPU_CMD_SUBMIT_3D */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_cmd_submit {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub size: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_cmd_submit {}
+
+pub const VIRTIO_GPU_CAPSET_VIRGL: u32 = 1;
+pub const VIRTIO_GPU_CAPSET_VIRGL2: u32 = 2;
+
+/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_get_capset_info {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub capset_index: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_get_capset_info {}
+
+/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resp_capset_info {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub capset_id: Le32,
+ pub capset_max_version: Le32,
+ pub capset_max_size: Le32,
+ pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resp_capset_info {}
+
+/* VIRTIO_GPU_CMD_GET_CAPSET */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_get_capset {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub capset_id: Le32,
+ pub capset_version: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_get_capset {}
+
+/* VIRTIO_GPU_RESP_OK_CAPSET */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resp_capset {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub capset_data: PhantomData<[u8]>,
+}
+
+unsafe impl DataInit for virtio_gpu_resp_capset {}
+
+/* VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resp_resource_plane_info {
+ pub hdr: virtio_gpu_ctrl_hdr,
+ pub count: Le32,
+ pub padding: Le32,
+ pub format_modifier: Le64,
+ pub strides: [Le32; 4],
+ pub offsets: [Le32; 4],
+}
+
+unsafe impl DataInit for virtio_gpu_resp_resource_plane_info {}
+
+const PLANE_INFO_MAX_COUNT: usize = 4;
+
+pub const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_config {
+ pub events_read: Le32,
+ pub events_clear: Le32,
+ pub num_scanouts: Le32,
+ pub num_capsets: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_config {}
+
+/* simple formats for fbcon/X use */
+pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
+pub const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2;
+pub const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3;
+pub const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4;
+pub const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67;
+pub const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68;
+pub const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121;
+pub const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134;
+
+/// A virtio gpu command and associated metadata specific to each command.
+#[derive(Copy, Clone)]
+pub enum GpuCommand {
+ GetDisplayInfo(virtio_gpu_ctrl_hdr),
+ ResourceCreate2d(virtio_gpu_resource_create_2d),
+ ResourceUnref(virtio_gpu_resource_unref),
+ SetScanout(virtio_gpu_set_scanout),
+ ResourceFlush(virtio_gpu_resource_flush),
+ TransferToHost2d(virtio_gpu_transfer_to_host_2d),
+ ResourceAttachBacking(virtio_gpu_resource_attach_backing),
+ ResourceDetachBacking(virtio_gpu_resource_detach_backing),
+ GetCapsetInfo(virtio_gpu_get_capset_info),
+ GetCapset(virtio_gpu_get_capset),
+ CtxCreate(virtio_gpu_ctx_create),
+ CtxDestroy(virtio_gpu_ctx_destroy),
+ CtxAttachResource(virtio_gpu_ctx_resource),
+ CtxDetachResource(virtio_gpu_ctx_resource),
+ ResourceCreate3d(virtio_gpu_resource_create_3d),
+ TransferToHost3d(virtio_gpu_transfer_host_3d),
+ TransferFromHost3d(virtio_gpu_transfer_host_3d),
+ CmdSubmit3d(virtio_gpu_cmd_submit),
+ UpdateCursor(virtio_gpu_update_cursor),
+ MoveCursor(virtio_gpu_update_cursor),
+}
+
+/// An error indicating something went wrong decoding a `GpuCommand`. These correspond to
+/// `VIRTIO_GPU_CMD_*`.
+#[derive(Debug)]
+pub enum GpuCommandDecodeError {
+ /// The command referenced an inaccessible area of memory.
+ Memory(VolatileMemoryError),
+ /// The type of the command was invalid.
+ InvalidType(u32),
+}
+
+impl Display for GpuCommandDecodeError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::GpuCommandDecodeError::*;
+
+ match self {
+ Memory(e) => write!(
+ f,
+ "command referenced an inaccessible area of memory: {}",
+ e,
+ ),
+ InvalidType(n) => write!(f, "invalid command type ({})", n),
+ }
+ }
+}
+
+impl From<VolatileMemoryError> for GpuCommandDecodeError {
+ fn from(e: VolatileMemoryError) -> GpuCommandDecodeError {
+ GpuCommandDecodeError::Memory(e)
+ }
+}
+
+impl fmt::Debug for GpuCommand {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::GpuCommand::*;
+ match self {
+ GetDisplayInfo(_info) => f.debug_struct("GetDisplayInfo").finish(),
+ ResourceCreate2d(_info) => f.debug_struct("ResourceCreate2d").finish(),
+ ResourceUnref(_info) => f.debug_struct("ResourceUnref").finish(),
+ SetScanout(_info) => f.debug_struct("SetScanout").finish(),
+ ResourceFlush(_info) => f.debug_struct("ResourceFlush").finish(),
+ TransferToHost2d(_info) => f.debug_struct("TransferToHost2d").finish(),
+ ResourceAttachBacking(_info) => f.debug_struct("ResourceAttachBacking").finish(),
+ ResourceDetachBacking(_info) => f.debug_struct("ResourceDetachBacking").finish(),
+ GetCapsetInfo(_info) => f.debug_struct("GetCapsetInfo").finish(),
+ GetCapset(_info) => f.debug_struct("GetCapset").finish(),
+ CtxCreate(_info) => f.debug_struct("CtxCreate").finish(),
+ CtxDestroy(_info) => f.debug_struct("CtxDestroy").finish(),
+ CtxAttachResource(_info) => f.debug_struct("CtxAttachResource").finish(),
+ CtxDetachResource(_info) => f.debug_struct("CtxDetachResource").finish(),
+ ResourceCreate3d(_info) => f.debug_struct("ResourceCreate3d").finish(),
+ TransferToHost3d(_info) => f.debug_struct("TransferToHost3d").finish(),
+ TransferFromHost3d(_info) => f.debug_struct("TransferFromHost3d").finish(),
+ CmdSubmit3d(_info) => f.debug_struct("CmdSubmit3d").finish(),
+ UpdateCursor(_info) => f.debug_struct("UpdateCursor").finish(),
+ MoveCursor(_info) => f.debug_struct("MoveCursor").finish(),
+ }
+ }
+}
+
+impl GpuCommand {
+ /// Decodes a command from the given chunk of memory.
+ pub fn decode(cmd: VolatileSlice) -> Result<GpuCommand, GpuCommandDecodeError> {
+ use self::GpuCommand::*;
+ let hdr: virtio_gpu_ctrl_hdr = cmd.get_ref(0)?.load();
+ Ok(match hdr.type_.into() {
+ VIRTIO_GPU_CMD_GET_DISPLAY_INFO => GetDisplayInfo(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => ResourceCreate2d(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_RESOURCE_UNREF => ResourceUnref(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_SET_SCANOUT => SetScanout(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_RESOURCE_FLUSH => ResourceFlush(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => TransferToHost2d(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => ResourceAttachBacking(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => ResourceDetachBacking(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_GET_CAPSET_INFO => GetCapsetInfo(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_GET_CAPSET => GetCapset(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_CTX_CREATE => CtxCreate(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_CTX_DESTROY => CtxDestroy(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE => CtxAttachResource(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE => CtxDetachResource(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_3D => ResourceCreate3d(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D => TransferToHost3d(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => TransferFromHost3d(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_SUBMIT_3D => CmdSubmit3d(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_UPDATE_CURSOR => UpdateCursor(cmd.get_ref(0)?.load()),
+ VIRTIO_GPU_CMD_MOVE_CURSOR => MoveCursor(cmd.get_ref(0)?.load()),
+ _ => return Err(GpuCommandDecodeError::InvalidType(hdr.type_.into())),
+ })
+ }
+
+ /// Gets the generic `virtio_gpu_ctrl_hdr` from this command.
+ pub fn ctrl_hdr(&self) -> &virtio_gpu_ctrl_hdr {
+ use self::GpuCommand::*;
+ match self {
+ GetDisplayInfo(info) => info,
+ ResourceCreate2d(info) => &info.hdr,
+ ResourceUnref(info) => &info.hdr,
+ SetScanout(info) => &info.hdr,
+ ResourceFlush(info) => &info.hdr,
+ TransferToHost2d(info) => &info.hdr,
+ ResourceAttachBacking(info) => &info.hdr,
+ ResourceDetachBacking(info) => &info.hdr,
+ GetCapsetInfo(info) => &info.hdr,
+ GetCapset(info) => &info.hdr,
+ CtxCreate(info) => &info.hdr,
+ CtxDestroy(info) => &info.hdr,
+ CtxAttachResource(info) => &info.hdr,
+ CtxDetachResource(info) => &info.hdr,
+ ResourceCreate3d(info) => &info.hdr,
+ TransferToHost3d(info) => &info.hdr,
+ TransferFromHost3d(info) => &info.hdr,
+ CmdSubmit3d(info) => &info.hdr,
+ UpdateCursor(info) => &info.hdr,
+ MoveCursor(info) => &info.hdr,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq)]
+pub struct GpuResponsePlaneInfo {
+ pub stride: u32,
+ pub offset: u32,
+}
+
+/// A response to a `GpuCommand`. These correspond to `VIRTIO_GPU_RESP_*`.
+#[derive(Debug, PartialEq)]
+pub enum GpuResponse {
+ OkNoData,
+ OkDisplayInfo(Vec<(u32, u32)>),
+ OkCapsetInfo {
+ id: u32,
+ version: u32,
+ size: u32,
+ },
+ OkCapset(Vec<u8>),
+ OkResourcePlaneInfo {
+ format_modifier: u64,
+ plane_info: Vec<GpuResponsePlaneInfo>,
+ },
+ ErrUnspec,
+ ErrOutOfMemory,
+ ErrInvalidScanoutId,
+ ErrInvalidResourceId,
+ ErrInvalidContextId,
+ ErrInvalidParameter,
+}
+
+/// An error indicating something went wrong decoding a `GpuCommand`.
+#[derive(Debug)]
+pub enum GpuResponseEncodeError {
+ /// The response was encoded to an inaccessible area of memory.
+ Memory(VolatileMemoryError),
+ /// More displays than are valid were in a `OkDisplayInfo`.
+ TooManyDisplays(usize),
+ /// More planes than are valid were in a `OkResourcePlaneInfo`.
+ TooManyPlanes(usize),
+}
+
+impl Display for GpuResponseEncodeError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::GpuResponseEncodeError::*;
+
+ match self {
+ Memory(e) => write!(
+ f,
+ "response was encoded to an inaccessible area of memory: {}",
+ e,
+ ),
+ TooManyDisplays(n) => write!(f, "{} is more displays than are valid", n),
+ TooManyPlanes(n) => write!(f, "{} is more planes than are valid", n),
+ }
+ }
+}
+
+impl From<VolatileMemoryError> for GpuResponseEncodeError {
+ fn from(e: VolatileMemoryError) -> GpuResponseEncodeError {
+ GpuResponseEncodeError::Memory(e)
+ }
+}
+
+impl GpuResponse {
+ /// Encodes a this `GpuResponse` into `resp` and the given set of metadata.
+ pub fn encode(
+ &self,
+ flags: u32,
+ fence_id: u64,
+ ctx_id: u32,
+ resp: VolatileSlice,
+ ) -> Result<u32, GpuResponseEncodeError> {
+ let hdr = virtio_gpu_ctrl_hdr {
+ type_: Le32::from(self.get_type()),
+ flags: Le32::from(flags),
+ fence_id: Le64::from(fence_id),
+ ctx_id: Le32::from(ctx_id),
+ padding: Le32::from(0),
+ };
+ let len = match *self {
+ GpuResponse::OkDisplayInfo(ref info) => {
+ if info.len() > VIRTIO_GPU_MAX_SCANOUTS {
+ return Err(GpuResponseEncodeError::TooManyDisplays(info.len()));
+ }
+ let mut disp_info = virtio_gpu_resp_display_info {
+ hdr,
+ pmodes: Default::default(),
+ };
+ for (disp_mode, &(width, height)) in disp_info.pmodes.iter_mut().zip(info) {
+ disp_mode.r.width = Le32::from(width);
+ disp_mode.r.height = Le32::from(height);
+ disp_mode.enabled = Le32::from(1);
+ }
+ resp.get_ref(0)?.store(disp_info);
+ size_of_val(&disp_info)
+ }
+ GpuResponse::OkCapsetInfo { id, version, size } => {
+ resp.get_ref(0)?.store(virtio_gpu_resp_capset_info {
+ hdr,
+ capset_id: Le32::from(id),
+ capset_max_version: Le32::from(version),
+ capset_max_size: Le32::from(size),
+ padding: Le32::from(0),
+ });
+ size_of::<virtio_gpu_resp_capset_info>()
+ }
+ GpuResponse::OkCapset(ref data) => {
+ resp.get_ref(0)?.store(hdr);
+ let resp_data_slice =
+ resp.get_slice(size_of_val(&hdr) as u64, data.len() as u64)?;
+ resp_data_slice.copy_from(data);
+ size_of_val(&hdr) + data.len()
+ }
+ GpuResponse::OkResourcePlaneInfo {
+ format_modifier,
+ ref plane_info,
+ } => {
+ if plane_info.len() > PLANE_INFO_MAX_COUNT {
+ return Err(GpuResponseEncodeError::TooManyPlanes(plane_info.len()));
+ }
+ let mut strides = [Le32::default(); PLANE_INFO_MAX_COUNT];
+ let mut offsets = [Le32::default(); PLANE_INFO_MAX_COUNT];
+ for (plane_index, plane) in plane_info.iter().enumerate() {
+ strides[plane_index] = plane.stride.into();
+ offsets[plane_index] = plane.offset.into();
+ }
+ let plane_info = virtio_gpu_resp_resource_plane_info {
+ hdr,
+ count: Le32::from(plane_info.len() as u32),
+ padding: 0.into(),
+ format_modifier: format_modifier.into(),
+ strides,
+ offsets,
+ };
+ match resp.get_ref(0) {
+ Ok(resp_ref) => {
+ resp_ref.store(plane_info);
+ size_of_val(&plane_info)
+ }
+ _ => {
+ // In case there is too little room in the response slice to store the
+ // entire virtio_gpu_resp_resource_plane_info, convert response to a regular
+ // VIRTIO_GPU_RESP_OK_NODATA and attempt to return that.
+ resp.get_ref(0)?.store(virtio_gpu_ctrl_hdr {
+ type_: Le32::from(VIRTIO_GPU_RESP_OK_NODATA),
+ ..hdr
+ });
+ size_of_val(&hdr)
+ }
+ }
+ }
+ _ => {
+ resp.get_ref(0)?.store(hdr);
+ size_of_val(&hdr)
+ }
+ };
+ Ok(len as u32)
+ }
+
+ /// Gets the `VIRTIO_GPU_*` enum value that corresponds to this variant.
+ pub fn get_type(&self) -> u32 {
+ match self {
+ GpuResponse::OkNoData => VIRTIO_GPU_RESP_OK_NODATA,
+ GpuResponse::OkDisplayInfo(_) => VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+ GpuResponse::OkCapsetInfo { .. } => VIRTIO_GPU_RESP_OK_CAPSET_INFO,
+ GpuResponse::OkCapset(_) => VIRTIO_GPU_RESP_OK_CAPSET,
+ GpuResponse::OkResourcePlaneInfo { .. } => VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO,
+ GpuResponse::ErrUnspec => VIRTIO_GPU_RESP_ERR_UNSPEC,
+ GpuResponse::ErrOutOfMemory => VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+ GpuResponse::ErrInvalidScanoutId => VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+ GpuResponse::ErrInvalidResourceId => VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+ GpuResponse::ErrInvalidContextId => VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+ GpuResponse::ErrInvalidParameter => VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+ }
+ }
+
+ /// Returns true if this response indicates success.
+ pub fn is_ok(&self) -> bool {
+ match self {
+ GpuResponse::OkNoData => true,
+ GpuResponse::OkDisplayInfo(_) => true,
+ GpuResponse::OkCapsetInfo { .. } => true,
+ GpuResponse::OkCapset(_) => true,
+ GpuResponse::OkResourcePlaneInfo { .. } => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this response indicates an error.
+ pub fn is_err(&self) -> bool {
+ !self.is_ok()
+ }
+}
diff --git a/devices/src/virtio/input/constants.rs b/devices/src/virtio/input/constants.rs
new file mode 100644
index 0000000..6cc3574
--- /dev/null
+++ b/devices/src/virtio/input/constants.rs
@@ -0,0 +1,738 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Should match linux/input-event-codes.h
+pub const INPUT_PROP_POINTER: u16 = 0x00;
+pub const INPUT_PROP_DIRECT: u16 = 0x01;
+pub const INPUT_PROP_BUTTONPAD: u16 = 0x02;
+pub const INPUT_PROP_SEMI_MT: u16 = 0x03;
+pub const INPUT_PROP_TOPBUTTONPAD: u16 = 0x04;
+pub const INPUT_PROP_POINTING_STICK: u16 = 0x05;
+pub const INPUT_PROP_ACCELEROMETER: u16 = 0x06;
+
+pub const INPUT_PROP_MAX: u16 = 0x1f;
+pub const INPUT_PROP_CNT: u16 = (INPUT_PROP_MAX + 1);
+
+pub const EV_SYN: u16 = 0x00;
+pub const EV_KEY: u16 = 0x01;
+pub const EV_REL: u16 = 0x02;
+pub const EV_ABS: u16 = 0x03;
+pub const EV_MSC: u16 = 0x04;
+pub const EV_SW: u16 = 0x05;
+pub const EV_LED: u16 = 0x11;
+pub const EV_SND: u16 = 0x12;
+pub const EV_REP: u16 = 0x14;
+pub const EV_FF: u16 = 0x15;
+pub const EV_PWR: u16 = 0x16;
+pub const EV_FF_STATUS: u16 = 0x17;
+pub const EV_MAX: u16 = 0x1f;
+pub const EV_CNT: u16 = EV_MAX + 1;
+
+pub const SYN_REPORT: u16 = 0;
+pub const SYN_CONFIG: u16 = 1;
+pub const SYN_MT_REPORT: u16 = 2;
+pub const SYN_DROPPED: u16 = 3;
+
+pub const KEY_RESERVED: u16 = 0;
+pub const KEY_ESC: u16 = 1;
+pub const KEY_1: u16 = 2;
+pub const KEY_2: u16 = 3;
+pub const KEY_3: u16 = 4;
+pub const KEY_4: u16 = 5;
+pub const KEY_5: u16 = 6;
+pub const KEY_6: u16 = 7;
+pub const KEY_7: u16 = 8;
+pub const KEY_8: u16 = 9;
+pub const KEY_9: u16 = 10;
+pub const KEY_0: u16 = 11;
+pub const KEY_MINUS: u16 = 12;
+pub const KEY_EQUAL: u16 = 13;
+pub const KEY_BACKSPACE: u16 = 14;
+pub const KEY_TAB: u16 = 15;
+pub const KEY_Q: u16 = 16;
+pub const KEY_W: u16 = 17;
+pub const KEY_E: u16 = 18;
+pub const KEY_R: u16 = 19;
+pub const KEY_T: u16 = 20;
+pub const KEY_Y: u16 = 21;
+pub const KEY_U: u16 = 22;
+pub const KEY_I: u16 = 23;
+pub const KEY_O: u16 = 24;
+pub const KEY_P: u16 = 25;
+pub const KEY_LEFTBRACE: u16 = 26;
+pub const KEY_RIGHTBRACE: u16 = 27;
+pub const KEY_ENTER: u16 = 28;
+pub const KEY_LEFTCTRL: u16 = 29;
+pub const KEY_A: u16 = 30;
+pub const KEY_S: u16 = 31;
+pub const KEY_D: u16 = 32;
+pub const KEY_F: u16 = 33;
+pub const KEY_G: u16 = 34;
+pub const KEY_H: u16 = 35;
+pub const KEY_J: u16 = 36;
+pub const KEY_K: u16 = 37;
+pub const KEY_L: u16 = 38;
+pub const KEY_SEMICOLON: u16 = 39;
+pub const KEY_APOSTROPHE: u16 = 40;
+pub const KEY_GRAVE: u16 = 41;
+pub const KEY_LEFTSHIFT: u16 = 42;
+pub const KEY_BACKSLASH: u16 = 43;
+pub const KEY_Z: u16 = 44;
+pub const KEY_X: u16 = 45;
+pub const KEY_C: u16 = 46;
+pub const KEY_V: u16 = 47;
+pub const KEY_B: u16 = 48;
+pub const KEY_N: u16 = 49;
+pub const KEY_M: u16 = 50;
+pub const KEY_COMMA: u16 = 51;
+pub const KEY_DOT: u16 = 52;
+pub const KEY_SLASH: u16 = 53;
+pub const KEY_RIGHTSHIFT: u16 = 54;
+pub const KEY_KPASTERISK: u16 = 55;
+pub const KEY_LEFTALT: u16 = 56;
+pub const KEY_SPACE: u16 = 57;
+pub const KEY_CAPSLOCK: u16 = 58;
+pub const KEY_F1: u16 = 59;
+pub const KEY_F2: u16 = 60;
+pub const KEY_F3: u16 = 61;
+pub const KEY_F4: u16 = 62;
+pub const KEY_F5: u16 = 63;
+pub const KEY_F6: u16 = 64;
+pub const KEY_F7: u16 = 65;
+pub const KEY_F8: u16 = 66;
+pub const KEY_F9: u16 = 67;
+pub const KEY_F10: u16 = 68;
+pub const KEY_NUMLOCK: u16 = 69;
+pub const KEY_SCROLLLOCK: u16 = 70;
+pub const KEY_KP7: u16 = 71;
+pub const KEY_KP8: u16 = 72;
+pub const KEY_KP9: u16 = 73;
+pub const KEY_KPMINUS: u16 = 74;
+pub const KEY_KP4: u16 = 75;
+pub const KEY_KP5: u16 = 76;
+pub const KEY_KP6: u16 = 77;
+pub const KEY_KPPLUS: u16 = 78;
+pub const KEY_KP1: u16 = 79;
+pub const KEY_KP2: u16 = 80;
+pub const KEY_KP3: u16 = 81;
+pub const KEY_KP0: u16 = 82;
+pub const KEY_KPDOT: u16 = 83;
+
+pub const KEY_ZENKAKUHANKAKU: u16 = 85;
+pub const KEY_102ND: u16 = 86;
+pub const KEY_F11: u16 = 87;
+pub const KEY_F12: u16 = 88;
+pub const KEY_RO: u16 = 89;
+pub const KEY_KATAKANA: u16 = 90;
+pub const KEY_HIRAGANA: u16 = 91;
+pub const KEY_HENKAN: u16 = 92;
+pub const KEY_KATAKANAHIRAGANA: u16 = 93;
+pub const KEY_MUHENKAN: u16 = 94;
+pub const KEY_KPJPCOMMA: u16 = 95;
+pub const KEY_KPENTER: u16 = 96;
+pub const KEY_RIGHTCTRL: u16 = 97;
+pub const KEY_KPSLASH: u16 = 98;
+pub const KEY_SYSRQ: u16 = 99;
+pub const KEY_RIGHTALT: u16 = 100;
+pub const KEY_LINEFEED: u16 = 101;
+pub const KEY_HOME: u16 = 102;
+pub const KEY_UP: u16 = 103;
+pub const KEY_PAGEUP: u16 = 104;
+pub const KEY_LEFT: u16 = 105;
+pub const KEY_RIGHT: u16 = 106;
+pub const KEY_END: u16 = 107;
+pub const KEY_DOWN: u16 = 108;
+pub const KEY_PAGEDOWN: u16 = 109;
+pub const KEY_INSERT: u16 = 110;
+pub const KEY_DELETE: u16 = 111;
+pub const KEY_MACRO: u16 = 112;
+pub const KEY_MUTE: u16 = 113;
+pub const KEY_VOLUMEDOWN: u16 = 114;
+pub const KEY_VOLUMEUP: u16 = 115;
+pub const KEY_POWER: u16 = 116;
+pub const KEY_KPEQUAL: u16 = 117;
+pub const KEY_KPPLUSMINUS: u16 = 118;
+pub const KEY_PAUSE: u16 = 119;
+pub const KEY_SCALE: u16 = 120;
+
+pub const KEY_KPCOMMA: u16 = 121;
+pub const KEY_HANGEUL: u16 = 122;
+pub const KEY_HANGUEL: u16 = KEY_HANGEUL;
+pub const KEY_HANJA: u16 = 123;
+pub const KEY_YEN: u16 = 124;
+pub const KEY_LEFTMETA: u16 = 125;
+pub const KEY_RIGHTMETA: u16 = 126;
+pub const KEY_COMPOSE: u16 = 127;
+
+pub const KEY_STOP: u16 = 128;
+pub const KEY_AGAIN: u16 = 129;
+pub const KEY_PROPS: u16 = 130;
+pub const KEY_UNDO: u16 = 131;
+pub const KEY_FRONT: u16 = 132;
+pub const KEY_COPY: u16 = 133;
+pub const KEY_OPEN: u16 = 134;
+pub const KEY_PASTE: u16 = 135;
+pub const KEY_FIND: u16 = 136;
+pub const KEY_CUT: u16 = 137;
+pub const KEY_HELP: u16 = 138;
+pub const KEY_MENU: u16 = 139;
+pub const KEY_CALC: u16 = 140;
+pub const KEY_SETUP: u16 = 141;
+pub const KEY_SLEEP: u16 = 142;
+pub const KEY_WAKEUP: u16 = 143;
+pub const KEY_FILE: u16 = 144;
+pub const KEY_SENDFILE: u16 = 145;
+pub const KEY_DELETEFILE: u16 = 146;
+pub const KEY_XFER: u16 = 147;
+pub const KEY_PROG1: u16 = 148;
+pub const KEY_PROG2: u16 = 149;
+pub const KEY_WWW: u16 = 150;
+pub const KEY_MSDOS: u16 = 151;
+pub const KEY_COFFEE: u16 = 152;
+pub const KEY_SCREENLOCK: u16 = KEY_COFFEE;
+pub const KEY_ROTATE_DISPLAY: u16 = 153;
+pub const KEY_DIRECTION: u16 = KEY_ROTATE_DISPLAY;
+pub const KEY_CYCLEWINDOWS: u16 = 154;
+pub const KEY_MAIL: u16 = 155;
+pub const KEY_BOOKMARKS: u16 = 156;
+pub const KEY_COMPUTER: u16 = 157;
+pub const KEY_BACK: u16 = 158;
+pub const KEY_FORWARD: u16 = 159;
+pub const KEY_CLOSECD: u16 = 160;
+pub const KEY_EJECTCD: u16 = 161;
+pub const KEY_EJECTCLOSECD: u16 = 162;
+pub const KEY_NEXTSONG: u16 = 163;
+pub const KEY_PLAYPAUSE: u16 = 164;
+pub const KEY_PREVIOUSSONG: u16 = 165;
+pub const KEY_STOPCD: u16 = 166;
+pub const KEY_RECORD: u16 = 167;
+pub const KEY_REWIND: u16 = 168;
+pub const KEY_PHONE: u16 = 169;
+pub const KEY_ISO: u16 = 170;
+pub const KEY_CONFIG: u16 = 171;
+pub const KEY_HOMEPAGE: u16 = 172;
+pub const KEY_REFRESH: u16 = 173;
+pub const KEY_EXIT: u16 = 174;
+pub const KEY_MOVE: u16 = 175;
+pub const KEY_EDIT: u16 = 176;
+pub const KEY_SCROLLUP: u16 = 177;
+pub const KEY_SCROLLDOWN: u16 = 178;
+pub const KEY_KPLEFTPAREN: u16 = 179;
+pub const KEY_KPRIGHTPAREN: u16 = 180;
+pub const KEY_NEW: u16 = 181;
+pub const KEY_REDO: u16 = 182;
+
+pub const KEY_F13: u16 = 183;
+pub const KEY_F14: u16 = 184;
+pub const KEY_F15: u16 = 185;
+pub const KEY_F16: u16 = 186;
+pub const KEY_F17: u16 = 187;
+pub const KEY_F18: u16 = 188;
+pub const KEY_F19: u16 = 189;
+pub const KEY_F20: u16 = 190;
+pub const KEY_F21: u16 = 191;
+pub const KEY_F22: u16 = 192;
+pub const KEY_F23: u16 = 193;
+pub const KEY_F24: u16 = 194;
+
+pub const KEY_PLAYCD: u16 = 200;
+pub const KEY_PAUSECD: u16 = 201;
+pub const KEY_PROG3: u16 = 202;
+pub const KEY_PROG4: u16 = 203;
+pub const KEY_DASHBOARD: u16 = 204;
+pub const KEY_SUSPEND: u16 = 205;
+pub const KEY_CLOSE: u16 = 206;
+pub const KEY_PLAY: u16 = 207;
+pub const KEY_FASTFORWARD: u16 = 208;
+pub const KEY_BASSBOOST: u16 = 209;
+pub const KEY_PRINT: u16 = 210;
+pub const KEY_HP: u16 = 211;
+pub const KEY_CAMERA: u16 = 212;
+pub const KEY_SOUND: u16 = 213;
+pub const KEY_QUESTION: u16 = 214;
+pub const KEY_EMAIL: u16 = 215;
+pub const KEY_CHAT: u16 = 216;
+pub const KEY_SEARCH: u16 = 217;
+pub const KEY_CONNECT: u16 = 218;
+pub const KEY_FINANCE: u16 = 219;
+pub const KEY_SPORT: u16 = 220;
+pub const KEY_SHOP: u16 = 221;
+pub const KEY_ALTERASE: u16 = 222;
+pub const KEY_CANCEL: u16 = 223;
+pub const KEY_BRIGHTNESSDOWN: u16 = 224;
+pub const KEY_BRIGHTNESSUP: u16 = 225;
+pub const KEY_MEDIA: u16 = 226;
+
+pub const KEY_SWITCHVIDEOMODE: u16 = 227;
+pub const KEY_KBDILLUMTOGGLE: u16 = 228;
+pub const KEY_KBDILLUMDOWN: u16 = 229;
+pub const KEY_KBDILLUMUP: u16 = 230;
+
+pub const KEY_SEND: u16 = 231;
+pub const KEY_REPLY: u16 = 232;
+pub const KEY_FORWARDMAIL: u16 = 233;
+pub const KEY_SAVE: u16 = 234;
+pub const KEY_DOCUMENTS: u16 = 235;
+
+pub const KEY_BATTERY: u16 = 236;
+
+pub const KEY_BLUETOOTH: u16 = 237;
+pub const KEY_WLAN: u16 = 238;
+pub const KEY_UWB: u16 = 239;
+
+pub const KEY_UNKNOWN: u16 = 240;
+
+pub const KEY_VIDEO_NEXT: u16 = 241;
+pub const KEY_VIDEO_PREV: u16 = 242;
+pub const KEY_BRIGHTNESS_CYCLE: u16 = 243;
+pub const KEY_BRIGHTNESS_AUTO: u16 = 244;
+pub const KEY_BRIGHTNESS_ZERO: u16 = KEY_BRIGHTNESS_AUTO;
+pub const KEY_DISPLAY_OFF: u16 = 245;
+
+pub const KEY_WWAN: u16 = 246;
+pub const KEY_WIMAX: u16 = KEY_WWAN;
+pub const KEY_RFKILL: u16 = 247;
+
+pub const KEY_MICMUTE: u16 = 248;
+
+pub const BTN_MISC: u16 = 0x100;
+pub const BTN_0: u16 = 0x100;
+pub const BTN_1: u16 = 0x101;
+pub const BTN_2: u16 = 0x102;
+pub const BTN_3: u16 = 0x103;
+pub const BTN_4: u16 = 0x104;
+pub const BTN_5: u16 = 0x105;
+pub const BTN_6: u16 = 0x106;
+pub const BTN_7: u16 = 0x107;
+pub const BTN_8: u16 = 0x108;
+pub const BTN_9: u16 = 0x109;
+
+pub const BTN_MOUSE: u16 = 0x110;
+pub const BTN_LEFT: u16 = 0x110;
+pub const BTN_RIGHT: u16 = 0x111;
+pub const BTN_MIDDLE: u16 = 0x112;
+pub const BTN_SIDE: u16 = 0x113;
+pub const BTN_EXTRA: u16 = 0x114;
+pub const BTN_FORWARD: u16 = 0x115;
+pub const BTN_BACK: u16 = 0x116;
+pub const BTN_TASK: u16 = 0x117;
+
+pub const BTN_JOYSTICK: u16 = 0x120;
+pub const BTN_TRIGGER: u16 = 0x120;
+pub const BTN_THUMB: u16 = 0x121;
+pub const BTN_THUMB2: u16 = 0x122;
+pub const BTN_TOP: u16 = 0x123;
+pub const BTN_TOP2: u16 = 0x124;
+pub const BTN_PINKIE: u16 = 0x125;
+pub const BTN_BASE: u16 = 0x126;
+pub const BTN_BASE2: u16 = 0x127;
+pub const BTN_BASE3: u16 = 0x128;
+pub const BTN_BASE4: u16 = 0x129;
+pub const BTN_BASE5: u16 = 0x12a;
+pub const BTN_BASE6: u16 = 0x12b;
+pub const BTN_DEAD: u16 = 0x12f;
+
+pub const BTN_GAMEPAD: u16 = 0x130;
+pub const BTN_SOUTH: u16 = 0x130;
+pub const BTN_A: u16 = BTN_SOUTH;
+pub const BTN_EAST: u16 = 0x131;
+pub const BTN_B: u16 = BTN_EAST;
+pub const BTN_C: u16 = 0x132;
+pub const BTN_NORTH: u16 = 0x133;
+pub const BTN_X: u16 = BTN_NORTH;
+pub const BTN_WEST: u16 = 0x134;
+pub const BTN_Y: u16 = BTN_WEST;
+pub const BTN_Z: u16 = 0x135;
+pub const BTN_TL: u16 = 0x136;
+pub const BTN_TR: u16 = 0x137;
+pub const BTN_TL2: u16 = 0x138;
+pub const BTN_TR2: u16 = 0x139;
+pub const BTN_SELECT: u16 = 0x13a;
+pub const BTN_START: u16 = 0x13b;
+pub const BTN_MODE: u16 = 0x13c;
+pub const BTN_THUMBL: u16 = 0x13d;
+pub const BTN_THUMBR: u16 = 0x13e;
+
+pub const BTN_DIGI: u16 = 0x140;
+pub const BTN_TOOL_PEN: u16 = 0x140;
+pub const BTN_TOOL_RUBBER: u16 = 0x141;
+pub const BTN_TOOL_BRUSH: u16 = 0x142;
+pub const BTN_TOOL_PENCIL: u16 = 0x143;
+pub const BTN_TOOL_AIRBRUSH: u16 = 0x144;
+pub const BTN_TOOL_FINGER: u16 = 0x145;
+pub const BTN_TOOL_MOUSE: u16 = 0x146;
+pub const BTN_TOOL_LENS: u16 = 0x147;
+pub const BTN_TOOL_QUINTTAP: u16 = 0x148;
+pub const BTN_STYLUS3: u16 = 0x149;
+pub const BTN_TOUCH: u16 = 0x14a;
+pub const BTN_STYLUS: u16 = 0x14b;
+pub const BTN_STYLUS2: u16 = 0x14c;
+pub const BTN_TOOL_DOUBLETAP: u16 = 0x14d;
+pub const BTN_TOOL_TRIPLETAP: u16 = 0x14e;
+pub const BTN_TOOL_QUADTAP: u16 = 0x14f;
+
+pub const BTN_WHEEL: u16 = 0x150;
+pub const BTN_GEAR_DOWN: u16 = 0x150;
+pub const BTN_GEAR_UP: u16 = 0x151;
+
+pub const KEY_OK: u16 = 0x160;
+pub const KEY_SELECT: u16 = 0x161;
+pub const KEY_GOTO: u16 = 0x162;
+pub const KEY_CLEAR: u16 = 0x163;
+pub const KEY_POWER2: u16 = 0x164;
+pub const KEY_OPTION: u16 = 0x165;
+pub const KEY_INFO: u16 = 0x166;
+pub const KEY_TIME: u16 = 0x167;
+pub const KEY_VENDOR: u16 = 0x168;
+pub const KEY_ARCHIVE: u16 = 0x169;
+pub const KEY_PROGRAM: u16 = 0x16a;
+pub const KEY_CHANNEL: u16 = 0x16b;
+pub const KEY_FAVORITES: u16 = 0x16c;
+pub const KEY_EPG: u16 = 0x16d;
+pub const KEY_PVR: u16 = 0x16e;
+pub const KEY_MHP: u16 = 0x16f;
+pub const KEY_LANGUAGE: u16 = 0x170;
+pub const KEY_TITLE: u16 = 0x171;
+pub const KEY_SUBTITLE: u16 = 0x172;
+pub const KEY_ANGLE: u16 = 0x173;
+pub const KEY_ZOOM: u16 = 0x174;
+pub const KEY_MODE: u16 = 0x175;
+pub const KEY_KEYBOARD: u16 = 0x176;
+pub const KEY_SCREEN: u16 = 0x177;
+pub const KEY_PC: u16 = 0x178;
+pub const KEY_TV: u16 = 0x179;
+pub const KEY_TV2: u16 = 0x17a;
+pub const KEY_VCR: u16 = 0x17b;
+pub const KEY_VCR2: u16 = 0x17c;
+pub const KEY_SAT: u16 = 0x17d;
+pub const KEY_SAT2: u16 = 0x17e;
+pub const KEY_CD: u16 = 0x17f;
+pub const KEY_TAPE: u16 = 0x180;
+pub const KEY_RADIO: u16 = 0x181;
+pub const KEY_TUNER: u16 = 0x182;
+pub const KEY_PLAYER: u16 = 0x183;
+pub const KEY_TEXT: u16 = 0x184;
+pub const KEY_DVD: u16 = 0x185;
+pub const KEY_AUX: u16 = 0x186;
+pub const KEY_MP3: u16 = 0x187;
+pub const KEY_AUDIO: u16 = 0x188;
+pub const KEY_VIDEO: u16 = 0x189;
+pub const KEY_DIRECTORY: u16 = 0x18a;
+pub const KEY_LIST: u16 = 0x18b;
+pub const KEY_MEMO: u16 = 0x18c;
+pub const KEY_CALENDAR: u16 = 0x18d;
+pub const KEY_RED: u16 = 0x18e;
+pub const KEY_GREEN: u16 = 0x18f;
+pub const KEY_YELLOW: u16 = 0x190;
+pub const KEY_BLUE: u16 = 0x191;
+pub const KEY_CHANNELUP: u16 = 0x192;
+pub const KEY_CHANNELDOWN: u16 = 0x193;
+pub const KEY_FIRST: u16 = 0x194;
+pub const KEY_LAST: u16 = 0x195;
+pub const KEY_AB: u16 = 0x196;
+pub const KEY_NEXT: u16 = 0x197;
+pub const KEY_RESTART: u16 = 0x198;
+pub const KEY_SLOW: u16 = 0x199;
+pub const KEY_SHUFFLE: u16 = 0x19a;
+pub const KEY_BREAK: u16 = 0x19b;
+pub const KEY_PREVIOUS: u16 = 0x19c;
+pub const KEY_DIGITS: u16 = 0x19d;
+pub const KEY_TEEN: u16 = 0x19e;
+pub const KEY_TWEN: u16 = 0x19f;
+pub const KEY_VIDEOPHONE: u16 = 0x1a0;
+pub const KEY_GAMES: u16 = 0x1a1;
+pub const KEY_ZOOMIN: u16 = 0x1a2;
+pub const KEY_ZOOMOUT: u16 = 0x1a3;
+pub const KEY_ZOOMRESET: u16 = 0x1a4;
+pub const KEY_WORDPROCESSOR: u16 = 0x1a5;
+pub const KEY_EDITOR: u16 = 0x1a6;
+pub const KEY_SPREADSHEET: u16 = 0x1a7;
+pub const KEY_GRAPHICSEDITOR: u16 = 0x1a8;
+pub const KEY_PRESENTATION: u16 = 0x1a9;
+pub const KEY_DATABASE: u16 = 0x1aa;
+pub const KEY_NEWS: u16 = 0x1ab;
+pub const KEY_VOICEMAIL: u16 = 0x1ac;
+pub const KEY_ADDRESSBOOK: u16 = 0x1ad;
+pub const KEY_MESSENGER: u16 = 0x1ae;
+pub const KEY_DISPLAYTOGGLE: u16 = 0x1af;
+pub const KEY_BRIGHTNESS_TOGGLE: u16 = KEY_DISPLAYTOGGLE;
+pub const KEY_SPELLCHECK: u16 = 0x1b0;
+pub const KEY_LOGOFF: u16 = 0x1b1;
+
+pub const KEY_DOLLAR: u16 = 0x1b2;
+pub const KEY_EURO: u16 = 0x1b3;
+
+pub const KEY_FRAMEBACK: u16 = 0x1b4;
+pub const KEY_FRAMEFORWARD: u16 = 0x1b5;
+pub const KEY_CONTEXT_MENU: u16 = 0x1b6;
+pub const KEY_MEDIA_REPEAT: u16 = 0x1b7;
+pub const KEY_10CHANNELSUP: u16 = 0x1b8;
+pub const KEY_10CHANNELSDOWN: u16 = 0x1b9;
+pub const KEY_IMAGES: u16 = 0x1ba;
+
+pub const KEY_DEL_EOL: u16 = 0x1c0;
+pub const KEY_DEL_EOS: u16 = 0x1c1;
+pub const KEY_INS_LINE: u16 = 0x1c2;
+pub const KEY_DEL_LINE: u16 = 0x1c3;
+
+pub const KEY_FN: u16 = 0x1d0;
+pub const KEY_FN_ESC: u16 = 0x1d1;
+pub const KEY_FN_F1: u16 = 0x1d2;
+pub const KEY_FN_F2: u16 = 0x1d3;
+pub const KEY_FN_F3: u16 = 0x1d4;
+pub const KEY_FN_F4: u16 = 0x1d5;
+pub const KEY_FN_F5: u16 = 0x1d6;
+pub const KEY_FN_F6: u16 = 0x1d7;
+pub const KEY_FN_F7: u16 = 0x1d8;
+pub const KEY_FN_F8: u16 = 0x1d9;
+pub const KEY_FN_F9: u16 = 0x1da;
+pub const KEY_FN_F10: u16 = 0x1db;
+pub const KEY_FN_F11: u16 = 0x1dc;
+pub const KEY_FN_F12: u16 = 0x1dd;
+pub const KEY_FN_1: u16 = 0x1de;
+pub const KEY_FN_2: u16 = 0x1df;
+pub const KEY_FN_D: u16 = 0x1e0;
+pub const KEY_FN_E: u16 = 0x1e1;
+pub const KEY_FN_F: u16 = 0x1e2;
+pub const KEY_FN_S: u16 = 0x1e3;
+pub const KEY_FN_B: u16 = 0x1e4;
+
+pub const KEY_BRL_DOT1: u16 = 0x1f1;
+pub const KEY_BRL_DOT2: u16 = 0x1f2;
+pub const KEY_BRL_DOT3: u16 = 0x1f3;
+pub const KEY_BRL_DOT4: u16 = 0x1f4;
+pub const KEY_BRL_DOT5: u16 = 0x1f5;
+pub const KEY_BRL_DOT6: u16 = 0x1f6;
+pub const KEY_BRL_DOT7: u16 = 0x1f7;
+pub const KEY_BRL_DOT8: u16 = 0x1f8;
+pub const KEY_BRL_DOT9: u16 = 0x1f9;
+pub const KEY_BRL_DOT10: u16 = 0x1fa;
+
+pub const KEY_NUMERIC_0: u16 = 0x200;
+pub const KEY_NUMERIC_1: u16 = 0x201;
+pub const KEY_NUMERIC_2: u16 = 0x202;
+pub const KEY_NUMERIC_3: u16 = 0x203;
+pub const KEY_NUMERIC_4: u16 = 0x204;
+pub const KEY_NUMERIC_5: u16 = 0x205;
+pub const KEY_NUMERIC_6: u16 = 0x206;
+pub const KEY_NUMERIC_7: u16 = 0x207;
+pub const KEY_NUMERIC_8: u16 = 0x208;
+pub const KEY_NUMERIC_9: u16 = 0x209;
+pub const KEY_NUMERIC_STAR: u16 = 0x20a;
+pub const KEY_NUMERIC_POUND: u16 = 0x20b;
+pub const KEY_NUMERIC_A: u16 = 0x20c;
+pub const KEY_NUMERIC_B: u16 = 0x20d;
+pub const KEY_NUMERIC_C: u16 = 0x20e;
+pub const KEY_NUMERIC_D: u16 = 0x20f;
+
+pub const KEY_CAMERA_FOCUS: u16 = 0x210;
+pub const KEY_WPS_BUTTON: u16 = 0x211;
+
+pub const KEY_TOUCHPAD_TOGGLE: u16 = 0x212;
+pub const KEY_TOUCHPAD_ON: u16 = 0x213;
+pub const KEY_TOUCHPAD_OFF: u16 = 0x214;
+
+pub const KEY_CAMERA_ZOOMIN: u16 = 0x215;
+pub const KEY_CAMERA_ZOOMOUT: u16 = 0x216;
+pub const KEY_CAMERA_UP: u16 = 0x217;
+pub const KEY_CAMERA_DOWN: u16 = 0x218;
+pub const KEY_CAMERA_LEFT: u16 = 0x219;
+pub const KEY_CAMERA_RIGHT: u16 = 0x21a;
+
+pub const KEY_ATTENDANT_ON: u16 = 0x21b;
+pub const KEY_ATTENDANT_OFF: u16 = 0x21c;
+pub const KEY_ATTENDANT_TOGGLE: u16 = 0x21d;
+pub const KEY_LIGHTS_TOGGLE: u16 = 0x21e;
+
+pub const BTN_DPAD_UP: u16 = 0x220;
+pub const BTN_DPAD_DOWN: u16 = 0x221;
+pub const BTN_DPAD_LEFT: u16 = 0x222;
+pub const BTN_DPAD_RIGHT: u16 = 0x223;
+
+pub const KEY_ALS_TOGGLE: u16 = 0x230;
+pub const KEY_ROTATE_LOCK_TOGGLE: u16 = 0x231;
+
+pub const KEY_BUTTONCONFIG: u16 = 0x240;
+pub const KEY_TASKMANAGER: u16 = 0x241;
+pub const KEY_JOURNAL: u16 = 0x242;
+pub const KEY_CONTROLPANEL: u16 = 0x243;
+pub const KEY_APPSELECT: u16 = 0x244;
+pub const KEY_SCREENSAVER: u16 = 0x245;
+pub const KEY_VOICECOMMAND: u16 = 0x246;
+pub const KEY_ASSISTANT: u16 = 0x247;
+
+pub const KEY_BRIGHTNESS_MIN: u16 = 0x250;
+pub const KEY_BRIGHTNESS_MAX: u16 = 0x251;
+
+pub const KEY_KBDINPUTASSIST_PREV: u16 = 0x260;
+pub const KEY_KBDINPUTASSIST_NEXT: u16 = 0x261;
+pub const KEY_KBDINPUTASSIST_PREVGROUP: u16 = 0x262;
+pub const KEY_KBDINPUTASSIST_NEXTGROUP: u16 = 0x263;
+pub const KEY_KBDINPUTASSIST_ACCEPT: u16 = 0x264;
+pub const KEY_KBDINPUTASSIST_CANCEL: u16 = 0x265;
+
+pub const KEY_RIGHT_UP: u16 = 0x266;
+pub const KEY_RIGHT_DOWN: u16 = 0x267;
+pub const KEY_LEFT_UP: u16 = 0x268;
+pub const KEY_LEFT_DOWN: u16 = 0x269;
+
+pub const KEY_ROOT_MENU: u16 = 0x26a;
+
+pub const KEY_MEDIA_TOP_MENU: u16 = 0x26b;
+pub const KEY_NUMERIC_11: u16 = 0x26c;
+pub const KEY_NUMERIC_12: u16 = 0x26d;
+pub const KEY_AUDIO_DESC: u16 = 0x26e;
+pub const KEY_3D_MODE: u16 = 0x26f;
+pub const KEY_NEXT_FAVORITE: u16 = 0x270;
+pub const KEY_STOP_RECORD: u16 = 0x271;
+pub const KEY_PAUSE_RECORD: u16 = 0x272;
+pub const KEY_VOD: u16 = 0x273;
+pub const KEY_UNMUTE: u16 = 0x274;
+pub const KEY_FASTREVERSE: u16 = 0x275;
+pub const KEY_SLOWREVERSE: u16 = 0x276;
+pub const KEY_DATA: u16 = 0x277;
+pub const KEY_ONSCREEN_KEYBOARD: u16 = 0x278;
+
+pub const BTN_TRIGGER_HAPPY: u16 = 0x2c0;
+pub const BTN_TRIGGER_HAPPY1: u16 = 0x2c0;
+pub const BTN_TRIGGER_HAPPY2: u16 = 0x2c1;
+pub const BTN_TRIGGER_HAPPY3: u16 = 0x2c2;
+pub const BTN_TRIGGER_HAPPY4: u16 = 0x2c3;
+pub const BTN_TRIGGER_HAPPY5: u16 = 0x2c4;
+pub const BTN_TRIGGER_HAPPY6: u16 = 0x2c5;
+pub const BTN_TRIGGER_HAPPY7: u16 = 0x2c6;
+pub const BTN_TRIGGER_HAPPY8: u16 = 0x2c7;
+pub const BTN_TRIGGER_HAPPY9: u16 = 0x2c8;
+pub const BTN_TRIGGER_HAPPY10: u16 = 0x2c9;
+pub const BTN_TRIGGER_HAPPY11: u16 = 0x2ca;
+pub const BTN_TRIGGER_HAPPY12: u16 = 0x2cb;
+pub const BTN_TRIGGER_HAPPY13: u16 = 0x2cc;
+pub const BTN_TRIGGER_HAPPY14: u16 = 0x2cd;
+pub const BTN_TRIGGER_HAPPY15: u16 = 0x2ce;
+pub const BTN_TRIGGER_HAPPY16: u16 = 0x2cf;
+pub const BTN_TRIGGER_HAPPY17: u16 = 0x2d0;
+pub const BTN_TRIGGER_HAPPY18: u16 = 0x2d1;
+pub const BTN_TRIGGER_HAPPY19: u16 = 0x2d2;
+pub const BTN_TRIGGER_HAPPY20: u16 = 0x2d3;
+pub const BTN_TRIGGER_HAPPY21: u16 = 0x2d4;
+pub const BTN_TRIGGER_HAPPY22: u16 = 0x2d5;
+pub const BTN_TRIGGER_HAPPY23: u16 = 0x2d6;
+pub const BTN_TRIGGER_HAPPY24: u16 = 0x2d7;
+pub const BTN_TRIGGER_HAPPY25: u16 = 0x2d8;
+pub const BTN_TRIGGER_HAPPY26: u16 = 0x2d9;
+pub const BTN_TRIGGER_HAPPY27: u16 = 0x2da;
+pub const BTN_TRIGGER_HAPPY28: u16 = 0x2db;
+pub const BTN_TRIGGER_HAPPY29: u16 = 0x2dc;
+pub const BTN_TRIGGER_HAPPY30: u16 = 0x2dd;
+pub const BTN_TRIGGER_HAPPY31: u16 = 0x2de;
+pub const BTN_TRIGGER_HAPPY32: u16 = 0x2df;
+pub const BTN_TRIGGER_HAPPY33: u16 = 0x2e0;
+pub const BTN_TRIGGER_HAPPY34: u16 = 0x2e1;
+pub const BTN_TRIGGER_HAPPY35: u16 = 0x2e2;
+pub const BTN_TRIGGER_HAPPY36: u16 = 0x2e3;
+pub const BTN_TRIGGER_HAPPY37: u16 = 0x2e4;
+pub const BTN_TRIGGER_HAPPY38: u16 = 0x2e5;
+pub const BTN_TRIGGER_HAPPY39: u16 = 0x2e6;
+pub const BTN_TRIGGER_HAPPY40: u16 = 0x2e7;
+
+pub const KEY_MIN_INTERESTING: u16 = KEY_MUTE;
+pub const KEY_MAX: u16 = 0x2ff;
+pub const KEY_CNT: u16 = (KEY_MAX + 1);
+
+pub const REL_X: u16 = 0x00;
+pub const REL_Y: u16 = 0x01;
+pub const REL_Z: u16 = 0x02;
+pub const REL_RX: u16 = 0x03;
+pub const REL_RY: u16 = 0x04;
+pub const REL_RZ: u16 = 0x05;
+pub const REL_HWHEEL: u16 = 0x06;
+pub const REL_DIAL: u16 = 0x07;
+pub const REL_WHEEL: u16 = 0x08;
+pub const REL_MISC: u16 = 0x09;
+pub const REL_MAX: u16 = 0x0f;
+pub const REL_CNT: u16 = (REL_MAX + 1);
+
+pub const ABS_X: u16 = 0x00;
+pub const ABS_Y: u16 = 0x01;
+pub const ABS_Z: u16 = 0x02;
+pub const ABS_RX: u16 = 0x03;
+pub const ABS_RY: u16 = 0x04;
+pub const ABS_RZ: u16 = 0x05;
+pub const ABS_THROTTLE: u16 = 0x06;
+pub const ABS_RUDDER: u16 = 0x07;
+pub const ABS_WHEEL: u16 = 0x08;
+pub const ABS_GAS: u16 = 0x09;
+pub const ABS_BRAKE: u16 = 0x0a;
+pub const ABS_HAT0X: u16 = 0x10;
+pub const ABS_HAT0Y: u16 = 0x11;
+pub const ABS_HAT1X: u16 = 0x12;
+pub const ABS_HAT1Y: u16 = 0x13;
+pub const ABS_HAT2X: u16 = 0x14;
+pub const ABS_HAT2Y: u16 = 0x15;
+pub const ABS_HAT3X: u16 = 0x16;
+pub const ABS_HAT3Y: u16 = 0x17;
+pub const ABS_PRESSURE: u16 = 0x18;
+pub const ABS_DISTANCE: u16 = 0x19;
+pub const ABS_TILT_X: u16 = 0x1a;
+pub const ABS_TILT_Y: u16 = 0x1b;
+pub const ABS_TOOL_WIDTH: u16 = 0x1c;
+
+pub const ABS_VOLUME: u16 = 0x20;
+
+pub const ABS_MISC: u16 = 0x28;
+
+pub const ABS_MT_SLOT: u16 = 0x2f;
+pub const ABS_MT_TOUCH_MAJOR: u16 = 0x30;
+pub const ABS_MT_TOUCH_MINOR: u16 = 0x31;
+pub const ABS_MT_WIDTH_MAJOR: u16 = 0x32;
+pub const ABS_MT_WIDTH_MINOR: u16 = 0x33;
+pub const ABS_MT_ORIENTATION: u16 = 0x34;
+pub const ABS_MT_POSITION_X: u16 = 0x35;
+pub const ABS_MT_POSITION_Y: u16 = 0x36;
+pub const ABS_MT_TOOL_TYPE: u16 = 0x37;
+pub const ABS_MT_BLOB_ID: u16 = 0x38;
+pub const ABS_MT_TRACKING_ID: u16 = 0x39;
+pub const ABS_MT_PRESSURE: u16 = 0x3a;
+pub const ABS_MT_DISTANCE: u16 = 0x3b;
+pub const ABS_MT_TOOL_X: u16 = 0x3c;
+pub const ABS_MT_TOOL_Y: u16 = 0x3d;
+
+pub const ABS_MAX: u16 = 0x3f;
+pub const ABS_CNT: u16 = (ABS_MAX + 1);
+
+pub const MSC_SERIAL: u16 = 0x00;
+pub const MSC_PULSELED: u16 = 0x01;
+pub const MSC_GESTURE: u16 = 0x02;
+pub const MSC_RAW: u16 = 0x03;
+pub const MSC_SCAN: u16 = 0x04;
+pub const MSC_TIMESTAMP: u16 = 0x05;
+pub const MSC_MAX: u16 = 0x07;
+pub const MSC_CNT: u16 = (MSC_MAX + 1);
+
+pub const LED_NUML: u16 = 0x00;
+pub const LED_CAPSL: u16 = 0x01;
+pub const LED_SCROLLL: u16 = 0x02;
+pub const LED_COMPOSE: u16 = 0x03;
+pub const LED_KANA: u16 = 0x04;
+pub const LED_SLEEP: u16 = 0x05;
+pub const LED_SUSPEND: u16 = 0x06;
+pub const LED_MUTE: u16 = 0x07;
+pub const LED_MISC: u16 = 0x08;
+pub const LED_MAIL: u16 = 0x09;
+pub const LED_CHARGING: u16 = 0x0a;
+pub const LED_MAX: u16 = 0x0f;
+pub const LED_CNT: u16 = (LED_MAX + 1);
+
+pub const REP_DELAY: u16 = 0x00;
+pub const REP_PERIOD: u16 = 0x01;
+pub const REP_MAX: u16 = 0x01;
+pub const REP_CNT: u16 = (REP_MAX + 1);
+
+// Should match linux/virtio_input.h
+pub const VIRTIO_INPUT_CFG_ID_NAME: u8 = 0x01;
+pub const VIRTIO_INPUT_CFG_ID_SERIAL: u8 = 0x02;
+pub const VIRTIO_INPUT_CFG_PROP_BITS: u8 = 0x10;
+pub const VIRTIO_INPUT_CFG_EV_BITS: u8 = 0x11;
+pub const VIRTIO_INPUT_CFG_ABS_INFO: u8 = 0x12;
+pub const VIRTIO_INPUT_CFG_ID_DEVIDS: u8 = 0x03;
diff --git a/devices/src/virtio/input/defaults.rs b/devices/src/virtio/input/defaults.rs
new file mode 100644
index 0000000..4bf1ba2
--- /dev/null
+++ b/devices/src/virtio/input/defaults.rs
@@ -0,0 +1,229 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::collections::BTreeMap;
+
+use super::constants::*;
+use super::virtio_input_absinfo;
+use super::virtio_input_bitmap;
+use super::virtio_input_device_ids;
+use super::VirtioInputConfig;
+
+/// Instantiates a VirtioInputConfig object with the default configuration for a trackpad. It
+/// supports touch, left button and right button events, as well as X and Y axis.
+pub fn new_trackpad_config(width: u32, height: u32) -> VirtioInputConfig {
+ VirtioInputConfig::new(
+ virtio_input_device_ids::new(0, 0, 0, 0),
+ b"Crosvm Virtio Trackpad".to_vec(),
+ b"virtio-trackpad".to_vec(),
+ virtio_input_bitmap::new([0u8; 128]),
+ default_trackpad_events(),
+ default_trackpad_absinfo(width, height),
+ )
+}
+
+/// Instantiates a VirtioInputConfig object with the default configuration for a mouse.
+/// It supports left, right and middle buttons, as wel as X, Y and wheel relative axes.
+pub fn new_mouse_config() -> VirtioInputConfig {
+ VirtioInputConfig::new(
+ virtio_input_device_ids::new(0, 0, 0, 0),
+ b"Crosvm Virtio Mouse".to_vec(),
+ b"virtio-mouse".to_vec(),
+ virtio_input_bitmap::new([0u8; 128]),
+ default_mouse_events(),
+ BTreeMap::new(),
+ )
+}
+
+/// Instantiates a VirtioInputConfig object with the default configuration for a keyboard.
+/// It supports the same keys as a en-us keyboard and the CAPSLOCK, NUMLOCK and SCROLLLOCK leds.
+pub fn new_keyboard_config() -> VirtioInputConfig {
+ VirtioInputConfig::new(
+ virtio_input_device_ids::new(0, 0, 0, 0),
+ b"Crosvm Virtio Keyboard".to_vec(),
+ b"virtio-keyboard".to_vec(),
+ virtio_input_bitmap::new([0u8; 128]),
+ default_keyboard_events(),
+ BTreeMap::new(),
+ )
+}
+
+/// Instantiates a VirtioInputConfig object with the default configuration for a touchscreen (no
+/// multitouch support).
+pub fn new_single_touch_config(width: u32, height: u32) -> VirtioInputConfig {
+ VirtioInputConfig::new(
+ virtio_input_device_ids::new(0, 0, 0, 0),
+ b"Crosvm Virtio Touchscreen".to_vec(),
+ b"virtio-touchscreen".to_vec(),
+ virtio_input_bitmap::from_bits(&[INPUT_PROP_DIRECT]),
+ default_touchscreen_events(),
+ default_touchscreen_absinfo(width, height),
+ )
+}
+
+fn default_touchscreen_absinfo(width: u32, height: u32) -> BTreeMap<u16, virtio_input_absinfo> {
+ let mut absinfo: BTreeMap<u16, virtio_input_absinfo> = BTreeMap::new();
+ absinfo.insert(ABS_X, virtio_input_absinfo::new(0, width, 0, 0));
+ absinfo.insert(ABS_Y, virtio_input_absinfo::new(0, height, 0, 0));
+ absinfo
+}
+
+fn default_touchscreen_events() -> BTreeMap<u16, virtio_input_bitmap> {
+ let mut supported_events: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
+ supported_events.insert(EV_KEY, virtio_input_bitmap::from_bits(&[BTN_TOUCH]));
+ supported_events.insert(EV_ABS, virtio_input_bitmap::from_bits(&[ABS_X, ABS_Y]));
+ supported_events
+}
+
+fn default_trackpad_absinfo(width: u32, height: u32) -> BTreeMap<u16, virtio_input_absinfo> {
+ let mut absinfo: BTreeMap<u16, virtio_input_absinfo> = BTreeMap::new();
+ absinfo.insert(ABS_X, virtio_input_absinfo::new(0, width, 0, 0));
+ absinfo.insert(ABS_Y, virtio_input_absinfo::new(0, height, 0, 0));
+ absinfo
+}
+
+fn default_trackpad_events() -> BTreeMap<u16, virtio_input_bitmap> {
+ let mut supported_events: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
+ supported_events.insert(
+ EV_KEY,
+ virtio_input_bitmap::from_bits(&[BTN_TOOL_FINGER, BTN_TOUCH, BTN_LEFT, BTN_RIGHT]),
+ );
+ supported_events.insert(EV_ABS, virtio_input_bitmap::from_bits(&[ABS_X, ABS_Y]));
+ supported_events
+}
+
+fn default_mouse_events() -> BTreeMap<u16, virtio_input_bitmap> {
+ let mut supported_events: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
+ supported_events.insert(
+ EV_KEY,
+ virtio_input_bitmap::from_bits(&[BTN_LEFT, BTN_RIGHT, BTN_MIDDLE]),
+ );
+ supported_events.insert(
+ EV_REL,
+ virtio_input_bitmap::from_bits(&[REL_X, REL_Y, REL_WHEEL]),
+ );
+ supported_events
+}
+
+fn default_keyboard_events() -> BTreeMap<u16, virtio_input_bitmap> {
+ let mut supported_events: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
+ supported_events.insert(
+ EV_KEY,
+ virtio_input_bitmap::from_bits(&[
+ KEY_ESC,
+ KEY_1,
+ KEY_2,
+ KEY_3,
+ KEY_4,
+ KEY_5,
+ KEY_6,
+ KEY_7,
+ KEY_8,
+ KEY_9,
+ KEY_0,
+ KEY_MINUS,
+ KEY_EQUAL,
+ KEY_BACKSPACE,
+ KEY_TAB,
+ KEY_Q,
+ KEY_W,
+ KEY_E,
+ KEY_R,
+ KEY_T,
+ KEY_Y,
+ KEY_U,
+ KEY_I,
+ KEY_O,
+ KEY_P,
+ KEY_LEFTBRACE,
+ KEY_RIGHTBRACE,
+ KEY_ENTER,
+ KEY_LEFTCTRL,
+ KEY_A,
+ KEY_S,
+ KEY_D,
+ KEY_F,
+ KEY_G,
+ KEY_H,
+ KEY_J,
+ KEY_K,
+ KEY_L,
+ KEY_SEMICOLON,
+ KEY_APOSTROPHE,
+ KEY_GRAVE,
+ KEY_LEFTSHIFT,
+ KEY_BACKSLASH,
+ KEY_Z,
+ KEY_X,
+ KEY_C,
+ KEY_V,
+ KEY_B,
+ KEY_N,
+ KEY_M,
+ KEY_COMMA,
+ KEY_DOT,
+ KEY_SLASH,
+ KEY_RIGHTSHIFT,
+ KEY_KPASTERISK,
+ KEY_LEFTALT,
+ KEY_SPACE,
+ KEY_CAPSLOCK,
+ KEY_F1,
+ KEY_F2,
+ KEY_F3,
+ KEY_F4,
+ KEY_F5,
+ KEY_F6,
+ KEY_F7,
+ KEY_F8,
+ KEY_F9,
+ KEY_F10,
+ KEY_NUMLOCK,
+ KEY_SCROLLLOCK,
+ KEY_KP7,
+ KEY_KP8,
+ KEY_KP9,
+ KEY_KPMINUS,
+ KEY_KP4,
+ KEY_KP5,
+ KEY_KP6,
+ KEY_KPPLUS,
+ KEY_KP1,
+ KEY_KP2,
+ KEY_KP3,
+ KEY_KP0,
+ KEY_KPDOT,
+ KEY_F11,
+ KEY_F12,
+ KEY_KPENTER,
+ KEY_RIGHTCTRL,
+ KEY_KPSLASH,
+ KEY_SYSRQ,
+ KEY_RIGHTALT,
+ KEY_HOME,
+ KEY_UP,
+ KEY_PAGEUP,
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_END,
+ KEY_DOWN,
+ KEY_PAGEDOWN,
+ KEY_INSERT,
+ KEY_DELETE,
+ KEY_PAUSE,
+ KEY_MENU,
+ KEY_PRINT,
+ KEY_POWER,
+ ]),
+ );
+ supported_events.insert(
+ EV_REP,
+ virtio_input_bitmap::from_bits(&[REP_DELAY, REP_PERIOD]),
+ );
+ supported_events.insert(
+ EV_LED,
+ virtio_input_bitmap::from_bits(&[LED_CAPSL, LED_NUML, LED_SCROLLL]),
+ );
+ supported_events
+}
diff --git a/devices/src/virtio/input/evdev.rs b/devices/src/virtio/input/evdev.rs
new file mode 100644
index 0000000..6301dfa
--- /dev/null
+++ b/devices/src/virtio/input/evdev.rs
@@ -0,0 +1,257 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::unix::io::AsRawFd;
+
+use data_model::Le32;
+use sys_util::{ioctl_ior_nr, ioctl_iow_nr, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};
+
+use super::constants::*;
+use super::virtio_input_absinfo;
+use super::virtio_input_bitmap;
+use super::virtio_input_device_ids;
+use super::InputError;
+use super::Result;
+
+use std::collections::BTreeMap;
+use std::os::raw::c_uint;
+use std::ptr::null;
+
+const EVDEV: c_uint = 69;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct evdev_buffer {
+ buffer: [std::os::raw::c_uchar; 128],
+}
+
+impl evdev_buffer {
+ fn new() -> evdev_buffer {
+ evdev_buffer {
+ buffer: [0 as std::os::raw::c_uchar; 128],
+ }
+ }
+
+ fn get(&self, bit: usize) -> bool {
+ let idx = bit / 8;
+ let inner_bit = bit % 8;
+ self.buffer
+ .get(idx)
+ .map_or(false, |val| val & (1u8 << inner_bit) != 0)
+ }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct evdev_id {
+ bustype: u16,
+ vendor: u16,
+ product: u16,
+ version: u16,
+}
+
+impl evdev_id {
+ fn new() -> evdev_id {
+ evdev_id {
+ bustype: 0,
+ vendor: 0,
+ product: 0,
+ version: 0,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct evdev_abs_info {
+ // These should technically by signed ints, but Le32 is only compatible with u32 and we only
+ // forward the bytes but don't care about its actual values.
+ value: u32,
+ minimum: u32,
+ maximum: u32,
+ fuzz: u32,
+ flat: u32,
+ resolution: u32,
+}
+
+impl evdev_abs_info {
+ fn new() -> evdev_abs_info {
+ evdev_abs_info {
+ value: 0,
+ minimum: 0,
+ maximum: 0,
+ fuzz: 0,
+ flat: 0,
+ resolution: 0,
+ }
+ }
+}
+
+impl From<evdev_abs_info> for virtio_input_absinfo {
+ fn from(other: evdev_abs_info) -> Self {
+ virtio_input_absinfo {
+ min: Le32::from(other.minimum),
+ max: Le32::from(other.maximum),
+ fuzz: Le32::from(other.fuzz),
+ flat: Le32::from(other.flat),
+ }
+ }
+}
+
+ioctl_ior_nr!(EVIOCGID, EVDEV, 0x02, evdev_id);
+ioctl_ior_nr!(EVIOCGNAME, EVDEV, 0x06, evdev_buffer);
+ioctl_ior_nr!(EVIOCGUNIQ, EVDEV, 0x08, evdev_buffer);
+ioctl_ior_nr!(EVIOCGPROP, EVDEV, 0x09, evdev_buffer);
+ioctl_ior_nr!(EVIOCGBIT, EVDEV, 0x20 + evt, evdev_buffer, evt);
+ioctl_ior_nr!(EVIOCGABS, EVDEV, 0x40 + abs, evdev_abs_info, abs);
+ioctl_iow_nr!(EVIOCGRAB, EVDEV, 0x90, u32);
+
+fn errno() -> sys_util::Error {
+ sys_util::Error::last()
+}
+
+/// 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> {
+ 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)
+ };
+ if len < 0 {
+ return Err(InputError::EvdevIdError(errno()));
+ }
+ Ok(virtio_input_device_ids::new(
+ dev_id.bustype,
+ dev_id.vendor,
+ dev_id.product,
+ dev_id.version,
+ ))
+}
+
+/// Gets the name of an event device (see EVIOCGNAME ioctl for details).
+pub fn name<T: AsRawFd>(fd: &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)
+ };
+ if len < 0 {
+ return Err(InputError::EvdevNameError(errno()));
+ }
+ Ok(name.buffer[0..len as usize].to_vec())
+}
+
+/// 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>> {
+ 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)
+ };
+ if len < 0 {
+ return Err(InputError::EvdevSerialError(errno()));
+ }
+ Ok(uniq.buffer[0..len as usize].to_vec())
+}
+
+/// Gets the properties of an event device (see EVIOCGPROP ioctl for details).
+pub fn properties<T: AsRawFd>(fd: &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)
+ };
+ if len < 0 {
+ return Err(InputError::EvdevPropertiesError(errno()));
+ }
+ Ok(virtio_input_bitmap::new(props.buffer))
+}
+
+/// 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>> {
+ 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)
+ };
+ if len < 0 {
+ return Err(InputError::EvdevEventTypesError(errno()));
+ }
+
+ // no need to ask for zero (EV_SYN) since it's always supported and treated as a special case
+ for ev in 1..EV_MAX {
+ if ev == EV_REP || !evt_types.get(ev as usize) {
+ // Event type not supported, skip it.
+ continue;
+ }
+ // Create a new zero-filled buffer every time to avoid carry-overs.
+ let mut evt_codes = 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(ev as c_uint), &mut evt_codes)
+ };
+ if len < 0 {
+ return Err(InputError::EvdevEventTypesError(errno()));
+ }
+ evts.insert(ev, virtio_input_bitmap::new(evt_codes.buffer));
+ }
+ Ok(evts)
+}
+
+/// 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> {
+ let mut ret: BTreeMap<u16, virtio_input_absinfo> = BTreeMap::new();
+
+ for abs in 0..ABS_MAX {
+ // Create a new one, zero-ed out every time to avoid carry-overs.
+ let mut abs_info = evdev_abs_info::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, EVIOCGABS(abs as c_uint), &mut abs_info)
+ };
+ if len > 0 {
+ ret.insert(abs, virtio_input_absinfo::from(abs_info));
+ }
+ }
+ ret
+}
+
+/// 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
+/// the host.
+pub fn grab_evdev<T: AsRawFd>(fd: &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)
+ };
+ if ret == 0 {
+ Ok(())
+ } else {
+ Err(InputError::EvdevGrabError(errno()))
+ }
+}
+
+pub fn ungrab_evdev<T: AsRawFd>(fd: &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>())
+ };
+ if ret == 0 {
+ Ok(())
+ } else {
+ Err(InputError::EvdevGrabError(errno()))
+ }
+}
diff --git a/devices/src/virtio/input/event_source.rs b/devices/src/virtio/input/event_source.rs
new file mode 100644
index 0000000..a92dc83
--- /dev/null
+++ b/devices/src/virtio/input/event_source.rs
@@ -0,0 +1,519 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::constants::*;
+use super::evdev::{grab_evdev, ungrab_evdev};
+use super::virtio_input_event;
+use super::InputError;
+use super::Result;
+use data_model::DataInit;
+use std::collections::VecDeque;
+use std::io::Read;
+use std::io::Write;
+use std::mem::size_of;
+use std::os::unix::io::{AsRawFd, RawFd};
+use sys_util::{error, warn};
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct input_event {
+ timestamp_fields: [u64; 2],
+ pub type_: u16,
+ pub code: u16,
+ pub value: u32,
+}
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for input_event {}
+
+impl input_event {
+ const EVENT_SIZE: usize = size_of::<input_event>();
+
+ fn from_virtio_input_event(other: &virtio_input_event) -> input_event {
+ input_event {
+ timestamp_fields: [0, 0],
+ type_: other.type_.into(),
+ code: other.code.into(),
+ value: other.value.into(),
+ }
+ }
+}
+
+/// 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: Read + Write + AsRawFd {
+ /// Perform any necessary initialization before receiving and sending events from/to the source.
+ fn init(&mut self) -> Result<()> {
+ Ok(())
+ }
+ /// Perform any necessary cleanup when the device will no longer be used.
+ fn finalize(&mut self) -> Result<()> {
+ Ok(())
+ }
+
+ /// Receive events from the source, filters them and stores them in a queue for future
+ /// consumption by reading from this object. Returns the number of new non filtered events
+ /// received. This function may block waiting for events to be available.
+ fn receive_events(&mut self) -> Result<usize>;
+ /// Returns the number of received events that have not been filtered or consumed yet.
+ fn available_events_count(&self) -> usize;
+}
+
+// Try to read 16 events at a time to match what the linux guest driver does.
+const READ_BUFFER_SIZE: usize = 16 * size_of::<input_event>();
+
+// The read buffer needs to be aligned to the alignment of input_event, which is aligned as u64
+#[repr(align(8))]
+pub struct ReadBuffer {
+ buffer: [u8; READ_BUFFER_SIZE],
+}
+
+/// Encapsulates implementation details common to all kinds of event sources.
+pub struct EventSourceImpl<T> {
+ source: T,
+ queue: VecDeque<virtio_input_event>,
+ read_buffer: ReadBuffer,
+ // The read index accounts for incomplete events read previously.
+ read_idx: usize,
+}
+
+// Reads input events from the source.
+// Events are originally read as input_event structs and converted to virtio_input_event internally.
+impl<T: Read> EventSourceImpl<T> {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ let mut bytes = 0usize;
+ for evt_slice in buf.chunks_exact_mut(virtio_input_event::EVENT_SIZE) {
+ match self.queue.pop_front() {
+ None => {
+ break;
+ }
+ Some(evt) => {
+ evt_slice.copy_from_slice(evt.as_slice());
+ bytes += evt_slice.len();
+ }
+ }
+ }
+ Ok(bytes)
+ }
+}
+
+// Writes input events to the source.
+// Events come as virtio_input_event structs and are converted to input_event internally.
+impl<T: Write> EventSourceImpl<T> {
+ fn write<F: Fn(&virtio_input_event) -> bool>(
+ &mut self,
+ buf: &[u8],
+ event_filter: F,
+ ) -> std::io::Result<usize> {
+ for evt_slice in buf.chunks_exact(virtio_input_event::EVENT_SIZE) {
+ // Don't use from_slice() here, the buffer is not guaranteed to be properly aligned.
+ let mut vio_evt = virtio_input_event::new(0, 0, 0);
+ vio_evt.as_mut_slice().copy_from_slice(evt_slice);
+ if !event_filter(&vio_evt) {
+ continue;
+ }
+ let evt = input_event::from_virtio_input_event(&vio_evt);
+ self.source.write_all(evt.as_slice())?;
+ }
+
+ let len = buf.len() - buf.len() % virtio_input_event::EVENT_SIZE;
+ Ok(len)
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ self.source.flush()
+ }
+}
+
+impl<T: AsRawFd> EventSourceImpl<T> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.source.as_raw_fd()
+ }
+}
+
+impl<T> EventSourceImpl<T>
+where
+ T: Read + Write,
+{
+ // Receive events from the source and store them in a queue, unless they should be filtered out.
+ fn receive_events<F: Fn(&input_event) -> bool>(&mut self, event_filter: F) -> Result<usize> {
+ let read = self
+ .source
+ .read(&mut self.read_buffer.buffer[self.read_idx..])
+ .map_err(InputError::EventsReadError)?;
+ let buff_size = read + self.read_idx;
+
+ for evt_slice in self.read_buffer.buffer[..buff_size].chunks_exact(input_event::EVENT_SIZE)
+ {
+ let input_evt = match input_event::from_slice(evt_slice) {
+ Some(x) => x,
+ None => {
+ // This shouldn't happen because all slices (even the last one) are guaranteed
+ // to have the correct size and be properly aligned.
+ error!(
+ "Failed converting a slice of sice {} to input_event",
+ evt_slice.len()
+ );
+ // Skipping the event here effectively means no events will be received, because
+ // if from_slice fails once it will fail always.
+ continue;
+ }
+ };
+ if !event_filter(&input_evt) {
+ continue;
+ }
+ let vio_evt = virtio_input_event::from_input_event(input_evt);
+ self.queue.push_back(vio_evt);
+ }
+
+ let remainder = buff_size % input_event::EVENT_SIZE;
+ // If there is an incomplete event at the end of the buffer, it needs to be moved to the
+ // beginning and the next read operation must write right after it.
+ if remainder != 0 {
+ warn!("read incomplete event from source");
+ // The copy should only happen if there is at least one complete event in the buffer,
+ // otherwise source and destination would be the same.
+ if buff_size != remainder {
+ let (des, src) = self.read_buffer.buffer.split_at_mut(buff_size - remainder);
+ des[..remainder].copy_from_slice(src);
+ }
+ }
+ self.read_idx = remainder;
+
+ let received_events = buff_size / input_event::EVENT_SIZE;
+
+ Ok(received_events)
+ }
+
+ fn available_events(&self) -> usize {
+ self.queue.len()
+ }
+
+ fn new(source: T) -> EventSourceImpl<T> {
+ EventSourceImpl {
+ source,
+ queue: VecDeque::new(),
+ read_buffer: ReadBuffer {
+ buffer: [0u8; READ_BUFFER_SIZE],
+ },
+ read_idx: 0,
+ }
+ }
+}
+
+/// Encapsulates a (unix) socket as an event source.
+pub struct SocketEventSource<T> {
+ evt_source_impl: EventSourceImpl<T>,
+}
+
+impl<T> SocketEventSource<T>
+where
+ T: Read + Write + AsRawFd,
+{
+ pub fn new(source: T) -> SocketEventSource<T> {
+ SocketEventSource {
+ evt_source_impl: EventSourceImpl::new(source),
+ }
+ }
+}
+
+impl<T: Read> Read for SocketEventSource<T> {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ self.evt_source_impl.read(buf)
+ }
+}
+
+impl<T> Write for SocketEventSource<T>
+where
+ T: Read + Write + AsRawFd,
+{
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ self.evt_source_impl.write(buf, |_evt| true)
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ self.evt_source_impl.flush()
+ }
+}
+
+impl<T: AsRawFd> AsRawFd for SocketEventSource<T> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.evt_source_impl.as_raw_fd()
+ }
+}
+
+impl<T> EventSource for SocketEventSource<T>
+where
+ T: Read + Write + AsRawFd,
+{
+ fn init(&mut self) -> Result<()> {
+ Ok(())
+ }
+
+ fn finalize(&mut self) -> Result<()> {
+ ungrab_evdev(self)
+ }
+
+ fn receive_events(&mut self) -> Result<usize> {
+ self.evt_source_impl.receive_events(|_evt| true)
+ }
+
+ fn available_events_count(&self) -> usize {
+ self.evt_source_impl.available_events()
+ }
+}
+
+/// Encapsulates an event device node as an event source
+pub struct EvdevEventSource<T> {
+ evt_source_impl: EventSourceImpl<T>,
+}
+
+impl<T> EvdevEventSource<T>
+where
+ T: Read + Write + AsRawFd,
+{
+ pub fn new(source: T) -> EvdevEventSource<T> {
+ EvdevEventSource {
+ evt_source_impl: EventSourceImpl::new(source),
+ }
+ }
+}
+
+impl<T: Read> Read for EvdevEventSource<T> {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ self.evt_source_impl.read(buf)
+ }
+}
+
+impl<T> Write for EvdevEventSource<T>
+where
+ T: Read + Write + AsRawFd,
+{
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ self.evt_source_impl.write(buf, |evt| {
+ // Miscellaneous events produced by the device are sent back to it by the kernel input
+ // subsystem, but because these events are handled by the host kernel as well as the
+ // guest the device would get them twice. Which would prompt the device to send the
+ // event to the guest again entering an infinite loop.
+ evt.type_ != EV_MSC
+ })
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ self.evt_source_impl.flush()
+ }
+}
+
+impl<T: AsRawFd> AsRawFd for EvdevEventSource<T> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.evt_source_impl.as_raw_fd()
+ }
+}
+
+impl<T> EventSource for EvdevEventSource<T>
+where
+ T: Read + Write + AsRawFd,
+{
+ fn init(&mut self) -> Result<()> {
+ grab_evdev(self)
+ }
+
+ fn finalize(&mut self) -> Result<()> {
+ ungrab_evdev(self)
+ }
+
+ fn receive_events(&mut self) -> Result<usize> {
+ self.evt_source_impl.receive_events(|_evt| true)
+ }
+
+ fn available_events_count(&self) -> usize {
+ self.evt_source_impl.available_events()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::virtio::input::event_source::input_event;
+ use crate::virtio::input::event_source::EventSourceImpl;
+ use crate::virtio::input::virtio_input_event;
+ use data_model::{DataInit, Le16, Le32};
+ use std::cmp::min;
+ use std::io::Read;
+ use std::io::Write;
+ use std::mem::size_of;
+
+ struct SourceMock {
+ events: Vec<u8>,
+ }
+
+ impl SourceMock {
+ fn new(evts: &Vec<input_event>) -> SourceMock {
+ let mut events: Vec<u8> = vec![];
+ for evt in evts {
+ for byte in evt.as_slice() {
+ events.push(byte.clone());
+ }
+ }
+ SourceMock { events }
+ }
+ }
+
+ impl Read for SourceMock {
+ fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {
+ let copy_size = min(buf.len(), self.events.len());
+ buf[..copy_size].copy_from_slice(&self.events[..copy_size]);
+ Ok(copy_size)
+ }
+ }
+ impl Write for SourceMock {
+ fn write(&mut self, buf: &[u8]) -> std::result::Result<usize, std::io::Error> {
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> std::result::Result<(), std::io::Error> {
+ Ok(())
+ }
+ }
+
+ #[test]
+ fn empty_new() {
+ let mut source = EventSourceImpl::new(SourceMock::new(&vec![]));
+ assert_eq!(
+ source.available_events(),
+ 0,
+ "zero events should be available"
+ );
+ let mut buffer = [0u8, 80];
+ assert_eq!(
+ source.read(&mut buffer).unwrap(),
+ 0,
+ "zero events should be read"
+ );
+ }
+
+ #[test]
+ fn empty_receive() {
+ let mut source = EventSourceImpl::new(SourceMock::new(&vec![]));
+ assert_eq!(
+ source.receive_events(|_| true).unwrap(),
+ 0,
+ "zero events should be received"
+ );
+ let mut buffer = [0u8, 80];
+ assert_eq!(
+ source.read(&mut buffer).unwrap(),
+ 0,
+ "zero events should be read"
+ );
+ }
+
+ fn instantiate_input_events(count: usize) -> Vec<input_event> {
+ let mut ret: Vec<input_event> = Vec::with_capacity(count);
+ for idx in 0..count {
+ ret.push(input_event {
+ timestamp_fields: [0, 0],
+ type_: 3 * (idx as u16) + 1,
+ code: 3 * (idx as u16) + 2,
+ value: 3 * (idx as u32) + 3,
+ });
+ }
+ ret
+ }
+
+ fn assert_events_match(e1: &virtio_input_event, e2: &input_event) {
+ assert_eq!(e1.type_, Le16::from(e2.type_), "type should match");
+ assert_eq!(e1.code, Le16::from(e2.code), "code should match");
+ assert_eq!(e1.value, Le32::from(e2.value), "value should match");
+ }
+
+ #[test]
+ fn partial_read() {
+ let evts = instantiate_input_events(4usize);
+ let mut source = EventSourceImpl::new(SourceMock::new(&evts));
+ assert_eq!(
+ source.receive_events(|_| true).unwrap(),
+ evts.len(),
+ "should receive all events"
+ );
+ let mut evt = virtio_input_event {
+ type_: Le16::from(0),
+ code: Le16::from(0),
+ value: Le32::from(0),
+ };
+ assert_eq!(
+ source.read(evt.as_mut_slice()).unwrap(),
+ virtio_input_event::EVENT_SIZE,
+ "should read a single event"
+ );
+ assert_events_match(&evt, &evts[0]);
+ }
+
+ #[test]
+ fn exact_read() {
+ const EVENT_COUNT: usize = 4;
+ let evts = instantiate_input_events(EVENT_COUNT);
+ let mut source = EventSourceImpl::new(SourceMock::new(&evts));
+ assert_eq!(
+ source.receive_events(|_| true).unwrap(),
+ evts.len(),
+ "should receive all events"
+ );
+ let mut vio_evts = [0u8; EVENT_COUNT * size_of::<virtio_input_event>()];
+ assert_eq!(
+ source.read(&mut vio_evts).unwrap(),
+ EVENT_COUNT * virtio_input_event::EVENT_SIZE,
+ "should read all events"
+ );
+
+ let mut chunks = vio_evts.chunks_exact(virtio_input_event::EVENT_SIZE);
+ for idx in 0..EVENT_COUNT {
+ let evt = virtio_input_event::from_slice(chunks.next().unwrap()).unwrap();
+ assert_events_match(&evt, &evts[idx]);
+ }
+ }
+
+ #[test]
+ fn over_read() {
+ const EVENT_COUNT: usize = 4;
+ let evts = instantiate_input_events(EVENT_COUNT);
+ let mut source = EventSourceImpl::new(SourceMock::new(&evts));
+ assert_eq!(
+ source.receive_events(|_| true).unwrap(),
+ evts.len(),
+ "should receive all events"
+ );
+ let mut vio_evts = [0u8; 2 * EVENT_COUNT * size_of::<virtio_input_event>()];
+ assert_eq!(
+ source.read(&mut vio_evts).unwrap(),
+ EVENT_COUNT * virtio_input_event::EVENT_SIZE,
+ "should only read available events"
+ );
+
+ let mut chunks = vio_evts.chunks_exact(virtio_input_event::EVENT_SIZE);
+ for idx in 0..EVENT_COUNT {
+ let evt = virtio_input_event::from_slice(chunks.next().unwrap()).unwrap();
+ assert_events_match(&evt, &evts[idx]);
+ }
+ }
+
+ #[test]
+ fn incomplete_read() {
+ const EVENT_COUNT: usize = 4;
+ let evts = instantiate_input_events(EVENT_COUNT);
+ let mut source = EventSourceImpl::new(SourceMock::new(&evts));
+ assert_eq!(
+ source.receive_events(|_| true).unwrap(),
+ evts.len(),
+ "should receive all events"
+ );
+ let mut vio_evts = [0u8; 3];
+ assert_eq!(
+ source.read(&mut vio_evts).unwrap(),
+ 0,
+ "shouldn't read incomplete events"
+ );
+ }
+}
diff --git a/devices/src/virtio/input/mod.rs b/devices/src/virtio/input/mod.rs
new file mode 100644
index 0000000..205d82f
--- /dev/null
+++ b/devices/src/virtio/input/mod.rs
@@ -0,0 +1,731 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[allow(dead_code)]
+mod constants;
+mod defaults;
+mod evdev;
+mod event_source;
+
+use self::constants::*;
+
+use std::os::unix::io::{AsRawFd, RawFd};
+
+use data_model::{DataInit, Le16, Le32};
+use sys_util::{error, warn, EventFd, GuestMemory, PollContext, PollToken};
+
+use self::event_source::{input_event, EvdevEventSource, EventSource, SocketEventSource};
+use super::{Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_INPUT};
+use std::cmp::min;
+use std::collections::BTreeMap;
+use std::fmt::{self, Display};
+use std::io::Read;
+use std::io::Write;
+use std::mem::size_of;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+const EVENT_QUEUE_SIZE: u16 = 64;
+const STATUS_QUEUE_SIZE: u16 = 64;
+const QUEUE_SIZES: &[u16] = &[EVENT_QUEUE_SIZE, STATUS_QUEUE_SIZE];
+
+#[derive(Debug)]
+pub enum InputError {
+ /// Failed to write events to the source
+ EventsWriteError(sys_util::GuestMemoryError),
+ /// Failed to read events from the source
+ EventsReadError(std::io::Error),
+ // Failed to get name of event device
+ EvdevIdError(sys_util::Error),
+ // Failed to get name of event device
+ EvdevNameError(sys_util::Error),
+ // Failed to get serial name of event device
+ EvdevSerialError(sys_util::Error),
+ // Failed to get properties of event device
+ EvdevPropertiesError(sys_util::Error),
+ // Failed to get event types supported by device
+ EvdevEventTypesError(sys_util::Error),
+ // Failed to get axis information of event device
+ EvdevAbsInfoError(sys_util::Error),
+ // Failed to grab event device
+ EvdevGrabError(sys_util::Error),
+}
+pub type Result<T> = std::result::Result<T, InputError>;
+
+impl Display for InputError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::InputError::*;
+
+ match self {
+ EventsWriteError(e) => write!(f, "failed to write events to the source: {}", e),
+ EventsReadError(e) => write!(f, "failed to read events from the source: {}", e),
+ EvdevIdError(e) => write!(f, "failed to get id of event device: {}", e),
+ EvdevNameError(e) => write!(f, "failed to get name of event device: {}", e),
+ EvdevSerialError(e) => write!(f, "failed to get serial name of event device: {}", e),
+ EvdevPropertiesError(e) => write!(f, "failed to get properties of event device: {}", e),
+ EvdevEventTypesError(e) => {
+ write!(f, "failed to get event types supported by device: {}", e)
+ }
+ EvdevAbsInfoError(e) => {
+ write!(f, "failed to get axis information of event device: {}", e)
+ }
+ EvdevGrabError(e) => write!(f, "failed to grab event device: {}", e),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Default, Debug)]
+#[repr(C)]
+pub struct virtio_input_device_ids {
+ bustype: Le16,
+ vendor: Le16,
+ product: Le16,
+ version: Le16,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_input_device_ids {}
+
+impl virtio_input_device_ids {
+ fn new(bustype: u16, product: u16, vendor: u16, version: u16) -> virtio_input_device_ids {
+ virtio_input_device_ids {
+ bustype: Le16::from(bustype),
+ vendor: Le16::from(vendor),
+ product: Le16::from(product),
+ version: Le16::from(version),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Default, Debug)]
+#[repr(C)]
+pub struct virtio_input_absinfo {
+ min: Le32,
+ max: Le32,
+ fuzz: Le32,
+ flat: Le32,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_input_absinfo {}
+
+impl virtio_input_absinfo {
+ fn new(min: u32, max: u32, fuzz: u32, flat: u32) -> virtio_input_absinfo {
+ virtio_input_absinfo {
+ min: Le32::from(min),
+ max: Le32::from(max),
+ fuzz: Le32::from(fuzz),
+ flat: Le32::from(flat),
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct virtio_input_config {
+ select: u8,
+ subsel: u8,
+ size: u8,
+ reserved: [u8; 5],
+ payload: [u8; 128],
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_input_config {}
+
+impl virtio_input_config {
+ fn new() -> virtio_input_config {
+ virtio_input_config {
+ select: 0,
+ subsel: 0,
+ size: 0,
+ reserved: [0u8; 5],
+ payload: [0u8; 128],
+ }
+ }
+
+ fn set_payload_slice(&mut self, slice: &[u8]) {
+ let bytes_written = match (&mut self.payload[..]).write(slice) {
+ Ok(x) => x,
+ Err(_) => {
+ // This won't happen because write is guaranteed to succeed with slices
+ unreachable!();
+ }
+ };
+ self.size = bytes_written as u8;
+ if bytes_written < slice.len() {
+ // This shouldn't happen since everywhere this function is called the size is guaranteed
+ // to be at most 128 bytes (the size of the payload)
+ warn!("Slice is too long to fit in payload");
+ }
+ }
+
+ fn set_payload_bitmap(&mut self, bitmap: &virtio_input_bitmap) {
+ self.size = bitmap.min_size();
+ self.payload.copy_from_slice(&bitmap.bitmap);
+ }
+
+ fn set_absinfo(&mut self, absinfo: &virtio_input_absinfo) {
+ self.set_payload_slice(absinfo.as_slice());
+ }
+
+ fn set_device_ids(&mut self, device_ids: &virtio_input_device_ids) {
+ self.set_payload_slice(device_ids.as_slice());
+ }
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct virtio_input_bitmap {
+ bitmap: [u8; 128],
+}
+
+impl virtio_input_bitmap {
+ fn new(bitmap: [u8; 128]) -> virtio_input_bitmap {
+ virtio_input_bitmap { bitmap }
+ }
+
+ fn len(&self) -> usize {
+ self.bitmap.len()
+ }
+
+ // Creates a bitmap from an array of bit indices
+ fn from_bits(set_indices: &[u16]) -> virtio_input_bitmap {
+ let mut ret = virtio_input_bitmap { bitmap: [0u8; 128] };
+ for idx in set_indices {
+ let byte_pos = (idx / 8) as usize;
+ let bit_byte = 1u8 << (idx % 8);
+ if byte_pos < ret.len() {
+ ret.bitmap[byte_pos] |= bit_byte;
+ } else {
+ // This would only happen if new event codes (or types, or ABS_*, etc) are defined to be
+ // larger than or equal to 1024, in which case a new version of the virtio input
+ // protocol needs to be defined.
+ // There is nothing we can do about this error except log it.
+ error!("Attempted to set an out of bounds bit: {}", idx);
+ }
+ }
+ ret
+ }
+
+ // Returns the length of the minimum array that can hold all set bits in the map
+ fn min_size(&self) -> u8 {
+ self.bitmap
+ .iter()
+ .rposition(|v| *v != 0)
+ .map_or(0, |i| i + 1) as u8
+ }
+}
+
+pub struct VirtioInputConfig {
+ select: u8,
+ subsel: u8,
+ device_ids: virtio_input_device_ids,
+ name: Vec<u8>,
+ serial_name: Vec<u8>,
+ properties: virtio_input_bitmap,
+ supported_events: BTreeMap<u16, virtio_input_bitmap>,
+ axis_info: BTreeMap<u16, virtio_input_absinfo>,
+}
+
+impl VirtioInputConfig {
+ const CONFIG_MEM_SIZE: usize = size_of::<virtio_input_config>();
+
+ fn new(
+ device_ids: virtio_input_device_ids,
+ name: Vec<u8>,
+ serial_name: Vec<u8>,
+ properties: virtio_input_bitmap,
+ supported_events: BTreeMap<u16, virtio_input_bitmap>,
+ axis_info: BTreeMap<u16, virtio_input_absinfo>,
+ ) -> VirtioInputConfig {
+ VirtioInputConfig {
+ select: 0,
+ subsel: 0,
+ device_ids,
+ name,
+ serial_name,
+ properties,
+ supported_events,
+ axis_info,
+ }
+ }
+
+ fn from_evdev<T: AsRawFd>(source: &T) -> Result<VirtioInputConfig> {
+ Ok(VirtioInputConfig::new(
+ evdev::device_ids(source)?,
+ evdev::name(source)?,
+ evdev::serial_name(source)?,
+ evdev::properties(source)?,
+ evdev::supported_events(source)?,
+ evdev::abs_info(source),
+ ))
+ }
+
+ fn validate_read_offsets(&self, offset: usize, len: usize) -> bool {
+ if offset + len > VirtioInputConfig::CONFIG_MEM_SIZE {
+ error!(
+ "Attempt to read from invalid config range: [{}..{}], valid ranges in [0..{}]",
+ offset,
+ offset + len,
+ VirtioInputConfig::CONFIG_MEM_SIZE
+ );
+ return false;
+ }
+ true
+ }
+
+ fn build_config_memory(&self) -> virtio_input_config {
+ let mut cfg = virtio_input_config::new();
+ cfg.select = self.select;
+ cfg.subsel = self.subsel;
+ match self.select {
+ VIRTIO_INPUT_CFG_ID_NAME => {
+ cfg.set_payload_slice(&self.name);
+ }
+ VIRTIO_INPUT_CFG_ID_SERIAL => {
+ cfg.set_payload_slice(&self.serial_name);
+ }
+ VIRTIO_INPUT_CFG_PROP_BITS => {
+ cfg.set_payload_bitmap(&self.properties);
+ }
+ VIRTIO_INPUT_CFG_EV_BITS => {
+ let ev_type = self.subsel as u16;
+ // zero is a special case: return all supported event types (just like EVIOCGBIT)
+ if ev_type == 0 {
+ let events_bm = virtio_input_bitmap::from_bits(
+ &self.supported_events.keys().cloned().collect::<Vec<u16>>(),
+ );
+ cfg.set_payload_bitmap(&events_bm);
+ } else if let Some(supported_codes) = self.supported_events.get(&ev_type) {
+ cfg.set_payload_bitmap(&supported_codes);
+ }
+ }
+ VIRTIO_INPUT_CFG_ABS_INFO => {
+ let abs_axis = self.subsel as u16;
+ if let Some(absinfo) = self.axis_info.get(&abs_axis) {
+ cfg.set_absinfo(absinfo);
+ } // else all zeroes in the payload
+ }
+ VIRTIO_INPUT_CFG_ID_DEVIDS => {
+ cfg.set_device_ids(&self.device_ids);
+ }
+ _ => {
+ warn!("Unsuported virtio input config selection: {}", self.select);
+ }
+ }
+ cfg
+ }
+
+ fn read(&self, offset: usize, data: &mut [u8]) {
+ let data_len = data.len();
+ if self.validate_read_offsets(offset, data_len) {
+ let config = self.build_config_memory();
+ data.clone_from_slice(&config.as_slice()[offset..offset + data_len]);
+ }
+ }
+
+ fn validate_write_offsets(&self, offset: usize, len: usize) -> bool {
+ const MAX_WRITABLE_BYTES: usize = 2;
+ if offset + len > MAX_WRITABLE_BYTES {
+ error!(
+ "Attempt to write to invalid config range: [{}..{}], valid ranges in [0..{}]",
+ offset,
+ offset + len,
+ MAX_WRITABLE_BYTES
+ );
+ return false;
+ }
+ true
+ }
+
+ fn write(&mut self, offset: usize, data: &[u8]) {
+ let len = data.len();
+ if self.validate_write_offsets(offset, len) {
+ let mut selectors: [u8; 2] = [self.select, self.subsel];
+ selectors[offset..offset + len].clone_from_slice(&data);
+ self.select = selectors[0];
+ self.subsel = selectors[1];
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_input_event {
+ type_: Le16,
+ code: Le16,
+ value: Le32,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_input_event {}
+
+impl virtio_input_event {
+ const EVENT_SIZE: usize = size_of::<virtio_input_event>();
+ fn new(type_: u16, code: u16, value: u32) -> virtio_input_event {
+ virtio_input_event {
+ type_: Le16::from(type_),
+ code: Le16::from(code),
+ value: Le32::from(value),
+ }
+ }
+
+ fn from_input_event(other: &input_event) -> virtio_input_event {
+ virtio_input_event {
+ type_: Le16::from(other.type_),
+ code: Le16::from(other.code),
+ value: Le32::from(other.value),
+ }
+ }
+}
+
+struct Worker<T: EventSource> {
+ event_source: T,
+ event_queue: Queue,
+ status_queue: Queue,
+ guest_memory: GuestMemory,
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+}
+
+impl<T: EventSource> Worker<T> {
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ self.interrupt_evt.write(1).unwrap();
+ }
+
+ // Send events from the source to the guest
+ fn send_events(&mut self) -> bool {
+ let queue = &mut self.event_queue;
+ let mut needs_interrupt = false;
+
+ // Only consume from the queue iterator if we know we have events to send
+ while self.event_source.available_events_count() > 0 {
+ match queue.pop(&self.guest_memory) {
+ None => {
+ break;
+ }
+ Some(avail_desc) => {
+ if !avail_desc.is_write_only() {
+ panic!("Received a read only descriptor on event queue");
+ }
+ let avail_events_size =
+ self.event_source.available_events_count() * virtio_input_event::EVENT_SIZE;
+ let len = min(avail_desc.len as usize, avail_events_size);
+ if let Err(e) =
+ self.guest_memory
+ .read_to_memory(avail_desc.addr, &self.event_source, len)
+ {
+ // Read is guaranteed to succeed here, so the only possible failure would be
+ // writing outside the guest memory region, which would mean the address and
+ // length given in the queue descriptor are wrong.
+ panic!("failed reading events into guest memory: {}", e);
+ }
+
+ queue.add_used(&self.guest_memory, avail_desc.index, len as u32);
+ needs_interrupt = true;
+ }
+ }
+ }
+
+ needs_interrupt
+ }
+
+ fn process_status_queue(&mut self) -> Result<bool> {
+ let queue = &mut self.status_queue;
+
+ let mut needs_interrupt = false;
+ while let Some(avail_desc) = queue.pop(&self.guest_memory) {
+ if !avail_desc.is_read_only() {
+ panic!("Received a writable descriptor on status queue");
+ }
+ let len = avail_desc.len as usize;
+ if len % virtio_input_event::EVENT_SIZE != 0 {
+ warn!(
+ "Ignoring buffer of unexpected size on status queue: {:0}",
+ len
+ );
+ } else {
+ self.guest_memory
+ .write_from_memory(avail_desc.addr, &self.event_source, len)
+ .map_err(InputError::EventsWriteError)?;
+ }
+
+ queue.add_used(&self.guest_memory, avail_desc.index, len as u32);
+ needs_interrupt = true;
+ }
+
+ Ok(needs_interrupt)
+ }
+
+ fn run(
+ &mut self,
+ event_queue_evt_fd: EventFd,
+ status_queue_evt_fd: EventFd,
+ kill_evt: EventFd,
+ ) {
+ if let Err(e) = self.event_source.init() {
+ error!("failed initializing event source: {}", e);
+ return;
+ }
+
+ #[derive(PollToken)]
+ enum Token {
+ EventQAvailable,
+ StatusQAvailable,
+ InputEventsAvailable,
+ InterruptResample,
+ Kill,
+ }
+ let poll_ctx: PollContext<Token> = match PollContext::new()
+ .and_then(|pc| {
+ pc.add(&event_queue_evt_fd, Token::EventQAvailable)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| {
+ pc.add(&status_queue_evt_fd, Token::StatusQAvailable)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| {
+ pc.add(&self.event_source, Token::InputEventsAvailable)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ {
+ Ok(poll_ctx) => poll_ctx,
+ Err(e) => {
+ error!("failed creating PollContext: {}", e);
+ return;
+ }
+ };
+
+ 'poll: loop {
+ let poll_events = match poll_ctx.wait() {
+ Ok(poll_events) => poll_events,
+ Err(e) => {
+ error!("failed polling for events: {}", e);
+ break;
+ }
+ };
+
+ let mut needs_interrupt = false;
+ for poll_event in poll_events.iter_readable() {
+ match poll_event.token() {
+ Token::EventQAvailable => {
+ if let Err(e) = event_queue_evt_fd.read() {
+ error!("failed reading event queue EventFd: {}", e);
+ break 'poll;
+ }
+ needs_interrupt |= self.send_events();
+ }
+ Token::StatusQAvailable => {
+ if let Err(e) = status_queue_evt_fd.read() {
+ error!("failed reading status queue EventFd: {}", e);
+ break 'poll;
+ }
+ match self.process_status_queue() {
+ Ok(b) => needs_interrupt |= b,
+ Err(e) => error!("failed processing status events: {}", e),
+ }
+ }
+ Token::InputEventsAvailable => match self.event_source.receive_events() {
+ Err(e) => error!("error receiving events: {}", e),
+ Ok(_cnt) => needs_interrupt |= self.send_events(),
+ },
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_evt.write(1).unwrap();
+ }
+ }
+ Token::Kill => {
+ let _ = kill_evt.read();
+ break 'poll;
+ }
+ }
+ }
+ if needs_interrupt {
+ self.signal_used_queue();
+ }
+ }
+
+ if let Err(e) = self.event_source.finalize() {
+ error!("failed finalizing event source: {}", e);
+ return;
+ }
+ }
+}
+
+/// Virtio input device
+
+pub struct Input<T: EventSource> {
+ kill_evt: Option<EventFd>,
+ config: VirtioInputConfig,
+ source: Option<T>,
+}
+
+impl<T: EventSource> Drop for Input<T> {
+ fn drop(&mut self) {
+ if let Some(kill_evt) = self.kill_evt.take() {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = kill_evt.write(1);
+ }
+ }
+}
+
+impl<T> VirtioDevice for Input<T>
+where
+ T: 'static + EventSource + Send,
+{
+ fn keep_fds(&self) -> Vec<RawFd> {
+ if let Some(source) = &self.source {
+ return vec![source.as_raw_fd()];
+ }
+ Vec::new()
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_INPUT
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn read_config(&self, offset: u64, data: &mut [u8]) {
+ self.config.read(offset as usize, data);
+ }
+
+ fn write_config(&mut self, offset: u64, data: &[u8]) {
+ self.config.write(offset as usize, data);
+ }
+
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ mut queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != 2 || queue_evts.len() != 2 {
+ return;
+ }
+
+ let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed to create kill EventFd pair: {}", e);
+ return;
+ }
+ };
+ self.kill_evt = Some(self_kill_evt);
+
+ // Status is queue 1, event is queue 0
+ let status_queue = queues.remove(1);
+ let status_queue_evt_fd = queue_evts.remove(1);
+
+ let event_queue = queues.remove(0);
+ let event_queue_evt_fd = queue_evts.remove(0);
+
+ if let Some(source) = self.source.take() {
+ let worker_result = thread::Builder::new()
+ .name(String::from("virtio_input"))
+ .spawn(move || {
+ let mut worker = Worker {
+ event_source: source,
+ event_queue,
+ status_queue,
+ guest_memory: mem,
+ interrupt_status: status,
+ interrupt_evt,
+ interrupt_resample_evt,
+ };
+ worker.run(event_queue_evt_fd, status_queue_evt_fd, kill_evt);
+ });
+
+ if let Err(e) = worker_result {
+ error!("failed to spawn virtio_input worker: {}", e);
+ return;
+ }
+ } else {
+ error!("tried to activate device without a source for events");
+ return;
+ }
+ }
+}
+
+/// Creates a new virtio input device from an event device node
+pub fn new_evdev<T>(source: T) -> Result<Input<EvdevEventSource<T>>>
+where
+ T: Read + Write + AsRawFd,
+{
+ Ok(Input {
+ kill_evt: None,
+ config: VirtioInputConfig::from_evdev(&source)?,
+ source: Some(EvdevEventSource::new(source)),
+ })
+}
+
+/// Creates a new virtio touch device which supports single touch only.
+pub fn new_single_touch<T>(
+ source: T,
+ width: u32,
+ height: u32,
+) -> Result<Input<SocketEventSource<T>>>
+where
+ T: Read + Write + AsRawFd,
+{
+ Ok(Input {
+ kill_evt: None,
+ config: defaults::new_single_touch_config(width, height),
+ source: Some(SocketEventSource::new(source)),
+ })
+}
+
+/// Creates a new virtio trackpad device which supports (single) touch, primary and secondary
+/// buttons as well as X and Y axis.
+pub fn new_trackpad<T>(source: T, width: u32, height: u32) -> Result<Input<SocketEventSource<T>>>
+where
+ T: Read + Write + AsRawFd,
+{
+ Ok(Input {
+ kill_evt: None,
+ config: defaults::new_trackpad_config(width, height),
+ source: Some(SocketEventSource::new(source)),
+ })
+}
+
+/// Creates a new virtio mouse which supports primary, secondary, wheel and REL events.
+pub fn new_mouse<T>(source: T) -> Result<Input<SocketEventSource<T>>>
+where
+ T: Read + Write + AsRawFd,
+{
+ Ok(Input {
+ kill_evt: None,
+ config: defaults::new_mouse_config(),
+ source: Some(SocketEventSource::new(source)),
+ })
+}
+
+/// Creates a new virtio keyboard, which supports the same events as an en-us physical keyboard.
+pub fn new_keyboard<T>(source: T) -> Result<Input<SocketEventSource<T>>>
+where
+ T: Read + Write + AsRawFd,
+{
+ Ok(Input {
+ kill_evt: None,
+ config: defaults::new_keyboard_config(),
+ source: Some(SocketEventSource::new(source)),
+ })
+}
diff --git a/devices/src/virtio/mod.rs b/devices/src/virtio/mod.rs
new file mode 100644
index 0000000..3531ccd
--- /dev/null
+++ b/devices/src/virtio/mod.rs
@@ -0,0 +1,87 @@
+// Copyright 2017 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.
+
+//! Implements virtio devices, queues, and transport mechanisms.
+
+mod balloon;
+mod block;
+#[cfg(feature = "gpu")]
+mod gpu;
+mod input;
+mod net;
+mod p9;
+mod pmem;
+mod queue;
+mod rng;
+#[cfg(feature = "tpm")]
+mod tpm;
+mod virtio_device;
+mod virtio_pci_common_config;
+mod virtio_pci_device;
+mod wl;
+
+pub mod resource_bridge;
+pub mod vhost;
+
+pub use self::balloon::*;
+pub use self::block::*;
+#[cfg(feature = "gpu")]
+pub use self::gpu::*;
+pub use self::input::*;
+pub use self::net::*;
+pub use self::p9::*;
+pub use self::pmem::*;
+pub use self::queue::*;
+pub use self::rng::*;
+#[cfg(feature = "tpm")]
+pub use self::tpm::*;
+pub use self::virtio_device::*;
+pub use self::virtio_pci_device::*;
+pub use self::wl::*;
+
+const DEVICE_ACKNOWLEDGE: u32 = 0x01;
+const DEVICE_DRIVER: u32 = 0x02;
+const DEVICE_DRIVER_OK: u32 = 0x04;
+const DEVICE_FEATURES_OK: u32 = 0x08;
+const DEVICE_FAILED: u32 = 0x80;
+
+// Types taken from linux/virtio_ids.h
+const TYPE_NET: u32 = 1;
+const TYPE_BLOCK: u32 = 2;
+const TYPE_RNG: u32 = 4;
+const TYPE_BALLOON: u32 = 5;
+#[allow(dead_code)]
+const TYPE_GPU: u32 = 16;
+const TYPE_9P: u32 = 9;
+const TYPE_INPUT: u32 = 18;
+const TYPE_VSOCK: u32 = 19;
+const TYPE_PMEM: u32 = 27;
+// Additional types invented by crosvm
+const TYPE_WL: u32 = 30;
+#[cfg(feature = "tpm")]
+const TYPE_TPM: u32 = 31;
+
+const VIRTIO_F_VERSION_1: u32 = 32;
+
+const INTERRUPT_STATUS_USED_RING: u32 = 0x1;
+const INTERRUPT_STATUS_CONFIG_CHANGED: u32 = 0x2;
+
+/// Offset from the base MMIO address of a virtio device used by the guest to notify the device of
+/// queue events.
+pub const NOTIFY_REG_OFFSET: u32 = 0x50;
+
+/// Returns a string representation of the given virtio device type number.
+pub fn type_to_str(type_: u32) -> Option<&'static str> {
+ Some(match type_ {
+ TYPE_NET => "net",
+ TYPE_BLOCK => "block",
+ TYPE_RNG => "rng",
+ TYPE_BALLOON => "balloon",
+ TYPE_GPU => "gpu",
+ TYPE_9P => "9p",
+ TYPE_VSOCK => "vsock",
+ TYPE_WL => "wl",
+ _ => return None,
+ })
+}
diff --git a/devices/src/virtio/net.rs b/devices/src/virtio/net.rs
new file mode 100644
index 0000000..61a82cc
--- /dev/null
+++ b/devices/src/virtio/net.rs
@@ -0,0 +1,520 @@
+// Copyright 2017 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::cmp;
+use std::fmt::{self, Display};
+use std::mem;
+use std::net::Ipv4Addr;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+use libc::EAGAIN;
+use net_sys;
+use net_util::{Error as TapError, MacAddress, TapT};
+use sys_util::Error as SysError;
+use sys_util::{error, warn, EventFd, GuestMemory, PollContext, PollToken};
+use virtio_sys::virtio_net::virtio_net_hdr_v1;
+use virtio_sys::{vhost, virtio_net};
+
+use super::{Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_NET};
+
+/// The maximum buffer size when segmentation offload is enabled. This
+/// includes the 12-byte virtio net header.
+/// http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html#x1-1740003
+const MAX_BUFFER_SIZE: usize = 65562;
+const QUEUE_SIZE: u16 = 256;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE, QUEUE_SIZE];
+
+#[derive(Debug)]
+pub enum NetError {
+ /// Creating kill eventfd failed.
+ CreateKillEventFd(SysError),
+ /// Creating PollContext failed.
+ CreatePollContext(SysError),
+ /// Cloning kill eventfd failed.
+ CloneKillEventFd(SysError),
+ /// Open tap device failed.
+ TapOpen(TapError),
+ /// Setting tap IP failed.
+ TapSetIp(TapError),
+ /// Setting tap netmask failed.
+ TapSetNetmask(TapError),
+ /// Setting tap mac address failed.
+ TapSetMacAddress(TapError),
+ /// Setting tap interface offload flags failed.
+ TapSetOffload(TapError),
+ /// Setting vnet header size failed.
+ TapSetVnetHdrSize(TapError),
+ /// Enabling tap interface failed.
+ TapEnable(TapError),
+ /// Validating tap interface failed.
+ TapValidate(String),
+ /// Error while polling for events.
+ PollError(SysError),
+}
+
+impl Display for NetError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::NetError::*;
+
+ match self {
+ CreateKillEventFd(e) => write!(f, "failed to create kill eventfd: {}", e),
+ CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
+ CloneKillEventFd(e) => write!(f, "failed to clone kill eventfd: {}", e),
+ TapOpen(e) => write!(f, "failed to open tap device: {}", e),
+ TapSetIp(e) => write!(f, "failed to set tap IP: {}", e),
+ TapSetNetmask(e) => write!(f, "failed to set tap netmask: {}", e),
+ TapSetMacAddress(e) => write!(f, "failed to set tap mac address: {}", e),
+ TapSetOffload(e) => write!(f, "failed to set tap interface offload flags: {}", e),
+ TapSetVnetHdrSize(e) => write!(f, "failed to set vnet header size: {}", e),
+ TapEnable(e) => write!(f, "failed to enable tap interface: {}", e),
+ TapValidate(s) => write!(f, "failed to validate tap interface: {}", s),
+ PollError(e) => write!(f, "error while polling for events: {}", e),
+ }
+ }
+}
+
+struct Worker<T: TapT> {
+ mem: GuestMemory,
+ rx_queue: Queue,
+ tx_queue: Queue,
+ tap: T,
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ rx_buf: [u8; MAX_BUFFER_SIZE],
+ rx_count: usize,
+ deferred_rx: bool,
+ // TODO(smbarber): http://crbug.com/753630
+ // Remove once MRG_RXBUF is supported and this variable is actually used.
+ #[allow(dead_code)]
+ acked_features: u64,
+}
+
+impl<T> Worker<T>
+where
+ T: TapT,
+{
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ self.interrupt_evt.write(1).unwrap();
+ }
+
+ // Copies a single frame from `self.rx_buf` into the guest. Returns true
+ // if a buffer was used, and false if the frame must be deferred until a buffer
+ // is made available by the driver.
+ fn rx_single_frame(&mut self) -> bool {
+ let mut next_desc = self.rx_queue.pop(&self.mem);
+
+ if next_desc.is_none() {
+ return false;
+ }
+
+ // We just checked that the head descriptor exists.
+ let head_index = next_desc.as_ref().unwrap().index;
+ let mut write_count = 0;
+
+ // Copy from frame into buffer, which may span multiple descriptors.
+ loop {
+ match next_desc {
+ Some(desc) => {
+ if !desc.is_write_only() {
+ break;
+ }
+ let limit = cmp::min(write_count + desc.len as usize, self.rx_count);
+ let source_slice = &self.rx_buf[write_count..limit];
+ let write_result = self.mem.write_at_addr(source_slice, desc.addr);
+
+ match write_result {
+ Ok(sz) => {
+ write_count += sz;
+ }
+ Err(e) => {
+ warn!("net: rx: failed to write slice: {}", e);
+ break;
+ }
+ };
+
+ if write_count >= self.rx_count {
+ break;
+ }
+ next_desc = desc.next_descriptor();
+ }
+ None => {
+ warn!(
+ "net: rx: buffer is too small to hold frame of size {}",
+ self.rx_count
+ );
+ break;
+ }
+ }
+ }
+
+ self.rx_queue
+ .add_used(&self.mem, head_index, write_count as u32);
+
+ // Interrupt the guest immediately for received frames to
+ // reduce latency.
+ self.signal_used_queue();
+
+ true
+ }
+
+ fn process_rx(&mut self) {
+ // Read as many frames as possible.
+ loop {
+ let res = self.tap.read(&mut self.rx_buf);
+ match res {
+ Ok(count) => {
+ self.rx_count = count;
+ if !self.rx_single_frame() {
+ self.deferred_rx = true;
+ break;
+ }
+ }
+ Err(e) => {
+ // The tap device is nonblocking, so any error aside from EAGAIN is
+ // unexpected.
+ if e.raw_os_error().unwrap() != EAGAIN {
+ warn!("net: rx: failed to read tap: {}", e);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ fn process_tx(&mut self) {
+ let mut frame = [0u8; MAX_BUFFER_SIZE];
+
+ while let Some(avail_desc) = self.tx_queue.pop(&self.mem) {
+ let head_index = avail_desc.index;
+ let mut next_desc = Some(avail_desc);
+ let mut read_count = 0;
+
+ // Copy buffer from across multiple descriptors.
+ while let Some(desc) = next_desc {
+ if desc.is_write_only() {
+ break;
+ }
+ let limit = cmp::min(read_count + desc.len as usize, frame.len());
+ let read_result = self
+ .mem
+ .read_at_addr(&mut frame[read_count..limit as usize], desc.addr);
+ match read_result {
+ Ok(sz) => {
+ read_count += sz;
+ }
+ Err(e) => {
+ warn!("net: tx: failed to read slice: {}", e);
+ break;
+ }
+ }
+ next_desc = desc.next_descriptor();
+ }
+
+ let write_result = self.tap.write(&frame[..read_count as usize]);
+ match write_result {
+ Ok(_) => {}
+ Err(e) => {
+ warn!("net: tx: error failed to write to tap: {}", e);
+ }
+ };
+
+ self.tx_queue.add_used(&self.mem, head_index, 0);
+ }
+
+ self.signal_used_queue();
+ }
+
+ fn run(
+ &mut self,
+ rx_queue_evt: EventFd,
+ tx_queue_evt: EventFd,
+ kill_evt: EventFd,
+ ) -> Result<(), NetError> {
+ #[derive(PollToken)]
+ enum Token {
+ // A frame is available for reading from the tap device to receive in the guest.
+ RxTap,
+ // The guest has made a buffer available to receive a frame into.
+ RxQueue,
+ // The transmit queue has a frame that is ready to send from the guest.
+ TxQueue,
+ // Check if any interrupts need to be re-asserted.
+ InterruptResample,
+ // crosvm has requested the device to shut down.
+ Kill,
+ }
+
+ let poll_ctx: PollContext<Token> = PollContext::new()
+ .and_then(|pc| pc.add(&self.tap, Token::RxTap).and(Ok(pc)))
+ .and_then(|pc| pc.add(&rx_queue_evt, Token::RxQueue).and(Ok(pc)))
+ .and_then(|pc| pc.add(&tx_queue_evt, Token::TxQueue).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ .map_err(NetError::CreatePollContext)?;
+
+ 'poll: loop {
+ let events = poll_ctx.wait().map_err(NetError::PollError)?;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::RxTap => {
+ // Process a deferred frame first if available. Don't read from tap again
+ // until we manage to receive this deferred frame.
+ if self.deferred_rx {
+ if self.rx_single_frame() {
+ self.deferred_rx = false;
+ } else {
+ continue;
+ }
+ }
+ self.process_rx();
+ }
+ Token::RxQueue => {
+ if let Err(e) = rx_queue_evt.read() {
+ error!("net: error reading rx queue EventFd: {}", e);
+ break 'poll;
+ }
+ // There should be a buffer available now to receive the frame into.
+ if self.deferred_rx && self.rx_single_frame() {
+ self.deferred_rx = false;
+ }
+ }
+ Token::TxQueue => {
+ if let Err(e) = tx_queue_evt.read() {
+ error!("net: error reading tx queue EventFd: {}", e);
+ break 'poll;
+ }
+ self.process_tx();
+ }
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_evt.write(1).unwrap();
+ }
+ }
+ Token::Kill => break 'poll,
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+pub struct Net<T: TapT> {
+ workers_kill_evt: Option<EventFd>,
+ kill_evt: EventFd,
+ tap: Option<T>,
+ avail_features: u64,
+ acked_features: u64,
+}
+
+impl<T> Net<T>
+where
+ T: TapT,
+{
+ /// Create a new virtio network device with the given IP address and
+ /// netmask.
+ pub fn new(
+ ip_addr: Ipv4Addr,
+ netmask: Ipv4Addr,
+ mac_addr: MacAddress,
+ ) -> Result<Net<T>, NetError> {
+ let tap: T = T::new(true).map_err(NetError::TapOpen)?;
+ tap.set_ip_addr(ip_addr).map_err(NetError::TapSetIp)?;
+ tap.set_netmask(netmask).map_err(NetError::TapSetNetmask)?;
+ tap.set_mac_address(mac_addr)
+ .map_err(NetError::TapSetMacAddress)?;
+
+ tap.enable().map_err(NetError::TapEnable)?;
+
+ Net::from(tap)
+ }
+
+ /// Creates a new virtio network device from a tap device that has already been
+ /// configured.
+ pub fn from(tap: T) -> Result<Net<T>, NetError> {
+ // This would also validate a tap created by Self::new(), but that's a good thing as it
+ // would ensure that any changes in the creation procedure are matched in the validation.
+ // Plus we still need to set the offload and vnet_hdr_size values.
+ validate_and_configure_tap(&tap)?;
+
+ let avail_features = 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM
+ | 1 << virtio_net::VIRTIO_NET_F_CSUM
+ | 1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4
+ | 1 << virtio_net::VIRTIO_NET_F_GUEST_UFO
+ | 1 << virtio_net::VIRTIO_NET_F_HOST_TSO4
+ | 1 << virtio_net::VIRTIO_NET_F_HOST_UFO
+ | 1 << vhost::VIRTIO_F_VERSION_1;
+
+ let kill_evt = EventFd::new().map_err(NetError::CreateKillEventFd)?;
+ Ok(Net {
+ workers_kill_evt: Some(kill_evt.try_clone().map_err(NetError::CloneKillEventFd)?),
+ kill_evt,
+ tap: Some(tap),
+ avail_features,
+ acked_features: 0u64,
+ })
+ }
+}
+
+// Ensure that the tap interface has the correct flags and sets the offload and VNET header size
+// to the appropriate values.
+fn validate_and_configure_tap<T: TapT>(tap: &T) -> Result<(), NetError> {
+ let flags = tap.if_flags();
+ let required_flags = [
+ (net_sys::IFF_TAP, "IFF_TAP"),
+ (net_sys::IFF_NO_PI, "IFF_NO_PI"),
+ (net_sys::IFF_VNET_HDR, "IFF_VNET_HDR"),
+ ];
+ let missing_flags = required_flags
+ .iter()
+ .filter_map(
+ |(value, name)| {
+ if value & flags == 0 {
+ Some(name)
+ } else {
+ None
+ }
+ },
+ )
+ .collect::<Vec<_>>();
+
+ if !missing_flags.is_empty() {
+ return Err(NetError::TapValidate(format!(
+ "Missing flags: {:?}",
+ missing_flags
+ )));
+ }
+
+ // Set offload flags to match the virtio features below.
+ tap.set_offload(
+ net_sys::TUN_F_CSUM | net_sys::TUN_F_UFO | net_sys::TUN_F_TSO4 | net_sys::TUN_F_TSO6,
+ )
+ .map_err(NetError::TapSetOffload)?;
+
+ let vnet_hdr_size = mem::size_of::<virtio_net_hdr_v1>() as i32;
+ tap.set_vnet_hdr_size(vnet_hdr_size)
+ .map_err(NetError::TapSetVnetHdrSize)?;
+
+ Ok(())
+}
+
+impl<T> Drop for Net<T>
+where
+ T: TapT,
+{
+ fn drop(&mut self) {
+ // Only kill the child if it claimed its eventfd.
+ if self.workers_kill_evt.is_none() {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = self.kill_evt.write(1);
+ }
+ }
+}
+
+impl<T> VirtioDevice for Net<T>
+where
+ T: 'static + TapT,
+{
+ fn keep_fds(&self) -> Vec<RawFd> {
+ let mut keep_fds = Vec::new();
+
+ if let Some(tap) = &self.tap {
+ keep_fds.push(tap.as_raw_fd());
+ }
+
+ if let Some(workers_kill_evt) = &self.workers_kill_evt {
+ keep_fds.push(workers_kill_evt.as_raw_fd());
+ }
+
+ keep_fds
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_NET
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn features(&self) -> u64 {
+ self.avail_features
+ }
+
+ fn ack_features(&mut self, value: u64) {
+ let mut v = value;
+
+ // Check if the guest is ACK'ing a feature that we didn't claim to have.
+ let unrequested_features = v & !self.avail_features;
+ if unrequested_features != 0 {
+ warn!("net: virtio net got unknown feature ack: {:x}", v);
+
+ // Don't count these features as acked.
+ v &= !unrequested_features;
+ }
+ self.acked_features |= v;
+ }
+
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ mut queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != 2 || queue_evts.len() != 2 {
+ error!("net: expected 2 queues, got {}", queues.len());
+ return;
+ }
+
+ if let Some(tap) = self.tap.take() {
+ if let Some(kill_evt) = self.workers_kill_evt.take() {
+ let acked_features = self.acked_features;
+ let worker_result =
+ thread::Builder::new()
+ .name("virtio_net".to_string())
+ .spawn(move || {
+ // First queue is rx, second is tx.
+ let rx_queue = queues.remove(0);
+ let tx_queue = queues.remove(0);
+ let mut worker = Worker {
+ mem,
+ rx_queue,
+ tx_queue,
+ tap,
+ interrupt_status: status,
+ interrupt_evt,
+ interrupt_resample_evt,
+ rx_buf: [0u8; MAX_BUFFER_SIZE],
+ rx_count: 0,
+ deferred_rx: false,
+ acked_features,
+ };
+ let rx_queue_evt = queue_evts.remove(0);
+ let tx_queue_evt = queue_evts.remove(0);
+ let result = worker.run(rx_queue_evt, tx_queue_evt, kill_evt);
+ if let Err(e) = result {
+ error!("net worker thread exited with error: {}", e);
+ }
+ });
+
+ if let Err(e) = worker_result {
+ error!("failed to spawn virtio_net worker: {}", e);
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/devices/src/virtio/p9.rs b/devices/src/virtio/p9.rs
new file mode 100644
index 0000000..b5d0842
--- /dev/null
+++ b/devices/src/virtio/p9.rs
@@ -0,0 +1,443 @@
+// Copyright 2018 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::cmp::min;
+use std::fmt::{self, Display};
+use std::io::{self, Read, Write};
+use std::iter::Peekable;
+use std::mem;
+use std::os::unix::io::RawFd;
+use std::path::{Path, PathBuf};
+use std::result;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+use p9;
+use sys_util::{
+ error, warn, Error as SysError, EventFd, GuestAddress, GuestMemory, PollContext, PollToken,
+};
+use virtio_sys::vhost::VIRTIO_F_VERSION_1;
+
+use super::{DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_9P};
+
+const QUEUE_SIZE: u16 = 128;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
+
+// The only virtio_9p feature.
+const VIRTIO_9P_MOUNT_TAG: u8 = 0;
+
+/// Errors that occur during operation of a virtio 9P device.
+#[derive(Debug)]
+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),
+ /// Error while polling for events.
+ PollError(SysError),
+ /// Error while reading from the virtio queue's EventFd.
+ ReadQueueEventFd(SysError),
+ /// A request is missing readable descriptors.
+ NoReadableDescriptors,
+ /// A request is missing writable descriptors.
+ NoWritableDescriptors,
+ /// A descriptor contained an invalid guest address range.
+ InvalidGuestAddress(GuestAddress, u32),
+ /// Failed to signal the virio used queue.
+ SignalUsedQueue(SysError),
+ /// An internal I/O error occurred.
+ Internal(io::Error),
+}
+
+impl std::error::Error for P9Error {}
+
+impl Display for P9Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::P9Error::*;
+
+ match self {
+ TagTooLong(len) => write!(
+ f,
+ "P9 device tag is too long: len = {}, max = {}",
+ 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),
+ ReadQueueEventFd(err) => write!(f, "failed to read from virtio queue EventFd: {}", err),
+ NoReadableDescriptors => write!(f, "request does not have any readable descriptors"),
+ NoWritableDescriptors => write!(f, "request does not have any writable descriptors"),
+ InvalidGuestAddress(addr, len) => write!(
+ f,
+ "descriptor contained invalid guest address range: address = {}, len = {}",
+ addr, len
+ ),
+ SignalUsedQueue(err) => write!(f, "failed to signal used queue: {}", err),
+ Internal(err) => write!(f, "P9 internal server error: {}", err),
+ }
+ }
+}
+
+pub type P9Result<T> = result::Result<T, P9Error>;
+
+struct Reader<'a, I>
+where
+ I: Iterator<Item = DescriptorChain<'a>>,
+{
+ mem: &'a GuestMemory,
+ offset: u32,
+ iter: Peekable<I>,
+}
+
+impl<'a, I> Read for Reader<'a, I>
+where
+ I: Iterator<Item = DescriptorChain<'a>>,
+{
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let needs_advance = if let Some(current) = self.iter.peek() {
+ self.offset >= current.len
+ } else {
+ false
+ };
+
+ if needs_advance {
+ self.offset = 0;
+ self.iter.next();
+ }
+
+ if let Some(current) = self.iter.peek() {
+ debug_assert!(current.is_read_only());
+ let addr = current
+ .addr
+ .checked_add(self.offset as u64)
+ .ok_or_else(|| {
+ io::Error::new(
+ io::ErrorKind::InvalidData,
+ P9Error::InvalidGuestAddress(current.addr, current.len),
+ )
+ })?;
+ let len = min(buf.len(), (current.len - self.offset) as usize);
+ let count = self
+ .mem
+ .read_at_addr(&mut buf[..len], addr)
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
+
+ // |count| has to fit into a u32 because it must be less than or equal to
+ // |current.len|, which does fit into a u32.
+ self.offset += count as u32;
+
+ Ok(count)
+ } else {
+ // Nothing left to read.
+ Ok(0)
+ }
+ }
+}
+
+struct Writer<'a, I>
+where
+ I: Iterator<Item = DescriptorChain<'a>>,
+{
+ mem: &'a GuestMemory,
+ bytes_written: u32,
+ offset: u32,
+ iter: Peekable<I>,
+}
+
+impl<'a, I> Write for Writer<'a, I>
+where
+ I: Iterator<Item = DescriptorChain<'a>>,
+{
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let needs_advance = if let Some(current) = self.iter.peek() {
+ self.offset >= current.len
+ } else {
+ false
+ };
+
+ if needs_advance {
+ self.offset = 0;
+ self.iter.next();
+ }
+
+ if let Some(current) = self.iter.peek() {
+ debug_assert!(current.is_write_only());
+ let addr = current
+ .addr
+ .checked_add(self.offset as u64)
+ .ok_or_else(|| {
+ io::Error::new(
+ io::ErrorKind::InvalidData,
+ P9Error::InvalidGuestAddress(current.addr, current.len),
+ )
+ })?;
+
+ let len = min(buf.len(), (current.len - self.offset) as usize);
+ let count = self
+ .mem
+ .write_at_addr(&buf[..len], addr)
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
+
+ // |count| has to fit into a u32 because it must be less than or equal to
+ // |current.len|, which does fit into a u32.
+ self.offset += count as u32;
+ self.bytes_written += count as u32;
+
+ Ok(count)
+ } else {
+ // No more room in the descriptor chain.
+ Ok(0)
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ // Nothing to flush since the writes go straight into the buffer.
+ Ok(())
+ }
+}
+
+struct Worker {
+ mem: GuestMemory,
+ queue: Queue,
+ server: p9::Server,
+ irq_status: Arc<AtomicUsize>,
+ irq_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+}
+
+impl Worker {
+ fn signal_used_queue(&self) -> P9Result<()> {
+ self.irq_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ self.irq_evt.write(1).map_err(P9Error::SignalUsedQueue)
+ }
+
+ fn process_queue(&mut self) -> P9Result<()> {
+ while let Some(avail_desc) = self.queue.pop(&self.mem) {
+ let mut reader = Reader {
+ mem: &self.mem,
+ offset: 0,
+ iter: avail_desc.clone().into_iter().readable().peekable(),
+ };
+ let mut writer = Writer {
+ mem: &self.mem,
+ bytes_written: 0,
+ offset: 0,
+ iter: avail_desc.clone().into_iter().writable().peekable(),
+ };
+
+ self.server
+ .handle_message(&mut reader, &mut writer)
+ .map_err(P9Error::Internal)?;
+
+ self.queue
+ .add_used(&self.mem, avail_desc.index, writer.bytes_written);
+ }
+
+ self.signal_used_queue()?;
+
+ Ok(())
+ }
+
+ fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd) -> P9Result<()> {
+ #[derive(PollToken)]
+ enum Token {
+ // A request is ready on the queue.
+ QueueReady,
+ // Check if any interrupts need to be re-asserted.
+ InterruptResample,
+ // The parent thread requested an exit.
+ Kill,
+ }
+
+ let poll_ctx: PollContext<Token> = PollContext::new()
+ .and_then(|pc| pc.add(&queue_evt, Token::QueueReady).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ .map_err(P9Error::CreatePollContext)?;
+
+ loop {
+ let events = poll_ctx.wait().map_err(P9Error::PollError)?;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::QueueReady => {
+ queue_evt.read().map_err(P9Error::ReadQueueEventFd)?;
+ self.process_queue()?;
+ }
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.irq_status.load(Ordering::SeqCst) != 0 {
+ self.irq_evt.write(1).unwrap();
+ }
+ }
+ Token::Kill => return Ok(()),
+ }
+ }
+ }
+ }
+}
+
+/// Virtio device for sharing specific directories on the host system with the guest VM.
+pub struct P9 {
+ config: Vec<u8>,
+ server: Option<p9::Server>,
+ kill_evt: Option<EventFd>,
+ avail_features: u64,
+ acked_features: u64,
+ worker: Option<thread::JoinHandle<P9Result<()>>>,
+}
+
+impl P9 {
+ pub fn new<P: AsRef<Path>>(root: P, tag: &str) -> 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);
+ cfg.push((len >> 8) as u8);
+
+ cfg.write_all(tag.as_bytes()).map_err(P9Error::Internal)?;
+
+ Ok(P9 {
+ config: cfg,
+ server: Some(p9::Server::new(root)),
+ kill_evt: None,
+ avail_features: 1 << VIRTIO_9P_MOUNT_TAG | 1 << VIRTIO_F_VERSION_1,
+ acked_features: 0,
+ worker: None,
+ })
+ }
+}
+
+impl VirtioDevice for P9 {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ Vec::new()
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_9P
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn features(&self) -> u64 {
+ self.avail_features
+ }
+
+ fn ack_features(&mut self, value: u64) {
+ let mut v = value;
+
+ // Check if the guest is ACK'ing a feature that we didn't claim to have.
+ let unrequested_features = v & !self.avail_features;
+ if unrequested_features != 0 {
+ warn!("virtio_9p got unknown feature ack: {:x}", v);
+
+ // Don't count these features as acked.
+ v &= !unrequested_features;
+ }
+ self.acked_features |= v;
+ }
+
+ fn read_config(&self, offset: u64, data: &mut [u8]) {
+ if offset >= self.config.len() as u64 {
+ // Nothing to see here.
+ return;
+ }
+
+ // The config length cannot be more than ::std::u16::MAX + mem::size_of::<u16>(), which
+ // is significantly smaller than ::std::usize::MAX on the architectures we care about so
+ // if we reach this point then we know that `offset` will fit into a usize.
+ let offset = offset as usize;
+ let len = min(data.len(), self.config.len() - offset);
+ data[..len].copy_from_slice(&self.config[offset..offset + len])
+ }
+
+ fn activate(
+ &mut self,
+ guest_mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ mut queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != 1 || queue_evts.len() != 1 {
+ return;
+ }
+
+ let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed creating kill EventFd pair: {}", e);
+ return;
+ }
+ };
+ self.kill_evt = Some(self_kill_evt);
+
+ if let Some(server) = self.server.take() {
+ let worker_result =
+ thread::Builder::new()
+ .name("virtio_9p".to_string())
+ .spawn(move || {
+ let mut worker = Worker {
+ mem: guest_mem,
+ queue: queues.remove(0),
+ server,
+ irq_status: status,
+ irq_evt: interrupt_evt,
+ interrupt_resample_evt,
+ };
+
+ worker.run(queue_evts.remove(0), kill_evt)
+ });
+
+ match worker_result {
+ Ok(worker) => self.worker = Some(worker),
+ Err(e) => error!("failed to spawn virtio_9p worker: {}", e),
+ }
+ }
+ }
+}
+
+impl Drop for P9 {
+ fn drop(&mut self) {
+ if let Some(kill_evt) = self.kill_evt.take() {
+ if let Err(e) = kill_evt.write(1) {
+ error!("failed to kill virtio_9p worker thread: {}", e);
+ return;
+ }
+
+ // Only wait on the child thread if we were able to send it a kill event.
+ if let Some(worker) = self.worker.take() {
+ match worker.join() {
+ Ok(r) => {
+ if let Err(e) = r {
+ error!("virtio_9p worker thread exited with error: {}", e)
+ }
+ }
+ Err(e) => error!("virtio_9p worker thread panicked: {:?}", e),
+ }
+ }
+ }
+ }
+}
diff --git a/devices/src/virtio/pmem.rs b/devices/src/virtio/pmem.rs
new file mode 100644
index 0000000..5a4fa32
--- /dev/null
+++ b/devices/src/virtio/pmem.rs
@@ -0,0 +1,367 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::cmp;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::Write;
+use std::mem::{size_of, size_of_val};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::result;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+use sys_util::Result as SysResult;
+use sys_util::{
+ error, EventFd, GuestAddress, GuestMemory, GuestMemoryError, PollContext, PollToken,
+};
+
+use data_model::{DataInit, Le32, Le64};
+
+use super::{
+ DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_PMEM, VIRTIO_F_VERSION_1,
+};
+
+const QUEUE_SIZE: u16 = 256;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
+
+const VIRTIO_PMEM_REQ_TYPE_FLUSH: u32 = 0;
+const VIRTIO_PMEM_RESP_TYPE_OK: u32 = 0;
+const VIRTIO_PMEM_RESP_TYPE_EIO: u32 = 1;
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_pmem_config {
+ start_address: Le64,
+ size: Le64,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_pmem_config {}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_pmem_resp {
+ status_code: Le32,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_pmem_resp {}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_pmem_req {
+ type_: Le32,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_pmem_req {}
+
+#[derive(Debug)]
+enum ParseError {
+ /// Guest gave us bad memory addresses.
+ GuestMemory(GuestMemoryError),
+ /// Guest gave us a write only descriptor that protocol says to read from.
+ UnexpectedWriteOnlyDescriptor,
+ /// Guest gave us a read only descriptor that protocol says to write to.
+ UnexpectedReadOnlyDescriptor,
+ /// Guest gave us too few descriptors in a descriptor chain.
+ DescriptorChainTooShort,
+ /// Guest gave us a buffer that was too short to use.
+ BufferLengthTooSmall,
+ /// Guest sent us invalid request.
+ InvalidRequest,
+}
+
+impl Display for ParseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::ParseError::*;
+
+ match self {
+ BufferLengthTooSmall => write!(f, "buffer length too small"),
+ DescriptorChainTooShort => write!(f, "descriptor chain too short"),
+ GuestMemory(e) => write!(f, "bad guest memory address: {}", e),
+ InvalidRequest => write!(f, "invalid request"),
+ UnexpectedReadOnlyDescriptor => write!(f, "unexpected read-only descriptor"),
+ UnexpectedWriteOnlyDescriptor => write!(f, "unexpected write-only descriptor"),
+ }
+ }
+}
+
+enum Request {
+ Flush { status_address: GuestAddress },
+}
+
+impl Request {
+ fn parse(
+ avail_desc: &DescriptorChain,
+ memory: &GuestMemory,
+ ) -> result::Result<Request, ParseError> {
+ // The head contains the request type which MUST be readable.
+ if avail_desc.is_write_only() {
+ return Err(ParseError::UnexpectedWriteOnlyDescriptor);
+ }
+
+ if avail_desc.len as usize != size_of::<virtio_pmem_req>() {
+ return Err(ParseError::InvalidRequest);
+ }
+
+ let request: virtio_pmem_req = memory
+ .read_obj_from_addr(avail_desc.addr)
+ .map_err(ParseError::GuestMemory)?;
+
+ // Currently, there is only one virtio-pmem request, FLUSH.
+ if request.type_ != VIRTIO_PMEM_REQ_TYPE_FLUSH {
+ error!("unknown request type: {}", request.type_.to_native());
+ return Err(ParseError::InvalidRequest);
+ }
+
+ let status_desc = avail_desc
+ .next_descriptor()
+ .ok_or(ParseError::DescriptorChainTooShort)?;
+
+ // The status MUST always be writable
+ if status_desc.is_read_only() {
+ return Err(ParseError::UnexpectedReadOnlyDescriptor);
+ }
+
+ if (status_desc.len as usize) < size_of::<virtio_pmem_resp>() {
+ return Err(ParseError::BufferLengthTooSmall);
+ }
+
+ Ok(Request::Flush {
+ status_address: status_desc.addr,
+ })
+ }
+}
+
+struct Worker {
+ queue: Queue,
+ memory: GuestMemory,
+ disk_image: File,
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_event: EventFd,
+ interrupt_resample_event: EventFd,
+}
+
+impl Worker {
+ fn process_queue(&mut self) -> bool {
+ let mut needs_interrupt = false;
+ while let Some(avail_desc) = self.queue.pop(&self.memory) {
+ let len;
+ match Request::parse(&avail_desc, &self.memory) {
+ Ok(Request::Flush { status_address }) => {
+ let status_code = match self.disk_image.sync_all() {
+ Ok(()) => VIRTIO_PMEM_RESP_TYPE_OK,
+ Err(e) => {
+ error!("failed flushing disk image: {}", e);
+ VIRTIO_PMEM_RESP_TYPE_EIO
+ }
+ };
+
+ let response = virtio_pmem_resp {
+ status_code: status_code.into(),
+ };
+ len = match self.memory.write_obj_at_addr(response, status_address) {
+ Ok(_) => size_of::<virtio_pmem_resp>() as u32,
+ Err(e) => {
+ error!("bad guest memory address: {}", e);
+ 0
+ }
+ }
+ }
+ Err(e) => {
+ error!("failed processing available descriptor chain: {}", e);
+ len = 0;
+ }
+ }
+ self.queue.add_used(&self.memory, avail_desc.index, len);
+ needs_interrupt = true;
+ }
+
+ needs_interrupt
+ }
+
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ self.interrupt_event.write(1).unwrap();
+ }
+
+ fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd) {
+ #[derive(PollToken)]
+ enum Token {
+ QueueAvailable,
+ InterruptResample,
+ Kill,
+ }
+
+ let poll_ctx: PollContext<Token> = match PollContext::new()
+ .and_then(|pc| pc.add(&queue_evt, Token::QueueAvailable).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_event, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ {
+ Ok(pc) => pc,
+ Err(e) => {
+ error!("failed creating PollContext: {}", e);
+ return;
+ }
+ };
+
+ 'poll: loop {
+ let events = match poll_ctx.wait() {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed polling for events: {}", e);
+ break;
+ }
+ };
+
+ let mut needs_interrupt = false;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::QueueAvailable => {
+ if let Err(e) = queue_evt.read() {
+ error!("failed reading queue EventFd: {}", e);
+ break 'poll;
+ }
+ needs_interrupt |= self.process_queue();
+ }
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_event.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_event.write(1).unwrap();
+ }
+ }
+ Token::Kill => break 'poll,
+ }
+ }
+ if needs_interrupt {
+ self.signal_used_queue();
+ }
+ }
+ }
+}
+
+pub struct Pmem {
+ kill_event: Option<EventFd>,
+ disk_image: Option<File>,
+ mapping_address: GuestAddress,
+ mapping_size: u64,
+}
+
+impl Pmem {
+ pub fn new(
+ disk_image: File,
+ mapping_address: GuestAddress,
+ mapping_size: u64,
+ ) -> SysResult<Pmem> {
+ Ok(Pmem {
+ kill_event: None,
+ disk_image: Some(disk_image),
+ mapping_address,
+ mapping_size,
+ })
+ }
+}
+
+impl Drop for Pmem {
+ fn drop(&mut self) {
+ if let Some(kill_evt) = self.kill_event.take() {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = kill_evt.write(1);
+ }
+ }
+}
+
+impl VirtioDevice for Pmem {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ if let Some(disk_image) = &self.disk_image {
+ vec![disk_image.as_raw_fd()]
+ } else {
+ vec![]
+ }
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_PMEM
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn features(&self) -> u64 {
+ 1 << VIRTIO_F_VERSION_1
+ }
+
+ fn read_config(&self, offset: u64, mut data: &mut [u8]) {
+ let config = virtio_pmem_config {
+ start_address: Le64::from(self.mapping_address.offset()),
+ size: Le64::from(self.mapping_size as u64),
+ };
+ let config_len = size_of_val(&config) as u64;
+ if offset >= config_len {
+ return;
+ }
+
+ if let Some(end) = offset.checked_add(data.len() as u64) {
+ let offset = offset as usize;
+ let end = cmp::min(end, config_len) as usize;
+ // This write can't fail, offset and end are checked against config_len.
+ data.write_all(&config.as_slice()[offset..end]).unwrap();
+ }
+ }
+
+ fn activate(
+ &mut self,
+ memory: GuestMemory,
+ interrupt_event: EventFd,
+ interrupt_resample_event: EventFd,
+ status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ mut queue_events: Vec<EventFd>,
+ ) {
+ if queues.len() != 1 || queue_events.len() != 1 {
+ return;
+ }
+
+ let queue = queues.remove(0);
+ let queue_event = queue_events.remove(0);
+
+ if let Some(disk_image) = self.disk_image.take() {
+ let (self_kill_event, kill_event) =
+ match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed creating kill EventFd pair: {}", e);
+ return;
+ }
+ };
+ self.kill_event = Some(self_kill_event);
+
+ let worker_result = thread::Builder::new()
+ .name("virtio_pmem".to_string())
+ .spawn(move || {
+ let mut worker = Worker {
+ memory,
+ disk_image,
+ queue,
+ interrupt_status: status,
+ interrupt_event,
+ interrupt_resample_event,
+ };
+ worker.run(queue_event, kill_event);
+ });
+ if let Err(e) = worker_result {
+ error!("failed to spawn virtio_pmem worker: {}", e);
+ return;
+ }
+ }
+ }
+}
diff --git a/devices/src/virtio/queue.rs b/devices/src/virtio/queue.rs
new file mode 100644
index 0000000..9324199
--- /dev/null
+++ b/devices/src/virtio/queue.rs
@@ -0,0 +1,344 @@
+// Copyright 2017 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::cmp::min;
+use std::num::Wrapping;
+use std::sync::atomic::{fence, Ordering};
+
+use sys_util::{error, GuestAddress, GuestMemory};
+
+const VIRTQ_DESC_F_NEXT: u16 = 0x1;
+const VIRTQ_DESC_F_WRITE: u16 = 0x2;
+#[allow(dead_code)]
+const VIRTQ_DESC_F_INDIRECT: u16 = 0x4;
+
+/// An iterator over a single descriptor chain. Not to be confused with AvailIter,
+/// which iterates over the descriptor chain heads in a queue.
+pub struct DescIter<'a> {
+ next: Option<DescriptorChain<'a>>,
+}
+
+impl<'a> DescIter<'a> {
+ /// Returns an iterator that only yields the readable descriptors in the chain.
+ pub fn readable(self) -> impl Iterator<Item = DescriptorChain<'a>> {
+ self.take_while(DescriptorChain::is_read_only)
+ }
+
+ /// Returns an iterator that only yields the writable descriptors in the chain.
+ pub fn writable(self) -> impl Iterator<Item = DescriptorChain<'a>> {
+ self.skip_while(DescriptorChain::is_read_only)
+ }
+}
+
+impl<'a> Iterator for DescIter<'a> {
+ type Item = DescriptorChain<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(current) = self.next.take() {
+ self.next = current.next_descriptor();
+ Some(current)
+ } else {
+ None
+ }
+ }
+}
+
+/// A virtio descriptor chain.
+#[derive(Clone)]
+pub struct DescriptorChain<'a> {
+ mem: &'a GuestMemory,
+ desc_table: GuestAddress,
+ queue_size: u16,
+ ttl: u16, // used to prevent infinite chain cycles
+
+ /// Index into the descriptor table
+ pub index: u16,
+
+ /// Guest physical address of device specific data
+ pub addr: GuestAddress,
+
+ /// Length of device specific data
+ pub len: u32,
+
+ /// Includes next, write, and indirect bits
+ pub flags: u16,
+
+ /// Index into the descriptor table of the next descriptor if flags has
+ /// the next bit set
+ pub next: u16,
+}
+
+impl<'a> DescriptorChain<'a> {
+ fn checked_new(
+ mem: &GuestMemory,
+ desc_table: GuestAddress,
+ queue_size: u16,
+ index: u16,
+ ) -> Option<DescriptorChain> {
+ if index >= queue_size {
+ return None;
+ }
+
+ let desc_head = match mem.checked_offset(desc_table, (index as u64) * 16) {
+ Some(a) => a,
+ None => return None,
+ };
+ // These reads can't fail unless Guest memory is hopelessly broken.
+ let addr = GuestAddress(mem.read_obj_from_addr::<u64>(desc_head).unwrap() as u64);
+ if mem.checked_offset(desc_head, 16).is_none() {
+ return None;
+ }
+ let len: u32 = mem.read_obj_from_addr(desc_head.unchecked_add(8)).unwrap();
+ let flags: u16 = mem.read_obj_from_addr(desc_head.unchecked_add(12)).unwrap();
+ let next: u16 = mem.read_obj_from_addr(desc_head.unchecked_add(14)).unwrap();
+ let chain = DescriptorChain {
+ mem,
+ desc_table,
+ queue_size,
+ ttl: queue_size,
+ index,
+ addr,
+ len,
+ flags,
+ next,
+ };
+
+ if chain.is_valid() {
+ Some(chain)
+ } else {
+ None
+ }
+ }
+
+ #[allow(clippy::if_same_then_else)]
+ fn is_valid(&self) -> bool {
+ if self
+ .mem
+ .checked_offset(self.addr, self.len as u64)
+ .is_none()
+ {
+ false
+ } else if self.has_next() && self.next >= self.queue_size {
+ false
+ } else {
+ true
+ }
+ }
+
+ /// Gets if this descriptor chain has another descriptor chain linked after it.
+ pub fn has_next(&self) -> bool {
+ self.flags & VIRTQ_DESC_F_NEXT != 0 && self.ttl > 1
+ }
+
+ /// If the driver designated this as a write only descriptor.
+ ///
+ /// If this is false, this descriptor is read only.
+ /// Write only means the the emulated device can write and the driver can read.
+ pub fn is_write_only(&self) -> bool {
+ self.flags & VIRTQ_DESC_F_WRITE != 0
+ }
+
+ /// If the driver designated this as a read only descriptor.
+ ///
+ /// If this is false, this descriptor is write only.
+ /// Read only means the emulated device can read and the driver can write.
+ pub fn is_read_only(&self) -> bool {
+ self.flags & VIRTQ_DESC_F_WRITE == 0
+ }
+
+ /// Gets the next descriptor in this descriptor chain, if there is one.
+ ///
+ /// Note that this is distinct from the next descriptor chain returned by `AvailIter`, which is
+ /// the head of the next _available_ descriptor chain.
+ pub fn next_descriptor(&self) -> Option<DescriptorChain<'a>> {
+ if self.has_next() {
+ DescriptorChain::checked_new(self.mem, self.desc_table, self.queue_size, self.next).map(
+ |mut c| {
+ c.ttl = self.ttl - 1;
+ c
+ },
+ )
+ } else {
+ None
+ }
+ }
+
+ /// Produces an iterator over all the descriptors in this chain.
+ pub fn into_iter(self) -> DescIter<'a> {
+ DescIter { next: Some(self) }
+ }
+}
+
+/// Consuming iterator over all available descriptor chain heads in the queue.
+pub struct AvailIter<'a, 'b> {
+ mem: &'a GuestMemory,
+ queue: &'b mut Queue,
+}
+
+impl<'a, 'b> Iterator for AvailIter<'a, 'b> {
+ type Item = DescriptorChain<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.queue.pop(self.mem)
+ }
+}
+
+#[derive(Clone)]
+/// A virtio queue's parameters.
+pub struct Queue {
+ /// The maximal size in elements offered by the device
+ pub max_size: u16,
+
+ /// The queue size in elements the driver selected
+ pub size: u16,
+
+ /// Inidcates if the queue is finished with configuration
+ pub ready: bool,
+
+ /// Guest physical address of the descriptor table
+ pub desc_table: GuestAddress,
+
+ /// Guest physical address of the available ring
+ pub avail_ring: GuestAddress,
+
+ /// Guest physical address of the used ring
+ pub used_ring: GuestAddress,
+
+ next_avail: Wrapping<u16>,
+ next_used: Wrapping<u16>,
+}
+
+impl Queue {
+ /// Constructs an empty virtio queue with the given `max_size`.
+ pub fn new(max_size: u16) -> Queue {
+ Queue {
+ max_size,
+ size: max_size,
+ ready: false,
+ desc_table: GuestAddress(0),
+ avail_ring: GuestAddress(0),
+ used_ring: GuestAddress(0),
+ next_avail: Wrapping(0),
+ next_used: Wrapping(0),
+ }
+ }
+
+ /// Return the actual size of the queue, as the driver may not set up a
+ /// queue as big as the device allows.
+ pub fn actual_size(&self) -> u16 {
+ min(self.size, self.max_size)
+ }
+
+ pub fn is_valid(&self, mem: &GuestMemory) -> bool {
+ let queue_size = self.actual_size() as usize;
+ let desc_table = self.desc_table;
+ let desc_table_size = 16 * queue_size;
+ let avail_ring = self.avail_ring;
+ let avail_ring_size = 6 + 2 * queue_size;
+ let used_ring = self.used_ring;
+ let used_ring_size = 6 + 8 * queue_size;
+ if !self.ready {
+ error!("attempt to use virtio queue that is not marked ready");
+ false
+ } else if self.size > self.max_size || self.size == 0 || (self.size & (self.size - 1)) != 0
+ {
+ error!("virtio queue with invalid size: {}", self.size);
+ false
+ } else if desc_table
+ .checked_add(desc_table_size as u64)
+ .map_or(true, |v| !mem.address_in_range(v))
+ {
+ error!(
+ "virtio queue descriptor table goes out of bounds: start:0x{:08x} size:0x{:08x}",
+ desc_table.offset(),
+ desc_table_size
+ );
+ false
+ } else if avail_ring
+ .checked_add(avail_ring_size as u64)
+ .map_or(true, |v| !mem.address_in_range(v))
+ {
+ error!(
+ "virtio queue available ring goes out of bounds: start:0x{:08x} size:0x{:08x}",
+ avail_ring.offset(),
+ avail_ring_size
+ );
+ false
+ } else if used_ring
+ .checked_add(used_ring_size as u64)
+ .map_or(true, |v| !mem.address_in_range(v))
+ {
+ error!(
+ "virtio queue used ring goes out of bounds: start:0x{:08x} size:0x{:08x}",
+ used_ring.offset(),
+ used_ring_size
+ );
+ false
+ } else {
+ true
+ }
+ }
+
+ /// If a new DescriptorHead is available, returns one and removes it from the queue.
+ pub fn pop<'a>(&mut self, mem: &'a GuestMemory) -> Option<DescriptorChain<'a>> {
+ if !self.is_valid(mem) {
+ return None;
+ }
+
+ 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();
+ let avail_len = Wrapping(avail_index) - self.next_avail;
+
+ if avail_len.0 > queue_size || self.next_avail == Wrapping(avail_index) {
+ return None;
+ }
+
+ let desc_idx_addr_offset = (4 + (self.next_avail.0 % queue_size) * 2) as u64;
+ let desc_idx_addr = mem.checked_offset(self.avail_ring, desc_idx_addr_offset)?;
+
+ // This index is checked below in checked_new.
+ let descriptor_index: u16 = mem.read_obj_from_addr(desc_idx_addr).unwrap();
+
+ let descriptor_chain =
+ DescriptorChain::checked_new(mem, self.desc_table, queue_size, descriptor_index);
+ if descriptor_chain.is_some() {
+ self.next_avail += Wrapping(1);
+ }
+ descriptor_chain
+ }
+
+ /// A consuming iterator over all available descriptor chain heads offered by the driver.
+ pub fn iter<'a, 'b>(&'b mut self, mem: &'a GuestMemory) -> AvailIter<'a, 'b> {
+ AvailIter { mem, queue: self }
+ }
+
+ /// Puts an available descriptor head into the used ring for use by the guest.
+ pub fn add_used(&mut self, mem: &GuestMemory, desc_index: u16, len: u32) {
+ if desc_index >= self.actual_size() {
+ error!(
+ "attempted to add out of bounds descriptor to used ring: {}",
+ desc_index
+ );
+ return;
+ }
+
+ let used_ring = self.used_ring;
+ let next_used = (self.next_used.0 % self.actual_size()) as usize;
+ let used_elem = used_ring.unchecked_add((4 + next_used * 8) as u64);
+
+ // These writes can't fail as we are guaranteed to be within the descriptor ring.
+ mem.write_obj_at_addr(desc_index as u32, used_elem).unwrap();
+ mem.write_obj_at_addr(len as u32, used_elem.unchecked_add(4))
+ .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();
+ }
+}
diff --git a/devices/src/virtio/resource_bridge.rs b/devices/src/virtio/resource_bridge.rs
new file mode 100644
index 0000000..d3a3375
--- /dev/null
+++ b/devices/src/virtio/resource_bridge.rs
@@ -0,0 +1,30 @@
+// Copyright 2018 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 defines the protocol between `virtio-wayland` and `virtio-gpu` for sharing resources
+//! that are backed by file descriptors.
+
+use std::fs::File;
+use std::io::Result;
+
+use msg_on_socket_derive::MsgOnSocket;
+use msg_socket::MsgSocket;
+
+#[derive(MsgOnSocket)]
+pub enum ResourceRequest {
+ GetResource { id: u32 },
+}
+
+#[derive(MsgOnSocket)]
+pub enum ResourceResponse {
+ Resource(File),
+ Invalid,
+}
+
+pub type ResourceRequestSocket = MsgSocket<ResourceRequest, ResourceResponse>;
+pub type ResourceResponseSocket = MsgSocket<ResourceResponse, ResourceRequest>;
+
+pub fn pair() -> Result<(ResourceRequestSocket, ResourceResponseSocket)> {
+ msg_socket::pair()
+}
diff --git a/devices/src/virtio/rng.rs b/devices/src/virtio/rng.rs
new file mode 100644
index 0000000..19f83bc
--- /dev/null
+++ b/devices/src/virtio/rng.rs
@@ -0,0 +1,229 @@
+// Copyright 2017 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;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+use sys_util::{error, EventFd, GuestMemory, PollContext, PollToken};
+
+use super::{Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_RNG};
+
+const QUEUE_SIZE: u16 = 256;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
+
+#[derive(Debug)]
+pub enum RngError {
+ /// Can't access /dev/urandom
+ AccessingRandomDev(io::Error),
+}
+pub type Result<T> = std::result::Result<T, RngError>;
+
+impl Display for RngError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::RngError::*;
+
+ match self {
+ AccessingRandomDev(e) => write!(f, "failed to access /dev/urandom: {}", e),
+ }
+ }
+}
+
+struct Worker {
+ queue: Queue,
+ mem: GuestMemory,
+ random_file: File,
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+}
+
+impl Worker {
+ fn process_queue(&mut self) -> bool {
+ let queue = &mut self.queue;
+
+ let mut needs_interrupt = false;
+ while let Some(avail_desc) = queue.pop(&self.mem) {
+ let mut len = 0;
+
+ // Drivers can only read from the random device.
+ if avail_desc.is_write_only() {
+ // Fill the read with data from the random device on the host.
+ if self
+ .mem
+ .read_to_memory(avail_desc.addr, &self.random_file, avail_desc.len as usize)
+ .is_ok()
+ {
+ len = avail_desc.len;
+ }
+ }
+
+ queue.add_used(&self.mem, avail_desc.index, len);
+ needs_interrupt = true;
+ }
+
+ needs_interrupt
+ }
+
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ self.interrupt_evt.write(1).unwrap();
+ }
+
+ fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd) {
+ #[derive(PollToken)]
+ enum Token {
+ QueueAvailable,
+ InterruptResample,
+ Kill,
+ }
+
+ let poll_ctx: PollContext<Token> = match PollContext::new()
+ .and_then(|pc| pc.add(&queue_evt, Token::QueueAvailable).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ {
+ Ok(pc) => pc,
+ Err(e) => {
+ error!("failed creating PollContext: {}", e);
+ return;
+ }
+ };
+
+ 'poll: loop {
+ let events = match poll_ctx.wait() {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed polling for events: {}", e);
+ break;
+ }
+ };
+
+ let mut needs_interrupt = false;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::QueueAvailable => {
+ if let Err(e) = queue_evt.read() {
+ error!("failed reading queue EventFd: {}", e);
+ break 'poll;
+ }
+ needs_interrupt |= self.process_queue();
+ }
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_evt.write(1).unwrap();
+ }
+ }
+ Token::Kill => break 'poll,
+ }
+ }
+ if needs_interrupt {
+ self.signal_used_queue();
+ }
+ }
+ }
+}
+
+/// Virtio device for exposing entropy to the guest OS through virtio.
+pub struct Rng {
+ kill_evt: Option<EventFd>,
+ random_file: Option<File>,
+}
+
+impl Rng {
+ /// Create a new virtio rng device that gets random data from /dev/urandom.
+ pub fn new() -> Result<Rng> {
+ let random_file = File::open("/dev/urandom").map_err(RngError::AccessingRandomDev)?;
+ Ok(Rng {
+ kill_evt: None,
+ random_file: Some(random_file),
+ })
+ }
+}
+
+impl Drop for Rng {
+ fn drop(&mut self) {
+ if let Some(kill_evt) = self.kill_evt.take() {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = kill_evt.write(1);
+ }
+ }
+}
+
+impl VirtioDevice for Rng {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ let mut keep_fds = Vec::new();
+
+ if let Some(random_file) = &self.random_file {
+ keep_fds.push(random_file.as_raw_fd());
+ }
+
+ keep_fds
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_RNG
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ mut queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != 1 || queue_evts.len() != 1 {
+ return;
+ }
+
+ let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed to create kill EventFd pair: {}", e);
+ return;
+ }
+ };
+ self.kill_evt = Some(self_kill_evt);
+
+ let queue = queues.remove(0);
+
+ if let Some(random_file) = self.random_file.take() {
+ let worker_result =
+ thread::Builder::new()
+ .name("virtio_rng".to_string())
+ .spawn(move || {
+ let mut worker = Worker {
+ queue,
+ mem,
+ random_file,
+ interrupt_status: status,
+ interrupt_evt,
+ interrupt_resample_evt,
+ };
+ worker.run(queue_evts.remove(0), kill_evt);
+ });
+
+ if let Err(e) = worker_result {
+ error!("failed to spawn virtio_rng worker: {}", e);
+ return;
+ }
+ }
+ }
+}
diff --git a/devices/src/virtio/tpm.rs b/devices/src/virtio/tpm.rs
new file mode 100644
index 0000000..8715abe
--- /dev/null
+++ b/devices/src/virtio/tpm.rs
@@ -0,0 +1,338 @@
+// Copyright 2018 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::env;
+use std::fmt::{self, Display};
+use std::fs;
+use std::ops::BitOrAssign;
+use std::os::unix::io::RawFd;
+use std::path::PathBuf;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+use sys_util::{error, EventFd, GuestMemory, GuestMemoryError, PollContext, PollToken};
+use tpm2;
+
+use super::{DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_TPM};
+
+// A single queue of size 2. The guest kernel driver will enqueue a single
+// descriptor chain containing one command buffer and one response buffer at a
+// time.
+const QUEUE_SIZE: u16 = 2;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
+
+// Maximum command or response message size permitted by this device
+// implementation. Named to match the equivalent constant in Linux's tpm.h.
+// There is no hard requirement that the value is the same but it makes sense.
+const TPM_BUFSIZE: usize = 4096;
+
+struct Worker {
+ queue: Queue,
+ mem: GuestMemory,
+ interrupt_status: Arc<AtomicUsize>,
+ queue_evt: EventFd,
+ kill_evt: EventFd,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ device: Device,
+}
+
+struct Device {
+ simulator: tpm2::Simulator,
+}
+
+// Checks that the input descriptor chain holds a read-only descriptor followed
+// by a write-only descriptor, as required of the guest's virtio tpm driver.
+//
+// Returns those descriptors as a tuple: `(read_desc, write_desc)`.
+fn two_input_descriptors(desc: DescriptorChain) -> Result<(DescriptorChain, DescriptorChain)> {
+ let read_desc = desc;
+ if !read_desc.is_read_only() {
+ return Err(Error::ExpectedReadOnly);
+ }
+
+ let write_desc = match read_desc.next_descriptor() {
+ Some(desc) => desc,
+ None => return Err(Error::ExpectedSecondBuffer),
+ };
+
+ if !write_desc.is_write_only() {
+ return Err(Error::ExpectedWriteOnly);
+ }
+
+ Ok((read_desc, write_desc))
+}
+
+impl Device {
+ fn perform_work(&mut self, mem: &GuestMemory, desc: DescriptorChain) -> Result<u32> {
+ let (read_desc, write_desc) = two_input_descriptors(desc)?;
+
+ if read_desc.len > TPM_BUFSIZE as u32 {
+ return Err(Error::CommandTooLong {
+ size: read_desc.len as usize,
+ });
+ }
+
+ let mut command = vec![0u8; read_desc.len as usize];
+ mem.read_exact_at_addr(&mut command, read_desc.addr)
+ .map_err(Error::Read)?;
+
+ let response = self.simulator.execute_command(&command);
+
+ if response.len() > TPM_BUFSIZE {
+ return Err(Error::ResponseTooLong {
+ size: response.len(),
+ });
+ }
+
+ if response.len() > write_desc.len as usize {
+ return Err(Error::BufferTooSmall {
+ size: write_desc.len as usize,
+ required: response.len(),
+ });
+ }
+
+ mem.write_all_at_addr(&response, write_desc.addr)
+ .map_err(Error::Write)?;
+
+ Ok(response.len() as u32)
+ }
+}
+
+impl Worker {
+ fn process_queue(&mut self) -> NeedsInterrupt {
+ let avail_desc = match self.queue.pop(&self.mem) {
+ Some(avail_desc) => avail_desc,
+ None => return NeedsInterrupt::No,
+ };
+
+ let index = avail_desc.index;
+
+ let len = match self.device.perform_work(&self.mem, avail_desc) {
+ Ok(len) => len,
+ Err(err) => {
+ error!("{}", err);
+ 0
+ }
+ };
+
+ self.queue.add_used(&self.mem, index, len);
+ NeedsInterrupt::Yes
+ }
+
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ let _ = self.interrupt_evt.write(1);
+ }
+
+ fn run(mut self) {
+ #[derive(PollToken, Debug)]
+ enum Token {
+ // A request is ready on the queue.
+ QueueAvailable,
+ // Check if any interrupts need to be re-asserted.
+ InterruptResample,
+ // The parent thread requested an exit.
+ Kill,
+ }
+
+ let poll_ctx = match PollContext::new()
+ .and_then(|pc| pc.add(&self.queue_evt, Token::QueueAvailable).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&self.kill_evt, Token::Kill).and(Ok(pc)))
+ {
+ Ok(pc) => pc,
+ Err(e) => {
+ error!("vtpm failed creating PollContext: {}", e);
+ return;
+ }
+ };
+
+ 'poll: loop {
+ let events = match poll_ctx.wait() {
+ Ok(v) => v,
+ Err(e) => {
+ error!("vtpm failed polling for events: {}", e);
+ break;
+ }
+ };
+
+ let mut needs_interrupt = NeedsInterrupt::No;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::QueueAvailable => {
+ if let Err(e) = self.queue_evt.read() {
+ error!("vtpm failed reading queue EventFd: {}", e);
+ break 'poll;
+ }
+ needs_interrupt |= self.process_queue();
+ }
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ let _ = self.interrupt_evt.write(1);
+ }
+ }
+ Token::Kill => break 'poll,
+ }
+ }
+ if needs_interrupt == NeedsInterrupt::Yes {
+ self.signal_used_queue();
+ }
+ }
+ }
+}
+
+/// Virtio vTPM device.
+pub struct Tpm {
+ storage: PathBuf,
+ kill_evt: Option<EventFd>,
+}
+
+impl Tpm {
+ pub fn new(storage: PathBuf) -> Tpm {
+ Tpm {
+ storage,
+ kill_evt: None,
+ }
+ }
+}
+
+impl Drop for Tpm {
+ fn drop(&mut self) {
+ if let Some(kill_evt) = self.kill_evt.take() {
+ let _ = kill_evt.write(1);
+ }
+ }
+}
+
+impl VirtioDevice for Tpm {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ Vec::new()
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_TPM
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ interrupt_status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ mut queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != 1 || queue_evts.len() != 1 {
+ return;
+ }
+ let queue = queues.remove(0);
+ let queue_evt = queue_evts.remove(0);
+
+ if let Err(err) = fs::create_dir_all(&self.storage) {
+ error!("vtpm failed to create directory for simulator: {}", err);
+ return;
+ }
+ if let Err(err) = env::set_current_dir(&self.storage) {
+ error!("vtpm failed to change into simulator directory: {}", err);
+ return;
+ }
+ let simulator = tpm2::Simulator::singleton_in_current_directory();
+
+ let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(err) => {
+ error!("vtpm failed to create kill EventFd pair: {}", err);
+ return;
+ }
+ };
+ self.kill_evt = Some(self_kill_evt);
+
+ let worker = Worker {
+ queue,
+ mem,
+ interrupt_status,
+ queue_evt,
+ interrupt_evt,
+ interrupt_resample_evt,
+ kill_evt,
+ device: Device { simulator },
+ };
+
+ let worker_result = thread::Builder::new()
+ .name("virtio_tpm".to_string())
+ .spawn(|| worker.run());
+
+ if let Err(e) = worker_result {
+ error!("vtpm failed to spawn virtio_tpm worker: {}", e);
+ return;
+ }
+ }
+}
+
+#[derive(PartialEq)]
+enum NeedsInterrupt {
+ Yes,
+ No,
+}
+
+impl BitOrAssign for NeedsInterrupt {
+ fn bitor_assign(&mut self, rhs: NeedsInterrupt) {
+ if rhs == NeedsInterrupt::Yes {
+ *self = NeedsInterrupt::Yes;
+ }
+ }
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+enum Error {
+ ExpectedReadOnly,
+ ExpectedSecondBuffer,
+ ExpectedWriteOnly,
+ CommandTooLong { size: usize },
+ Read(GuestMemoryError),
+ ResponseTooLong { size: usize },
+ BufferTooSmall { size: usize, required: usize },
+ Write(GuestMemoryError),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ ExpectedReadOnly => write!(f, "vtpm expected first descriptor to be read-only"),
+ ExpectedSecondBuffer => write!(f, "vtpm expected a second descriptor"),
+ ExpectedWriteOnly => write!(f, "vtpm expected second descriptor to be write-only"),
+ CommandTooLong { size } => write!(
+ f,
+ "vtpm command is too long: {} > {} bytes",
+ size, TPM_BUFSIZE
+ ),
+ Read(e) => write!(f, "vtpm failed to read from guest memory: {}", e),
+ ResponseTooLong { size } => write!(
+ f,
+ "vtpm simulator generated a response that is unexpectedly long: {} > {} bytes",
+ size, TPM_BUFSIZE
+ ),
+ BufferTooSmall { size, required } => write!(
+ f,
+ "vtpm response buffer is too small: {} < {} bytes",
+ size, required
+ ),
+ Write(e) => write!(f, "vtpm failed to write to guest memory: {}", e),
+ }
+ }
+}
diff --git a/devices/src/virtio/vhost/mod.rs b/devices/src/virtio/vhost/mod.rs
new file mode 100644
index 0000000..66c62d0
--- /dev/null
+++ b/devices/src/virtio/vhost/mod.rs
@@ -0,0 +1,116 @@
+// Copyright 2017 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.
+
+//! Implements vhost-based virtio devices.
+
+use std;
+use std::fmt::{self, Display};
+
+use net_util::Error as TapError;
+use remain::sorted;
+use sys_util::Error as SysError;
+use vhost::Error as VhostError;
+
+mod net;
+mod vsock;
+mod worker;
+
+pub use self::net::Net;
+pub use self::vsock::Vsock;
+
+#[sorted]
+#[derive(Debug)]
+pub enum Error {
+ /// Cloning kill eventfd failed.
+ CloneKillEventFd(SysError),
+ /// Creating kill eventfd failed.
+ CreateKillEventFd(SysError),
+ /// Creating poll context failed.
+ CreatePollContext(SysError),
+ /// Error while polling for events.
+ PollError(SysError),
+ /// Enabling tap interface failed.
+ TapEnable(TapError),
+ /// Open tap device failed.
+ TapOpen(TapError),
+ /// Setting tap IP failed.
+ TapSetIp(TapError),
+ /// Setting tap mac address failed.
+ TapSetMacAddress(TapError),
+ /// Setting tap netmask failed.
+ TapSetNetmask(TapError),
+ /// Setting tap interface offload flags failed.
+ TapSetOffload(TapError),
+ /// Setting vnet header size failed.
+ TapSetVnetHdrSize(TapError),
+ /// Get features failed.
+ VhostGetFeatures(VhostError),
+ /// Failed to create vhost eventfd.
+ VhostIrqCreate(SysError),
+ /// Failed to read vhost eventfd.
+ VhostIrqRead(SysError),
+ /// Net set backend failed.
+ VhostNetSetBackend(VhostError),
+ /// Failed to open vhost device.
+ VhostOpen(VhostError),
+ /// Set features failed.
+ VhostSetFeatures(VhostError),
+ /// Set mem table failed.
+ VhostSetMemTable(VhostError),
+ /// Set owner failed.
+ VhostSetOwner(VhostError),
+ /// Set vring addr failed.
+ VhostSetVringAddr(VhostError),
+ /// Set vring base failed.
+ VhostSetVringBase(VhostError),
+ /// Set vring call failed.
+ VhostSetVringCall(VhostError),
+ /// Set vring kick failed.
+ VhostSetVringKick(VhostError),
+ /// Set vring num failed.
+ VhostSetVringNum(VhostError),
+ /// Failed to set CID for guest.
+ VhostVsockSetCid(VhostError),
+ /// Failed to start vhost-vsock driver.
+ VhostVsockStart(VhostError),
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ #[remain::check]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ #[sorted]
+ match self {
+ CloneKillEventFd(e) => write!(f, "failed to clone kill eventfd: {}", e),
+ CreateKillEventFd(e) => write!(f, "failed to create kill eventfd: {}", e),
+ CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
+ PollError(e) => write!(f, "failed polling for events: {}", 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),
+ TapSetMacAddress(e) => write!(f, "failed to set tap mac address: {}", e),
+ TapSetNetmask(e) => write!(f, "failed to set tap netmask: {}", e),
+ TapSetOffload(e) => write!(f, "failed to set tap interface offload flags: {}", e),
+ TapSetVnetHdrSize(e) => write!(f, "failed to set vnet header size: {}", e),
+ VhostGetFeatures(e) => write!(f, "failed to get features: {}", e),
+ VhostIrqCreate(e) => write!(f, "failed to create vhost eventfd: {}", e),
+ VhostIrqRead(e) => write!(f, "failed to read vhost eventfd: {}", e),
+ VhostNetSetBackend(e) => write!(f, "net set backend failed: {}", e),
+ VhostOpen(e) => write!(f, "failed to open vhost device: {}", e),
+ VhostSetFeatures(e) => write!(f, "failed to set features: {}", e),
+ VhostSetMemTable(e) => write!(f, "failed to set mem table: {}", e),
+ VhostSetOwner(e) => write!(f, "failed to set owner: {}", e),
+ VhostSetVringAddr(e) => write!(f, "failed to set vring addr: {}", e),
+ VhostSetVringBase(e) => write!(f, "failed to set vring base: {}", e),
+ VhostSetVringCall(e) => write!(f, "failed to set vring call: {}", e),
+ VhostSetVringKick(e) => write!(f, "failed to set vring kick: {}", e),
+ 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),
+ }
+ }
+}
diff --git a/devices/src/virtio/vhost/net.rs b/devices/src/virtio/vhost/net.rs
new file mode 100644
index 0000000..a7eaf18
--- /dev/null
+++ b/devices/src/virtio/vhost/net.rs
@@ -0,0 +1,285 @@
+// Copyright 2017 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::mem;
+use std::net::Ipv4Addr;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::sync::atomic::AtomicUsize;
+use std::sync::Arc;
+use std::thread;
+
+use net_sys;
+use net_util::{MacAddress, TapT};
+
+use ::vhost::NetT as VhostNetT;
+use sys_util::{error, warn, EventFd, GuestMemory};
+use virtio_sys::{vhost, virtio_net};
+
+use super::worker::Worker;
+use super::{Error, Result};
+use crate::virtio::{Queue, VirtioDevice, TYPE_NET};
+
+const QUEUE_SIZE: u16 = 256;
+const NUM_QUEUES: usize = 2;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
+
+pub struct Net<T: TapT, U: VhostNetT<T>> {
+ workers_kill_evt: Option<EventFd>,
+ kill_evt: EventFd,
+ tap: Option<T>,
+ vhost_net_handle: Option<U>,
+ vhost_interrupt: Option<EventFd>,
+ avail_features: u64,
+ acked_features: u64,
+}
+
+impl<T, U> Net<T, U>
+where
+ T: TapT,
+ U: VhostNetT<T>,
+{
+ /// Create a new virtio network device with the given IP address and
+ /// netmask.
+ pub fn new(
+ ip_addr: Ipv4Addr,
+ netmask: Ipv4Addr,
+ mac_addr: MacAddress,
+ mem: &GuestMemory,
+ ) -> Result<Net<T, U>> {
+ let kill_evt = EventFd::new().map_err(Error::CreateKillEventFd)?;
+
+ let tap: T = T::new(true).map_err(Error::TapOpen)?;
+ tap.set_ip_addr(ip_addr).map_err(Error::TapSetIp)?;
+ tap.set_netmask(netmask).map_err(Error::TapSetNetmask)?;
+ tap.set_mac_address(mac_addr)
+ .map_err(Error::TapSetMacAddress)?;
+
+ // Set offload flags to match the virtio features below.
+ tap.set_offload(
+ net_sys::TUN_F_CSUM | net_sys::TUN_F_UFO | net_sys::TUN_F_TSO4 | net_sys::TUN_F_TSO6,
+ )
+ .map_err(Error::TapSetOffload)?;
+
+ // We declare VIRTIO_NET_F_MRG_RXBUF, so set the vnet hdr size to match.
+ let vnet_hdr_size = mem::size_of::<virtio_net::virtio_net_hdr_mrg_rxbuf>() as i32;
+ tap.set_vnet_hdr_size(vnet_hdr_size)
+ .map_err(Error::TapSetVnetHdrSize)?;
+
+ tap.enable().map_err(Error::TapEnable)?;
+ let vhost_net_handle = U::new(mem).map_err(Error::VhostOpen)?;
+
+ let avail_features = 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM
+ | 1 << virtio_net::VIRTIO_NET_F_CSUM
+ | 1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4
+ | 1 << virtio_net::VIRTIO_NET_F_GUEST_UFO
+ | 1 << virtio_net::VIRTIO_NET_F_HOST_TSO4
+ | 1 << virtio_net::VIRTIO_NET_F_HOST_UFO
+ | 1 << virtio_net::VIRTIO_NET_F_MRG_RXBUF
+ | 1 << vhost::VIRTIO_RING_F_INDIRECT_DESC
+ | 1 << vhost::VIRTIO_RING_F_EVENT_IDX
+ | 1 << vhost::VIRTIO_F_NOTIFY_ON_EMPTY
+ | 1 << vhost::VIRTIO_F_VERSION_1;
+
+ Ok(Net {
+ workers_kill_evt: Some(kill_evt.try_clone().map_err(Error::CloneKillEventFd)?),
+ kill_evt,
+ tap: Some(tap),
+ vhost_net_handle: Some(vhost_net_handle),
+ vhost_interrupt: Some(EventFd::new().map_err(Error::VhostIrqCreate)?),
+ avail_features,
+ acked_features: 0u64,
+ })
+ }
+}
+
+impl<T, U> Drop for Net<T, U>
+where
+ T: TapT,
+ U: VhostNetT<T>,
+{
+ fn drop(&mut self) {
+ // Only kill the child if it claimed its eventfd.
+ if self.workers_kill_evt.is_none() {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = self.kill_evt.write(1);
+ }
+ }
+}
+
+impl<T, U> VirtioDevice for Net<T, U>
+where
+ T: TapT + 'static,
+ U: VhostNetT<T> + 'static,
+{
+ fn keep_fds(&self) -> Vec<RawFd> {
+ let mut keep_fds = Vec::new();
+
+ if let Some(tap) = &self.tap {
+ keep_fds.push(tap.as_raw_fd());
+ }
+
+ if let Some(vhost_net_handle) = &self.vhost_net_handle {
+ keep_fds.push(vhost_net_handle.as_raw_fd());
+ }
+
+ if let Some(vhost_interrupt) = &self.vhost_interrupt {
+ keep_fds.push(vhost_interrupt.as_raw_fd());
+ }
+
+ if let Some(workers_kill_evt) = &self.workers_kill_evt {
+ keep_fds.push(workers_kill_evt.as_raw_fd());
+ }
+
+ keep_fds
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_NET
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn features(&self) -> u64 {
+ self.avail_features
+ }
+
+ fn ack_features(&mut self, value: u64) {
+ let mut v = value;
+
+ // Check if the guest is ACK'ing a feature that we didn't claim to have.
+ let unrequested_features = v & !self.avail_features;
+ if unrequested_features != 0 {
+ warn!("net: virtio net got unknown feature ack: {:x}", v);
+
+ // Don't count these features as acked.
+ v &= !unrequested_features;
+ }
+ self.acked_features |= v;
+ }
+
+ fn activate(
+ &mut self,
+ _: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ queues: Vec<Queue>,
+ queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != NUM_QUEUES || queue_evts.len() != NUM_QUEUES {
+ error!("net: expected {} queues, got {}", NUM_QUEUES, queues.len());
+ return;
+ }
+
+ if let Some(vhost_net_handle) = self.vhost_net_handle.take() {
+ if let Some(tap) = self.tap.take() {
+ if let Some(vhost_interrupt) = self.vhost_interrupt.take() {
+ if let Some(kill_evt) = self.workers_kill_evt.take() {
+ let acked_features = self.acked_features;
+ let worker_result = thread::Builder::new()
+ .name("vhost_net".to_string())
+ .spawn(move || {
+ let mut worker = Worker::new(
+ queues,
+ vhost_net_handle,
+ vhost_interrupt,
+ status,
+ interrupt_evt,
+ interrupt_resample_evt,
+ acked_features,
+ );
+ let activate_vqs = |handle: &U| -> Result<()> {
+ for idx in 0..NUM_QUEUES {
+ handle
+ .set_backend(idx, &tap)
+ .map_err(Error::VhostNetSetBackend)?;
+ }
+ Ok(())
+ };
+ let result =
+ worker.run(queue_evts, QUEUE_SIZES, kill_evt, activate_vqs);
+ if let Err(e) = result {
+ error!("net worker thread exited with error: {}", e);
+ }
+ });
+
+ if let Err(e) = worker_result {
+ error!("failed to spawn vhost_net worker: {}", e);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+pub mod tests {
+ use super::*;
+ use ::vhost::net::fakes::FakeNet;
+ use net_util::fakes::FakeTap;
+ use std::result;
+ use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
+
+ fn create_guest_memory() -> result::Result<GuestMemory, GuestMemoryError> {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x1000);
+ GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x4000)])
+ }
+
+ fn create_net_common() -> Net<FakeTap, FakeNet<FakeTap>> {
+ let guest_memory = create_guest_memory().unwrap();
+ Net::<FakeTap, FakeNet<FakeTap>>::new(
+ Ipv4Addr::new(127, 0, 0, 1),
+ Ipv4Addr::new(255, 255, 255, 0),
+ "de:21:e8:47:6b:6a".parse().unwrap(),
+ &guest_memory,
+ )
+ .unwrap()
+ }
+
+ #[test]
+ fn create_net() {
+ create_net_common();
+ }
+
+ #[test]
+ fn keep_fds() {
+ let net = create_net_common();
+ let fds = net.keep_fds();
+ assert!(fds.len() >= 1, "We should have gotten at least one fd");
+ }
+
+ #[test]
+ fn features() {
+ let net = create_net_common();
+ assert_eq!(net.features(), 5117103235);
+ }
+
+ #[test]
+ fn ack_features() {
+ let mut net = create_net_common();
+ // Just testing that we don't panic, for now
+ net.ack_features(1);
+ net.ack_features(1 << 32);
+ }
+
+ #[test]
+ fn activate() {
+ let mut net = create_net_common();
+ let guest_memory = create_guest_memory().unwrap();
+ // Just testing that we don't panic, for now
+ net.activate(
+ guest_memory,
+ EventFd::new().unwrap(),
+ EventFd::new().unwrap(),
+ Arc::new(AtomicUsize::new(0)),
+ vec![Queue::new(1)],
+ vec![EventFd::new().unwrap()],
+ );
+ }
+}
diff --git a/devices/src/virtio/vhost/vsock.rs b/devices/src/virtio/vhost/vsock.rs
new file mode 100644
index 0000000..d07b639
--- /dev/null
+++ b/devices/src/virtio/vhost/vsock.rs
@@ -0,0 +1,273 @@
+// Copyright 2017 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, RawFd};
+use std::sync::atomic::AtomicUsize;
+use std::sync::Arc;
+use std::thread;
+
+use byteorder::{ByteOrder, LittleEndian};
+
+use ::vhost::Vsock as VhostVsockHandle;
+use sys_util::{error, warn, EventFd, GuestMemory};
+use virtio_sys::vhost;
+
+use super::worker::Worker;
+use super::{Error, Result};
+use crate::virtio::{Queue, VirtioDevice, TYPE_VSOCK};
+
+const QUEUE_SIZE: u16 = 256;
+const NUM_QUEUES: usize = 3;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
+
+pub struct Vsock {
+ worker_kill_evt: Option<EventFd>,
+ kill_evt: Option<EventFd>,
+ vhost_handle: Option<VhostVsockHandle>,
+ cid: u64,
+ interrupt: Option<EventFd>,
+ avail_features: u64,
+ acked_features: u64,
+}
+
+impl Vsock {
+ /// Create a new virtio-vsock device with the given VM cid.
+ pub fn new(cid: u64, mem: &GuestMemory) -> Result<Vsock> {
+ let kill_evt = EventFd::new().map_err(Error::CreateKillEventFd)?;
+ let handle = VhostVsockHandle::new(mem).map_err(Error::VhostOpen)?;
+
+ let avail_features = 1 << vhost::VIRTIO_F_NOTIFY_ON_EMPTY
+ | 1 << vhost::VIRTIO_RING_F_INDIRECT_DESC
+ | 1 << vhost::VIRTIO_RING_F_EVENT_IDX
+ | 1 << vhost::VHOST_F_LOG_ALL
+ | 1 << vhost::VIRTIO_F_ANY_LAYOUT
+ | 1 << vhost::VIRTIO_F_VERSION_1;
+
+ Ok(Vsock {
+ worker_kill_evt: Some(kill_evt.try_clone().map_err(Error::CloneKillEventFd)?),
+ kill_evt: Some(kill_evt),
+ vhost_handle: Some(handle),
+ cid,
+ interrupt: Some(EventFd::new().map_err(Error::VhostIrqCreate)?),
+ avail_features,
+ acked_features: 0,
+ })
+ }
+
+ pub fn new_for_testing(cid: u64, features: u64) -> Vsock {
+ Vsock {
+ worker_kill_evt: None,
+ kill_evt: None,
+ vhost_handle: None,
+ cid,
+ interrupt: None,
+ avail_features: features,
+ acked_features: 0,
+ }
+ }
+
+ pub fn acked_features(&self) -> u64 {
+ self.acked_features
+ }
+}
+
+impl Drop for Vsock {
+ fn drop(&mut self) {
+ // Only kill the child if it claimed its eventfd.
+ if self.worker_kill_evt.is_none() {
+ if let Some(kill_evt) = &self.kill_evt {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = kill_evt.write(1);
+ }
+ }
+ }
+}
+
+impl VirtioDevice for Vsock {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ let mut keep_fds = Vec::new();
+
+ if let Some(handle) = &self.vhost_handle {
+ keep_fds.push(handle.as_raw_fd());
+ }
+
+ if let Some(interrupt) = &self.interrupt {
+ keep_fds.push(interrupt.as_raw_fd());
+ }
+
+ if let Some(worker_kill_evt) = &self.worker_kill_evt {
+ keep_fds.push(worker_kill_evt.as_raw_fd());
+ }
+
+ keep_fds
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_VSOCK
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn features(&self) -> u64 {
+ self.avail_features
+ }
+
+ fn read_config(&self, offset: u64, data: &mut [u8]) {
+ match offset {
+ 0 if data.len() == 8 => LittleEndian::write_u64(data, self.cid),
+ 0 if data.len() == 4 => LittleEndian::write_u32(data, (self.cid & 0xffffffff) as u32),
+ 4 if data.len() == 4 => {
+ LittleEndian::write_u32(data, ((self.cid >> 32) & 0xffffffff) as u32)
+ }
+ _ => warn!(
+ "vsock: virtio-vsock received invalid read request of {} bytes at offset {}",
+ data.len(),
+ offset
+ ),
+ }
+ }
+
+ fn ack_features(&mut self, value: u64) {
+ let mut v = value;
+
+ // Check if the guest is ACK'ing a feature that we didn't claim to have.
+ let unrequested_features = v & !self.avail_features;
+ if unrequested_features != 0 {
+ warn!("vsock: virtio-vsock got unknown feature ack: {:x}", v);
+
+ // Don't count these features as acked.
+ v &= !unrequested_features;
+ }
+ self.acked_features |= v;
+ }
+
+ fn activate(
+ &mut self,
+ _: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ queues: Vec<Queue>,
+ queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != NUM_QUEUES || queue_evts.len() != NUM_QUEUES {
+ error!("net: expected {} queues, got {}", NUM_QUEUES, queues.len());
+ return;
+ }
+
+ if let Some(vhost_handle) = self.vhost_handle.take() {
+ if let Some(interrupt) = self.interrupt.take() {
+ if let Some(kill_evt) = self.worker_kill_evt.take() {
+ let acked_features = self.acked_features;
+ let cid = self.cid;
+ let worker_result = thread::Builder::new()
+ .name("vhost_vsock".to_string())
+ .spawn(move || {
+ // The third vq is an event-only vq that is not handled by the vhost
+ // subsystem (but still needs to exist). Split it off here.
+ let vhost_queues = queues[..2].to_vec();
+ let mut worker = Worker::new(
+ vhost_queues,
+ vhost_handle,
+ interrupt,
+ status,
+ interrupt_evt,
+ interrupt_resample_evt,
+ acked_features,
+ );
+ let activate_vqs = |handle: &VhostVsockHandle| -> Result<()> {
+ handle.set_cid(cid).map_err(Error::VhostVsockSetCid)?;
+ handle.start().map_err(Error::VhostVsockStart)?;
+ Ok(())
+ };
+ let result =
+ worker.run(queue_evts, QUEUE_SIZES, kill_evt, activate_vqs);
+ if let Err(e) = result {
+ error!("vsock worker thread exited with error: {:?}", e);
+ }
+ });
+
+ if let Err(e) = worker_result {
+ error!("failed to spawn vhost_vsock worker: {}", e);
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use byteorder::{ByteOrder, LittleEndian};
+ #[test]
+ fn ack_features() {
+ let cid = 5;
+ let features: u64 = (1 << 20) | (1 << 49) | (1 << 2) | (1 << 19);
+ let mut acked_features: u64 = 0;
+ let mut unavailable_features: u64 = 0;
+
+ let mut vsock = Vsock::new_for_testing(cid, features);
+ assert_eq!(acked_features, vsock.acked_features());
+
+ acked_features |= 1 << 2;
+ vsock.ack_features(acked_features);
+ assert_eq!(acked_features, vsock.acked_features());
+
+ acked_features |= 1 << 49;
+ vsock.ack_features(acked_features);
+ assert_eq!(acked_features, vsock.acked_features());
+
+ acked_features |= 1 << 60;
+ unavailable_features |= 1 << 60;
+ vsock.ack_features(acked_features);
+ assert_eq!(
+ acked_features & !unavailable_features,
+ vsock.acked_features()
+ );
+
+ acked_features |= 1 << 1;
+ unavailable_features |= 1 << 1;
+ vsock.ack_features(acked_features);
+ assert_eq!(
+ acked_features & !unavailable_features,
+ vsock.acked_features()
+ );
+ }
+
+ #[test]
+ fn read_config() {
+ let cid = 0xfca9a559fdcb9756;
+ let vsock = Vsock::new_for_testing(cid, 0);
+
+ let mut buf = [0 as u8; 8];
+ vsock.read_config(0, &mut buf);
+ assert_eq!(cid, LittleEndian::read_u64(&buf));
+
+ vsock.read_config(0, &mut buf[..4]);
+ assert_eq!((cid & 0xffffffff) as u32, LittleEndian::read_u32(&buf[..4]));
+
+ vsock.read_config(4, &mut buf[..4]);
+ assert_eq!((cid >> 32) as u32, LittleEndian::read_u32(&buf[..4]));
+
+ let data: [u8; 8] = [8, 226, 5, 46, 159, 59, 89, 77];
+ buf.copy_from_slice(&data);
+
+ vsock.read_config(12, &mut buf);
+ assert_eq!(&buf, &data);
+ }
+
+ #[test]
+ fn features() {
+ let cid = 5;
+ let features: u64 = 0xfc195ae8db88cff9;
+
+ let vsock = Vsock::new_for_testing(cid, features);
+ assert_eq!(features, vsock.features());
+ }
+}
diff --git a/devices/src/virtio/vhost/worker.rs b/devices/src/virtio/vhost/worker.rs
new file mode 100644
index 0000000..9389f82
--- /dev/null
+++ b/devices/src/virtio/vhost/worker.rs
@@ -0,0 +1,156 @@
+// Copyright 2017 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::raw::c_ulonglong;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+
+use sys_util::{EventFd, PollContext, PollToken};
+use vhost::Vhost;
+
+use super::{Error, Result};
+use crate::virtio::{Queue, INTERRUPT_STATUS_USED_RING};
+
+/// Worker that takes care of running the vhost device. This mainly involves forwarding interrupts
+/// from the vhost driver to the guest VM because crosvm only supports the virtio-mmio transport,
+/// which requires a bit to be set in the interrupt status register before triggering the interrupt
+/// and the vhost driver doesn't do this for us.
+pub struct Worker<T: Vhost> {
+ queues: Vec<Queue>,
+ vhost_handle: T,
+ vhost_interrupt: EventFd,
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ acked_features: u64,
+}
+
+impl<T: Vhost> Worker<T> {
+ pub fn new(
+ queues: Vec<Queue>,
+ vhost_handle: T,
+ vhost_interrupt: EventFd,
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ acked_features: u64,
+ ) -> Worker<T> {
+ Worker {
+ queues,
+ vhost_handle,
+ vhost_interrupt,
+ interrupt_status,
+ interrupt_evt,
+ interrupt_resample_evt,
+ acked_features,
+ }
+ }
+
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ self.interrupt_evt.write(1).unwrap();
+ }
+
+ pub fn run<F>(
+ &mut self,
+ queue_evts: Vec<EventFd>,
+ queue_sizes: &[u16],
+ kill_evt: EventFd,
+ activate_vqs: F,
+ ) -> Result<()>
+ where
+ F: FnOnce(&T) -> Result<()>,
+ {
+ // Preliminary setup for vhost net.
+ self.vhost_handle
+ .set_owner()
+ .map_err(Error::VhostSetOwner)?;
+
+ let avail_features = self
+ .vhost_handle
+ .get_features()
+ .map_err(Error::VhostGetFeatures)?;
+
+ let features: c_ulonglong = self.acked_features & avail_features;
+ self.vhost_handle
+ .set_features(features)
+ .map_err(Error::VhostSetFeatures)?;
+
+ self.vhost_handle
+ .set_mem_table()
+ .map_err(Error::VhostSetMemTable)?;
+
+ for (queue_index, queue) in self.queues.iter().enumerate() {
+ self.vhost_handle
+ .set_vring_num(queue_index, queue.max_size)
+ .map_err(Error::VhostSetVringNum)?;
+
+ self.vhost_handle
+ .set_vring_addr(
+ queue_sizes[queue_index],
+ queue.actual_size(),
+ queue_index,
+ 0,
+ queue.desc_table,
+ queue.used_ring,
+ queue.avail_ring,
+ None,
+ )
+ .map_err(Error::VhostSetVringAddr)?;
+ self.vhost_handle
+ .set_vring_base(queue_index, 0)
+ .map_err(Error::VhostSetVringBase)?;
+ self.vhost_handle
+ .set_vring_call(queue_index, &self.vhost_interrupt)
+ .map_err(Error::VhostSetVringCall)?;
+ self.vhost_handle
+ .set_vring_kick(queue_index, &queue_evts[queue_index])
+ .map_err(Error::VhostSetVringKick)?;
+ }
+
+ activate_vqs(&self.vhost_handle)?;
+
+ #[derive(PollToken)]
+ enum Token {
+ VhostIrq,
+ InterruptResample,
+ Kill,
+ }
+
+ let poll_ctx: PollContext<Token> = PollContext::new()
+ .and_then(|pc| pc.add(&self.vhost_interrupt, Token::VhostIrq).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ })
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ .map_err(Error::CreatePollContext)?;
+
+ 'poll: loop {
+ let events = poll_ctx.wait().map_err(Error::PollError)?;
+
+ let mut needs_interrupt = false;
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::VhostIrq => {
+ needs_interrupt = true;
+ self.vhost_interrupt.read().map_err(Error::VhostIrqRead)?;
+ }
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_evt.write(1).unwrap();
+ }
+ }
+ Token::Kill => break 'poll,
+ }
+ }
+ if needs_interrupt {
+ self.signal_used_queue();
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/devices/src/virtio/virtio_device.rs b/devices/src/virtio/virtio_device.rs
new file mode 100644
index 0000000..d5d5266
--- /dev/null
+++ b/devices/src/virtio/virtio_device.rs
@@ -0,0 +1,88 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::unix::io::RawFd;
+use std::sync::atomic::AtomicUsize;
+use std::sync::Arc;
+
+use sys_util::{EventFd, GuestMemory};
+
+use super::*;
+use crate::pci::{PciBarConfiguration, PciCapability};
+
+/// Trait for virtio devices to be driven by a virtio transport.
+///
+/// The lifecycle of a virtio device is to be moved to a virtio transport, which will then query the
+/// device. Once the guest driver has configured the device, `VirtioDevice::activate` will be called
+/// and all the events, memory, and queues for device operation will be moved into the device.
+/// Optionally, a virtio device can implement device reset in which it returns said resources and
+/// resets its internal.
+pub trait VirtioDevice: Send {
+ /// Returns a label suitable for debug output.
+ fn debug_label(&self) -> String {
+ match type_to_str(self.device_type()) {
+ Some(s) => format!("virtio-{}", s),
+ None => format!("virtio (type {})", self.device_type()),
+ }
+ }
+
+ /// 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>;
+
+ /// The virtio device type.
+ fn device_type(&self) -> u32;
+
+ /// The maximum size of each queue that this device supports.
+ fn queue_max_sizes(&self) -> &[u16];
+
+ /// The set of feature bits that this device supports.
+ fn features(&self) -> u64 {
+ 1 << VIRTIO_F_VERSION_1
+ }
+
+ /// Acknowledges that this set of features should be enabled.
+ fn ack_features(&mut self, value: u64) {
+ let _ = value;
+ }
+
+ /// Reads this device configuration space at `offset`.
+ fn read_config(&self, offset: u64, data: &mut [u8]) {
+ let _ = offset;
+ let _ = data;
+ }
+
+ /// Writes to this device configuration space at `offset`.
+ fn write_config(&mut self, offset: u64, data: &[u8]) {
+ let _ = offset;
+ let _ = data;
+ }
+
+ /// Activates this device for real usage.
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ queues: Vec<Queue>,
+ queue_evts: Vec<EventFd>,
+ );
+
+ /// Optionally deactivates this device and returns ownership of the guest memory map, interrupt
+ /// event, and queue events.
+ fn reset(&mut self) -> Option<(EventFd, Vec<EventFd>)> {
+ None
+ }
+
+ /// Returns any additional BAR configuration required by the device.
+ fn get_device_bars(&self) -> Vec<PciBarConfiguration> {
+ Vec::new()
+ }
+
+ /// Returns any additional capabiltiies required by the device.
+ fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> {
+ Vec::new()
+ }
+}
diff --git a/devices/src/virtio/virtio_pci_common_config.rs b/devices/src/virtio/virtio_pci_common_config.rs
new file mode 100644
index 0000000..7f7eaca
--- /dev/null
+++ b/devices/src/virtio/virtio_pci_common_config.rs
@@ -0,0 +1,308 @@
+// Copyright 2018 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 byteorder::{ByteOrder, LittleEndian};
+use sys_util::{warn, GuestAddress};
+
+use super::*;
+
+/// Contains the data for reading and writing the common configuration structure of a virtio PCI
+/// device.
+///
+/// * Registers:
+/// ** About the whole device.
+/// le32 device_feature_select; // read-write
+/// le32 device_feature; // read-only for driver
+/// le32 driver_feature_select; // read-write
+/// le32 driver_feature; // read-write
+/// le16 msix_config; // read-write
+/// le16 num_queues; // read-only for driver
+/// u8 device_status; // read-write (driver_status)
+/// u8 config_generation; // read-only for driver
+/// ** About a specific virtqueue.
+/// le16 queue_select; // read-write
+/// le16 queue_size; // read-write, power of 2, or 0.
+/// le16 queue_msix_vector; // read-write
+/// le16 queue_enable; // read-write (Ready)
+/// le16 queue_notify_off; // read-only for driver
+/// le64 queue_desc; // read-write
+/// le64 queue_avail; // read-write
+/// le64 queue_used; // read-write
+pub struct VirtioPciCommonConfig {
+ pub driver_status: u8,
+ pub config_generation: u8,
+ pub device_feature_select: u32,
+ pub driver_feature_select: u32,
+ pub queue_select: u16,
+}
+
+impl VirtioPciCommonConfig {
+ pub fn read(
+ &mut self,
+ offset: u64,
+ data: &mut [u8],
+ queues: &mut [Queue],
+ device: &mut dyn VirtioDevice,
+ ) {
+ match data.len() {
+ 1 => {
+ let v = self.read_common_config_byte(offset);
+ data[0] = v;
+ }
+ 2 => {
+ let v = self.read_common_config_word(offset, queues);
+ LittleEndian::write_u16(data, v);
+ }
+ 4 => {
+ let v = self.read_common_config_dword(offset, device);
+ LittleEndian::write_u32(data, v);
+ }
+ 8 => {
+ let v = self.read_common_config_qword(offset);
+ LittleEndian::write_u64(data, v);
+ }
+ _ => (),
+ }
+ }
+
+ pub fn write(
+ &mut self,
+ offset: u64,
+ data: &[u8],
+ queues: &mut [Queue],
+ device: &mut dyn VirtioDevice,
+ ) {
+ match data.len() {
+ 1 => self.write_common_config_byte(offset, data[0]),
+ 2 => self.write_common_config_word(offset, LittleEndian::read_u16(data), queues),
+ 4 => {
+ self.write_common_config_dword(offset, LittleEndian::read_u32(data), queues, device)
+ }
+ 8 => self.write_common_config_qword(offset, LittleEndian::read_u64(data), queues),
+ _ => (),
+ }
+ }
+
+ fn read_common_config_byte(&self, offset: u64) -> u8 {
+ // The driver is only allowed to do aligned, properly sized access.
+ match offset {
+ 0x14 => self.driver_status,
+ 0x15 => self.config_generation,
+ _ => 0,
+ }
+ }
+
+ fn write_common_config_byte(&mut self, offset: u64, value: u8) {
+ match offset {
+ 0x14 => self.driver_status = value,
+ _ => {
+ warn!("invalid virtio config byt access: 0x{:x}", offset);
+ }
+ }
+ }
+
+ fn read_common_config_word(&self, offset: u64, queues: &[Queue]) -> u16 {
+ match offset {
+ 0x10 => 0, // TODO msi-x (crbug/854765): self.msix_config,
+ 0x12 => queues.len() as u16, // num_queues
+ 0x16 => self.queue_select,
+ 0x18 => self.with_queue(queues, |q| q.size).unwrap_or(0),
+ 0x1c => {
+ if self.with_queue(queues, |q| q.ready).unwrap_or(false) {
+ 1
+ } else {
+ 0
+ }
+ }
+ 0x1e => self.queue_select, // notify_off
+ _ => 0,
+ }
+ }
+
+ fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut [Queue]) {
+ match offset {
+ 0x10 => (), // TODO msi-x (crbug/854765): self.msix_config = value,
+ 0x16 => self.queue_select = value,
+ 0x18 => self.with_queue_mut(queues, |q| q.size = value),
+ 0x1a => (), // TODO msi-x (crbug/854765): self.with_queue_mut(queues, |q| q.msix_vector = v),
+ 0x1c => self.with_queue_mut(queues, |q| q.ready = value == 1),
+ _ => {
+ warn!("invalid virtio register word write: 0x{:x}", offset);
+ }
+ }
+ }
+
+ fn read_common_config_dword(&self, offset: u64, device: &dyn VirtioDevice) -> u32 {
+ match offset {
+ 0x00 => self.device_feature_select,
+ 0x04 => {
+ // Only 64 bits of features (2 pages) are defined for now, so limit
+ // device_feature_select to avoid shifting by 64 or more bits.
+ if self.device_feature_select < 2 {
+ (device.features() >> (self.device_feature_select * 32)) as u32
+ } else {
+ 0
+ }
+ }
+ 0x08 => self.driver_feature_select,
+ _ => 0,
+ }
+ }
+
+ fn write_common_config_dword(
+ &mut self,
+ offset: u64,
+ value: u32,
+ queues: &mut [Queue],
+ device: &mut dyn VirtioDevice,
+ ) {
+ fn hi(v: &mut GuestAddress, x: u32) {
+ *v = (*v & 0xffffffff) | ((x as u64) << 32)
+ }
+
+ fn lo(v: &mut GuestAddress, x: u32) {
+ *v = (*v & !0xffffffff) | (x as u64)
+ }
+
+ match offset {
+ 0x00 => self.device_feature_select = value,
+ 0x08 => self.driver_feature_select = value,
+ 0x0c => {
+ if self.driver_feature_select < 2 {
+ device.ack_features((value as u64) << (self.driver_feature_select * 32));
+ } else {
+ warn!(
+ "invalid ack_features (page {}, value 0x{:x})",
+ self.driver_feature_select, value
+ );
+ }
+ }
+ 0x20 => self.with_queue_mut(queues, |q| lo(&mut q.desc_table, value)),
+ 0x24 => self.with_queue_mut(queues, |q| hi(&mut q.desc_table, value)),
+ 0x28 => self.with_queue_mut(queues, |q| lo(&mut q.avail_ring, value)),
+ 0x2c => self.with_queue_mut(queues, |q| hi(&mut q.avail_ring, value)),
+ 0x30 => self.with_queue_mut(queues, |q| lo(&mut q.used_ring, value)),
+ 0x34 => self.with_queue_mut(queues, |q| hi(&mut q.used_ring, value)),
+ _ => {
+ warn!("invalid virtio register dword write: 0x{:x}", offset);
+ }
+ }
+ }
+
+ fn read_common_config_qword(&self, _offset: u64) -> u64 {
+ 0 // Assume the guest has no reason to read write-only registers.
+ }
+
+ fn write_common_config_qword(&mut self, offset: u64, value: u64, queues: &mut [Queue]) {
+ match offset {
+ 0x20 => self.with_queue_mut(queues, |q| q.desc_table = GuestAddress(value)),
+ 0x28 => self.with_queue_mut(queues, |q| q.avail_ring = GuestAddress(value)),
+ 0x30 => self.with_queue_mut(queues, |q| q.used_ring = GuestAddress(value)),
+ _ => {
+ warn!("invalid virtio register qword write: 0x{:x}", offset);
+ }
+ }
+ }
+
+ fn with_queue<U, F>(&self, queues: &[Queue], f: F) -> Option<U>
+ where
+ F: FnOnce(&Queue) -> U,
+ {
+ queues.get(self.queue_select as usize).map(f)
+ }
+
+ fn with_queue_mut<F: FnOnce(&mut Queue)>(&self, queues: &mut [Queue], f: F) {
+ if let Some(queue) = queues.get_mut(self.queue_select as usize) {
+ f(queue);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use std::os::unix::io::RawFd;
+ use std::sync::atomic::AtomicUsize;
+ use std::sync::Arc;
+ use sys_util::{EventFd, GuestMemory};
+
+ struct DummyDevice(u32);
+ const QUEUE_SIZE: u16 = 256;
+ const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE];
+ const DUMMY_FEATURES: u64 = 0x5555_aaaa;
+ impl VirtioDevice for DummyDevice {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ Vec::new()
+ }
+ fn device_type(&self) -> u32 {
+ return self.0;
+ }
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+ fn activate(
+ &mut self,
+ _mem: GuestMemory,
+ _interrupt_evt: EventFd,
+ _interrupt_resample_evt: EventFd,
+ _status: Arc<AtomicUsize>,
+ _queues: Vec<Queue>,
+ _queue_evts: Vec<EventFd>,
+ ) {
+ }
+ fn features(&self) -> u64 {
+ DUMMY_FEATURES
+ }
+ }
+
+ #[test]
+ fn write_base_regs() {
+ let mut regs = VirtioPciCommonConfig {
+ driver_status: 0xaa,
+ config_generation: 0x55,
+ device_feature_select: 0x0,
+ driver_feature_select: 0x0,
+ queue_select: 0xff,
+ };
+
+ let dev = &mut DummyDevice(0) as &mut dyn VirtioDevice;
+ let mut queues = Vec::new();
+
+ // Can set all bits of driver_status.
+ regs.write(0x14, &[0x55], &mut queues, dev);
+ let mut read_back = vec![0x00];
+ regs.read(0x14, &mut read_back, &mut queues, dev);
+ assert_eq!(read_back[0], 0x55);
+
+ // The config generation register is read only.
+ regs.write(0x15, &[0xaa], &mut queues, dev);
+ let mut read_back = vec![0x00];
+ regs.read(0x15, &mut read_back, &mut queues, dev);
+ assert_eq!(read_back[0], 0x55);
+
+ // Device features is read-only and passed through from the device.
+ regs.write(0x04, &[0, 0, 0, 0], &mut queues, dev);
+ let mut read_back = vec![0, 0, 0, 0];
+ regs.read(0x04, &mut read_back, &mut queues, dev);
+ assert_eq!(LittleEndian::read_u32(&read_back), DUMMY_FEATURES as u32);
+
+ // Feature select registers are read/write.
+ regs.write(0x00, &[1, 2, 3, 4], &mut queues, dev);
+ let mut read_back = vec![0, 0, 0, 0];
+ regs.read(0x00, &mut read_back, &mut queues, dev);
+ assert_eq!(LittleEndian::read_u32(&read_back), 0x0403_0201);
+ regs.write(0x08, &[1, 2, 3, 4], &mut queues, dev);
+ let mut read_back = vec![0, 0, 0, 0];
+ regs.read(0x08, &mut read_back, &mut queues, dev);
+ assert_eq!(LittleEndian::read_u32(&read_back), 0x0403_0201);
+
+ // 'queue_select' can be read and written.
+ regs.write(0x16, &[0xaa, 0x55], &mut queues, dev);
+ let mut read_back = vec![0x00, 0x00];
+ regs.read(0x16, &mut read_back, &mut queues, dev);
+ assert_eq!(read_back[0], 0xaa);
+ assert_eq!(read_back[1], 0x55);
+ }
+}
diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs
new file mode 100644
index 0000000..45bd423
--- /dev/null
+++ b/devices/src/virtio/virtio_pci_device.rs
@@ -0,0 +1,528 @@
+// Copyright 2018 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;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+
+use data_model::{DataInit, Le32};
+use kvm::Datamatch;
+use resources::{Alloc, SystemAllocator};
+use sys_util::{EventFd, GuestMemory, Result};
+
+use super::*;
+use crate::pci::{
+ PciBarConfiguration, PciCapability, PciCapabilityID, PciClassCode, PciConfiguration, PciDevice,
+ PciDeviceError, PciHeaderType, PciInterruptPin, PciSubclass,
+};
+
+use self::virtio_pci_common_config::VirtioPciCommonConfig;
+
+pub enum PciCapabilityType {
+ CommonConfig = 1,
+ NotifyConfig = 2,
+ IsrConfig = 3,
+ DeviceConfig = 4,
+ PciConfig = 5,
+}
+
+#[allow(dead_code)]
+#[repr(C)]
+#[derive(Clone, Copy)]
+struct VirtioPciCap {
+ // _cap_vndr and _cap_next are autofilled based on id() in pci configuration
+ _cap_vndr: u8, // Generic PCI field: PCI_CAP_ID_VNDR
+ _cap_next: u8, // Generic PCI field: next ptr
+ cap_len: u8, // Generic PCI field: capability length
+ cfg_type: u8, // Identifies the structure.
+ bar: u8, // Where to find it.
+ padding: [u8; 3], // Pad to full dword.
+ offset: Le32, // Offset within bar.
+ length: Le32, // Length of the structure, in bytes.
+}
+// It is safe to implement DataInit; all members are simple numbers and any value is valid.
+unsafe impl DataInit for VirtioPciCap {}
+
+impl PciCapability for VirtioPciCap {
+ fn bytes(&self) -> &[u8] {
+ self.as_slice()
+ }
+
+ fn id(&self) -> PciCapabilityID {
+ PciCapabilityID::VendorSpecific
+ }
+}
+
+impl VirtioPciCap {
+ pub fn new(cfg_type: PciCapabilityType, bar: u8, offset: u32, length: u32) -> Self {
+ VirtioPciCap {
+ _cap_vndr: 0,
+ _cap_next: 0,
+ cap_len: std::mem::size_of::<VirtioPciCap>() as u8,
+ cfg_type: cfg_type as u8,
+ bar,
+ padding: [0; 3],
+ offset: Le32::from(offset),
+ length: Le32::from(length),
+ }
+ }
+}
+
+#[allow(dead_code)]
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct VirtioPciNotifyCap {
+ cap: VirtioPciCap,
+ notify_off_multiplier: Le32,
+}
+// It is safe to implement DataInit; all members are simple numbers and any value is valid.
+unsafe impl DataInit for VirtioPciNotifyCap {}
+
+impl PciCapability for VirtioPciNotifyCap {
+ fn bytes(&self) -> &[u8] {
+ self.as_slice()
+ }
+
+ fn id(&self) -> PciCapabilityID {
+ PciCapabilityID::VendorSpecific
+ }
+}
+
+impl VirtioPciNotifyCap {
+ pub fn new(
+ cfg_type: PciCapabilityType,
+ bar: u8,
+ offset: u32,
+ length: u32,
+ multiplier: Le32,
+ ) -> Self {
+ VirtioPciNotifyCap {
+ cap: VirtioPciCap {
+ _cap_vndr: 0,
+ _cap_next: 0,
+ cap_len: std::mem::size_of::<VirtioPciNotifyCap>() as u8,
+ cfg_type: cfg_type as u8,
+ bar,
+ padding: [0; 3],
+ offset: Le32::from(offset),
+ length: Le32::from(length),
+ },
+ notify_off_multiplier: multiplier,
+ }
+ }
+}
+
+/// Subclasses for virtio.
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+pub enum PciVirtioSubclass {
+ NonTransitionalBase = 0xff,
+}
+
+impl PciSubclass for PciVirtioSubclass {
+ fn get_register_value(&self) -> u8 {
+ *self as u8
+ }
+}
+
+// Allocate one bar for the structs pointed to by the capability structures.
+const COMMON_CONFIG_BAR_OFFSET: u64 = 0x0000;
+const COMMON_CONFIG_SIZE: u64 = 56;
+const ISR_CONFIG_BAR_OFFSET: u64 = 0x1000;
+const ISR_CONFIG_SIZE: u64 = 1;
+const DEVICE_CONFIG_BAR_OFFSET: u64 = 0x2000;
+const DEVICE_CONFIG_SIZE: u64 = 0x1000;
+const NOTIFICATION_BAR_OFFSET: u64 = 0x3000;
+const NOTIFICATION_SIZE: u64 = 0x1000;
+const CAPABILITY_BAR_SIZE: u64 = 0x4000;
+
+const NOTIFY_OFF_MULTIPLIER: u32 = 4; // A dword per notification address.
+
+const VIRTIO_PCI_VENDOR_ID: u16 = 0x1af4;
+const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040; // Add to device type to get device ID.
+
+/// Implements the
+/// [PCI](http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-650001)
+/// transport for virtio devices.
+pub struct VirtioPciDevice {
+ config_regs: PciConfiguration,
+ pci_bus_dev: Option<(u8, u8)>,
+
+ device: Box<dyn VirtioDevice>,
+ device_activated: bool,
+
+ interrupt_status: Arc<AtomicUsize>,
+ interrupt_evt: Option<EventFd>,
+ interrupt_resample_evt: Option<EventFd>,
+ queues: Vec<Queue>,
+ queue_evts: Vec<EventFd>,
+ mem: Option<GuestMemory>,
+ settings_bar: u8,
+
+ common_config: VirtioPciCommonConfig,
+}
+
+impl VirtioPciDevice {
+ /// Constructs a new PCI transport for the given virtio device.
+ pub fn new(mem: GuestMemory, device: Box<dyn VirtioDevice>) -> Result<Self> {
+ let mut queue_evts = Vec::new();
+ for _ in device.queue_max_sizes() {
+ queue_evts.push(EventFd::new()?)
+ }
+ let queues = device
+ .queue_max_sizes()
+ .iter()
+ .map(|&s| Queue::new(s))
+ .collect();
+
+ let pci_device_id = VIRTIO_PCI_DEVICE_ID_BASE + device.device_type() as u16;
+
+ let config_regs = PciConfiguration::new(
+ VIRTIO_PCI_VENDOR_ID,
+ pci_device_id,
+ PciClassCode::Other,
+ &PciVirtioSubclass::NonTransitionalBase,
+ None,
+ PciHeaderType::Device,
+ VIRTIO_PCI_VENDOR_ID,
+ pci_device_id,
+ );
+
+ Ok(VirtioPciDevice {
+ config_regs,
+ pci_bus_dev: None,
+ device,
+ device_activated: false,
+ interrupt_status: Arc::new(AtomicUsize::new(0)),
+ interrupt_evt: None,
+ interrupt_resample_evt: None,
+ queues,
+ queue_evts,
+ mem: Some(mem),
+ settings_bar: 0,
+ common_config: VirtioPciCommonConfig {
+ driver_status: 0,
+ config_generation: 0,
+ device_feature_select: 0,
+ driver_feature_select: 0,
+ queue_select: 0,
+ },
+ })
+ }
+
+ /// Gets the list of queue events that must be triggered whenever the VM writes to
+ /// `virtio::NOTIFY_REG_OFFSET` past the MMIO base. Each event must be triggered when the
+ /// value being written equals the index of the event in this list.
+ pub fn queue_evts(&self) -> &[EventFd] {
+ self.queue_evts.as_slice()
+ }
+
+ /// Gets the event this device uses to interrupt the VM when the used queue is changed.
+ pub fn interrupt_evt(&self) -> Option<&EventFd> {
+ self.interrupt_evt.as_ref()
+ }
+
+ fn is_driver_ready(&self) -> bool {
+ let ready_bits =
+ (DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_DRIVER_OK | DEVICE_FEATURES_OK) as u8;
+ self.common_config.driver_status == ready_bits
+ && self.common_config.driver_status & DEVICE_FAILED as u8 == 0
+ }
+
+ fn are_queues_valid(&self) -> bool {
+ if let Some(mem) = self.mem.as_ref() {
+ self.queues.iter().all(|q| q.is_valid(mem))
+ } else {
+ false
+ }
+ }
+
+ fn add_settings_pci_capabilities(
+ &mut self,
+ settings_bar: u8,
+ ) -> std::result::Result<(), PciDeviceError> {
+ // Add pointers to the different configuration structures from the PCI capabilities.
+ let common_cap = VirtioPciCap::new(
+ PciCapabilityType::CommonConfig,
+ settings_bar,
+ COMMON_CONFIG_BAR_OFFSET as u32,
+ COMMON_CONFIG_SIZE as u32,
+ );
+ self.config_regs
+ .add_capability(&common_cap)
+ .map_err(PciDeviceError::CapabilitiesSetup)?;
+
+ let isr_cap = VirtioPciCap::new(
+ PciCapabilityType::IsrConfig,
+ settings_bar,
+ ISR_CONFIG_BAR_OFFSET as u32,
+ ISR_CONFIG_SIZE as u32,
+ );
+ self.config_regs
+ .add_capability(&isr_cap)
+ .map_err(PciDeviceError::CapabilitiesSetup)?;
+
+ // TODO(dgreid) - set based on device's configuration size?
+ let device_cap = VirtioPciCap::new(
+ PciCapabilityType::DeviceConfig,
+ settings_bar,
+ DEVICE_CONFIG_BAR_OFFSET as u32,
+ DEVICE_CONFIG_SIZE as u32,
+ );
+ self.config_regs
+ .add_capability(&device_cap)
+ .map_err(PciDeviceError::CapabilitiesSetup)?;
+
+ let notify_cap = VirtioPciNotifyCap::new(
+ PciCapabilityType::NotifyConfig,
+ settings_bar,
+ NOTIFICATION_BAR_OFFSET as u32,
+ NOTIFICATION_SIZE as u32,
+ Le32::from(NOTIFY_OFF_MULTIPLIER),
+ );
+ self.config_regs
+ .add_capability(¬ify_cap)
+ .map_err(PciDeviceError::CapabilitiesSetup)?;
+
+ //TODO(dgreid) - How will the configuration_cap work?
+ let configuration_cap = VirtioPciCap::new(PciCapabilityType::PciConfig, 0, 0, 0);
+ self.config_regs
+ .add_capability(&configuration_cap)
+ .map_err(PciDeviceError::CapabilitiesSetup)?;
+
+ self.settings_bar = settings_bar;
+ Ok(())
+ }
+}
+
+impl PciDevice for VirtioPciDevice {
+ fn debug_label(&self) -> String {
+ format!("virtio-pci ({})", self.device.debug_label())
+ }
+
+ fn assign_bus_dev(&mut self, bus: u8, device: u8) {
+ self.pci_bus_dev = Some((bus, device));
+ }
+
+ fn keep_fds(&self) -> Vec<RawFd> {
+ let mut fds = self.device.keep_fds();
+ if let Some(interrupt_evt) = &self.interrupt_evt {
+ fds.push(interrupt_evt.as_raw_fd());
+ }
+ if let Some(interrupt_resample_evt) = &self.interrupt_resample_evt {
+ fds.push(interrupt_resample_evt.as_raw_fd());
+ }
+ fds
+ }
+
+ fn assign_irq(
+ &mut self,
+ irq_evt: EventFd,
+ irq_resample_evt: EventFd,
+ irq_num: u32,
+ irq_pin: PciInterruptPin,
+ ) {
+ self.config_regs.set_irq(irq_num as u8, irq_pin);
+ self.interrupt_evt = Some(irq_evt);
+ self.interrupt_resample_evt = Some(irq_resample_evt);
+ }
+
+ fn allocate_io_bars(
+ &mut self,
+ resources: &mut SystemAllocator,
+ ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> {
+ let (bus, dev) = self
+ .pci_bus_dev
+ .expect("assign_bus_dev must be called prior to allocate_io_bars");
+ // Allocate one bar for the structures pointed to by the capability structures.
+ let mut ranges = Vec::new();
+ let settings_config_addr = resources
+ .mmio_allocator()
+ .allocate(
+ CAPABILITY_BAR_SIZE,
+ Alloc::PciBar { bus, dev, bar: 0 },
+ format!(
+ "virtio-{}-cap_bar",
+ type_to_str(self.device.device_type()).unwrap_or("?")
+ ),
+ )
+ .map_err(|e| PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE, e))?;
+ let config = PciBarConfiguration::default()
+ .set_register_index(0)
+ .set_address(settings_config_addr)
+ .set_size(CAPABILITY_BAR_SIZE);
+ let settings_bar = self
+ .config_regs
+ .add_pci_bar(&config)
+ .map_err(|e| PciDeviceError::IoRegistrationFailed(settings_config_addr, e))?
+ as u8;
+ ranges.push((settings_config_addr, CAPABILITY_BAR_SIZE));
+
+ // Once the BARs are allocated, the capabilities can be added to the PCI configuration.
+ self.add_settings_pci_capabilities(settings_bar)?;
+
+ Ok(ranges)
+ }
+
+ fn allocate_device_bars(
+ &mut self,
+ resources: &mut SystemAllocator,
+ ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> {
+ let (bus, dev) = self
+ .pci_bus_dev
+ .expect("assign_bus_dev must be called prior to allocate_device_bars");
+ let mut ranges = Vec::new();
+ for config in self.device.get_device_bars() {
+ let device_addr = resources
+ .device_allocator()
+ .allocate(
+ config.get_size(),
+ Alloc::PciBar {
+ bus,
+ dev,
+ bar: config.get_register_index() as u8,
+ },
+ format!(
+ "virtio-{}-custom_bar",
+ type_to_str(self.device.device_type()).unwrap_or("?")
+ ),
+ )
+ .map_err(|e| PciDeviceError::IoAllocationFailed(config.get_size(), e))?;
+ let config = config.set_address(device_addr);
+ let _device_bar = self
+ .config_regs
+ .add_pci_bar(&config)
+ .map_err(|e| PciDeviceError::IoRegistrationFailed(device_addr, e))?;
+ ranges.push((device_addr, config.get_size()));
+ }
+ Ok(ranges)
+ }
+
+ fn register_device_capabilities(&mut self) -> std::result::Result<(), PciDeviceError> {
+ for cap in self.device.get_device_caps() {
+ self.config_regs
+ .add_capability(&*cap)
+ .map_err(PciDeviceError::CapabilitiesSetup)?;
+ }
+
+ Ok(())
+ }
+
+ fn ioeventfds(&self) -> Vec<(&EventFd, u64, Datamatch)> {
+ let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64;
+ let notify_base = bar0 + NOTIFICATION_BAR_OFFSET;
+ self.queue_evts()
+ .iter()
+ .enumerate()
+ .map(|(i, event)| {
+ (
+ event,
+ notify_base + i as u64 * NOTIFY_OFF_MULTIPLIER as u64,
+ Datamatch::U16(Some(i as u16)),
+ )
+ })
+ .collect()
+ }
+
+ fn config_registers(&self) -> &PciConfiguration {
+ &self.config_regs
+ }
+
+ fn config_registers_mut(&mut self) -> &mut PciConfiguration {
+ &mut self.config_regs
+ }
+
+ // Clippy: the value of COMMON_CONFIG_BAR_OFFSET happens to be zero so the
+ // expression `COMMON_CONFIG_BAR_OFFSET <= o` is always true, but this code
+ // is written such that the value of the const may be changed independently.
+ #[allow(clippy::absurd_extreme_comparisons)]
+ fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
+ // The driver is only allowed to do aligned, properly sized access.
+ let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64;
+ let offset = addr - bar0;
+ match offset {
+ o if COMMON_CONFIG_BAR_OFFSET <= o
+ && o < COMMON_CONFIG_BAR_OFFSET + COMMON_CONFIG_SIZE =>
+ {
+ self.common_config.read(
+ o - COMMON_CONFIG_BAR_OFFSET,
+ data,
+ &mut self.queues,
+ self.device.as_mut(),
+ )
+ }
+ o if ISR_CONFIG_BAR_OFFSET <= o && o < ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE => {
+ if let Some(v) = data.get_mut(0) {
+ // Reading this register resets it to 0.
+ *v = self.interrupt_status.swap(0, Ordering::SeqCst) as u8;
+ }
+ }
+ o if DEVICE_CONFIG_BAR_OFFSET <= o
+ && o < DEVICE_CONFIG_BAR_OFFSET + DEVICE_CONFIG_SIZE =>
+ {
+ self.device.read_config(o - DEVICE_CONFIG_BAR_OFFSET, data);
+ }
+ o if NOTIFICATION_BAR_OFFSET <= o
+ && o < NOTIFICATION_BAR_OFFSET + NOTIFICATION_SIZE =>
+ {
+ // Handled with ioeventfds.
+ }
+ _ => (),
+ }
+ }
+
+ #[allow(clippy::absurd_extreme_comparisons)]
+ fn write_bar(&mut self, addr: u64, data: &[u8]) {
+ let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64;
+ let offset = addr - bar0;
+ match offset {
+ o if COMMON_CONFIG_BAR_OFFSET <= o
+ && o < COMMON_CONFIG_BAR_OFFSET + COMMON_CONFIG_SIZE =>
+ {
+ self.common_config.write(
+ o - COMMON_CONFIG_BAR_OFFSET,
+ data,
+ &mut self.queues,
+ self.device.as_mut(),
+ )
+ }
+ o if ISR_CONFIG_BAR_OFFSET <= o && o < ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE => {
+ if let Some(v) = data.get(0) {
+ self.interrupt_status
+ .fetch_and(!(*v as usize), Ordering::SeqCst);
+ }
+ }
+ o if DEVICE_CONFIG_BAR_OFFSET <= o
+ && o < DEVICE_CONFIG_BAR_OFFSET + DEVICE_CONFIG_SIZE =>
+ {
+ self.device.write_config(o - DEVICE_CONFIG_BAR_OFFSET, data);
+ }
+ o if NOTIFICATION_BAR_OFFSET <= o
+ && o < NOTIFICATION_BAR_OFFSET + NOTIFICATION_SIZE =>
+ {
+ // Handled with ioeventfds.
+ }
+ _ => (),
+ };
+
+ if !self.device_activated && self.is_driver_ready() && self.are_queues_valid() {
+ if let Some(interrupt_evt) = self.interrupt_evt.take() {
+ if let Some(interrupt_resample_evt) = self.interrupt_resample_evt.take() {
+ if let Some(mem) = self.mem.take() {
+ self.device.activate(
+ mem,
+ interrupt_evt,
+ interrupt_resample_evt,
+ self.interrupt_status.clone(),
+ self.queues.clone(),
+ self.queue_evts.split_off(0),
+ );
+ self.device_activated = true;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs
new file mode 100644
index 0000000..dd8ce86
--- /dev/null
+++ b/devices/src/virtio/wl.rs
@@ -0,0 +1,1791 @@
+// Copyright 2017 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 virtio wayland used by the guest to access the host's wayland server.
+//!
+//! The virtio wayland protocol is done over two queues: `in` and `out`. The `in` queue is used for
+//! sending commands to the guest that are generated by the host, usually messages from the wayland
+//! server. The `out` queue is for commands from the guest, usually requests to allocate shared
+//! memory, open a wayland server connection, or send data over an existing connection.
+//!
+//! Each `WlVfd` represents one virtual file descriptor created by either the guest or the host.
+//! Virtual file descriptors contain actual file descriptors, either a shared memory file descriptor
+//! or a unix domain socket to the wayland server. In the shared memory case, there is also an
+//! associated slot that indicates which KVM memory slot the memory is installed into, as well as a
+//! page frame number that the guest can access the memory from.
+//!
+//! The types starting with `Ctrl` are structures representing the virtio wayland protocol "on the
+//! wire." They are decoded/encoded as some variant of `WlOp` for requests and `WlResp` for
+//! responses.
+//!
+//! There is one `WlState` instance that contains every known vfd and the current state of `in`
+//! queue. The `in` queue requires extra state to buffer messages to the guest in case the `in`
+//! queue is already full. The `WlState` also has a control socket necessary to fulfill certain
+//! requests, such as those registering guest memory.
+//!
+//! The `Worker` is responsible for the poll loop over all possible events, encoding/decoding from
+//! the virtio queue, and routing messages in and out of `WlState`. Possible events include the kill
+//! event, available descriptors on the `in` or `out` queue, and incoming data on any vfd's socket.
+
+use std::cell::RefCell;
+use std::collections::btree_map::Entry;
+use std::collections::{BTreeMap as Map, BTreeSet as Set, VecDeque};
+use std::convert::From;
+use std::error::Error as StdError;
+use std::ffi::CStr;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::{self, Read, Seek, SeekFrom};
+use std::mem::{size_of, size_of_val};
+#[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;
+use std::result;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+use std::time::Duration;
+
+#[cfg(feature = "wl-dmabuf")]
+use libc::{dup, EBADF, EINVAL};
+
+use data_model::VolatileMemoryError;
+use data_model::*;
+
+use msg_socket::{MsgError, MsgReceiver, MsgSender};
+#[cfg(feature = "wl-dmabuf")]
+use resources::GpuMemoryDesc;
+#[cfg(feature = "wl-dmabuf")]
+use sys_util::ioctl_iow_nr;
+use sys_util::{
+ error, pipe, round_up_to_page_size, warn, Error, EventFd, FileFlags, FileReadWriteVolatile,
+ GuestAddress, GuestMemory, GuestMemoryError, PollContext, PollToken, Result, ScmSocket,
+ SharedMemory,
+};
+
+#[cfg(feature = "wl-dmabuf")]
+use sys_util::ioctl_with_ref;
+
+use super::resource_bridge::*;
+use super::{
+ DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_WL, VIRTIO_F_VERSION_1,
+};
+use vm_control::{MaybeOwnedFd, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse};
+
+const VIRTWL_SEND_MAX_ALLOCS: usize = 28;
+const VIRTIO_WL_CMD_VFD_NEW: u32 = 256;
+const VIRTIO_WL_CMD_VFD_CLOSE: u32 = 257;
+const VIRTIO_WL_CMD_VFD_SEND: u32 = 258;
+const VIRTIO_WL_CMD_VFD_RECV: u32 = 259;
+const VIRTIO_WL_CMD_VFD_NEW_CTX: u32 = 260;
+const VIRTIO_WL_CMD_VFD_NEW_PIPE: u32 = 261;
+const VIRTIO_WL_CMD_VFD_HUP: u32 = 262;
+#[cfg(feature = "wl-dmabuf")]
+const VIRTIO_WL_CMD_VFD_NEW_DMABUF: u32 = 263;
+#[cfg(feature = "wl-dmabuf")]
+const VIRTIO_WL_CMD_VFD_DMABUF_SYNC: u32 = 264;
+#[cfg(feature = "gpu")]
+const VIRTIO_WL_CMD_VFD_SEND_FOREIGN_ID: u32 = 265;
+const VIRTIO_WL_RESP_OK: u32 = 4096;
+const VIRTIO_WL_RESP_VFD_NEW: u32 = 4097;
+#[cfg(feature = "wl-dmabuf")]
+const VIRTIO_WL_RESP_VFD_NEW_DMABUF: u32 = 4098;
+const VIRTIO_WL_RESP_ERR: u32 = 4352;
+const VIRTIO_WL_RESP_OUT_OF_MEMORY: u32 = 4353;
+const VIRTIO_WL_RESP_INVALID_ID: u32 = 4354;
+const VIRTIO_WL_RESP_INVALID_TYPE: u32 = 4355;
+const VIRTIO_WL_RESP_INVALID_FLAGS: u32 = 4356;
+const VIRTIO_WL_RESP_INVALID_CMD: u32 = 4357;
+const VIRTIO_WL_VFD_WRITE: u32 = 0x1;
+const VIRTIO_WL_VFD_READ: u32 = 0x2;
+const VIRTIO_WL_VFD_MAP: u32 = 0x2;
+const VIRTIO_WL_VFD_CONTROL: u32 = 0x4;
+const VIRTIO_WL_F_TRANS_FLAGS: u32 = 0x01;
+
+const QUEUE_SIZE: u16 = 16;
+const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE, QUEUE_SIZE];
+
+const NEXT_VFD_ID_BASE: u32 = 0x40000000;
+const VFD_ID_HOST_MASK: u32 = NEXT_VFD_ID_BASE;
+// Each in-vq buffer is one page, so we need to leave space for the control header and the maximum
+// number of allocs.
+const IN_BUFFER_LEN: usize =
+ 0x1000 - size_of::<CtrlVfdRecv>() - VIRTWL_SEND_MAX_ALLOCS * size_of::<Le32>();
+
+#[cfg(feature = "wl-dmabuf")]
+const VIRTIO_WL_VFD_DMABUF_SYNC_VALID_FLAG_MASK: u32 = 0x7;
+
+#[cfg(feature = "wl-dmabuf")]
+const DMA_BUF_IOCTL_BASE: c_uint = 0x62;
+
+#[cfg(feature = "wl-dmabuf")]
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct dma_buf_sync {
+ flags: c_ulonglong,
+}
+
+#[cfg(feature = "wl-dmabuf")]
+ioctl_iow_nr!(DMA_BUF_IOCTL_SYNC, DMA_BUF_IOCTL_BASE, 0, dma_buf_sync);
+
+const VIRTIO_WL_CTRL_VFD_SEND_KIND_LOCAL: u32 = 0;
+const VIRTIO_WL_CTRL_VFD_SEND_KIND_VIRTGPU: u32 = 1;
+
+fn parse_new(addr: GuestAddress, mem: &GuestMemory) -> WlResult<WlOp> {
+ const ID_OFFSET: u64 = 8;
+ const FLAGS_OFFSET: u64 = 12;
+ const SIZE_OFFSET: u64 = 24;
+
+ let id: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, ID_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let flags: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, FLAGS_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let size: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, SIZE_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ Ok(WlOp::NewAlloc {
+ id: id.into(),
+ flags: flags.into(),
+ size: size.into(),
+ })
+}
+
+fn parse_new_pipe(addr: GuestAddress, mem: &GuestMemory) -> WlResult<WlOp> {
+ const ID_OFFSET: u64 = 8;
+ const FLAGS_OFFSET: u64 = 12;
+
+ let id: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, ID_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let flags: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, FLAGS_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ Ok(WlOp::NewPipe {
+ id: id.into(),
+ flags: flags.into(),
+ })
+}
+
+#[cfg(feature = "wl-dmabuf")]
+fn parse_new_dmabuf(addr: GuestAddress, mem: &GuestMemory) -> WlResult<WlOp> {
+ const ID_OFFSET: u64 = 8;
+ const WIDTH_OFFSET: u64 = 28;
+ const HEIGHT_OFFSET: u64 = 32;
+ const FORMAT_OFFSET: u64 = 36;
+
+ let id: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, ID_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let width: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, WIDTH_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let height: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, HEIGHT_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let format: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, FORMAT_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ Ok(WlOp::NewDmabuf {
+ id: id.into(),
+ width: width.into(),
+ height: height.into(),
+ format: format.into(),
+ })
+}
+
+#[cfg(feature = "wl-dmabuf")]
+fn parse_dmabuf_sync(addr: GuestAddress, mem: &GuestMemory) -> WlResult<WlOp> {
+ const ID_OFFSET: u64 = 8;
+ const FLAGS_OFFSET: u64 = 12;
+ let id: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, ID_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let flags: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, FLAGS_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ Ok(WlOp::DmabufSync {
+ id: id.into(),
+ flags: flags.into(),
+ })
+}
+
+fn parse_send(addr: GuestAddress, len: u32, foreign_id: bool, mem: &GuestMemory) -> WlResult<WlOp> {
+ const ID_OFFSET: u64 = 8;
+ const VFD_COUNT_OFFSET: u64 = 12;
+ const VFDS_OFFSET: u64 = 16;
+
+ let id: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, ID_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let vfd_count: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, VFD_COUNT_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ let vfd_count: u32 = vfd_count.into();
+ let vfds_addr = mem
+ .checked_offset(addr, VFDS_OFFSET)
+ .ok_or(WlError::CheckedOffset)?;
+ let vfds_element_size = if foreign_id {
+ size_of::<CtrlVfdSendVfd>()
+ } else {
+ size_of::<Le32>()
+ } as u32;
+ let data_addr = mem
+ .checked_offset(vfds_addr, (vfd_count * vfds_element_size) as u64)
+ .ok_or(WlError::CheckedOffset)?;
+ Ok(WlOp::Send {
+ id: id.into(),
+ foreign_id,
+ vfds_addr,
+ vfd_count,
+ data_addr,
+ data_len: len - (VFDS_OFFSET as u32) - vfd_count * vfds_element_size,
+ })
+}
+
+fn parse_id(addr: GuestAddress, mem: &GuestMemory) -> WlResult<u32> {
+ const ID_OFFSET: u64 = 8;
+ let id: Le32 = mem.read_obj_from_addr(
+ mem.checked_offset(addr, ID_OFFSET)
+ .ok_or(WlError::CheckedOffset)?,
+ )?;
+ Ok(id.into())
+}
+
+fn parse_desc(desc: &DescriptorChain, mem: &GuestMemory) -> WlResult<WlOp> {
+ let type_: Le32 = mem.read_obj_from_addr(desc.addr)?;
+ match type_.into() {
+ VIRTIO_WL_CMD_VFD_NEW => parse_new(desc.addr, mem),
+ VIRTIO_WL_CMD_VFD_CLOSE => Ok(WlOp::Close {
+ id: parse_id(desc.addr, mem)?,
+ }),
+ VIRTIO_WL_CMD_VFD_SEND => parse_send(desc.addr, desc.len, false, mem),
+ VIRTIO_WL_CMD_VFD_NEW_CTX => Ok(WlOp::NewCtx {
+ id: parse_id(desc.addr, mem)?,
+ }),
+ VIRTIO_WL_CMD_VFD_NEW_PIPE => parse_new_pipe(desc.addr, mem),
+ #[cfg(feature = "wl-dmabuf")]
+ VIRTIO_WL_CMD_VFD_NEW_DMABUF => parse_new_dmabuf(desc.addr, mem),
+ #[cfg(feature = "wl-dmabuf")]
+ VIRTIO_WL_CMD_VFD_DMABUF_SYNC => parse_dmabuf_sync(desc.addr, mem),
+ #[cfg(feature = "gpu")]
+ VIRTIO_WL_CMD_VFD_SEND_FOREIGN_ID => parse_send(desc.addr, desc.len, true, mem),
+ v => Ok(WlOp::InvalidCommand { op_type: v }),
+ }
+}
+
+fn encode_vfd_new(
+ desc_mem: VolatileSlice,
+ resp: bool,
+ vfd_id: u32,
+ flags: u32,
+ pfn: u64,
+ size: u32,
+) -> WlResult<u32> {
+ let ctrl_vfd_new = CtrlVfdNew {
+ hdr: CtrlHeader {
+ type_: Le32::from(if resp {
+ VIRTIO_WL_RESP_VFD_NEW
+ } else {
+ VIRTIO_WL_CMD_VFD_NEW
+ }),
+ flags: Le32::from(0),
+ },
+ id: Le32::from(vfd_id),
+ flags: Le32::from(flags),
+ pfn: Le64::from(pfn),
+ size: Le32::from(size),
+ };
+
+ desc_mem.get_ref(0)?.store(ctrl_vfd_new);
+ Ok(size_of::<CtrlVfdNew>() as u32)
+}
+
+#[cfg(feature = "wl-dmabuf")]
+fn encode_vfd_new_dmabuf(
+ desc_mem: VolatileSlice,
+ vfd_id: u32,
+ flags: u32,
+ pfn: u64,
+ size: u32,
+ desc: GpuMemoryDesc,
+) -> WlResult<u32> {
+ let ctrl_vfd_new_dmabuf = CtrlVfdNewDmabuf {
+ hdr: CtrlHeader {
+ type_: Le32::from(VIRTIO_WL_RESP_VFD_NEW_DMABUF),
+ flags: Le32::from(0),
+ },
+ id: Le32::from(vfd_id),
+ flags: Le32::from(flags),
+ pfn: Le64::from(pfn),
+ size: Le32::from(size),
+ width: Le32::from(0),
+ height: Le32::from(0),
+ format: Le32::from(0),
+ stride0: Le32::from(desc.planes[0].stride),
+ stride1: Le32::from(desc.planes[1].stride),
+ stride2: Le32::from(desc.planes[2].stride),
+ offset0: Le32::from(desc.planes[0].offset),
+ offset1: Le32::from(desc.planes[1].offset),
+ offset2: Le32::from(desc.planes[2].offset),
+ };
+
+ desc_mem.get_ref(0)?.store(ctrl_vfd_new_dmabuf);
+ Ok(size_of::<CtrlVfdNewDmabuf>() as u32)
+}
+
+fn encode_vfd_recv(
+ desc_mem: VolatileSlice,
+ vfd_id: u32,
+ data: &[u8],
+ vfd_ids: &[u32],
+) -> WlResult<u32> {
+ let ctrl_vfd_recv = CtrlVfdRecv {
+ hdr: CtrlHeader {
+ type_: Le32::from(VIRTIO_WL_CMD_VFD_RECV),
+ flags: Le32::from(0),
+ },
+ id: Le32::from(vfd_id),
+ vfd_count: Le32::from(vfd_ids.len() as u32),
+ };
+ desc_mem.get_ref(0)?.store(ctrl_vfd_recv);
+
+ let vfd_slice = desc_mem.get_slice(
+ size_of::<CtrlVfdRecv>() as u64,
+ (vfd_ids.len() * size_of::<Le32>()) as u64,
+ )?;
+ for (i, &recv_vfd_id) in vfd_ids.iter().enumerate() {
+ vfd_slice
+ .get_ref((size_of::<Le32>() * i) as u64)?
+ .store(recv_vfd_id);
+ }
+
+ let data_slice = desc_mem.get_slice(
+ (size_of::<CtrlVfdRecv>() + vfd_ids.len() * size_of::<Le32>()) as u64,
+ data.len() as u64,
+ )?;
+ data_slice.copy_from(data);
+
+ Ok((size_of::<CtrlVfdRecv>() + vfd_ids.len() * size_of::<Le32>() + data.len()) as u32)
+}
+
+fn encode_vfd_hup(desc_mem: VolatileSlice, vfd_id: u32) -> WlResult<u32> {
+ let ctrl_vfd_new = CtrlVfd {
+ hdr: CtrlHeader {
+ type_: Le32::from(VIRTIO_WL_CMD_VFD_HUP),
+ flags: Le32::from(0),
+ },
+ id: Le32::from(vfd_id),
+ };
+
+ desc_mem.get_ref(0)?.store(ctrl_vfd_new);
+ Ok(size_of_val(&ctrl_vfd_new) as u32)
+}
+
+fn encode_resp(desc_mem: VolatileSlice, resp: WlResp) -> WlResult<u32> {
+ match resp {
+ WlResp::VfdNew {
+ id,
+ flags,
+ pfn,
+ size,
+ resp,
+ } => encode_vfd_new(desc_mem, resp, id, flags, pfn, size),
+ #[cfg(feature = "wl-dmabuf")]
+ WlResp::VfdNewDmabuf {
+ id,
+ flags,
+ pfn,
+ size,
+ desc,
+ } => encode_vfd_new_dmabuf(desc_mem, id, flags, pfn, size, desc),
+ WlResp::VfdRecv { id, data, vfds } => encode_vfd_recv(desc_mem, id, data, vfds),
+ WlResp::VfdHup { id } => encode_vfd_hup(desc_mem, id),
+ r => {
+ desc_mem.get_ref(0)?.store(Le32::from(r.get_code()));
+ Ok(size_of::<Le32>() as u32)
+ }
+ }
+}
+
+#[allow(dead_code)]
+#[derive(Debug)]
+enum WlError {
+ NewAlloc(Error),
+ NewPipe(Error),
+ AllocSetSize(Error),
+ SocketConnect(io::Error),
+ SocketNonBlock(io::Error),
+ VmControl(MsgError),
+ VmBadResponse,
+ CheckedOffset,
+ GuestMemory(GuestMemoryError),
+ VolatileMemory(VolatileMemoryError),
+ SendVfd(Error),
+ WritePipe(io::Error),
+ RecvVfd(Error),
+ ReadPipe(io::Error),
+ PollContextAdd(Error),
+ DmabufSync(io::Error),
+}
+
+impl Display for WlError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::WlError::*;
+
+ match self {
+ NewAlloc(e) => write!(f, "failed to create shared memory allocation: {}", e),
+ NewPipe(e) => write!(f, "failed to create pipe: {}", e),
+ AllocSetSize(e) => write!(f, "failed to set size of shared memory: {}", e),
+ SocketConnect(e) => write!(f, "failed to connect socket: {}", e),
+ SocketNonBlock(e) => write!(f, "failed to set socket as non-blocking: {}", e),
+ VmControl(e) => write!(f, "failed to control parent VM: {}", e),
+ VmBadResponse => write!(f, "invalid response from parent VM"),
+ CheckedOffset => write!(f, "overflow in calculation"),
+ GuestMemory(e) => write!(f, "access violation in guest memory: {}", e),
+ VolatileMemory(e) => write!(f, "access violating in guest volatile memory: {}", e),
+ SendVfd(e) => write!(f, "failed to send on a socket: {}", e),
+ 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),
+ DmabufSync(e) => write!(f, "failed to synchronize DMABuf access: {}", e),
+ }
+ }
+}
+
+impl std::error::Error for WlError {}
+
+type WlResult<T> = result::Result<T, WlError>;
+
+impl From<GuestMemoryError> for WlError {
+ fn from(e: GuestMemoryError) -> WlError {
+ WlError::GuestMemory(e)
+ }
+}
+
+impl From<VolatileMemoryError> for WlError {
+ fn from(e: VolatileMemoryError) -> WlError {
+ WlError::VolatileMemory(e)
+ }
+}
+
+#[derive(Clone)]
+struct VmRequester {
+ inner: Rc<RefCell<VmMemoryControlRequestSocket>>,
+}
+
+impl VmRequester {
+ fn new(vm_socket: VmMemoryControlRequestSocket) -> VmRequester {
+ VmRequester {
+ inner: Rc::new(RefCell::new(vm_socket)),
+ }
+ }
+
+ fn request(&self, request: VmMemoryRequest) -> WlResult<VmMemoryResponse> {
+ let mut inner = self.inner.borrow_mut();
+ let vm_socket = &mut *inner;
+ vm_socket.send(&request).map_err(WlError::VmControl)?;
+ vm_socket.recv().map_err(WlError::VmControl)
+ }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct CtrlHeader {
+ type_: Le32,
+ flags: Le32,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct CtrlVfdNew {
+ hdr: CtrlHeader,
+ id: Le32,
+ flags: Le32,
+ pfn: Le64,
+ size: Le32,
+}
+
+unsafe impl DataInit for CtrlVfdNew {}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+#[cfg(feature = "wl-dmabuf")]
+struct CtrlVfdNewDmabuf {
+ hdr: CtrlHeader,
+ id: Le32,
+ flags: Le32,
+ pfn: Le64,
+ size: Le32,
+ width: Le32,
+ height: Le32,
+ format: Le32,
+ stride0: Le32,
+ stride1: Le32,
+ stride2: Le32,
+ offset0: Le32,
+ offset1: Le32,
+ offset2: Le32,
+}
+
+#[cfg(feature = "wl-dmabuf")]
+unsafe impl DataInit for CtrlVfdNewDmabuf {}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct CtrlVfdRecv {
+ hdr: CtrlHeader,
+ id: Le32,
+ vfd_count: Le32,
+}
+
+unsafe impl DataInit for CtrlVfdRecv {}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct CtrlVfd {
+ hdr: CtrlHeader,
+ id: Le32,
+}
+
+unsafe impl DataInit for CtrlVfd {}
+
+#[repr(C)]
+#[derive(Copy, Clone, Default)]
+struct CtrlVfdSendVfd {
+ kind: Le32,
+ id: Le32,
+}
+
+unsafe impl DataInit for CtrlVfdSendVfd {}
+
+#[derive(Debug)]
+enum WlOp {
+ NewAlloc {
+ id: u32,
+ flags: u32,
+ size: u32,
+ },
+ Close {
+ id: u32,
+ },
+ Send {
+ id: u32,
+ foreign_id: bool,
+ vfds_addr: GuestAddress,
+ vfd_count: u32,
+ data_addr: GuestAddress,
+ data_len: u32,
+ },
+ NewCtx {
+ id: u32,
+ },
+ NewPipe {
+ id: u32,
+ flags: u32,
+ },
+ #[cfg(feature = "wl-dmabuf")]
+ NewDmabuf {
+ id: u32,
+ width: u32,
+ height: u32,
+ format: u32,
+ },
+ #[cfg(feature = "wl-dmabuf")]
+ DmabufSync {
+ id: u32,
+ flags: u32,
+ },
+ InvalidCommand {
+ op_type: u32,
+ },
+}
+
+#[derive(Debug)]
+#[allow(dead_code)]
+enum WlResp<'a> {
+ Ok,
+ VfdNew {
+ id: u32,
+ flags: u32,
+ pfn: u64,
+ size: u32,
+ // The VfdNew variant can be either a response or a command depending on this `resp`. This
+ // is important for the `get_code` method.
+ resp: bool,
+ },
+ #[cfg(feature = "wl-dmabuf")]
+ VfdNewDmabuf {
+ id: u32,
+ flags: u32,
+ pfn: u64,
+ size: u32,
+ desc: GpuMemoryDesc,
+ },
+ VfdRecv {
+ id: u32,
+ data: &'a [u8],
+ vfds: &'a [u32],
+ },
+ VfdHup {
+ id: u32,
+ },
+ Err(Box<dyn StdError>),
+ OutOfMemory,
+ InvalidId,
+ InvalidType,
+ InvalidFlags,
+ InvalidCommand,
+}
+
+impl<'a> WlResp<'a> {
+ fn get_code(&self) -> u32 {
+ match *self {
+ WlResp::Ok => VIRTIO_WL_RESP_OK,
+ WlResp::VfdNew { resp, .. } => {
+ if resp {
+ VIRTIO_WL_RESP_VFD_NEW
+ } else {
+ VIRTIO_WL_CMD_VFD_NEW
+ }
+ }
+ #[cfg(feature = "wl-dmabuf")]
+ WlResp::VfdNewDmabuf { .. } => VIRTIO_WL_RESP_VFD_NEW_DMABUF,
+ WlResp::VfdRecv { .. } => VIRTIO_WL_CMD_VFD_RECV,
+ WlResp::VfdHup { .. } => VIRTIO_WL_CMD_VFD_HUP,
+ WlResp::Err(_) => VIRTIO_WL_RESP_ERR,
+ WlResp::OutOfMemory => VIRTIO_WL_RESP_OUT_OF_MEMORY,
+ WlResp::InvalidId => VIRTIO_WL_RESP_INVALID_ID,
+ WlResp::InvalidType => VIRTIO_WL_RESP_INVALID_TYPE,
+ WlResp::InvalidFlags => VIRTIO_WL_RESP_INVALID_FLAGS,
+ WlResp::InvalidCommand => VIRTIO_WL_RESP_INVALID_CMD,
+ }
+ }
+}
+
+#[derive(Default)]
+struct WlVfd {
+ socket: Option<UnixStream>,
+ guest_shared_memory: Option<(u64 /* size */, File)>,
+ remote_pipe: Option<File>,
+ local_pipe: Option<(u32 /* flags */, File)>,
+ slot: Option<(u32 /* slot */, u64 /* pfn */, VmRequester)>,
+ #[cfg(feature = "wl-dmabuf")]
+ is_dmabuf: bool,
+}
+
+impl fmt::Debug for WlVfd {
+ 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())?;
+ }
+ 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())?;
+ }
+ if let Some((_, s)) = &self.local_pipe {
+ write!(f, " local: {}", s.as_raw_fd())?;
+ }
+ write!(f, " }}")
+ }
+}
+
+impl WlVfd {
+ fn connect<P: AsRef<Path>>(path: P) -> WlResult<WlVfd> {
+ let socket = UnixStream::connect(path).map_err(WlError::SocketConnect)?;
+ socket
+ .set_nonblocking(true)
+ .map_err(WlError::SocketNonBlock)?;
+ let mut vfd = WlVfd::default();
+ vfd.socket = Some(socket);
+ Ok(vfd)
+ }
+
+ fn allocate(vm: VmRequester, size: u64) -> WlResult<WlVfd> {
+ let size_page_aligned = round_up_to_page_size(size as usize) as u64;
+ let mut vfd_shm =
+ SharedMemory::new(Some(CStr::from_bytes_with_nul(b"virtwl_alloc\0").unwrap()))
+ .map_err(WlError::NewAlloc)?;
+ vfd_shm
+ .set_size(size_page_aligned)
+ .map_err(WlError::AllocSetSize)?;
+ let register_response = vm.request(VmMemoryRequest::RegisterMemory(
+ MaybeOwnedFd::Borrowed(vfd_shm.as_raw_fd()),
+ vfd_shm.size() as usize,
+ ))?;
+ match register_response {
+ VmMemoryResponse::RegisterMemory { pfn, slot } => {
+ let mut vfd = WlVfd::default();
+ vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm.into()));
+ vfd.slot = Some((slot, pfn, vm));
+ Ok(vfd)
+ }
+ _ => Err(WlError::VmBadResponse),
+ }
+ }
+
+ #[cfg(feature = "wl-dmabuf")]
+ fn dmabuf(
+ vm: VmRequester,
+ width: u32,
+ height: u32,
+ format: u32,
+ ) -> WlResult<(WlVfd, GpuMemoryDesc)> {
+ let allocate_and_register_gpu_memory_response =
+ vm.request(VmMemoryRequest::AllocateAndRegisterGpuMemory {
+ width,
+ height,
+ format,
+ })?;
+ match allocate_and_register_gpu_memory_response {
+ VmMemoryResponse::AllocateAndRegisterGpuMemory {
+ fd,
+ pfn,
+ slot,
+ desc,
+ } => {
+ let mut vfd = WlVfd::default();
+ // Duplicate FD for shared memory instance.
+ let raw_fd = unsafe { File::from_raw_fd(dup(fd.as_raw_fd())) };
+ let vfd_shm = SharedMemory::from_raw_fd(raw_fd).map_err(WlError::NewAlloc)?;
+ vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm.into()));
+ vfd.slot = Some((slot, pfn, vm));
+ vfd.is_dmabuf = true;
+ Ok((vfd, desc))
+ }
+ _ => Err(WlError::VmBadResponse),
+ }
+ }
+
+ #[cfg(feature = "wl-dmabuf")]
+ fn dmabuf_sync(&self, flags: u32) -> WlResult<()> {
+ if !self.is_dmabuf {
+ return Err(WlError::DmabufSync(io::Error::from_raw_os_error(EINVAL)));
+ }
+
+ match &self.guest_shared_memory {
+ Some((_, fd)) => {
+ 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 {
+ Err(WlError::DmabufSync(io::Error::last_os_error()))
+ } else {
+ Ok(())
+ }
+ }
+ None => Err(WlError::DmabufSync(io::Error::from_raw_os_error(EBADF))),
+ }
+ }
+
+ fn pipe_remote_read_local_write() -> WlResult<WlVfd> {
+ let (read_pipe, write_pipe) = pipe(true).map_err(WlError::NewPipe)?;
+ let mut vfd = WlVfd::default();
+ vfd.remote_pipe = Some(read_pipe);
+ vfd.local_pipe = Some((VIRTIO_WL_VFD_WRITE, write_pipe));
+ Ok(vfd)
+ }
+
+ fn pipe_remote_write_local_read() -> WlResult<WlVfd> {
+ let (read_pipe, write_pipe) = pipe(true).map_err(WlError::NewPipe)?;
+ let mut vfd = WlVfd::default();
+ vfd.remote_pipe = Some(write_pipe);
+ vfd.local_pipe = Some((VIRTIO_WL_VFD_READ, read_pipe));
+ Ok(vfd)
+ }
+
+ fn from_file(vm: VmRequester, mut fd: 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)) {
+ 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()),
+ size as usize,
+ ))?;
+
+ match register_response {
+ VmMemoryResponse::RegisterMemory { pfn, slot } => {
+ let mut vfd = WlVfd::default();
+ vfd.guest_shared_memory = Some((size, fd));
+ vfd.slot = Some((slot, pfn, vm));
+ Ok(vfd)
+ }
+ _ => Err(WlError::VmBadResponse),
+ }
+ }
+ _ => {
+ let flags = match FileFlags::from_file(&fd) {
+ 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));
+ Ok(vfd)
+ }
+ }
+ }
+
+ fn flags(&self, use_transition_flags: bool) -> u32 {
+ let mut flags = 0;
+ if use_transition_flags {
+ if self.socket.is_some() {
+ flags |= VIRTIO_WL_VFD_WRITE | VIRTIO_WL_VFD_READ;
+ }
+ if let Some((f, _)) = self.local_pipe {
+ flags |= f;
+ }
+ } else {
+ if self.socket.is_some() {
+ flags |= VIRTIO_WL_VFD_CONTROL;
+ }
+ if self.slot.is_some() {
+ flags |= VIRTIO_WL_VFD_WRITE | VIRTIO_WL_VFD_MAP
+ }
+ }
+ flags
+ }
+
+ // Page frame number in the guest this VFD was mapped at.
+ fn pfn(&self) -> Option<u64> {
+ self.slot.as_ref().map(|s| s.1)
+ }
+
+ // Size in bytes of the shared memory VFD.
+ fn size(&self) -> Option<u64> {
+ self.guest_shared_memory.as_ref().map(|&(size, _)| size)
+ }
+
+ // The FD that gets sent if this VFD is sent over a socket.
+ fn send_fd(&self) -> Option<RawFd> {
+ self.guest_shared_memory
+ .as_ref()
+ .map(|(_, fd)| fd.as_raw_fd())
+ .or(self.socket.as_ref().map(|s| s.as_raw_fd()))
+ .or(self.remote_pipe.as_ref().map(|p| p.as_raw_fd()))
+ }
+
+ // The FD that is used for polling for events on this VFD.
+ fn poll_fd(&self) -> Option<&dyn AsRawFd> {
+ self.socket
+ .as_ref()
+ .map(|s| s as &dyn AsRawFd)
+ .or(self.local_pipe.as_ref().map(|(_, p)| p as &dyn AsRawFd))
+ }
+
+ // Sends data/files from the guest to the host over this VFD.
+ fn send(&mut self, fds: &[RawFd], data: VolatileSlice) -> WlResult<WlResp> {
+ if let Some(socket) = &self.socket {
+ socket.send_with_fds(data, fds).map_err(WlError::SendVfd)?;
+ 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() {
+ return Ok(WlResp::InvalidType);
+ }
+ local_pipe
+ .write_volatile(data)
+ .map_err(WlError::WritePipe)?;
+ Ok(WlResp::Ok)
+ } else {
+ Ok(WlResp::InvalidType)
+ }
+ }
+
+ // Receives data/files from the host for this VFD and queues it for the guest.
+ fn recv(&mut self, in_file_queue: &mut Vec<File>) -> WlResult<Vec<u8>> {
+ if let Some(socket) = self.socket.take() {
+ let mut buf = vec![0; IN_BUFFER_LEN];
+ let mut fd_buf = [0; VIRTWL_SEND_MAX_ALLOCS];
+ // If any errors happen, the socket will get dropped, preventing more reading.
+ let (len, file_count) = socket
+ .recv_with_fds(&mut buf[..], &mut fd_buf)
+ .map_err(WlError::RecvVfd)?;
+ // If any data gets read, the put the socket back for future recv operations.
+ if len != 0 || file_count != 0 {
+ buf.truncate(len);
+ buf.shrink_to_fit();
+ self.socket = Some(socket);
+ // Safe because the first file_counts fds from recv_with_fds are owned by us and
+ // valid.
+ in_file_queue.extend(
+ fd_buf[..file_count]
+ .iter()
+ .map(|&fd| unsafe { File::from_raw_fd(fd) }),
+ );
+ return Ok(buf);
+ }
+ Ok(Vec::new())
+ } else if let Some((flags, mut local_pipe)) = self.local_pipe.take() {
+ let mut buf = Vec::new();
+ buf.resize(IN_BUFFER_LEN, 0);
+ let len = local_pipe.read(&mut buf[..]).map_err(WlError::ReadPipe)?;
+ if len != 0 {
+ buf.truncate(len);
+ buf.shrink_to_fit();
+ self.local_pipe = Some((flags, local_pipe));
+ return Ok(buf);
+ }
+ Ok(Vec::new())
+ } else {
+ Ok(Vec::new())
+ }
+ }
+
+ // Called after this VFD is sent over a socket to ensure the local end of the VFD receives hang
+ // up events.
+ fn close_remote(&mut self) {
+ self.remote_pipe = None;
+ }
+
+ fn close(&mut self) -> WlResult<()> {
+ if let Some((slot, _, vm)) = self.slot.take() {
+ vm.request(VmMemoryRequest::UnregisterMemory(slot))?;
+ }
+ self.socket = None;
+ self.remote_pipe = None;
+ self.local_pipe = None;
+ Ok(())
+ }
+}
+
+impl Drop for WlVfd {
+ fn drop(&mut self) {
+ let _ = self.close();
+ }
+}
+
+#[derive(Debug)]
+enum WlRecv {
+ Vfd { id: u32 },
+ Data { buf: Vec<u8> },
+ Hup,
+}
+
+struct WlState {
+ wayland_path: PathBuf,
+ vm: VmRequester,
+ resource_bridge: Option<ResourceRequestSocket>,
+ use_transition_flags: bool,
+ poll_ctx: PollContext<u32>,
+ vfds: Map<u32, WlVfd>,
+ next_vfd_id: u32,
+ in_file_queue: Vec<File>,
+ in_queue: VecDeque<(u32 /* vfd_id */, WlRecv)>,
+ current_recv_vfd: Option<u32>,
+ recv_vfds: Vec<u32>,
+}
+
+impl WlState {
+ fn new(
+ wayland_path: PathBuf,
+ vm_socket: VmMemoryControlRequestSocket,
+ use_transition_flags: bool,
+ resource_bridge: Option<ResourceRequestSocket>,
+ ) -> WlState {
+ WlState {
+ wayland_path,
+ vm: VmRequester::new(vm_socket),
+ resource_bridge,
+ poll_ctx: PollContext::new().expect("failed to create PollContext"),
+ use_transition_flags,
+ vfds: Map::new(),
+ next_vfd_id: NEXT_VFD_ID_BASE,
+ in_file_queue: Vec::new(),
+ in_queue: VecDeque::new(),
+ current_recv_vfd: None,
+ recv_vfds: Vec::new(),
+ }
+ }
+
+ fn new_pipe(&mut self, id: u32, flags: u32) -> WlResult<WlResp> {
+ if id & VFD_ID_HOST_MASK != 0 {
+ return Ok(WlResp::InvalidId);
+ }
+
+ if flags & !(VIRTIO_WL_VFD_WRITE | VIRTIO_WL_VFD_READ) != 0 {
+ return Ok(WlResp::InvalidFlags);
+ }
+
+ if flags & VIRTIO_WL_VFD_WRITE != 0 && flags & VIRTIO_WL_VFD_READ != 0 {
+ return Ok(WlResp::InvalidFlags);
+ }
+
+ match self.vfds.entry(id) {
+ Entry::Vacant(entry) => {
+ let vfd = if flags & VIRTIO_WL_VFD_WRITE != 0 {
+ WlVfd::pipe_remote_read_local_write()?
+ } else if flags & VIRTIO_WL_VFD_READ != 0 {
+ WlVfd::pipe_remote_write_local_read()?
+ } else {
+ return Ok(WlResp::InvalidFlags);
+ };
+ self.poll_ctx
+ .add(vfd.poll_fd().unwrap(), id)
+ .map_err(WlError::PollContextAdd)?;
+ let resp = WlResp::VfdNew {
+ id,
+ flags: 0,
+ pfn: 0,
+ size: 0,
+ resp: true,
+ };
+ entry.insert(vfd);
+ Ok(resp)
+ }
+ Entry::Occupied(_) => Ok(WlResp::InvalidId),
+ }
+ }
+
+ fn new_alloc(&mut self, id: u32, flags: u32, size: u32) -> WlResult<WlResp> {
+ if id & VFD_ID_HOST_MASK != 0 {
+ return Ok(WlResp::InvalidId);
+ }
+
+ if self.use_transition_flags {
+ if flags != 0 {
+ return Ok(WlResp::InvalidFlags);
+ }
+ } else if flags & !(VIRTIO_WL_VFD_WRITE | VIRTIO_WL_VFD_MAP) != 0 {
+ return Ok(WlResp::Err(Box::from("invalid flags")));
+ }
+
+ match self.vfds.entry(id) {
+ Entry::Vacant(entry) => {
+ let vfd = WlVfd::allocate(self.vm.clone(), size as u64)?;
+ let resp = WlResp::VfdNew {
+ id,
+ flags,
+ pfn: vfd.pfn().unwrap_or_default(),
+ size: vfd.size().unwrap_or_default() as u32,
+ resp: true,
+ };
+ entry.insert(vfd);
+ Ok(resp)
+ }
+ Entry::Occupied(_) => Ok(WlResp::InvalidId),
+ }
+ }
+
+ #[cfg(feature = "wl-dmabuf")]
+ fn new_dmabuf(&mut self, id: u32, width: u32, height: u32, format: u32) -> WlResult<WlResp> {
+ if id & VFD_ID_HOST_MASK != 0 {
+ return Ok(WlResp::InvalidId);
+ }
+
+ match self.vfds.entry(id) {
+ Entry::Vacant(entry) => {
+ let (vfd, desc) = WlVfd::dmabuf(self.vm.clone(), width, height, format)?;
+ let resp = WlResp::VfdNewDmabuf {
+ id,
+ flags: 0,
+ pfn: vfd.pfn().unwrap_or_default(),
+ size: vfd.size().unwrap_or_default() as u32,
+ desc,
+ };
+ entry.insert(vfd);
+ Ok(resp)
+ }
+ Entry::Occupied(_) => Ok(WlResp::InvalidId),
+ }
+ }
+
+ #[cfg(feature = "wl-dmabuf")]
+ fn dmabuf_sync(&mut self, vfd_id: u32, flags: u32) -> WlResult<WlResp> {
+ if flags & !(VIRTIO_WL_VFD_DMABUF_SYNC_VALID_FLAG_MASK) != 0 {
+ return Ok(WlResp::InvalidFlags);
+ }
+
+ match self.vfds.get_mut(&vfd_id) {
+ Some(vfd) => {
+ vfd.dmabuf_sync(flags)?;
+ Ok(WlResp::Ok)
+ }
+ None => Ok(WlResp::InvalidId),
+ }
+ }
+
+ fn new_context(&mut self, id: u32) -> WlResult<WlResp> {
+ if id & VFD_ID_HOST_MASK != 0 {
+ return Ok(WlResp::InvalidId);
+ }
+
+ let flags = if self.use_transition_flags {
+ VIRTIO_WL_VFD_WRITE | VIRTIO_WL_VFD_READ
+ } else {
+ VIRTIO_WL_VFD_CONTROL
+ };
+
+ match self.vfds.entry(id) {
+ Entry::Vacant(entry) => {
+ let vfd = entry.insert(WlVfd::connect(&self.wayland_path)?);
+ self.poll_ctx
+ .add(vfd.poll_fd().unwrap(), id)
+ .map_err(WlError::PollContextAdd)?;
+ Ok(WlResp::VfdNew {
+ id,
+ flags,
+ pfn: 0,
+ size: 0,
+ resp: true,
+ })
+ }
+ Entry::Occupied(_) => Ok(WlResp::InvalidId),
+ }
+ }
+
+ fn process_poll_context(&mut self) {
+ let events = match self.poll_ctx.wait_timeout(Duration::from_secs(0)) {
+ Ok(v) => v.to_owned(),
+ Err(e) => {
+ error!("failed polling for vfd evens: {}", e);
+ return;
+ }
+ };
+
+ for event in events.as_ref().iter_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) {
+ warn!("failed to remove hungup vfd from poll context: {}", e);
+ }
+ }
+ self.in_queue.push_back((vfd_id, WlRecv::Hup));
+ }
+ }
+ }
+
+ fn close(&mut self, vfd_id: u32) -> WlResult<WlResp> {
+ let mut to_delete = Set::new();
+ for (dest_vfd_id, q) in &self.in_queue {
+ if *dest_vfd_id == vfd_id {
+ if let WlRecv::Vfd { id } = q {
+ to_delete.insert(*id);
+ }
+ }
+ }
+ for vfd_id in to_delete {
+ // Sorry sub-error, we can't have cascading errors leaving us in an inconsistent state.
+ let _ = self.close(vfd_id);
+ }
+ match self.vfds.remove(&vfd_id) {
+ Some(mut vfd) => {
+ self.in_queue.retain(|&(id, _)| id != vfd_id);
+ vfd.close()?;
+ Ok(WlResp::Ok)
+ }
+ None => Ok(WlResp::InvalidId),
+ }
+ }
+
+ fn send(
+ &mut self,
+ vfd_id: u32,
+ foreign_id: bool,
+ vfds: VolatileSlice,
+ data: VolatileSlice,
+ ) -> WlResult<WlResp> {
+ // First stage gathers and normalizes all id information from guest memory.
+ let mut send_vfd_ids = [CtrlVfdSendVfd::default(); VIRTWL_SEND_MAX_ALLOCS];
+ let vfd_count = if foreign_id {
+ vfds.copy_to(&mut send_vfd_ids[..]);
+ vfds.size() as usize / size_of::<CtrlVfdSendVfd>()
+ } else {
+ let vfd_count = vfds.size() as usize / size_of::<Le32>();
+ let mut vfd_ids = [Le32::from(0); VIRTWL_SEND_MAX_ALLOCS];
+ vfds.copy_to(&mut vfd_ids[..]);
+ send_vfd_ids[..vfd_count]
+ .iter_mut()
+ .zip(&vfd_ids[..vfd_count])
+ .for_each(|(send_vfd_id, &vfd_id)| {
+ *send_vfd_id = CtrlVfdSendVfd {
+ kind: Le32::from(VIRTIO_WL_CTRL_VFD_SEND_KIND_LOCAL),
+ id: vfd_id,
+ }
+ });
+ vfd_count
+ };
+
+ // Next stage collects corresponding file descriptors for each id.
+ let mut fds = [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()) {
+ 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,
+ None => return Ok(WlResp::InvalidType),
+ },
+ None => {
+ warn!("attempt to send non-existant vfd 0x{:08x}", id);
+ return Ok(WlResp::InvalidId);
+ }
+ },
+ #[cfg(feature = "gpu")]
+ VIRTIO_WL_CTRL_VFD_SEND_KIND_VIRTGPU if self.resource_bridge.is_some() => {
+ if let Err(e) = self
+ .resource_bridge
+ .as_ref()
+ .unwrap()
+ .send(&ResourceRequest::GetResource { id })
+ {
+ error!("error sending resource bridge request: {}", e);
+ return Ok(WlResp::InvalidId);
+ }
+ match self.resource_bridge.as_ref().unwrap().recv() {
+ Ok(ResourceResponse::Resource(bridged_file)) => {
+ *fd = bridged_file.as_raw_fd();
+ bridged_files.push(bridged_file);
+ }
+ Ok(ResourceResponse::Invalid) => {
+ warn!("attempt to send non-existant gpu resource {}", id);
+ return Ok(WlResp::InvalidId);
+ }
+ Err(e) => {
+ error!("error receiving resource bridge response: {}", e);
+ // If there was an error with the resource bridge, it can no longer be
+ // trusted to continue to function.
+ self.resource_bridge = None;
+ return Ok(WlResp::InvalidId);
+ }
+ }
+ }
+ VIRTIO_WL_CTRL_VFD_SEND_KIND_VIRTGPU => {
+ let _ = self.resource_bridge.as_ref();
+ warn!("attempt to send foreign resource kind but feature is disabled");
+ }
+ kind => {
+ warn!(
+ "attempt to send unknown foreign resource kind: {} id: {:08x}",
+ kind, id
+ );
+ return Ok(WlResp::InvalidId);
+ }
+ }
+ }
+
+ // 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], data)? {
+ WlResp::Ok => {}
+ _ => return Ok(WlResp::InvalidType),
+ },
+ None => return Ok(WlResp::InvalidId),
+ }
+ // The vfds with remote FDs need to be closed so that the local side can receive
+ // hangup events.
+ for &send_vfd_id in &send_vfd_ids[..vfd_count] {
+ if send_vfd_id.kind == VIRTIO_WL_CTRL_VFD_SEND_KIND_LOCAL {
+ if let Some(vfd) = self.vfds.get_mut(&send_vfd_id.id.into()) {
+ vfd.close_remote();
+ }
+ }
+ }
+ Ok(WlResp::Ok)
+ }
+
+ fn recv(&mut self, vfd_id: u32) -> WlResult<()> {
+ let buf = match self.vfds.get_mut(&vfd_id) {
+ Some(vfd) => vfd.recv(&mut self.in_file_queue)?,
+ None => return Ok(()),
+ };
+ if self.in_file_queue.is_empty() && buf.is_empty() {
+ self.in_queue.push_back((vfd_id, WlRecv::Hup));
+ return Ok(());
+ }
+ 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)?;
+ }
+ self.vfds.insert(self.next_vfd_id, vfd);
+ self.in_queue.push_back((
+ vfd_id,
+ WlRecv::Vfd {
+ id: self.next_vfd_id,
+ },
+ ));
+ self.next_vfd_id += 1;
+ }
+ self.in_queue.push_back((vfd_id, WlRecv::Data { buf }));
+
+ Ok(())
+ }
+
+ fn execute(&mut self, mem: &GuestMemory, op: WlOp) -> WlResult<WlResp> {
+ match op {
+ WlOp::NewAlloc { id, flags, size } => self.new_alloc(id, flags, size),
+ WlOp::Close { id } => self.close(id),
+ WlOp::Send {
+ id,
+ foreign_id,
+ vfds_addr,
+ vfd_count,
+ data_addr,
+ data_len,
+ } => {
+ let vfd_size = if foreign_id {
+ size_of::<CtrlVfdSendVfd>()
+ } else {
+ size_of::<Le32>()
+ } as u32;
+ let vfd_mem = mem.get_slice(vfds_addr.0, (vfd_count * vfd_size) as u64)?;
+ let data_mem = mem.get_slice(data_addr.0, data_len as u64)?;
+ self.send(id, foreign_id, vfd_mem, data_mem)
+ }
+ WlOp::NewCtx { id } => self.new_context(id),
+ WlOp::NewPipe { id, flags } => self.new_pipe(id, flags),
+ #[cfg(feature = "wl-dmabuf")]
+ WlOp::NewDmabuf {
+ id,
+ width,
+ height,
+ format,
+ } => self.new_dmabuf(id, width, height, format),
+ #[cfg(feature = "wl-dmabuf")]
+ WlOp::DmabufSync { id, flags } => self.dmabuf_sync(id, flags),
+ WlOp::InvalidCommand { op_type } => {
+ warn!("unexpected command {}", op_type);
+ Ok(WlResp::InvalidCommand)
+ }
+ }
+ }
+
+ fn next_recv(&self) -> Option<WlResp> {
+ if let Some(q) = self.in_queue.front() {
+ match *q {
+ (vfd_id, WlRecv::Vfd { id }) => {
+ if self.current_recv_vfd.is_none() || self.current_recv_vfd == Some(vfd_id) {
+ match self.vfds.get(&id) {
+ Some(vfd) => Some(WlResp::VfdNew {
+ id,
+ flags: vfd.flags(self.use_transition_flags),
+ pfn: vfd.pfn().unwrap_or_default(),
+ size: vfd.size().unwrap_or_default() as u32,
+ resp: false,
+ }),
+ _ => Some(WlResp::VfdNew {
+ id,
+ flags: 0,
+ pfn: 0,
+ size: 0,
+ resp: false,
+ }),
+ }
+ } else {
+ Some(WlResp::VfdRecv {
+ id: self.current_recv_vfd.unwrap(),
+ data: &[],
+ vfds: &self.recv_vfds[..],
+ })
+ }
+ }
+ (vfd_id, WlRecv::Data { ref buf }) => {
+ if self.current_recv_vfd.is_none() || self.current_recv_vfd == Some(vfd_id) {
+ Some(WlResp::VfdRecv {
+ id: vfd_id,
+ data: &buf[..],
+ vfds: &self.recv_vfds[..],
+ })
+ } else {
+ Some(WlResp::VfdRecv {
+ id: self.current_recv_vfd.unwrap(),
+ data: &[],
+ vfds: &self.recv_vfds[..],
+ })
+ }
+ }
+ (vfd_id, WlRecv::Hup) => Some(WlResp::VfdHup { id: vfd_id }),
+ }
+ } else {
+ None
+ }
+ }
+
+ fn pop_recv(&mut self) {
+ if let Some(q) = self.in_queue.front() {
+ match *q {
+ (vfd_id, WlRecv::Vfd { id }) => {
+ if self.current_recv_vfd.is_none() || self.current_recv_vfd == Some(vfd_id) {
+ self.recv_vfds.push(id);
+ self.current_recv_vfd = Some(vfd_id);
+ } else {
+ self.recv_vfds.clear();
+ self.current_recv_vfd = None;
+ return;
+ }
+ }
+ (vfd_id, WlRecv::Data { .. }) => {
+ self.recv_vfds.clear();
+ self.current_recv_vfd = None;
+ if !(self.current_recv_vfd.is_none() || self.current_recv_vfd == Some(vfd_id)) {
+ return;
+ }
+ }
+ (_, WlRecv::Hup) => {
+ self.recv_vfds.clear();
+ self.current_recv_vfd = None;
+ }
+ }
+ }
+ self.in_queue.pop_front();
+ }
+}
+
+struct Worker {
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ interrupt_status: Arc<AtomicUsize>,
+ in_queue: Queue,
+ out_queue: Queue,
+ state: WlState,
+ in_desc_chains: VecDeque<(u16, GuestAddress, u32)>,
+}
+
+impl Worker {
+ fn new(
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ interrupt_status: Arc<AtomicUsize>,
+ in_queue: Queue,
+ out_queue: Queue,
+ wayland_path: PathBuf,
+ vm_socket: VmMemoryControlRequestSocket,
+ use_transition_flags: bool,
+ resource_bridge: Option<ResourceRequestSocket>,
+ ) -> Worker {
+ Worker {
+ mem,
+ interrupt_evt,
+ interrupt_resample_evt,
+ interrupt_status,
+ in_queue,
+ out_queue,
+ state: WlState::new(
+ wayland_path,
+ vm_socket,
+ use_transition_flags,
+ resource_bridge,
+ ),
+ in_desc_chains: VecDeque::with_capacity(QUEUE_SIZE as usize),
+ }
+ }
+
+ fn signal_used_queue(&self) {
+ self.interrupt_status
+ .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+ let _ = self.interrupt_evt.write(1);
+ }
+
+ fn run(&mut self, mut queue_evts: Vec<EventFd>, kill_evt: EventFd) {
+ let in_queue_evt = queue_evts.remove(0);
+ let out_queue_evt = queue_evts.remove(0);
+ #[derive(PollToken)]
+ enum Token {
+ InQueue,
+ OutQueue,
+ Kill,
+ State,
+ InterruptResample,
+ }
+
+ let poll_ctx: PollContext<Token> = match PollContext::new()
+ .and_then(|pc| pc.add(&in_queue_evt, Token::InQueue).and(Ok(pc)))
+ .and_then(|pc| pc.add(&out_queue_evt, Token::OutQueue).and(Ok(pc)))
+ .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+ .and_then(|pc| pc.add(&self.state.poll_ctx, Token::State).and(Ok(pc)))
+ .and_then(|pc| {
+ pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+ .and(Ok(pc))
+ }) {
+ Ok(pc) => pc,
+ Err(e) => {
+ error!("failed creating PollContext: {}", e);
+ return;
+ }
+ };
+
+ 'poll: loop {
+ let mut signal_used = false;
+ let events = match poll_ctx.wait() {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed polling for events: {}", e);
+ break;
+ }
+ };
+
+ for event in &events {
+ match event.token() {
+ Token::InQueue => {
+ let _ = in_queue_evt.read();
+ // Used to buffer descriptor indexes that are invalid for our uses.
+ let mut rejects = [0u16; QUEUE_SIZE as usize];
+ let mut rejects_len = 0;
+ let min_in_desc_len = (size_of::<CtrlVfdRecv>()
+ + size_of::<Le32>() * VIRTWL_SEND_MAX_ALLOCS)
+ as u32;
+ self.in_desc_chains
+ .extend(self.in_queue.iter(&self.mem).filter_map(|d| {
+ if d.len >= min_in_desc_len && d.is_write_only() {
+ Some((d.index, d.addr, d.len))
+ } else {
+ // Can not use queue.add_used directly because it's being borrowed
+ // for the iterator chain, so we buffer the descriptor index in
+ // rejects.
+ rejects[rejects_len] = d.index;
+ rejects_len += 1;
+ None
+ }
+ }));
+ for &reject in &rejects[..rejects_len] {
+ signal_used = true;
+ self.in_queue.add_used(&self.mem, reject, 0);
+ }
+ }
+ Token::OutQueue => {
+ let _ = out_queue_evt.read();
+ let min_resp_desc_len = size_of::<CtrlHeader>() as u32;
+ while let Some(desc) = self.out_queue.pop(&self.mem) {
+ // Expects that each descriptor chain is made of one "in" followed by
+ // one "out" descriptor.
+ if !desc.is_write_only() {
+ if let Some(resp_desc) = desc.next_descriptor() {
+ if resp_desc.is_write_only()
+ && resp_desc.len >= min_resp_desc_len
+ {
+ let resp = match parse_desc(&desc, &self.mem) {
+ Ok(op) => match self.state.execute(&self.mem, op) {
+ Ok(r) => r,
+ Err(e) => WlResp::Err(Box::new(e)),
+ },
+ Err(e) => WlResp::Err(Box::new(e)),
+ };
+
+ let resp_mem = self
+ .mem
+ .get_slice(resp_desc.addr.0, resp_desc.len as u64)
+ .unwrap();
+ let used_len =
+ encode_resp(resp_mem, resp).unwrap_or_default();
+
+ self.out_queue.add_used(&self.mem, desc.index, used_len);
+ signal_used = true;
+ }
+ }
+ } else {
+ // Chains that are unusable get sent straight back to the used
+ // queue.
+ self.out_queue.add_used(&self.mem, desc.index, 0);
+ signal_used = true;
+ }
+ }
+ }
+ Token::Kill => break 'poll,
+ Token::State => self.state.process_poll_context(),
+ Token::InterruptResample => {
+ let _ = self.interrupt_resample_evt.read();
+ if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+ self.interrupt_evt.write(1).unwrap();
+ }
+ }
+ }
+ }
+
+ // Because this loop should be retried after the in queue is usable or after one of the
+ // VFDs was read, we do it after the poll event responses.
+ while !self.in_desc_chains.is_empty() {
+ let mut should_pop = false;
+ if let Some(in_resp) = self.state.next_recv() {
+ // self.in_desc_chains is not empty (checked by loop condition) so unwrap is
+ // safe.
+ let (index, addr, desc_len) = self.in_desc_chains.pop_front().unwrap();
+ // This memory location is valid because it came from a queue which always
+ // checks the descriptor memory locations.
+ let desc_mem = self.mem.get_slice(addr.0, desc_len as u64).unwrap();
+ let len = match encode_resp(desc_mem, in_resp) {
+ Ok(len) => {
+ should_pop = true;
+ len
+ }
+ Err(e) => {
+ error!("failed to encode response to descriptor chain: {}", e);
+ 0
+ }
+ };
+ signal_used = true;
+ self.in_queue.add_used(&self.mem, index, len);
+ } else {
+ break;
+ }
+ if should_pop {
+ self.state.pop_recv();
+ }
+ }
+
+ if signal_used {
+ self.signal_used_queue();
+ }
+ }
+ }
+}
+
+pub struct Wl {
+ kill_evt: Option<EventFd>,
+ wayland_path: PathBuf,
+ vm_socket: Option<VmMemoryControlRequestSocket>,
+ resource_bridge: Option<ResourceRequestSocket>,
+ use_transition_flags: bool,
+}
+
+impl Wl {
+ pub fn new<P: AsRef<Path>>(
+ wayland_path: P,
+ vm_socket: VmMemoryControlRequestSocket,
+ resource_bridge: Option<ResourceRequestSocket>,
+ ) -> Result<Wl> {
+ Ok(Wl {
+ kill_evt: None,
+ wayland_path: wayland_path.as_ref().to_owned(),
+ vm_socket: Some(vm_socket),
+ resource_bridge,
+ use_transition_flags: false,
+ })
+ }
+}
+
+impl Drop for Wl {
+ fn drop(&mut self) {
+ if let Some(kill_evt) = self.kill_evt.take() {
+ // Ignore the result because there is nothing we can do about it.
+ let _ = kill_evt.write(1);
+ }
+ }
+}
+
+impl VirtioDevice for Wl {
+ fn keep_fds(&self) -> Vec<RawFd> {
+ let mut keep_fds = Vec::new();
+
+ if let Some(vm_socket) = &self.vm_socket {
+ keep_fds.push(vm_socket.as_raw_fd());
+ }
+ if let Some(resource_bridge) = &self.resource_bridge {
+ keep_fds.push(resource_bridge.as_raw_fd());
+ }
+
+ keep_fds
+ }
+
+ fn device_type(&self) -> u32 {
+ TYPE_WL
+ }
+
+ fn queue_max_sizes(&self) -> &[u16] {
+ QUEUE_SIZES
+ }
+
+ fn features(&self) -> u64 {
+ 1 << VIRTIO_WL_F_TRANS_FLAGS | 1 << VIRTIO_F_VERSION_1
+ }
+
+ fn ack_features(&mut self, value: u64) {
+ if value & (1 << VIRTIO_WL_F_TRANS_FLAGS) != 0 {
+ self.use_transition_flags = true;
+ }
+ }
+
+ fn activate(
+ &mut self,
+ mem: GuestMemory,
+ interrupt_evt: EventFd,
+ interrupt_resample_evt: EventFd,
+ status: Arc<AtomicUsize>,
+ mut queues: Vec<Queue>,
+ queue_evts: Vec<EventFd>,
+ ) {
+ if queues.len() != QUEUE_SIZES.len() || queue_evts.len() != QUEUE_SIZES.len() {
+ return;
+ }
+
+ let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed creating kill EventFd pair: {}", e);
+ return;
+ }
+ };
+ self.kill_evt = Some(self_kill_evt);
+
+ if let Some(vm_socket) = self.vm_socket.take() {
+ let wayland_path = self.wayland_path.clone();
+ let use_transition_flags = self.use_transition_flags;
+ let resource_bridge = self.resource_bridge.take();
+ let worker_result =
+ thread::Builder::new()
+ .name("virtio_wl".to_string())
+ .spawn(move || {
+ Worker::new(
+ mem,
+ interrupt_evt,
+ interrupt_resample_evt,
+ status,
+ queues.remove(0),
+ queues.remove(0),
+ wayland_path,
+ vm_socket,
+ use_transition_flags,
+ resource_bridge,
+ )
+ .run(queue_evts, kill_evt);
+ });
+
+ if let Err(e) = worker_result {
+ error!("failed to spawn virtio_wl worker: {}", e);
+ return;
+ }
+ }
+ }
+}
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..dea2fc4
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,149 @@
+# Copyright 2018 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.
+
+FROM debian:stretch
+LABEL description="Test crosvm using a command like the following: \
+docker run --privileged -v /dev/log:/dev/log -v <path to crosvm>:/platform/crosvm:ro <crosvm base image>"
+
+RUN apt-get update && apt-get install -y \
+ autoconf \
+ automake \
+ curl \
+ gcc \
+ g++ \
+ git \
+ libcap-dev \
+ libdbus-1-dev \
+ libdrm-dev \
+ libfdt-dev \
+ libegl1-mesa-dev \
+ libgl1-mesa-dev \
+ libgles1-mesa-dev \
+ libgles2-mesa-dev \
+ libssl1.0-dev \
+ libtool \
+ libusb-1.0-0-dev \
+ libwayland-dev \
+ make \
+ nasm \
+ ninja-build \
+ pkg-config \
+ protobuf-compiler \
+ python3
+
+ENV RUSTUP_HOME=/usr/local/rustup \
+ CARGO_HOME=/usr/local/cargo \
+ PATH=/usr/local/cargo/bin:$PATH \
+ RUST_VERSION=1.35.0 \
+ RUSTFLAGS='--cfg hermetic'
+
+# Debian usually has an old rust version in the repository. Instead of using that, we use rustup to
+# pull in a toolchain versions of our choosing.
+RUN curl -LO "https://static.rust-lang.org/rustup/archive/1.14.0/x86_64-unknown-linux-gnu/rustup-init" \
+ && echo "0077ff9c19f722e2be202698c037413099e1188c0c233c12a2297bf18e9ff6e7 *rustup-init" | sha256sum -c - \
+ && chmod +x rustup-init \
+ && ./rustup-init -y --no-modify-path --default-toolchain $RUST_VERSION \
+ && rustup component add rustfmt-preview \
+ && rm rustup-init \
+ && chmod -R a+w $RUSTUP_HOME $CARGO_HOME \
+ && rustup --version \
+ && cargo --version \
+ && rustc --version
+
+# Warms up the cargo registry cache for future cargo runs. Cargo will still update the cache using a
+# git pull, but it only needs to download files that were changed since this image was built.
+RUN cargo install thisiznotarealpackage -q || true
+
+# Used /scratch for building dependencies which are too new or don't exist on Debian stretch.
+WORKDIR /scratch
+
+# minijail does not exist in upstream linux distros.
+RUN git clone https://android.googlesource.com/platform/external/minijail \
+ && cd minijail \
+ && make -j$(nproc) \
+ && cp libminijail.so /usr/lib/x86_64-linux-gnu/
+
+# The gbm used by upstream linux distros is not compatible with crosvm, which must use Chrome OS's
+# minigbm.
+RUN dpkg --force-depends -r libgbm1
+RUN git clone https://chromium.googlesource.com/chromiumos/platform/minigbm \
+ && cd minigbm \
+ && sed 's/-Wall/-Wno-maybe-uninitialized/g' -i Makefile \
+ && make install -j$(nproc)
+
+# New libepoxy requires newer meson than is in Debian stretch.
+ARG MESON_COMMIT=master
+RUN git clone https://github.com/mesonbuild/meson \
+ && cd meson \
+ && git checkout $MESON_COMMIT \
+ && ln -s $PWD/meson.py /usr/bin/meson
+
+# New libepoxy has EGL_KHR_DEBUG entry points needed by crosvm.
+ARG LIBEPOXY_COMMIT=master
+RUN git clone https://github.com/anholt/libepoxy.git \
+ && cd libepoxy \
+ && git checkout $LIBEPOXY_COMMIT \
+ && mkdir build \
+ && cd build \
+ && meson \
+ && ninja install
+
+# virglrenderer is under heavy development on master and we want the very latest.
+RUN git clone https://gitlab.freedesktop.org/virgl/virglrenderer.git \
+ && cd virglrenderer \
+ && ./autogen.sh \
+ && make install -j$(nproc)
+
+# Install libtpm2 so that tpm2-sys/build.rs does not try to build it in place in
+# the read-only source directory.
+ARG TPM2_COMMIT=master
+RUN git clone https://chromium.googlesource.com/chromiumos/third_party/tpm2 \
+ && cd tpm2 \
+ && git checkout $TPM2_COMMIT \
+ && make -j$(nproc) \
+ && cp build/libtpm2.a /lib
+
+# Install librendernodehost
+ARG PLATFORM2_COMMIT=master
+RUN git clone https://chromium.googlesource.com/chromiumos/platform2 \
+ && cd platform2 \
+ && git checkout $PLATFORM2_COMMIT \
+ && cd rendernodehost \
+ && gcc -c src.c -o src.o \
+ && ar rcs librendernodehost.a src.o \
+ && cp librendernodehost.a /lib
+
+# Set up sysroot from which system_api proto files are built.
+ENV SYSROOT=/sysroot
+RUN mkdir -p $SYSROOT/usr/include/chromeos/dbus/trunks \
+ && cp platform2/trunks/interface.proto \
+ $SYSROOT/usr/include/chromeos/dbus/trunks
+
+# Inform pkg-config where libraries we install are placed.
+COPY pkgconfig/* /usr/lib/pkgconfig
+
+# Reduces image size and prevents accidentally using /scratch files
+RUN rm -r /scratch /usr/bin/meson
+
+# The manual installation of shared objects requires an ld.so.cache refresh.
+RUN ldconfig
+
+# Pull down repositories that crosvm depends on to cros checkout-like locations.
+ENV CROS_ROOT=/
+ENV THIRD_PARTY_ROOT=$CROS_ROOT/third_party
+RUN mkdir -p $THIRD_PARTY_ROOT
+ENV PLATFORM_ROOT=$CROS_ROOT/platform
+RUN mkdir -p $PLATFORM_ROOT
+
+# Pull the cras library for audio access.
+ARG ADHD_COMMIT=master
+RUN git clone https://chromium.googlesource.com/chromiumos/third_party/adhd $THIRD_PARTY_ROOT/adhd \
+ && cd $THIRD_PARTY_ROOT/adhd \
+ && git checkout $ADHD_COMMIT
+
+# The /build directory is used so that the bind mounted /platform/crosvm volume
+# does not get scribbled on.
+ENV CARGO_TARGET_DIR=/build
+RUN mkdir -p $CARGO_TARGET_DIR
+WORKDIR /platform/crosvm
diff --git a/docker/Dockerfile.crosvm b/docker/Dockerfile.crosvm
new file mode 100644
index 0000000..3a7f293
--- /dev/null
+++ b/docker/Dockerfile.crosvm
@@ -0,0 +1,16 @@
+FROM crosvm-base
+
+COPY . /platform/crosvm
+
+RUN cargo install --features 'default-no-sandbox wl-dmabuf gpu' --path . --root /usr
+
+ARG UID=1000
+ARG GID=1000
+
+RUN export uid=$UID gid=$GID && \
+ mkdir -p /home/chronos && \
+ echo "chronos:x:${uid}:${gid}:Developer,,,:/home/chronos:/bin/bash" >> /etc/passwd && \
+ echo "chronos:x:${uid}:" >> /etc/group && \
+ chown ${uid}:${gid} -R /home/chronos
+
+ENTRYPOINT ["crosvm"]
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..df38448
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,47 @@
+# Docker for Building/Running crosvm
+
+This module contains various pieces of Docker infrastructure for supporting crosvm outside of Chrome
+OS environments. This includes the kokoro build environment.
+
+[TOC]
+
+## Introduction
+
+Ordinarily, crosvm is built using the standard `cargo build` command inside of a Chrome OS chroot.
+The chroot requirement is there because of various path dependencies in the crosvm `Cargo.toml` are
+targeted to paths outside of the crosvm repo itself. If one were to checkout crosvm in isolation,
+`cargo build` would be inadequate, failing with an error related to these missing paths.
+Additionally, crosvm depends on native packages that are not ordinarily available from an OS package
+manager (e.g. minijail) or have been forked in the Chrome OS project in an incompatible fashion
+(libusb).
+
+## `crosvm-base` Docker Image
+
+To support building crosvm outside of a Chrome OS chroot, this modules contains a `Dockerfile` that
+is used to build the `crosvm-base` docker image. Part of that image build process is downloading
+various repos, checking out pinned commits (specified in `checkout_commits.env`), and installing
+them. For the path dependencies in the `Cargo.toml`, the `Dockerfile` downloads and places the
+source code in the correct spot relative to the crosvm source repository. The `crosvm-base` build
+step stops short of actually building crosvm. It doesn't even have the source code for crosvm. The
+intent here is to use `crosvm-base` for building and running any version of crosvm.
+
+To build the `crosvm-base` image, run `build_crosvm_base.sh`. The script will automatically use the
+checkouts from `checkout_commits.env` which can be reconfigured to point to any commit desired. To
+upgrade `checkout_commits.sh` to the HEAD of each remote master branch, run the
+`upgrade_checkout_commits.sh` script.
+
+## `crosvm` Docker Image
+
+After generating a `crosvm-base`, the system is ready to build crosvm into its own `crosvm` docker
+image. The resulting docker image will be capable of running VMs without fear of missing native
+dependencies. Run the `build_crosvm.sh` script to build crosvm into a docker image. Once that
+completes, use the `crosvm_wrapper.sh` script to run crosvm within the docker image. That script
+will pass the arguments given to it verbatim to crosvm. In addition, the current working directory
+is bind mounted into the container so that file paths passed to `crosvm_wrapper.sh` should work as
+long as they are relative paths to files contained in the working directory.
+
+## `smoke_test`
+
+There is a convenience wrapper for `smoke_test` that uses the `crosvm` docker image to execute
+all the tests. Run `wrapped_smoke_test.sh` after building `crosvm-base` docker image to run the
+`smoke_test` within docker.
diff --git a/docker/build_crosvm.sh b/docker/build_crosvm.sh
new file mode 100755
index 0000000..3c6d359
--- /dev/null
+++ b/docker/build_crosvm.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -ex
+cd "${0%/*}"
+
+src_root="$(realpath ..)"
+
+docker build -t crosvm -f Dockerfile.crosvm --build-arg UID --build-arg GID "${src_root}"
diff --git a/docker/build_crosvm_base.sh b/docker/build_crosvm_base.sh
new file mode 100755
index 0000000..2041384
--- /dev/null
+++ b/docker/build_crosvm_base.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -ex
+cd "${0%/*}"
+
+gen_build_args() {
+ for arg in $(cat ./checkout_commits.env); do
+ echo --build-arg "${arg}"
+ done
+}
+
+docker build $(gen_build_args) -t crosvm-base .
diff --git a/docker/checkout_commits.env b/docker/checkout_commits.env
new file mode 100644
index 0000000..6d77478
--- /dev/null
+++ b/docker/checkout_commits.env
@@ -0,0 +1,5 @@
+MESON_COMMIT=5a0fec13b6463f45f88860d67e8fb50f34c8d739
+LIBEPOXY_COMMIT=d536f78db81853b18ffc733af8a1474e9ca08950
+TPM2_COMMIT=1dba349a7b272071d613869adaaef7bd576ae0c2
+PLATFORM2_COMMIT=c08db1d4dc6d91230fe3820a736b7ebd2c6e901d
+ADHD_COMMIT=40a296cfff7b88f2c14701627cff4c233d94a975
diff --git a/docker/crosvm_wrapper.sh b/docker/crosvm_wrapper.sh
new file mode 100755
index 0000000..33891d6
--- /dev/null
+++ b/docker/crosvm_wrapper.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -ex
+cd "${0%/*}"
+
+exec docker run -it --rm \
+ --privileged \
+ -e DISPLAY=$DISPLAY -e XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR \
+ -v /dev/log:/dev/log \
+ -v /tmp/.X11-unix:/tmp/.X11-unix \
+ --volume "$PWD":/wd \
+ --workdir /wd \
+ crosvm \
+ "$@"
diff --git a/docker/pkgconfig/libtpm2.pc b/docker/pkgconfig/libtpm2.pc
new file mode 100644
index 0000000..c9ff8f0
--- /dev/null
+++ b/docker/pkgconfig/libtpm2.pc
@@ -0,0 +1,5 @@
+Name: tpm2
+Description: TPM simulator extracted from the TCG TPM 2.0 library specification
+Version: 2.0.0
+Requires.private: libcrypto
+Libs: -L/lib -ltpm2
diff --git a/docker/upgrade_checkout_commits.sh b/docker/upgrade_checkout_commits.sh
new file mode 100755
index 0000000..4a4d3e0
--- /dev/null
+++ b/docker/upgrade_checkout_commits.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+cd "${0%/*}"
+
+remotes=(
+ "https://github.com/mesonbuild/meson"
+ "https://github.com/anholt/libepoxy.git"
+ "https://chromium.googlesource.com/chromiumos/third_party/tpm2"
+ "https://chromium.googlesource.com/chromiumos/platform2"
+ "https://chromium.googlesource.com/chromiumos/third_party/adhd"
+)
+
+keys=(
+ "MESON_COMMIT"
+ "LIBEPOXY_COMMIT"
+ "TPM2_COMMIT"
+ "PLATFORM2_COMMIT"
+ "ADHD_COMMIT"
+)
+
+for (( i=0; i<${#remotes[*]}; ++i)); do
+ remote="${remotes[$i]}"
+ key="${keys[$i]}"
+ remote_chunk=$(git ls-remote --exit-code "${remote}" refs/heads/master)
+ commit=$(echo "${remote_chunk}" | cut -f 1 -)
+ echo $key=$commit
+done
diff --git a/docker/wrapped_smoke_test.sh b/docker/wrapped_smoke_test.sh
new file mode 100755
index 0000000..324ab7c
--- /dev/null
+++ b/docker/wrapped_smoke_test.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -ex
+cd "${0%/*}"
+
+src_root="$(realpath ..)"
+
+docker run \
+ --rm \
+ --privileged \
+ -e TEST_RUNNER_FLAGS='--format terse' \
+ -v /dev/log:/dev/log \
+ -v "${src_root}":/platform/crosvm:ro \
+ crosvm-base \
+ bin/smoke_test
+
diff --git a/enumn/Cargo.toml b/enumn/Cargo.toml
new file mode 100644
index 0000000..1708678
--- /dev/null
+++ b/enumn/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "enumn"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "0.4"
+quote = "0.6"
+syn = "0.15"
diff --git a/enumn/src/lib.rs b/enumn/src/lib.rs
new file mode 100644
index 0000000..7a04303
--- /dev/null
+++ b/enumn/src/lib.rs
@@ -0,0 +1,207 @@
+// Copyright 2018 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.
+
+//! Convert number to enum.
+//!
+//! This crate provides a derive macro to generate a function for converting a
+//! primitive integer into the corresponding variant of an enum.
+//!
+//! The generated function is named `n` and has the following signature:
+//!
+//! ```rust
+//! # const IGNORE: &str = stringify! {
+//! impl YourEnum {
+//! pub fn n(value: Repr) -> Option<Self>;
+//! }
+//! # };
+//! ```
+//!
+//! where `Repr` is an integer type of the right size as described in more
+//! detail below.
+//!
+//! # Example
+//!
+//! ```rust
+//! use enumn::N;
+//!
+//! #[derive(PartialEq, Debug, N)]
+//! enum Status {
+//! LegendaryTriumph,
+//! QualifiedSuccess,
+//! FortuitousRevival,
+//! IndeterminateStalemate,
+//! RecoverableSetback,
+//! DireMisadventure,
+//! AbjectFailure,
+//! }
+//!
+//! fn main() {
+//! let s = Status::n(1);
+//! assert_eq!(s, Some(Status::QualifiedSuccess));
+//!
+//! let s = Status::n(9);
+//! assert_eq!(s, None);
+//! }
+//! ```
+//!
+//! # Signature
+//!
+//! The generated signature depends on whether the enum has a `#[repr(..)]`
+//! attribute. If a `repr` is specified, the input to `n` will be required to be
+//! of that type.
+//!
+//! ```ignore
+//! use enumn::N;
+//!
+//! #[derive(N)]
+//! #[repr(u8)]
+//! enum E {
+//! /* ... */
+//! # IGNORE
+//! }
+//!
+//! // expands to:
+//! impl E {
+//! pub fn n(value: u8) -> Option<Self> {
+//! /* ... */
+//! # unimplemented!()
+//! }
+//! }
+//! ```
+//!
+//! On the other hand if no `repr` is specified then we get a signature that is
+//! generic over a variety of possible types.
+//!
+//! ```ignore
+//! # enum E {}
+//! #
+//! impl E {
+//! pub fn n<REPR: Into<i64>>(value: REPR) -> Option<Self> {
+//! /* ... */
+//! # unimplemented!()
+//! }
+//! }
+//! ```
+//!
+//! # Discriminants
+//!
+//! The conversion respects explictly specified enum discriminants. Consider
+//! this enum:
+//!
+//! ```rust
+//! use enumn::N;
+//!
+//! #[derive(N)]
+//! enum Letter {
+//! A = 65,
+//! B = 66,
+//! }
+//! ```
+//!
+//! Here `Letter::n(65)` would return `Some(Letter::A)`.
+
+#![recursion_limit = "128"]
+
+extern crate proc_macro;
+
+#[cfg(test)]
+mod tests;
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::parse::Error;
+use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, Meta, NestedMeta};
+
+fn testable_derive(input: DeriveInput) -> proc_macro2::TokenStream {
+ let variants = match input.data {
+ Data::Enum(data) => data.variants,
+ Data::Struct(_) | Data::Union(_) => panic!("input must be an enum"),
+ };
+
+ for variant in &variants {
+ match variant.fields {
+ Fields::Unit => {}
+ Fields::Named(_) | Fields::Unnamed(_) => {
+ let span = variant.ident.span();
+ let err = Error::new(span, "enumn: variant with data is not supported");
+ return err.to_compile_error();
+ }
+ }
+ }
+
+ // Parse repr attribute like #[repr(u16)].
+ let mut repr = None;
+ for attr in input.attrs {
+ if let Ok(Meta::List(list)) = attr.parse_meta() {
+ if list.ident == "repr" {
+ if let Some(NestedMeta::Meta(Meta::Word(word))) = list.nested.into_iter().next() {
+ match word.to_string().as_str() {
+ "u8" | "u16" | "u32" | "u64" | "u128" | "usize" | "i8" | "i16" | "i32"
+ | "i64" | "i128" | "isize" => {
+ repr = Some(word);
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+ }
+
+ let signature;
+ let value;
+ match &repr {
+ Some(repr) => {
+ signature = quote! {
+ fn n(value: #repr)
+ };
+ value = quote!(value);
+ }
+ None => {
+ repr = Some(parse_quote!(i64));
+ signature = quote! {
+ fn n<REPR: Into<i64>>(value: REPR)
+ };
+ value = quote! {
+ <REPR as Into<i64>>::into(value)
+ };
+ }
+ }
+
+ let ident = input.ident;
+ let declare_discriminants = variants.iter().map(|variant| {
+ let variant = &variant.ident;
+ quote! {
+ const #variant: #repr = #ident::#variant as #repr;
+ }
+ });
+ let match_discriminants = variants.iter().map(|variant| {
+ let variant = &variant.ident;
+ quote! {
+ discriminant::#variant => Some(#ident::#variant),
+ }
+ });
+
+ quote! {
+ #[allow(non_upper_case_globals)]
+ impl #ident {
+ pub #signature -> Option<Self> {
+ struct discriminant;
+ impl discriminant {
+ #(#declare_discriminants)*
+ }
+ match #value {
+ #(#match_discriminants)*
+ _ => None,
+ }
+ }
+ }
+ }
+}
+
+#[proc_macro_derive(N)]
+pub fn derive(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ let expanded = testable_derive(input);
+ TokenStream::from(expanded)
+}
diff --git a/enumn/src/tests.rs b/enumn/src/tests.rs
new file mode 100644
index 0000000..cf5dd42
--- /dev/null
+++ b/enumn/src/tests.rs
@@ -0,0 +1,71 @@
+// Copyright 2018 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 quote::quote;
+use syn::{parse_quote, DeriveInput};
+
+#[test]
+fn test_repr() {
+ let input: DeriveInput = parse_quote! {
+ #[repr(u8)]
+ enum E {
+ A,
+ B,
+ C,
+ }
+ };
+ let actual = crate::testable_derive(input);
+ let expected = quote! {
+ #[allow(non_upper_case_globals)]
+ impl E {
+ pub fn n(value: u8) -> Option<Self> {
+ struct discriminant;
+ impl discriminant {
+ const A: u8 = E::A as u8;
+ const B: u8 = E::B as u8;
+ const C: u8 = E::C as u8;
+ }
+ match value {
+ discriminant::A => Some(E::A),
+ discriminant::B => Some(E::B),
+ discriminant::C => Some(E::C),
+ _ => None,
+ }
+ }
+ }
+ };
+ assert_eq!(actual.to_string(), expected.to_string());
+}
+
+#[test]
+fn test_no_repr() {
+ let input: DeriveInput = parse_quote! {
+ enum E {
+ A,
+ B,
+ C,
+ }
+ };
+ let actual = crate::testable_derive(input);
+ let expected = quote! {
+ #[allow(non_upper_case_globals)]
+ impl E {
+ pub fn n<REPR: Into<i64>>(value: REPR) -> Option<Self> {
+ struct discriminant;
+ impl discriminant {
+ const A: i64 = E::A as i64;
+ const B: i64 = E::B as i64;
+ const C: i64 = E::C as i64;
+ }
+ match <REPR as Into<i64>>::into(value) {
+ discriminant::A => Some(E::A),
+ discriminant::B => Some(E::B),
+ discriminant::C => Some(E::C),
+ _ => None,
+ }
+ }
+ }
+ };
+ assert_eq!(actual.to_string(), expected.to_string());
+}
diff --git a/fuzz/.gitignore b/fuzz/.gitignore
new file mode 100644
index 0000000..a092511
--- /dev/null
+++ b/fuzz/.gitignore
@@ -0,0 +1,3 @@
+target
+corpus
+artifacts
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
new file mode 100644
index 0000000..5e63344
--- /dev/null
+++ b/fuzz/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "crosvm-fuzz"
+version = "0.0.1"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+devices = { path = "../devices" }
+kernel_loader = { path = "../kernel_loader" }
+libc = "*"
+qcow = { path = "../qcow" }
+sys_util = { path = "../sys_util" }
+
+# Prevent this from interfering with workspaces
+[workspace]
+members = ["."]
+
+[[bin]]
+name = "crosvm_block_fuzzer"
+path = "block_fuzzer.rs"
+
+[[bin]]
+name = "crosvm_qcow_fuzzer"
+path = "qcow_fuzzer.rs"
+
+[[bin]]
+name = "crosvm_zimage_fuzzer"
+path = "zimage_fuzzer.rs"
diff --git a/fuzz/OWNERS b/fuzz/OWNERS
new file mode 100644
index 0000000..8c53fc5
--- /dev/null
+++ b/fuzz/OWNERS
@@ -0,0 +1 @@
+dgreid@chromium.org
diff --git a/fuzz/block_fuzzer.rs b/fuzz/block_fuzzer.rs
new file mode 100644
index 0000000..f315a87
--- /dev/null
+++ b/fuzz/block_fuzzer.rs
@@ -0,0 +1,117 @@
+// Copyright 2018 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.
+
+#![no_main]
+
+use std::fs::File;
+use std::io::{Cursor, Read, Seek, SeekFrom};
+use std::mem::size_of;
+use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::panic;
+use std::process;
+use std::slice;
+use std::sync::atomic::AtomicUsize;
+use std::sync::Arc;
+
+use devices::virtio::{Block, Queue, VirtioDevice};
+use sys_util::{EventFd, GuestAddress, GuestMemory, SharedMemory};
+
+const MEM_SIZE: u64 = 256 * 1024 * 1024;
+const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
+const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
+const CMD_SIZE: usize = 16; // Bytes in the command.
+
+// Take the first 64 bits of data as an address and the next 64 bits as data to
+// store there. The rest of the data is used as a qcow image.
+#[export_name = "LLVMFuzzerTestOneInput"]
+pub fn test_one_input(data: *const u8, size: usize) -> i32 {
+ // We cannot unwind past ffi boundaries.
+ panic::catch_unwind(|| {
+ // Safe because the libfuzzer runtime will guarantee that `data` is at least
+ // `size` bytes long and that it will be valid for the lifetime of this
+ // function.
+ let bytes = unsafe { slice::from_raw_parts(data, size) };
+ let size_u64 = size_of::<u64>();
+ let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
+
+ // The fuzz data is interpreted as:
+ // starting index 8 bytes
+ // command location 8 bytes
+ // command 16 bytes
+ // descriptors circular buffer 16 bytes * 3
+ if bytes.len() < 4 * size_u64 {
+ // Need an index to start.
+ return;
+ }
+
+ let mut data_image = Cursor::new(bytes);
+
+ let first_index = read_u64(&mut data_image);
+ if first_index > MEM_SIZE / DESC_SIZE {
+ return;
+ }
+ let first_offset = first_index * DESC_SIZE;
+ if first_offset as usize + size_u64 > bytes.len() {
+ return;
+ }
+
+ let command_addr = read_u64(&mut data_image);
+ if command_addr > MEM_SIZE - CMD_SIZE as u64 {
+ return;
+ }
+ if mem
+ .write_all_at_addr(
+ &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
+ GuestAddress(command_addr as u64),
+ )
+ .is_err()
+ {
+ return;
+ }
+
+ data_image.seek(SeekFrom::Start(first_offset)).unwrap();
+ let desc_table = read_u64(&mut data_image);
+
+ if mem
+ .write_all_at_addr(&bytes[32..], GuestAddress(desc_table as u64))
+ .is_err()
+ {
+ return;
+ }
+
+ let mut q = Queue::new(QUEUE_SIZE);
+ q.ready = true;
+ q.size = QUEUE_SIZE / 2;
+ q.max_size = QUEUE_SIZE;
+
+ let queue_evts: Vec<EventFd> = vec![EventFd::new().unwrap()];
+ let queue_fd = queue_evts[0].as_raw_fd();
+ let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) };
+
+ let shm = SharedMemory::new(None).unwrap();
+ let disk_file: File = shm.into();
+ let mut block = Block::new(disk_file, false, None).unwrap();
+
+ block.activate(
+ mem,
+ EventFd::new().unwrap(),
+ EventFd::new().unwrap(),
+ Arc::new(AtomicUsize::new(0)),
+ vec![q],
+ queue_evts,
+ );
+
+ queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
+ })
+ .err()
+ .map(|_| process::abort());
+
+ 0
+}
+
+fn read_u64<T: Read>(readable: &mut T) -> u64 {
+ let mut buf = [0u8; size_of::<u64>()];
+ readable.read_exact(&mut buf[..]).unwrap();
+ u64::from_le_bytes(buf)
+}
diff --git a/fuzz/qcow_fuzzer.rs b/fuzz/qcow_fuzzer.rs
new file mode 100644
index 0000000..e6175ff
--- /dev/null
+++ b/fuzz/qcow_fuzzer.rs
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![no_main]
+
+use qcow::QcowFile;
+use sys_util::SharedMemory;
+
+use std::fs::File;
+use std::io::{Cursor, Read, Seek, SeekFrom, Write};
+use std::mem::size_of;
+use std::panic;
+use std::process;
+use std::slice;
+
+// Take the first 64 bits of data as an address and the next 64 bits as data to
+// store there. The rest of the data is used as a qcow image.
+#[export_name = "LLVMFuzzerTestOneInput"]
+pub fn test_one_input(data: *const u8, size: usize) -> i32 {
+ // We cannot unwind past ffi boundaries.
+ panic::catch_unwind(|| {
+ // Safe because the libfuzzer runtime will guarantee that `data` is at least
+ // `size` bytes long and that it will be valid for the lifetime of this
+ // function.
+ let bytes = unsafe { slice::from_raw_parts(data, size) };
+ if bytes.len() < 16 {
+ // Need an address and data, each are 8 bytes.
+ return;
+ }
+ let mut disk_image = Cursor::new(bytes);
+ let addr = read_u64(&mut disk_image);
+ let value = read_u64(&mut disk_image);
+ let shm = SharedMemory::new(None).unwrap();
+ let mut disk_file: File = shm.into();
+ disk_file.write_all(&bytes[16..]).unwrap();
+ disk_file.seek(SeekFrom::Start(0)).unwrap();
+ if let Ok(mut qcow) = QcowFile::from(disk_file) {
+ if qcow.seek(SeekFrom::Start(addr)).is_ok() {
+ let _ = qcow.write_all(&value.to_le_bytes());
+ }
+ }
+ })
+ .err()
+ .map(|_| process::abort());
+
+ 0
+}
+
+fn read_u64<T: Read>(readable: &mut T) -> u64 {
+ let mut buf = [0u8; size_of::<u64>()];
+ readable.read_exact(&mut buf[..]).unwrap();
+ u64::from_le_bytes(buf)
+}
diff --git a/fuzz/zimage_fuzzer.rs b/fuzz/zimage_fuzzer.rs
new file mode 100644
index 0000000..f4aeb3f
--- /dev/null
+++ b/fuzz/zimage_fuzzer.rs
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![no_main]
+
+use sys_util::{GuestAddress, GuestMemory, SharedMemory};
+
+use std::fs::File;
+use std::io::Write;
+use std::panic;
+use std::process;
+use std::slice;
+
+fn make_elf_bin(elf_bytes: &[u8]) -> File {
+ let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
+ shm.set_size(elf_bytes.len() as u64)
+ .expect("failed to set shared memory size");
+ shm.write_all(elf_bytes)
+ .expect("failed to write elf to shared memoy");
+ shm.into()
+}
+
+#[export_name = "LLVMFuzzerTestOneInput"]
+pub fn test_one_input(data: *const u8, size: usize) -> i32 {
+ // We cannot unwind past ffi boundaries.
+ panic::catch_unwind(|| {
+ // Safe because the libfuzzer runtime will guarantee that `data` is at least
+ // `size` bytes long and that it will be valid for the lifetime of this
+ // function.
+ let bytes = unsafe { slice::from_raw_parts(data, size) };
+ let mut kimage = make_elf_bin(bytes);
+ let mem = GuestMemory::new(&[(GuestAddress(0), bytes.len() as u64 + 0x1000)]).unwrap();
+ let _ = kernel_loader::load_kernel(&mem, GuestAddress(0), &mut kimage);
+ })
+ .err()
+ .map(|_| process::abort());
+
+ 0
+}
diff --git a/gpu_buffer/Cargo.toml b/gpu_buffer/Cargo.toml
new file mode 100644
index 0000000..553e608
--- /dev/null
+++ b/gpu_buffer/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "gpu_buffer"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+data_model = { path = "../data_model" }
+sys_util = { path = "../sys_util" }
diff --git a/gpu_buffer/src/drm_formats.rs b/gpu_buffer/src/drm_formats.rs
new file mode 100644
index 0000000..bad5646
--- /dev/null
+++ b/gpu_buffer/src/drm_formats.rs
@@ -0,0 +1,72 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![allow(dead_code)]
+
+pub const DRM_FORMAT_C8: [u8; 4] = [b'C', b'8', b' ', b' '];
+pub const DRM_FORMAT_R8: [u8; 4] = [b'R', b'8', b' ', b' '];
+pub const DRM_FORMAT_R16: [u8; 4] = [b'R', b'1', b'6', b' '];
+pub const DRM_FORMAT_RG88: [u8; 4] = [b'R', b'G', b'8', b'8'];
+pub const DRM_FORMAT_GR88: [u8; 4] = [b'G', b'R', b'8', b'8'];
+pub const DRM_FORMAT_RG1616: [u8; 4] = [b'R', b'G', b'3', b'2'];
+pub const DRM_FORMAT_GR1616: [u8; 4] = [b'G', b'R', b'3', b'2'];
+pub const DRM_FORMAT_RGB332: [u8; 4] = [b'R', b'G', b'B', b'8'];
+pub const DRM_FORMAT_BGR233: [u8; 4] = [b'B', b'G', b'R', b'8'];
+pub const DRM_FORMAT_XRGB4444: [u8; 4] = [b'X', b'R', b'1', b'2'];
+pub const DRM_FORMAT_XBGR4444: [u8; 4] = [b'X', b'B', b'1', b'2'];
+pub const DRM_FORMAT_RGBX4444: [u8; 4] = [b'R', b'X', b'1', b'2'];
+pub const DRM_FORMAT_BGRX4444: [u8; 4] = [b'B', b'X', b'1', b'2'];
+pub const DRM_FORMAT_ARGB4444: [u8; 4] = [b'A', b'R', b'1', b'2'];
+pub const DRM_FORMAT_ABGR4444: [u8; 4] = [b'A', b'B', b'1', b'2'];
+pub const DRM_FORMAT_RGBA4444: [u8; 4] = [b'R', b'A', b'1', b'2'];
+pub const DRM_FORMAT_BGRA4444: [u8; 4] = [b'B', b'A', b'1', b'2'];
+pub const DRM_FORMAT_XRGB1555: [u8; 4] = [b'X', b'R', b'1', b'5'];
+pub const DRM_FORMAT_XBGR1555: [u8; 4] = [b'X', b'B', b'1', b'5'];
+pub const DRM_FORMAT_RGBX5551: [u8; 4] = [b'R', b'X', b'1', b'5'];
+pub const DRM_FORMAT_BGRX5551: [u8; 4] = [b'B', b'X', b'1', b'5'];
+pub const DRM_FORMAT_ARGB1555: [u8; 4] = [b'A', b'R', b'1', b'5'];
+pub const DRM_FORMAT_ABGR1555: [u8; 4] = [b'A', b'B', b'1', b'5'];
+pub const DRM_FORMAT_RGBA5551: [u8; 4] = [b'R', b'A', b'1', b'5'];
+pub const DRM_FORMAT_BGRA5551: [u8; 4] = [b'B', b'A', b'1', b'5'];
+pub const DRM_FORMAT_RGB565: [u8; 4] = [b'R', b'G', b'1', b'6'];
+pub const DRM_FORMAT_BGR565: [u8; 4] = [b'B', b'G', b'1', b'6'];
+pub const DRM_FORMAT_RGB888: [u8; 4] = [b'R', b'G', b'2', b'4'];
+pub const DRM_FORMAT_BGR888: [u8; 4] = [b'B', b'G', b'2', b'4'];
+pub const DRM_FORMAT_XRGB8888: [u8; 4] = [b'X', b'R', b'2', b'4'];
+pub const DRM_FORMAT_XBGR8888: [u8; 4] = [b'X', b'B', b'2', b'4'];
+pub const DRM_FORMAT_RGBX8888: [u8; 4] = [b'R', b'X', b'2', b'4'];
+pub const DRM_FORMAT_BGRX8888: [u8; 4] = [b'B', b'X', b'2', b'4'];
+pub const DRM_FORMAT_ARGB8888: [u8; 4] = [b'A', b'R', b'2', b'4'];
+pub const DRM_FORMAT_ABGR8888: [u8; 4] = [b'A', b'B', b'2', b'4'];
+pub const DRM_FORMAT_RGBA8888: [u8; 4] = [b'R', b'A', b'2', b'4'];
+pub const DRM_FORMAT_BGRA8888: [u8; 4] = [b'B', b'A', b'2', b'4'];
+pub const DRM_FORMAT_XRGB2101010: [u8; 4] = [b'X', b'R', b'3', b'0'];
+pub const DRM_FORMAT_XBGR2101010: [u8; 4] = [b'X', b'B', b'3', b'0'];
+pub const DRM_FORMAT_RGBX1010102: [u8; 4] = [b'R', b'X', b'3', b'0'];
+pub const DRM_FORMAT_BGRX1010102: [u8; 4] = [b'B', b'X', b'3', b'0'];
+pub const DRM_FORMAT_ARGB2101010: [u8; 4] = [b'A', b'R', b'3', b'0'];
+pub const DRM_FORMAT_ABGR2101010: [u8; 4] = [b'A', b'B', b'3', b'0'];
+pub const DRM_FORMAT_RGBA1010102: [u8; 4] = [b'R', b'A', b'3', b'0'];
+pub const DRM_FORMAT_BGRA1010102: [u8; 4] = [b'B', b'A', b'3', b'0'];
+pub const DRM_FORMAT_YUYV: [u8; 4] = [b'Y', b'U', b'Y', b'V'];
+pub const DRM_FORMAT_YVYU: [u8; 4] = [b'Y', b'V', b'Y', b'U'];
+pub const DRM_FORMAT_UYVY: [u8; 4] = [b'U', b'Y', b'V', b'Y'];
+pub const DRM_FORMAT_VYUY: [u8; 4] = [b'V', b'Y', b'U', b'Y'];
+pub const DRM_FORMAT_AYUV: [u8; 4] = [b'A', b'Y', b'U', b'V'];
+pub const DRM_FORMAT_NV12: [u8; 4] = [b'N', b'V', b'1', b'2'];
+pub const DRM_FORMAT_NV21: [u8; 4] = [b'N', b'V', b'2', b'1'];
+pub const DRM_FORMAT_NV16: [u8; 4] = [b'N', b'V', b'1', b'6'];
+pub const DRM_FORMAT_NV61: [u8; 4] = [b'N', b'V', b'6', b'1'];
+pub const DRM_FORMAT_NV24: [u8; 4] = [b'N', b'V', b'2', b'4'];
+pub const DRM_FORMAT_NV42: [u8; 4] = [b'N', b'V', b'4', b'2'];
+pub const DRM_FORMAT_YUV410: [u8; 4] = [b'Y', b'U', b'V', b'9'];
+pub const DRM_FORMAT_YVU410: [u8; 4] = [b'Y', b'V', b'U', b'9'];
+pub const DRM_FORMAT_YUV411: [u8; 4] = [b'Y', b'U', b'1', b'1'];
+pub const DRM_FORMAT_YVU411: [u8; 4] = [b'Y', b'V', b'1', b'1'];
+pub const DRM_FORMAT_YUV420: [u8; 4] = [b'Y', b'U', b'1', b'2'];
+pub const DRM_FORMAT_YVU420: [u8; 4] = [b'Y', b'V', b'1', b'2'];
+pub const DRM_FORMAT_YUV422: [u8; 4] = [b'Y', b'U', b'1', b'6'];
+pub const DRM_FORMAT_YVU422: [u8; 4] = [b'Y', b'V', b'1', b'6'];
+pub const DRM_FORMAT_YUV444: [u8; 4] = [b'Y', b'U', b'2', b'4'];
+pub const DRM_FORMAT_YVU444: [u8; 4] = [b'Y', b'V', b'2', b'4'];
diff --git a/gpu_buffer/src/lib.rs b/gpu_buffer/src/lib.rs
new file mode 100644
index 0000000..16aae50
--- /dev/null
+++ b/gpu_buffer/src/lib.rs
@@ -0,0 +1,853 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! A crate for creating [DRM](https://en.wikipedia.org/wiki/Direct_Rendering_Manager) managed
+//! buffer objects. Such objects are useful for exporting as DMABUFs/prime FDs, texturing, render
+//! targets, memory mapping, and scanout.
+//!
+//! # Examples
+//!
+//! ```rust
+//! # use std::error::Error;
+//! # use std::fs::File;
+//! # use std::result::Result;
+//! # use gpu_buffer::*;
+//! # fn test() -> Result<(), Box<Error>> {
+//! let drm_card = File::open("/dev/dri/card0")?;
+//! let device = Device::new(drm_card).map_err(|_| "failed to create device")?;
+//! let bo = device
+//! .create_buffer(1024,
+//! 512,
+//! Format::new(b'X', b'R', b'2', b'4'),
+//! Flags::empty().use_scanout(true))
+//! .map_err(|_| "failed to create buffer")?;
+//! assert_eq!(bo.width(), 1024);
+//! assert_eq!(bo.height(), 512);
+//! assert_eq!(bo.format(), Format::new(b'X', b'R', b'2', b'4'));
+//! assert_eq!(bo.num_planes(), 1);
+//! # Ok(())
+//! # }
+//! ```
+
+mod drm_formats;
+mod raw;
+pub mod rendernode;
+
+use std::cmp::min;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::isize;
+use std::os::raw::c_void;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::ptr::null_mut;
+use std::rc::Rc;
+use std::result::Result;
+
+use data_model::{VolatileMemory, VolatileMemoryError, VolatileSlice};
+
+use crate::drm_formats::*;
+use crate::raw::*;
+
+const MAP_FAILED: *mut c_void = (-1isize as *mut _);
+
+#[derive(Debug)]
+pub enum Error {
+ GbmFailed,
+ ExportFailed(sys_util::Error),
+ MapFailed,
+ UnknownFormat(Format),
+ CheckedArithmetic {
+ field1: (&'static str, usize),
+ field2: (&'static str, usize),
+ op: &'static str,
+ },
+ InvalidPrecondition {
+ field1: (&'static str, usize),
+ field2: (&'static str, usize),
+ op: &'static str,
+ },
+ Memcopy(VolatileMemoryError),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ GbmFailed => write!(f, "internal GBM failure"),
+ ExportFailed(e) => write!(f, "export failed: {}", e),
+ MapFailed => write!(f, "map failed"),
+ CheckedArithmetic {
+ field1: (label1, value1),
+ field2: (label2, value2),
+ op,
+ } => write!(
+ f,
+ "arithmetic failed: {}({}) {} {}({})",
+ label1, value1, op, label2, value2
+ ),
+ InvalidPrecondition {
+ field1: (label1, value1),
+ field2: (label2, value2),
+ op,
+ } => write!(
+ f,
+ "invalid precondition: {}({}) {} {}({})",
+ label1, value1, op, label2, value2
+ ),
+ UnknownFormat(format) => write!(f, "unknown format {:?}", format),
+ Memcopy(e) => write!(f, "error copying memory: {}", e),
+ }
+ }
+}
+
+macro_rules! checked_arithmetic {
+ ($x:ident $op:ident $y:ident $op_name:expr) => {
+ $x.$op($y).ok_or_else(|| Error::CheckedArithmetic {
+ field1: (stringify!($x), $x as usize),
+ field2: (stringify!($y), $y as usize),
+ op: $op_name,
+ })
+ };
+ ($x:ident + $y:ident) => {
+ checked_arithmetic!($x checked_add $y "+")
+ };
+ ($x:ident - $y:ident) => {
+ checked_arithmetic!($x checked_sub $y "-")
+ };
+ ($x:ident * $y:ident) => {
+ checked_arithmetic!($x checked_mul $y "*")
+ };
+}
+
+macro_rules! checked_range {
+ ($x:expr; <= $y:expr) => {
+ if $x <= $y {
+ Ok(())
+ } else {
+ Err(Error::InvalidPrecondition {
+ field1: (stringify!($x), $x as usize),
+ field2: (stringify!($y), $y as usize),
+ op: "<=",
+ })
+ }
+ };
+ ($x:ident <= $y:ident) => {
+ check_range!($x; <= $y)
+ };
+}
+
+/// A [fourcc](https://en.wikipedia.org/wiki/FourCC) format identifier.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Format(u32);
+
+impl Format {
+ /// Constructs a format identifer using a fourcc byte sequence.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use gpu_buffer::Format;
+ ///
+ /// let format = Format::new(b'X', b'R', b'2', b'4');
+ /// println!("format: {:?}", format);
+ /// ```
+ #[inline(always)]
+ pub fn new(a: u8, b: u8, c: u8, d: u8) -> Format {
+ Format(a as u32 | (b as u32) << 8 | (c as u32) << 16 | (d as u32) << 24)
+ }
+
+ /// Returns the fourcc code as a sequence of bytes.
+ #[inline(always)]
+ pub fn to_bytes(&self) -> [u8; 4] {
+ let f = self.0;
+ [f as u8, (f >> 8) as u8, (f >> 16) as u8, (f >> 24) as u8]
+ }
+
+ /// Returns the number of bytes per pixel for the given plane, suitable for making copies
+ /// to/from the plane.
+ pub fn bytes_per_pixel(&self, plane: usize) -> Option<usize> {
+ let b = self.to_bytes();
+
+ // NV12 and NV21 have 2 planes with 1 byte per pixel.
+ if (b == DRM_FORMAT_NV12 || b == DRM_FORMAT_NV21) && plane < 2 {
+ return Some(1);
+ }
+
+ // YVU420 has 3 planes, all with the same 1 byte per pixel.
+ if b == DRM_FORMAT_YVU420 && plane < 3 {
+ return Some(1);
+ }
+
+ if plane != 0 {
+ return None;
+ }
+
+ let bpp = match self.to_bytes() {
+ DRM_FORMAT_BGR233 => 1,
+ DRM_FORMAT_C8 => 1,
+ DRM_FORMAT_R8 => 1,
+ DRM_FORMAT_RGB332 => 1,
+ DRM_FORMAT_ABGR1555 => 2,
+ DRM_FORMAT_ABGR4444 => 2,
+ DRM_FORMAT_ARGB1555 => 2,
+ DRM_FORMAT_ARGB4444 => 2,
+ DRM_FORMAT_BGR565 => 2,
+ DRM_FORMAT_BGRA4444 => 2,
+ DRM_FORMAT_BGRA5551 => 2,
+ DRM_FORMAT_BGRX4444 => 2,
+ DRM_FORMAT_BGRX5551 => 2,
+ DRM_FORMAT_GR88 => 2,
+ DRM_FORMAT_RG88 => 2,
+ DRM_FORMAT_RGB565 => 2,
+ DRM_FORMAT_RGBA4444 => 2,
+ DRM_FORMAT_RGBA5551 => 2,
+ DRM_FORMAT_RGBX4444 => 2,
+ DRM_FORMAT_RGBX5551 => 2,
+ DRM_FORMAT_UYVY => 2,
+ DRM_FORMAT_VYUY => 2,
+ DRM_FORMAT_XBGR1555 => 2,
+ DRM_FORMAT_XBGR4444 => 2,
+ DRM_FORMAT_XRGB1555 => 2,
+ DRM_FORMAT_XRGB4444 => 2,
+ DRM_FORMAT_YUYV => 2,
+ DRM_FORMAT_YVYU => 2,
+ DRM_FORMAT_BGR888 => 3,
+ DRM_FORMAT_RGB888 => 3,
+ DRM_FORMAT_ABGR2101010 => 4,
+ DRM_FORMAT_ABGR8888 => 4,
+ DRM_FORMAT_ARGB2101010 => 4,
+ DRM_FORMAT_ARGB8888 => 4,
+ DRM_FORMAT_AYUV => 4,
+ DRM_FORMAT_BGRA1010102 => 4,
+ DRM_FORMAT_BGRA8888 => 4,
+ DRM_FORMAT_BGRX1010102 => 4,
+ DRM_FORMAT_BGRX8888 => 4,
+ DRM_FORMAT_RGBA1010102 => 4,
+ DRM_FORMAT_RGBA8888 => 4,
+ DRM_FORMAT_RGBX1010102 => 4,
+ DRM_FORMAT_RGBX8888 => 4,
+ DRM_FORMAT_XBGR2101010 => 4,
+ DRM_FORMAT_XBGR8888 => 4,
+ DRM_FORMAT_XRGB2101010 => 4,
+ DRM_FORMAT_XRGB8888 => 4,
+ _ => return None,
+ };
+ Some(bpp)
+ }
+}
+
+impl From<u32> for Format {
+ fn from(u: u32) -> Format {
+ Format(u)
+ }
+}
+
+impl From<Format> for u32 {
+ fn from(f: Format) -> u32 {
+ f.0
+ }
+}
+
+impl fmt::Debug for Format {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let b = self.to_bytes();
+ if b.iter().all(u8::is_ascii_graphic) {
+ write!(
+ f,
+ "fourcc({}{}{}{})",
+ b[0] as char, b[1] as char, b[2] as char, b[3] as char
+ )
+ } else {
+ write!(
+ f,
+ "fourcc(0x{:02x}{:02x}{:02x}{:02x})",
+ b[0], b[1], b[2], b[3]
+ )
+ }
+ }
+}
+
+/// Usage flags for constructing a buffer object.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Flags(u32);
+
+impl Flags {
+ /// Returns empty set of flags.
+ #[inline(always)]
+ pub fn empty() -> Flags {
+ Flags(0)
+ }
+
+ /// Returns the given set of raw `GBM_BO` flags wrapped in a `Flags` struct.
+ #[inline(always)]
+ pub fn new(raw: u32) -> Flags {
+ Flags(raw)
+ }
+
+ /// Sets the scanout flag's presence
+ #[inline(always)]
+ pub fn use_scanout(self, e: bool) -> Flags {
+ if e {
+ Flags(self.0 | GBM_BO_USE_SCANOUT)
+ } else {
+ Flags(self.0 & !GBM_BO_USE_SCANOUT)
+ }
+ }
+
+ /// Sets the cursor flag's presence
+ #[inline(always)]
+ pub fn use_cursor(self, e: bool) -> Flags {
+ if e {
+ Flags(self.0 | GBM_BO_USE_CURSOR)
+ } else {
+ Flags(self.0 & !GBM_BO_USE_CURSOR)
+ }
+ }
+
+ /// Sets the cursor 64x64 flag's presence
+ #[inline(always)]
+ pub fn use_cursor64(self, e: bool) -> Flags {
+ if e {
+ Flags(self.0 | GBM_BO_USE_CURSOR_64X64)
+ } else {
+ Flags(self.0 & !GBM_BO_USE_CURSOR_64X64)
+ }
+ }
+
+ /// Sets the rendering flag's presence
+ #[inline(always)]
+ pub fn use_rendering(self, e: bool) -> Flags {
+ if e {
+ Flags(self.0 | GBM_BO_USE_RENDERING)
+ } else {
+ Flags(self.0 & !GBM_BO_USE_RENDERING)
+ }
+ }
+
+ /// Sets the linear flag's presence
+ #[inline(always)]
+ pub fn use_linear(self, e: bool) -> Flags {
+ if e {
+ Flags(self.0 | GBM_BO_USE_LINEAR)
+ } else {
+ Flags(self.0 & !GBM_BO_USE_LINEAR)
+ }
+ }
+
+ /// Sets the texturing flag's presence
+ #[inline(always)]
+ pub fn use_texturing(self, e: bool) -> Flags {
+ if e {
+ Flags(self.0 | GBM_BO_USE_TEXTURING)
+ } else {
+ Flags(self.0 & !GBM_BO_USE_TEXTURING)
+ }
+ }
+}
+
+struct DeviceInner {
+ _fd: File,
+ gbm: *mut gbm_device,
+}
+
+impl Drop for DeviceInner {
+ fn drop(self: &mut DeviceInner) {
+ // Safe because DeviceInner is only constructed with a valid gbm_device.
+ unsafe {
+ gbm_device_destroy(self.gbm);
+ }
+ }
+}
+
+/// A device capable of allocating `Buffer`.
+#[derive(Clone)]
+pub struct Device(Rc<DeviceInner>);
+
+impl Device {
+ /// Returns a new `Device` using the given `fd` opened from a device in `/dev/dri/`.
+ pub fn new(fd: File) -> Result<Device, ()> {
+ // gbm_create_device is safe to call with a valid fd, and we check that a valid one is
+ // returned. The FD is not of the appropriate kind (i.e. not a DRM device),
+ // gbm_create_device should reject it.
+ let gbm = unsafe { gbm_create_device(fd.as_raw_fd()) };
+ if gbm.is_null() {
+ Err(())
+ } else {
+ Ok(Device(Rc::new(DeviceInner { _fd: fd, gbm })))
+ }
+ }
+
+ /// Creates a new buffer with the given metadata.
+ pub fn create_buffer(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ usage: Flags,
+ ) -> Result<Buffer, Error> {
+ // This is safe because only a valid gbm_device is used and the return value is checked.
+ let bo = unsafe { gbm_bo_create(self.0.gbm, width, height, format.0, usage.0) };
+ if bo.is_null() {
+ Err(Error::GbmFailed)
+ } else {
+ Ok(Buffer(bo, self.clone()))
+ }
+ }
+}
+
+/// An allocation from a `Device`.
+pub struct Buffer(*mut gbm_bo, Device);
+
+impl Buffer {
+ /// The device
+ pub fn device(&self) -> &Device {
+ &self.1
+ }
+
+ /// Width in pixels.
+ pub fn width(&self) -> u32 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_width(self.0) }
+ }
+
+ /// Height in pixels.
+ pub fn height(&self) -> u32 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_height(self.0) }
+ }
+
+ /// Length in bytes of one row of the buffer.
+ pub fn stride(&self) -> u32 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_stride(self.0) }
+ }
+
+ /// Length in bytes of the stride or tiling.
+ pub fn stride_or_tiling(&self) -> u32 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_stride_or_tiling(self.0) }
+ }
+
+ /// `Format` of the buffer.
+ pub fn format(&self) -> Format {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { Format(gbm_bo_get_format(self.0)) }
+ }
+
+ /// Format modifier flags for the buffer.
+ pub fn format_modifier(&self) -> u64 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_modifier(self.0) }
+ }
+
+ /// Number of planes present in this buffer.
+ pub fn num_planes(&self) -> usize {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_plane_count(self.0) }
+ }
+
+ /// Handle as u64 for the given plane.
+ pub fn plane_handle(&self, plane: usize) -> u64 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_handle_for_plane(self.0, plane).u64 }
+ }
+
+ /// Offset in bytes for the given plane.
+ pub fn plane_offset(&self, plane: usize) -> u32 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_offset(self.0, plane) }
+ }
+
+ /// Length in bytes of one row for the given plane.
+ pub fn plane_stride(&self, plane: usize) -> u32 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_stride_for_plane(self.0, plane) }
+ }
+
+ /// Size of a plane, in bytes.
+ pub fn plane_size(&self, plane: usize) -> u32 {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_plane_size(self.0, plane) }
+ }
+
+ /// Exports a new dmabuf/prime file descriptor for the given plane.
+ pub fn export_plane_fd(&self, plane: usize) -> Result<File, i32> {
+ // This is always safe to call with a valid gbm_bo pointer.
+ match unsafe { gbm_bo_get_plane_fd(self.0, plane) } {
+ fd if fd >= 0 => Ok(unsafe { File::from_raw_fd(fd) }),
+ ret => Err(ret),
+ }
+ }
+
+ fn map(
+ &self,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ plane: usize,
+ flags: u32,
+ ) -> Result<BufferMapping, Error> {
+ checked_range!(checked_arithmetic!(x + width)?; <= self.width())?;
+ checked_range!(checked_arithmetic!(y + height)?; <= self.height())?;
+ checked_range!(plane; <= self.num_planes())?;
+
+ let bytes_per_pixel = self
+ .format()
+ .bytes_per_pixel(plane)
+ .ok_or(Error::UnknownFormat(self.format()))? as u32;
+
+ let mut stride = 0;
+ let mut map_data = null_mut();
+ // Safe because only a valid gbm_bo object is used and the return value is checked. Only
+ // pointers coerced from stack references are used for returned values, and we trust gbm to
+ // only write as many bytes as the size of the pointed to values.
+ let mapping = unsafe {
+ gbm_bo_map(
+ self.0,
+ x,
+ y,
+ width,
+ height,
+ flags,
+ &mut stride,
+ &mut map_data,
+ plane,
+ )
+ };
+ if mapping == MAP_FAILED {
+ return Err(Error::MapFailed);
+ }
+
+ // The size of returned slice is equal the size of a row in bytes multiplied by the height
+ // of the mapped region, subtracted by the offset into the first mapped row. The 'x' and
+ // 'y's in the below diagram of a 2D buffer are bytes in the mapping. The first 'y' is what
+ // the mapping points to in memory, and the '-'s are unmapped bytes of the buffer.
+ // |----------|
+ // |--stride--|
+ // |-----yyyyx| h
+ // |xxxxxyyyyx| e
+ // |xxxxxyyyyx| i
+ // |xxxxxyyyyx| g
+ // |xxxxxyyyyx| h
+ // |xxxxxyyyyx| t
+ // |----------|
+ let size = checked_arithmetic!(stride * height)?;
+ let x_offset_bytes = checked_arithmetic!(x * bytes_per_pixel)?;
+ let slice_size = checked_arithmetic!(size - x_offset_bytes)? as u64;
+
+ Ok(BufferMapping {
+ // Safe because the chunk of memory starting at mapping with size `slice_size` is valid
+ // and tied to the lifetime of `buffer_mapping`.
+ slice: unsafe { VolatileSlice::new(mapping as *mut u8, slice_size) },
+ stride,
+ map_data,
+ buffer: self,
+ })
+ }
+
+ /// Reads the given subsection of the buffer to `dst`.
+ pub fn read_to_volatile(
+ &self,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ plane: usize,
+ dst: VolatileSlice,
+ ) -> Result<(), Error> {
+ if width == 0 || height == 0 {
+ return Ok(());
+ }
+
+ let mapping = self.map(x, y, width, height, plane, GBM_BO_TRANSFER_READ)?;
+
+ if x == 0 && width == self.width() {
+ mapping.as_volatile_slice().copy_to_volatile_slice(dst);
+ } else {
+ // This path is more complicated because there are gaps in the data between lines.
+ let width = width as u64;
+ let stride = mapping.stride() as u64;
+ let bytes_per_pixel = match self.format().bytes_per_pixel(plane) {
+ Some(bpp) => bpp as u64,
+ None => return Err(Error::UnknownFormat(self.format())),
+ };
+ let line_copy_size = checked_arithmetic!(width * bytes_per_pixel)?;
+ let src = mapping.as_volatile_slice();
+ for yy in 0..(height as u64) {
+ let line_offset = checked_arithmetic!(yy * stride)?;
+ let src_line = src
+ .get_slice(line_offset, line_copy_size)
+ .map_err(Error::Memcopy)?;
+ let dst_line = dst
+ .get_slice(line_offset, line_copy_size)
+ .map_err(Error::Memcopy)?;
+ src_line.copy_to_volatile_slice(dst_line);
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Writes to the given subsection of the buffer from `sgs`.
+ pub fn write_from_sg<'a, S: Iterator<Item = VolatileSlice<'a>>>(
+ &self,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ plane: usize,
+ src_offset: usize,
+ mut sgs: S,
+ ) -> Result<(), Error> {
+ if width == 0 || height == 0 {
+ return Ok(());
+ }
+
+ checked_range!(src_offset; <= isize::MAX as usize)?;
+
+ let mapping = self.map(x, y, width, height, plane, GBM_BO_TRANSFER_WRITE)?;
+ let mut dst_slice = mapping.as_volatile_slice();
+ let stride = mapping.stride() as u64;
+ let mut height = height as u64;
+ let mut src_offset = src_offset as u64;
+
+ if x == 0 && width == self.width() {
+ // This path is a simple copy from the scatter gather iterator to the buffer objection,
+ // with no gaps in the data.
+ let mut copy_size = checked_arithmetic!(stride * height)?;
+ for sg in sgs {
+ // Skip src_offset into this scatter gather item, or the entire thing if offset is
+ // larger.
+ let sg_size = match sg.size().checked_sub(src_offset) {
+ Some(sg_remaining_size) => sg_remaining_size,
+ None => {
+ src_offset -= sg.size();
+ continue;
+ }
+ };
+ let copy_sg_size = min(sg_size, copy_size);
+ let src_slice = sg
+ .get_slice(src_offset, copy_sg_size)
+ .map_err(Error::Memcopy)?;
+ src_slice.copy_to_volatile_slice(dst_slice);
+
+ src_offset = 0;
+ dst_slice = dst_slice.offset(copy_sg_size).map_err(Error::Memcopy)?;
+ copy_size -= copy_sg_size;
+ if copy_size == 0 {
+ break;
+ }
+ }
+ } else {
+ let width = width as u64;
+ // This path is more complicated because there are gaps in the data between lines.
+ let bytes_per_pixel = self.format().bytes_per_pixel(plane).unwrap_or(0) as u64;
+ let line_copy_size = checked_arithmetic!(width * bytes_per_pixel)?;
+ let line_end_skip = checked_arithmetic!(stride - line_copy_size)?;
+ let mut remaining_line_copy_size = line_copy_size;
+ let mut sg_opt = sgs.next();
+ while let Some(sg) = sg_opt {
+ // Skip src_offset into this scatter gather item, or the entire thing if offset is
+ // larger.
+ let sg_size = match sg.size().checked_sub(src_offset) {
+ None | Some(0) => {
+ src_offset -= sg.size();
+ sg_opt = sgs.next();
+ continue;
+ }
+ Some(sg_remaining_size) => sg_remaining_size,
+ };
+ let copy_sg_size = min(sg_size, remaining_line_copy_size);
+ let src_slice = sg
+ .get_slice(src_offset, copy_sg_size)
+ .map_err(Error::Memcopy)?;
+ src_slice.copy_to_volatile_slice(dst_slice);
+
+ src_offset += copy_sg_size;
+ dst_slice = dst_slice.offset(copy_sg_size).map_err(Error::Memcopy)?;
+ remaining_line_copy_size -= copy_sg_size;
+ if remaining_line_copy_size == 0 {
+ remaining_line_copy_size = line_copy_size;
+ height -= 1;
+ if height == 0 {
+ break;
+ }
+
+ src_offset += line_end_skip;
+ dst_slice = dst_slice.offset(line_end_skip).map_err(Error::Memcopy)?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl Drop for Buffer {
+ fn drop(&mut self) {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_destroy(self.0) }
+ }
+}
+
+impl AsRawFd for Buffer {
+ fn as_raw_fd(&self) -> RawFd {
+ // This is always safe to call with a valid gbm_bo pointer.
+ unsafe { gbm_bo_get_fd(self.0) }
+ }
+}
+
+struct BufferMapping<'a> {
+ slice: VolatileSlice<'a>,
+ stride: u32,
+ map_data: *mut c_void,
+ buffer: &'a Buffer,
+}
+
+impl<'a> BufferMapping<'a> {
+ fn as_volatile_slice(&self) -> VolatileSlice {
+ self.slice
+ }
+
+ fn stride(&self) -> u32 {
+ self.stride
+ }
+}
+
+impl<'a> Drop for BufferMapping<'a> {
+ fn drop(&mut self) {
+ // safe because the gbm_bo is assumed to be valid and the map_data is the same one given by
+ // gbm_bo_map.
+ unsafe {
+ gbm_bo_unmap(self.buffer.0, self.map_data);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use data_model::VolatileMemory;
+ use std::fmt::Write;
+
+ #[test]
+ fn format_debug() {
+ let f = Format::new(b'X', b'R', b'2', b'4');
+ let mut buf = String::new();
+ write!(&mut buf, "{:?}", f).unwrap();
+ assert_eq!(buf, "fourcc(XR24)");
+
+ let f = Format::new(0, 1, 2, 16);
+ let mut buf = String::new();
+ write!(&mut buf, "{:?}", f).unwrap();
+ assert_eq!(buf, "fourcc(0x00010210)");
+ }
+
+ #[test]
+ fn format_bytes_per_pixel() {
+ let f = Format::new(b'X', b'R', b'2', b'4');
+ assert_eq!(f.bytes_per_pixel(0), Some(4));
+ assert_eq!(f.bytes_per_pixel(1), None);
+ let f = Format::new(b'N', b'V', b'1', b'2');
+ assert_eq!(f.bytes_per_pixel(0), Some(1));
+ assert_eq!(f.bytes_per_pixel(1), Some(1));
+ assert_eq!(f.bytes_per_pixel(2), None);
+ let f = Format::new(b'R', b'8', b' ', b' ');
+ assert_eq!(f.bytes_per_pixel(0), Some(1));
+ assert_eq!(f.bytes_per_pixel(1), None);
+ let f = Format::new(b'B', b'G', b'2', b'4');
+ assert_eq!(f.bytes_per_pixel(0), Some(3));
+ assert_eq!(f.bytes_per_pixel(1), None);
+ let f = Format::new(b'G', b'R', b'8', b'8');
+ assert_eq!(f.bytes_per_pixel(0), Some(2));
+ assert_eq!(f.bytes_per_pixel(1), None);
+ let f = Format::new(b'Z', b'A', b'C', b'H');
+ assert_eq!(f.bytes_per_pixel(0), None);
+ }
+
+ #[test]
+ #[ignore] // no access to /dev/dri
+ fn open_device() {
+ let drm_card = File::open("/dev/dri/card0").expect("failed to open card");
+ Device::new(drm_card).expect("failed to create device with card");
+ }
+
+ #[test]
+ #[ignore] // no access to /dev/dri
+ fn create_buffer() {
+ let drm_card = File::open("/dev/dri/card0").expect("failed to open card");
+ let device = Device::new(drm_card).expect("failed to create device with card");
+ let bo = device
+ .create_buffer(
+ 1024,
+ 512,
+ Format::new(b'X', b'R', b'2', b'4'),
+ Flags::empty().use_scanout(true),
+ )
+ .expect("failed to create buffer");
+
+ assert_eq!(bo.width(), 1024);
+ assert_eq!(bo.height(), 512);
+ assert_eq!(bo.format(), Format::new(b'X', b'R', b'2', b'4'));
+ assert_eq!(bo.num_planes(), 1);
+ }
+
+ #[test]
+ #[ignore] // no access to /dev/dri
+ fn export_buffer() {
+ let drm_card = File::open("/dev/dri/card0").expect("failed to open card");
+ let device = Device::new(drm_card).expect("failed to create device with card");
+ let bo = device
+ .create_buffer(
+ 1024,
+ 1024,
+ Format::new(b'X', b'R', b'2', b'4'),
+ Flags::empty().use_scanout(true),
+ )
+ .expect("failed to create buffer");
+ bo.export_plane_fd(0).expect("failed to export plane");
+ }
+
+ #[test]
+ #[ignore] // no access to /dev/dri
+ fn buffer_transfer() {
+ let drm_card = File::open("/dev/dri/card0").expect("failed to open card");
+ let device = Device::new(drm_card).expect("failed to create device with card");
+ let bo = device
+ .create_buffer(
+ 1024,
+ 1024,
+ Format::new(b'X', b'R', b'2', b'4'),
+ Flags::empty().use_scanout(true).use_linear(true),
+ )
+ .expect("failed to create buffer");
+ let mut dst: Vec<u8> = Vec::new();
+ dst.resize((bo.stride() * bo.height()) as usize, 0x4A);
+ let dst_len = dst.len() as u64;
+ bo.write_from_sg(
+ 0,
+ 0,
+ 1024,
+ 1024,
+ 0,
+ 0,
+ [dst.as_mut_slice().get_slice(0, dst_len).unwrap()]
+ .iter()
+ .cloned(),
+ )
+ .expect("failed to read bo");
+ bo.read_to_volatile(
+ 0,
+ 0,
+ 1024,
+ 1024,
+ 0,
+ dst.as_mut_slice().get_slice(0, dst_len).unwrap(),
+ )
+ .expect("failed to read bo");
+ assert!(dst.iter().all(|&x| x == 0x4A));
+ }
+}
diff --git a/gpu_buffer/src/raw.rs b/gpu_buffer/src/raw.rs
new file mode 100644
index 0000000..b4255ca
--- /dev/null
+++ b/gpu_buffer/src/raw.rs
@@ -0,0 +1,215 @@
+// Copyright 2018 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.
+
+// Generated with bindgen --whitelist-function='gbm_.*' --whitelist-type='gbm_.*' minigbm/gbm.h
+// Hand-modified by zachr
+
+#![allow(dead_code)]
+
+use std::os::raw::{c_char, c_int, c_void};
+
+/// \file gbm.h
+/// \brief Generic Buffer Manager
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gbm_device {
+ _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gbm_bo {
+ _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gbm_surface {
+ _unused: [u8; 0],
+}
+/// Abstraction representing the handle to a buffer allocated by the
+/// manager
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union gbm_bo_handle {
+ pub ptr: *mut c_void,
+ pub s32: i32,
+ pub u32: u32,
+ pub s64: i64,
+ pub u64: u64,
+ _bindgen_union_align: u64,
+}
+
+/// Buffer is going to be presented to the screen using an API such as KMS
+pub const GBM_BO_USE_SCANOUT: gbm_bo_flags = 1;
+/// Buffer is going to be used as cursor
+pub const GBM_BO_USE_CURSOR: gbm_bo_flags = 2;
+/// Deprecated
+pub const GBM_BO_USE_CURSOR_64X64: gbm_bo_flags = 2;
+/// Buffer is to be used for rendering - for example it is going to be used
+/// as the storage for a color buffer
+pub const GBM_BO_USE_RENDERING: gbm_bo_flags = 4;
+/// Deprecated
+pub const GBM_BO_USE_WRITE: gbm_bo_flags = 8;
+/// Buffer is guaranteed to be laid out linearly in memory. That is, the
+/// buffer is laid out as an array with 'height' blocks, each block with
+/// length 'stride'. Each stride is in the same order as the rows of the
+/// buffer. This is intended to be used with buffers that will be accessed
+/// via dma-buf mmap().
+pub const GBM_BO_USE_LINEAR: gbm_bo_flags = 16;
+/// The buffer will be used as a texture that will be sampled from.
+pub const GBM_BO_USE_TEXTURING: gbm_bo_flags = 32;
+/// The buffer will be written to by a camera subsystem.
+pub const GBM_BO_USE_CAMERA_WRITE: gbm_bo_flags = 64;
+/// The buffer will be read from by a camera subsystem.
+pub const GBM_BO_USE_CAMERA_READ: gbm_bo_flags = 128;
+/// Buffer inaccessible to unprivileged users.
+pub const GBM_BO_USE_PROTECTED: gbm_bo_flags = 256;
+/// These flags specify the frequency of software access. These flags do not
+/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
+/// present a linear view.
+pub const GBM_BO_USE_SW_READ_OFTEN: gbm_bo_flags = 512;
+/// These flags specify the frequency of software access. These flags do not
+/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
+/// present a linear view.
+pub const GBM_BO_USE_SW_READ_RARELY: gbm_bo_flags = 1024;
+/// These flags specify the frequency of software access. These flags do not
+/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
+/// present a linear view.
+pub const GBM_BO_USE_SW_WRITE_OFTEN: gbm_bo_flags = 2048;
+/// These flags specify the frequency of software access. These flags do not
+/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
+/// present a linear view.
+pub const GBM_BO_USE_SW_WRITE_RARELY: gbm_bo_flags = 4096;
+/// Flags to indicate the intended use for the buffer - these are passed into
+/// gbm_bo_create(). The caller must set the union of all the flags that are
+/// appropriate
+///
+/// \sa Use gbm_device_is_format_supported() to check if the combination of format
+/// and use flags are supported
+#[allow(non_camel_case_types)]
+pub type gbm_bo_flags = u32;
+
+#[link(name = "gbm")]
+extern "C" {
+ pub fn gbm_device_get_fd(gbm: *mut gbm_device) -> c_int;
+ pub fn gbm_device_get_backend_name(gbm: *mut gbm_device) -> *const c_char;
+ pub fn gbm_device_is_format_supported(gbm: *mut gbm_device, format: u32, usage: u32) -> c_int;
+ pub fn gbm_device_destroy(gbm: *mut gbm_device);
+ pub fn gbm_create_device(fd: c_int) -> *mut gbm_device;
+ pub fn gbm_bo_create(
+ gbm: *mut gbm_device,
+ width: u32,
+ height: u32,
+ format: u32,
+ flags: u32,
+ ) -> *mut gbm_bo;
+ pub fn gbm_bo_create_with_modifiers(
+ gbm: *mut gbm_device,
+ width: u32,
+ height: u32,
+ format: u32,
+ modifiers: *const u64,
+ count: u32,
+ ) -> *mut gbm_bo;
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gbm_import_fd_data {
+ pub fd: c_int,
+ pub width: u32,
+ pub height: u32,
+ pub stride: u32,
+ pub format: u32,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gbm_import_fd_planar_data {
+ pub fds: [c_int; 4usize],
+ pub width: u32,
+ pub height: u32,
+ pub format: u32,
+ pub strides: [u32; 4usize],
+ pub offsets: [u32; 4usize],
+ pub format_modifiers: [u64; 4usize],
+}
+
+extern "C" {
+ pub fn gbm_bo_import(
+ gbm: *mut gbm_device,
+ type_: u32,
+ buffer: *mut c_void,
+ usage: u32,
+ ) -> *mut gbm_bo;
+}
+
+/// Buffer contents read back (or accessed directly) at transfer
+/// create time.
+pub const GBM_BO_TRANSFER_READ: gbm_bo_transfer_flags = 1;
+/// Buffer contents will be written back at unmap time
+/// (or modified as a result of being accessed directly).
+pub const GBM_BO_TRANSFER_WRITE: gbm_bo_transfer_flags = 2;
+/// Read/modify/write
+pub const GBM_BO_TRANSFER_READ_WRITE: gbm_bo_transfer_flags = 3;
+
+/// Flags to indicate the type of mapping for the buffer - these are
+/// passed into gbm_bo_map(). The caller must set the union of all the
+/// flags that are appropriate.
+///
+/// These flags are independent of the GBM_BO_USE_* creation flags. However,
+/// mapping the buffer may require copying to/from a staging buffer.
+///
+/// See also: pipe_transfer_usage
+#[allow(non_camel_case_types)]
+pub type gbm_bo_transfer_flags = u32;
+
+extern "C" {
+ pub fn gbm_bo_map(
+ bo: *mut gbm_bo,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ flags: u32,
+ stride: *mut u32,
+ map_data: *mut *mut c_void,
+ plane: usize,
+ ) -> *mut c_void;
+ pub fn gbm_bo_unmap(bo: *mut gbm_bo, map_data: *mut c_void);
+ pub fn gbm_bo_get_width(bo: *mut gbm_bo) -> u32;
+ pub fn gbm_bo_get_height(bo: *mut gbm_bo) -> u32;
+ pub fn gbm_bo_get_stride(bo: *mut gbm_bo) -> u32;
+ pub fn gbm_bo_get_stride_or_tiling(bo: *mut gbm_bo) -> u32;
+ pub fn gbm_bo_get_format(bo: *mut gbm_bo) -> u32;
+ pub fn gbm_bo_get_modifier(bo: *mut gbm_bo) -> u64;
+ pub fn gbm_bo_get_device(bo: *mut gbm_bo) -> *mut gbm_device;
+ pub fn gbm_bo_get_handle(bo: *mut gbm_bo) -> gbm_bo_handle;
+ pub fn gbm_bo_get_fd(bo: *mut gbm_bo) -> c_int;
+ pub fn gbm_bo_get_plane_count(bo: *mut gbm_bo) -> usize;
+ pub fn gbm_bo_get_handle_for_plane(bo: *mut gbm_bo, plane: usize) -> gbm_bo_handle;
+ pub fn gbm_bo_get_plane_fd(bo: *mut gbm_bo, plane: usize) -> c_int;
+ pub fn gbm_bo_get_offset(bo: *mut gbm_bo, plane: usize) -> u32;
+ pub fn gbm_bo_get_plane_size(bo: *mut gbm_bo, plane: usize) -> u32;
+ pub fn gbm_bo_get_stride_for_plane(bo: *mut gbm_bo, plane: usize) -> u32;
+ pub fn gbm_bo_get_plane_format_modifier(bo: *mut gbm_bo, plane: usize) -> u64;
+ // Did not generate cleanly by bindgen. Redone manually by zachr.
+ pub fn gbm_bo_set_user_data(
+ bo: *mut gbm_bo,
+ data: *mut c_void,
+ destroy_user_data: extern "C" fn(bo: *mut gbm_bo, data: *mut c_void),
+ );
+ pub fn gbm_bo_get_user_data(bo: *mut gbm_bo) -> *mut c_void;
+ pub fn gbm_bo_destroy(bo: *mut gbm_bo);
+ pub fn gbm_surface_create(
+ gbm: *mut gbm_device,
+ width: u32,
+ height: u32,
+ format: u32,
+ flags: u32,
+ ) -> *mut gbm_surface;
+ pub fn gbm_surface_lock_front_buffer(surface: *mut gbm_surface) -> *mut gbm_bo;
+ pub fn gbm_surface_release_buffer(surface: *mut gbm_surface, bo: *mut gbm_bo);
+ pub fn gbm_surface_has_free_buffers(surface: *mut gbm_surface) -> c_int;
+ pub fn gbm_surface_destroy(surface: *mut gbm_surface);
+}
diff --git a/gpu_buffer/src/rendernode.rs b/gpu_buffer/src/rendernode.rs
new file mode 100644
index 0000000..503d712
--- /dev/null
+++ b/gpu_buffer/src/rendernode.rs
@@ -0,0 +1,116 @@
+// Copyright 2018 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;
+use std::fs::{File, OpenOptions};
+#[cfg(target_pointer_width = "64")]
+use std::os::raw::c_ulong;
+use std::os::raw::{c_char, c_int, c_uint};
+use std::path::Path;
+use std::ptr::null_mut;
+
+use sys_util::{ioctl_iowr_nr, ioctl_with_mut_ref};
+
+// Consistent with __kernel_size_t in include/uapi/asm-generic/posix_types.h.
+#[cfg(not(target_pointer_width = "64"))]
+#[allow(non_camel_case_types)]
+type __kernel_size_t = c_uint;
+#[cfg(target_pointer_width = "64")]
+#[allow(non_camel_case_types)]
+type __kernel_size_t = c_ulong;
+
+const DRM_IOCTL_BASE: c_uint = 0x64;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct drm_version {
+ version_major: c_int,
+ version_minor: c_int,
+ version_patchlevel: c_int,
+ name_len: __kernel_size_t,
+ name: *mut c_char,
+ date_len: __kernel_size_t,
+ date: *mut c_char,
+ desc_len: __kernel_size_t,
+ desc: *mut c_char,
+}
+
+ioctl_iowr_nr!(DRM_IOCTL_VERSION, DRM_IOCTL_BASE, 0x0, drm_version);
+
+fn get_drm_device_name(fd: &File) -> Result<String, ()> {
+ let mut version = drm_version {
+ version_major: 0,
+ version_minor: 0,
+ version_patchlevel: 0,
+ name_len: 0,
+ name: null_mut(),
+ date_len: 0,
+ date: null_mut(),
+ desc_len: 0,
+ desc: null_mut(),
+ };
+
+ // Get the length of the device name.
+ if unsafe { ioctl_with_mut_ref(fd, DRM_IOCTL_VERSION(), &mut version) } < 0 {
+ return Err(());
+ }
+
+ // Enough bytes to hold the device name and terminating null character.
+ let mut name_bytes: Vec<u8> = vec![0; (version.name_len + 1) as usize];
+ let mut version = drm_version {
+ version_major: 0,
+ version_minor: 0,
+ version_patchlevel: 0,
+ name_len: name_bytes.len() as __kernel_size_t,
+ name: name_bytes.as_mut_ptr() as *mut c_char,
+ date_len: 0,
+ date: null_mut(),
+ desc_len: 0,
+ desc: null_mut(),
+ };
+
+ // Safe as no more than name_len + 1 bytes will be written to name.
+ if unsafe { ioctl_with_mut_ref(fd, DRM_IOCTL_VERSION(), &mut version) } < 0 {
+ return Err(());
+ }
+
+ Ok(CString::new(&name_bytes[..(version.name_len as usize)])
+ .map_err(|_| ())?
+ .into_string()
+ .map_err(|_| ())?)
+}
+
+/// Returns a `fd` for an opened rendernode device, while filtering out specified
+/// undesired drivers.
+pub fn open_device(undesired: &[&str]) -> Result<File, ()> {
+ const DRM_DIR_NAME: &str = "/dev/dri";
+ const DRM_MAX_MINOR: u32 = 15;
+ const RENDER_NODE_START: u32 = 128;
+
+ for n in RENDER_NODE_START..=RENDER_NODE_START + DRM_MAX_MINOR {
+ let path = Path::new(DRM_DIR_NAME).join(format!("renderD{}", n));
+
+ if let Ok(fd) = OpenOptions::new().read(true).write(true).open(path) {
+ if let Ok(name) = get_drm_device_name(&fd) {
+ if !undesired.iter().any(|item| *item == name) {
+ return Ok(fd);
+ }
+ }
+ }
+ }
+
+ Err(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ #[ignore] // no access to /dev/dri
+ fn open_rendernode_device() {
+ let undesired: &[&str] = &["bad_driver", "another_bad_driver"];
+ open_device(undesired).expect("failed to open rendernode");
+ }
+}
diff --git a/gpu_display/Cargo.toml b/gpu_display/Cargo.toml
new file mode 100644
index 0000000..0177f1e
--- /dev/null
+++ b/gpu_display/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "gpu_display"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+data_model = { path = "../data_model" }
+libc = "*"
+sys_util = { path = "../sys_util" }
+
+[build-dependencies]
+cc = "=1.0.25"
diff --git a/gpu_display/build.rs b/gpu_display/build.rs
new file mode 100644
index 0000000..69c0fae
--- /dev/null
+++ b/gpu_display/build.rs
@@ -0,0 +1,98 @@
+// Copyright 2018 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::env;
+use std::ffi::OsStr;
+use std::fs;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+// Performs a recursive search for a file with name under path and returns the full path if such a
+// file is found.
+fn scan_path<P: AsRef<Path>, O: AsRef<OsStr>>(path: P, name: O) -> Option<PathBuf> {
+ for entry in fs::read_dir(path).ok()? {
+ if let Ok(entry) = entry {
+ let file_type = match entry.file_type() {
+ Ok(t) => t,
+ Err(_) => continue,
+ };
+
+ if file_type.is_file() && entry.file_name() == name.as_ref() {
+ return Some(entry.path());
+ } else if file_type.is_dir() {
+ if let Some(found) = scan_path(entry.path(), name.as_ref()) {
+ return Some(found);
+ }
+ }
+ }
+ }
+ None
+}
+
+// Searches for the given protocol in both the system wide and bundles protocols path.
+fn find_protocol(name: &str) -> PathBuf {
+ let protocols_path =
+ env::var("WAYLAND_PROTOCOLS_PATH").unwrap_or("/usr/share/wayland-protocols".to_owned());
+ let protocol_file_name = PathBuf::from(format!("{}.xml", name));
+
+ // Prioritize the systems wayland protocols before using the bundled ones.
+ if let Some(found) = scan_path(protocols_path, &protocol_file_name) {
+ return found;
+ }
+
+ // Use bundled protocols as a fallback.
+ let protocol_path = Path::new("protocol").join(protocol_file_name);
+ assert!(
+ protocol_path.is_file(),
+ "unable to locate wayland protocol specification for `{}`",
+ name
+ );
+ protocol_path
+}
+
+fn compile_protocol<P: AsRef<Path>>(name: &str, out: P) -> PathBuf {
+ let in_protocol = find_protocol(name);
+ println!("cargo:rerun-if-changed={}", in_protocol.display());
+ let out_code = out.as_ref().join(format!("{}.c", name));
+ let out_header = out.as_ref().join(format!("{}.h", name));
+ eprintln!("building protocol: {}", name);
+ Command::new("wayland-scanner")
+ .arg("code")
+ .arg(&in_protocol)
+ .arg(&out_code)
+ .output()
+ .unwrap();
+ Command::new("wayland-scanner")
+ .arg("client-header")
+ .arg(&in_protocol)
+ .arg(&out_header)
+ .output()
+ .unwrap();
+ out_code
+}
+
+fn main() {
+ println!("cargo:rerun-if-env-changed=WAYLAND_PROTOCOLS_PATH");
+ let out_dir = env::var("OUT_DIR").unwrap();
+
+ let mut build = cc::Build::new();
+ build.warnings(true);
+ build.warnings_into_errors(true);
+ build.include(&out_dir);
+ build.flag("-std=gnu11");
+ build.file("src/display_wl.c");
+ println!("cargo:rerun-if-changed=src/display_wl.c");
+
+ for protocol in &[
+ "aura-shell",
+ "linux-dmabuf-unstable-v1",
+ "xdg-shell-unstable-v6",
+ "viewporter",
+ ] {
+ build.file(compile_protocol(protocol, &out_dir));
+ }
+ build.compile("display_wl");
+
+ println!("cargo:rustc-link-lib=dylib=wayland-client");
+}
diff --git a/gpu_display/examples/simple.rs b/gpu_display/examples/simple.rs
new file mode 100644
index 0000000..140e6cc
--- /dev/null
+++ b/gpu_display/examples/simple.rs
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use gpu_display::*;
+
+fn main() {
+ let mut disp = GpuDisplay::new("/run/wayland-0").unwrap();
+ let surface_id = disp.create_surface(None, 1280, 1024).unwrap();
+ while !disp.close_requested(surface_id) {
+ disp.dispatch_events();
+ }
+}
diff --git a/gpu_display/protocol/aura-shell.xml b/gpu_display/protocol/aura-shell.xml
new file mode 100644
index 0000000..74189f0
--- /dev/null
+++ b/gpu_display/protocol/aura-shell.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="aura_shell">
+ <copyright>
+ Copyright 2017 The Chromium Authors.
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+ <interface name="zaura_shell" version="6">
+ <description summary="aura_shell">
+ The global interface exposing aura shell capabilities is used to
+ instantiate an interface extension for a wl_surface object.
+ This extended interface will then allow the client to use aura shell
+ specific functionality.
+ </description>
+ <enum name="error">
+ <entry name="aura_surface_exists" value="0"
+ summary="the surface already has an aura surface object associated"/>
+ <entry name="aura_output_exists" value="1"
+ summary="the output already has an aura output object associated"/>
+ </enum>
+ <request name="get_aura_surface">
+ <description summary="extend surface interface for aura shell">
+ Instantiate an interface extension for the given wl_surface to
+ provide aura shell functionality. If the given wl_surface is not
+ associated with a shell surface, the shell_surface_missing protocol
+ error is raised.
+ </description>
+ <arg name="id" type="new_id" interface="zaura_surface"
+ summary="the new aura surface interface id"/>
+ <arg name="surface" type="object" interface="wl_surface"
+ summary="the surface"/>
+ </request>
+ <!-- Version 2 additions -->
+ <request name="get_aura_output" since="2">
+ <description summary="extend output interface for aura shell">
+ Instantiate an interface extension for the given wl_output to
+ provide aura shell functionality.
+ </description>
+ <arg name="id" type="new_id" interface="zaura_output"
+ summary="the new aura output interface id"/>
+ <arg name="output" type="object" interface="wl_output"
+ summary="the output"/>
+ </request>
+ </interface>
+ <interface name="zaura_surface" version="5">
+ <description summary="aura shell interface to a wl_surface">
+ An additional interface to a wl_surface object, which allows the
+ client to access aura shell specific functionality for surface.
+ </description>
+ <enum name="frame_type">
+ <description summary="different frame types">
+ Frame types that can be used to decorate a surface.
+ </description>
+ <entry name="none" value="0" summary="no frame"/>
+ <entry name="normal" value="1" summary="caption with shadow" />
+ <entry name="shadow" value="2" summary="shadow only"/>
+ </enum>
+ <request name="set_frame">
+ <description summary="request a frame for surface">
+ Suggests a surface should use a specific frame.
+ </description>
+ <arg name="type" type="uint" summary="the new frame type"/>
+ </request>
+ <!-- Version 2 additions -->
+ <request name="set_parent" since="2">
+ <description summary="set the parent of this surface">
+ Set the "parent" of this surface. "x" and "y" arguments specify the
+ initial position for surface relative to parent.
+ </description>
+ <arg name="parent" type="object" interface="zaura_surface" allow-null="true"/>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ </request>
+ <!-- Version 3 additions -->
+ <request name="set_frame_colors" since="3">
+ <description summary="set the frame colors of this surface">
+ Set the frame colors.
+ </description>
+ <arg name="active_color" type="uint" summary="32 bit ARGB color value, not premultiplied"/>
+ <arg name="inactive_color" type="uint" summary="32 bit ARGB color value, not premultiplied"/>
+ </request>
+ <!-- Version 4 additions -->
+ <request name="set_startup_id" since="4">
+ <description summary="set the startup ID of this surface">
+ Set the startup ID.
+ </description>
+ <arg name="startup_id" type="string" allow-null="true"/>
+ </request>
+ <!-- Version 5 additions -->
+ <request name="set_application_id" since="5">
+ <description summary="set the application ID of this surface">
+ Set the application ID.
+ </description>
+ <arg name="application_id" type="string" allow-null="true"/>
+ </request>
+ </interface>
+ <interface name="zaura_output" version="6">
+ <description summary="aura shell interface to a wl_output">
+ An additional interface to a wl_output object, which allows the
+ client to access aura shell specific functionality for output.
+ </description>
+ <!-- Version 2 additions -->
+ <enum name="scale_property" bitfield="true">
+ <description summary="scale information">
+ These flags describe properties of an output scale.
+ They are used in the flags bitfield of the scale event.
+ </description>
+ <entry name="current" value="0x1"
+ summary="indicates this is the current scale"/>
+ <entry name="preferred" value="0x2"
+ summary="indicates this is the preferred scale"/>
+ </enum>
+ <enum name="scale_factor">
+ <entry name="0400" value="400"/>
+ <entry name="0500" value="500"/>
+ <entry name="0550" value="550"/>
+ <entry name="0600" value="600"/>
+ <entry name="0625" value="625"/>
+ <entry name="0650" value="650"/>
+ <entry name="0700" value="700"/>
+ <entry name="0750" value="750"/>
+ <entry name="0800" value="800"/>
+ <entry name="0850" value="850"/>
+ <entry name="0900" value="900"/>
+ <entry name="0950" value="950"/>
+ <entry name="1000" value="1000"/>
+ <entry name="1050" value="1050"/>
+ <entry name="1100" value="1100"/>
+ <entry name="1150" value="1150"/>
+ <entry name="1125" value="1125"/>
+ <entry name="1200" value="1200"/>
+ <entry name="1250" value="1250"/>
+ <entry name="1300" value="1300"/>
+ <entry name="1400" value="1400"/>
+ <entry name="1450" value="1450"/>
+ <entry name="1500" value="1500"/>
+ <entry name="1600" value="1600"/>
+ <entry name="1750" value="1750"/>
+ <entry name="1800" value="1800"/>
+ <entry name="2000" value="2000"/>
+ <entry name="2200" value="2200"/>
+ <entry name="2250" value="2250"/>
+ <entry name="2500" value="2500"/>
+ <entry name="2750" value="2750"/>
+ <entry name="3000" value="3000"/>
+ <entry name="3500" value="3500"/>
+ <entry name="4000" value="4000"/>
+ <entry name="4500" value="4500"/>
+ <entry name="5000" value="5000"/>
+ </enum>
+ <event name="scale" since="2">
+ <description summary="advertise available scales for the output">
+ The scale event describes an available scale for the output.
+ The event is sent when binding to the output object and there
+ will always be one scale, the current scale. The event is sent
+ again if an output changes scale, for the scale that is now
+ current. In other words, the current scale is always the last
+ scale that was received with the current flag set.
+ </description>
+ <arg name="flags" type="uint" enum="scale_property" summary="bitfield of scale flags"/>
+ <arg name="scale" type="uint" enum="scale_factor" summary="output scale"/>
+ </event>
+ <!-- Version 5 additions -->
+ <enum name="connection_type">
+ <entry name="unknown" value="0"/>
+ <entry name="internal" value="1"/>
+ </enum>
+ <event name="connection" since="5">
+ <description summary="advertise connection for the output">
+ The connection event describes how the output is connected.
+ The event is sent when binding to the output object.
+ </description>
+ <arg name="connection" type="uint" enum="connection_type" summary="output connection"/>
+ </event>
+ <event name="device_scale_factor" since="5">
+ <description summary="advertise device scale factor for the output">
+ This event describes the device specific scale factor for the output.
+ The device specific scale factor is not expected the change during
+ the lifetime of the output. And it is not limited to an integer value
+ like the scale factor provided by wl_output interface. The exact
+ contents scale used by the compositor can be determined by combining
+ this device scale factor with the current output scale.
+ The event is sent when binding to the output object.
+ </description>
+ <arg name="scale" type="uint" enum="scale_factor" summary="output device scale factor"/>
+ </event>
+ </interface>
+</protocol>
diff --git a/gpu_display/protocol/linux-dmabuf-unstable-v1.xml b/gpu_display/protocol/linux-dmabuf-unstable-v1.xml
new file mode 100644
index 0000000..154afe2
--- /dev/null
+++ b/gpu_display/protocol/linux-dmabuf-unstable-v1.xml
@@ -0,0 +1,348 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="linux_dmabuf_unstable_v1">
+
+ <copyright>
+ Copyright © 2014, 2015 Collabora, Ltd.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="zwp_linux_dmabuf_v1" version="3">
+ <description summary="factory for creating dmabuf-based wl_buffers">
+ Following the interfaces from:
+ https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
+ and the Linux DRM sub-system's AddFb2 ioctl.
+
+ This interface offers ways to create generic dmabuf-based
+ wl_buffers. Immediately after a client binds to this interface,
+ the set of supported formats and format modifiers is sent with
+ 'format' and 'modifier' events.
+
+ The following are required from clients:
+
+ - Clients must ensure that either all data in the dma-buf is
+ coherent for all subsequent read access or that coherency is
+ correctly handled by the underlying kernel-side dma-buf
+ implementation.
+
+ - Don't make any more attachments after sending the buffer to the
+ compositor. Making more attachments later increases the risk of
+ the compositor not being able to use (re-import) an existing
+ dmabuf-based wl_buffer.
+
+ The underlying graphics stack must ensure the following:
+
+ - The dmabuf file descriptors relayed to the server will stay valid
+ for the whole lifetime of the wl_buffer. This means the server may
+ at any time use those fds to import the dmabuf into any kernel
+ sub-system that might accept it.
+
+ To create a wl_buffer from one or more dmabufs, a client creates a
+ zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params
+ request. All planes required by the intended format are added with
+ the 'add' request. Finally, a 'create' or 'create_immed' request is
+ issued, which has the following outcome depending on the import success.
+
+ The 'create' request,
+ - on success, triggers a 'created' event which provides the final
+ wl_buffer to the client.
+ - on failure, triggers a 'failed' event to convey that the server
+ cannot use the dmabufs received from the client.
+
+ For the 'create_immed' request,
+ - on success, the server immediately imports the added dmabufs to
+ create a wl_buffer. No event is sent from the server in this case.
+ - on failure, the server can choose to either:
+ - terminate the client by raising a fatal error.
+ - mark the wl_buffer as failed, and send a 'failed' event to the
+ client. If the client uses a failed wl_buffer as an argument to any
+ request, the behaviour is compositor implementation-defined.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="unbind the factory">
+ Objects created through this interface, especially wl_buffers, will
+ remain valid.
+ </description>
+ </request>
+
+ <request name="create_params">
+ <description summary="create a temporary object for buffer parameters">
+ This temporary object is used to collect multiple dmabuf handles into
+ a single batch to create a wl_buffer. It can only be used once and
+ should be destroyed after a 'created' or 'failed' event has been
+ received.
+ </description>
+ <arg name="params_id" type="new_id" interface="zwp_linux_buffer_params_v1"
+ summary="the new temporary"/>
+ </request>
+
+ <event name="format">
+ <description summary="supported buffer format">
+ This event advertises one buffer format that the server supports.
+ All the supported formats are advertised once when the client
+ binds to this interface. A roundtrip after binding guarantees
+ that the client has received all supported formats.
+
+ For the definition of the format codes, see the
+ zwp_linux_buffer_params_v1::create request.
+
+ Warning: the 'format' event is likely to be deprecated and replaced
+ with the 'modifier' event introduced in zwp_linux_dmabuf_v1
+ version 3, described below. Please refrain from using the information
+ received from this event.
+ </description>
+ <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+ </event>
+
+ <event name="modifier" since="3">
+ <description summary="supported buffer format modifier">
+ This event advertises the formats that the server supports, along with
+ the modifiers supported for each format. All the supported modifiers
+ for all the supported formats are advertised once when the client
+ binds to this interface. A roundtrip after binding guarantees that
+ the client has received all supported format-modifier pairs.
+
+ For the definition of the format and modifier codes, see the
+ zwp_linux_buffer_params_v1::create request.
+ </description>
+ <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+ <arg name="modifier_hi" type="uint"
+ summary="high 32 bits of layout modifier"/>
+ <arg name="modifier_lo" type="uint"
+ summary="low 32 bits of layout modifier"/>
+ </event>
+ </interface>
+
+ <interface name="zwp_linux_buffer_params_v1" version="3">
+ <description summary="parameters for creating a dmabuf-based wl_buffer">
+ This temporary object is a collection of dmabufs and other
+ parameters that together form a single logical buffer. The temporary
+ object may eventually create one wl_buffer unless cancelled by
+ destroying it before requesting 'create'.
+
+ Single-planar formats only require one dmabuf, however
+ multi-planar formats may require more than one dmabuf. For all
+ formats, an 'add' request must be called once per plane (even if the
+ underlying dmabuf fd is identical).
+
+ You must use consecutive plane indices ('plane_idx' argument for 'add')
+ from zero to the number of planes used by the drm_fourcc format code.
+ All planes required by the format must be given exactly once, but can
+ be given in any order. Each plane index can be set only once.
+ </description>
+
+ <enum name="error">
+ <entry name="already_used" value="0"
+ summary="the dmabuf_batch object has already been used to create a wl_buffer"/>
+ <entry name="plane_idx" value="1"
+ summary="plane index out of bounds"/>
+ <entry name="plane_set" value="2"
+ summary="the plane index was already set"/>
+ <entry name="incomplete" value="3"
+ summary="missing or too many planes to create a buffer"/>
+ <entry name="invalid_format" value="4"
+ summary="format not supported"/>
+ <entry name="invalid_dimensions" value="5"
+ summary="invalid width or height"/>
+ <entry name="out_of_bounds" value="6"
+ summary="offset + stride * height goes out of dmabuf bounds"/>
+ <entry name="invalid_wl_buffer" value="7"
+ summary="invalid wl_buffer resulted from importing dmabufs via
+ the create_immed request on given buffer_params"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="delete this object, used or not">
+ Cleans up the temporary data sent to the server for dmabuf-based
+ wl_buffer creation.
+ </description>
+ </request>
+
+ <request name="add">
+ <description summary="add a dmabuf to the temporary set">
+ This request adds one dmabuf to the set in this
+ zwp_linux_buffer_params_v1.
+
+ The 64-bit unsigned value combined from modifier_hi and modifier_lo
+ is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the
+ fb modifier, which is defined in drm_mode.h of Linux UAPI.
+ This is an opaque token. Drivers use this token to express tiling,
+ compression, etc. driver-specific modifications to the base format
+ defined by the DRM fourcc code.
+
+ This request raises the PLANE_IDX error if plane_idx is too large.
+ The error PLANE_SET is raised if attempting to set a plane that
+ was already set.
+ </description>
+ <arg name="fd" type="fd" summary="dmabuf fd"/>
+ <arg name="plane_idx" type="uint" summary="plane index"/>
+ <arg name="offset" type="uint" summary="offset in bytes"/>
+ <arg name="stride" type="uint" summary="stride in bytes"/>
+ <arg name="modifier_hi" type="uint"
+ summary="high 32 bits of layout modifier"/>
+ <arg name="modifier_lo" type="uint"
+ summary="low 32 bits of layout modifier"/>
+ </request>
+
+ <enum name="flags">
+ <entry name="y_invert" value="1" summary="contents are y-inverted"/>
+ <entry name="interlaced" value="2" summary="content is interlaced"/>
+ <entry name="bottom_first" value="4" summary="bottom field first"/>
+ </enum>
+
+ <request name="create">
+ <description summary="create a wl_buffer from the given dmabufs">
+ This asks for creation of a wl_buffer from the added dmabuf
+ buffers. The wl_buffer is not created immediately but returned via
+ the 'created' event if the dmabuf sharing succeeds. The sharing
+ may fail at runtime for reasons a client cannot predict, in
+ which case the 'failed' event is triggered.
+
+ The 'format' argument is a DRM_FORMAT code, as defined by the
+ libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the
+ authoritative source on how the format codes should work.
+
+ The 'flags' is a bitfield of the flags defined in enum "flags".
+ 'y_invert' means the that the image needs to be y-flipped.
+
+ Flag 'interlaced' means that the frame in the buffer is not
+ progressive as usual, but interlaced. An interlaced buffer as
+ supported here must always contain both top and bottom fields.
+ The top field always begins on the first pixel row. The temporal
+ ordering between the two fields is top field first, unless
+ 'bottom_first' is specified. It is undefined whether 'bottom_first'
+ is ignored if 'interlaced' is not set.
+
+ This protocol does not convey any information about field rate,
+ duration, or timing, other than the relative ordering between the
+ two fields in one buffer. A compositor may have to estimate the
+ intended field rate from the incoming buffer rate. It is undefined
+ whether the time of receiving wl_surface.commit with a new buffer
+ attached, applying the wl_surface state, wl_surface.frame callback
+ trigger, presentation, or any other point in the compositor cycle
+ is used to measure the frame or field times. There is no support
+ for detecting missed or late frames/fields/buffers either, and
+ there is no support whatsoever for cooperating with interlaced
+ compositor output.
+
+ The composited image quality resulting from the use of interlaced
+ buffers is explicitly undefined. A compositor may use elaborate
+ hardware features or software to deinterlace and create progressive
+ output frames from a sequence of interlaced input buffers, or it
+ may produce substandard image quality. However, compositors that
+ cannot guarantee reasonable image quality in all cases are recommended
+ to just reject all interlaced buffers.
+
+ Any argument errors, including non-positive width or height,
+ mismatch between the number of planes and the format, bad
+ format, bad offset or stride, may be indicated by fatal protocol
+ errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS,
+ OUT_OF_BOUNDS.
+
+ Dmabuf import errors in the server that are not obvious client
+ bugs are returned via the 'failed' event as non-fatal. This
+ allows attempting dmabuf sharing and falling back in the client
+ if it fails.
+
+ This request can be sent only once in the object's lifetime, after
+ which the only legal request is destroy. This object should be
+ destroyed after issuing a 'create' request. Attempting to use this
+ object after issuing 'create' raises ALREADY_USED protocol error.
+
+ It is not mandatory to issue 'create'. If a client wants to
+ cancel the buffer creation, it can just destroy this object.
+ </description>
+ <arg name="width" type="int" summary="base plane width in pixels"/>
+ <arg name="height" type="int" summary="base plane height in pixels"/>
+ <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+ <arg name="flags" type="uint" summary="see enum flags"/>
+ </request>
+
+ <event name="created">
+ <description summary="buffer creation succeeded">
+ This event indicates that the attempted buffer creation was
+ successful. It provides the new wl_buffer referencing the dmabuf(s).
+
+ Upon receiving this event, the client should destroy the
+ zlinux_dmabuf_params object.
+ </description>
+ <arg name="buffer" type="new_id" interface="wl_buffer"
+ summary="the newly created wl_buffer"/>
+ </event>
+
+ <event name="failed">
+ <description summary="buffer creation failed">
+ This event indicates that the attempted buffer creation has
+ failed. It usually means that one of the dmabuf constraints
+ has not been fulfilled.
+
+ Upon receiving this event, the client should destroy the
+ zlinux_buffer_params object.
+ </description>
+ </event>
+
+ <request name="create_immed" since="2">
+ <description summary="immediately create a wl_buffer from the given
+ dmabufs">
+ This asks for immediate creation of a wl_buffer by importing the
+ added dmabufs.
+
+ In case of import success, no event is sent from the server, and the
+ wl_buffer is ready to be used by the client.
+
+ Upon import failure, either of the following may happen, as seen fit
+ by the implementation:
+ - the client is terminated with one of the following fatal protocol
+ errors:
+ - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS,
+ in case of argument errors such as mismatch between the number
+ of planes and the format, bad format, non-positive width or
+ height, or bad offset or stride.
+ - INVALID_WL_BUFFER, in case the cause for failure is unknown or
+ plaform specific.
+ - the server creates an invalid wl_buffer, marks it as failed and
+ sends a 'failed' event to the client. The result of using this
+ invalid wl_buffer as an argument in any request by the client is
+ defined by the compositor implementation.
+
+ This takes the same arguments as a 'create' request, and obeys the
+ same restrictions.
+ </description>
+ <arg name="buffer_id" type="new_id" interface="wl_buffer"
+ summary="id for the newly created wl_buffer"/>
+ <arg name="width" type="int" summary="base plane width in pixels"/>
+ <arg name="height" type="int" summary="base plane height in pixels"/>
+ <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+ <arg name="flags" type="uint" summary="see enum flags"/>
+ </request>
+
+ </interface>
+
+</protocol>
diff --git a/gpu_display/protocol/viewporter.xml b/gpu_display/protocol/viewporter.xml
new file mode 100644
index 0000000..c732d8c
--- /dev/null
+++ b/gpu_display/protocol/viewporter.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="viewporter">
+
+ <copyright>
+ Copyright © 2013-2016 Collabora, Ltd.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="wp_viewporter" version="1">
+ <description summary="surface cropping and scaling">
+ The global interface exposing surface cropping and scaling
+ capabilities is used to instantiate an interface extension for a
+ wl_surface object. This extended interface will then allow
+ cropping and scaling the surface contents, effectively
+ disconnecting the direct relationship between the buffer and the
+ surface size.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="unbind from the cropping and scaling interface">
+ Informs the server that the client will not be using this
+ protocol object anymore. This does not affect any other objects,
+ wp_viewport objects included.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="viewport_exists" value="0"
+ summary="the surface already has a viewport object associated"/>
+ </enum>
+
+ <request name="get_viewport">
+ <description summary="extend surface interface for crop and scale">
+ Instantiate an interface extension for the given wl_surface to
+ crop and scale its content. If the given wl_surface already has
+ a wp_viewport object associated, the viewport_exists
+ protocol error is raised.
+ </description>
+ <arg name="id" type="new_id" interface="wp_viewport"
+ summary="the new viewport interface id"/>
+ <arg name="surface" type="object" interface="wl_surface"
+ summary="the surface"/>
+ </request>
+ </interface>
+
+ <interface name="wp_viewport" version="1">
+ <description summary="crop and scale interface to a wl_surface">
+ An additional interface to a wl_surface object, which allows the
+ client to specify the cropping and scaling of the surface
+ contents.
+
+ This interface works with two concepts: the source rectangle (src_x,
+ src_y, src_width, src_height), and the destination size (dst_width,
+ dst_height). The contents of the source rectangle are scaled to the
+ destination size, and content outside the source rectangle is ignored.
+ This state is double-buffered, and is applied on the next
+ wl_surface.commit.
+
+ The two parts of crop and scale state are independent: the source
+ rectangle, and the destination size. Initially both are unset, that
+ is, no scaling is applied. The whole of the current wl_buffer is
+ used as the source, and the surface size is as defined in
+ wl_surface.attach.
+
+ If the destination size is set, it causes the surface size to become
+ dst_width, dst_height. The source (rectangle) is scaled to exactly
+ this size. This overrides whatever the attached wl_buffer size is,
+ unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
+ has no content and therefore no size. Otherwise, the size is always
+ at least 1x1 in surface local coordinates.
+
+ If the source rectangle is set, it defines what area of the wl_buffer is
+ taken as the source. If the source rectangle is set and the destination
+ size is not set, then src_width and src_height must be integers, and the
+ surface size becomes the source rectangle size. This results in cropping
+ without scaling. If src_width or src_height are not integers and
+ destination size is not set, the bad_size protocol error is raised when
+ the surface state is applied.
+
+ The coordinate transformations from buffer pixel coordinates up to
+ the surface-local coordinates happen in the following order:
+ 1. buffer_transform (wl_surface.set_buffer_transform)
+ 2. buffer_scale (wl_surface.set_buffer_scale)
+ 3. crop and scale (wp_viewport.set*)
+ This means, that the source rectangle coordinates of crop and scale
+ are given in the coordinates after the buffer transform and scale,
+ i.e. in the coordinates that would be the surface-local coordinates
+ if the crop and scale was not applied.
+
+ If src_x or src_y are negative, the bad_value protocol error is raised.
+ Otherwise, if the source rectangle is partially or completely outside of
+ the non-NULL wl_buffer, then the out_of_buffer protocol error is raised
+ when the surface state is applied. A NULL wl_buffer does not raise the
+ out_of_buffer error.
+
+ The x, y arguments of wl_surface.attach are applied as normal to
+ the surface. They indicate how many pixels to remove from the
+ surface size from the left and the top. In other words, they are
+ still in the surface-local coordinate system, just like dst_width
+ and dst_height are.
+
+ If the wl_surface associated with the wp_viewport is destroyed,
+ all wp_viewport requests except 'destroy' raise the protocol error
+ no_surface.
+
+ If the wp_viewport object is destroyed, the crop and scale
+ state is removed from the wl_surface. The change will be applied
+ on the next wl_surface.commit.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="remove scaling and cropping from the surface">
+ The associated wl_surface's crop and scale state is removed.
+ The change is applied on the next wl_surface.commit.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="bad_value" value="0"
+ summary="negative or zero values in width or height"/>
+ <entry name="bad_size" value="1"
+ summary="destination size is not integer"/>
+ <entry name="out_of_buffer" value="2"
+ summary="source rectangle extends outside of the content area"/>
+ <entry name="no_surface" value="3"
+ summary="the wl_surface was destroyed"/>
+ </enum>
+
+ <request name="set_source">
+ <description summary="set the source rectangle for cropping">
+ Set the source rectangle of the associated wl_surface. See
+ wp_viewport for the description, and relation to the wl_buffer
+ size.
+
+ If all of x, y, width and height are -1.0, the source rectangle is
+ unset instead. Any other set of values where width or height are zero
+ or negative, or x or y are negative, raise the bad_value protocol
+ error.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+ </description>
+ <arg name="x" type="fixed" summary="source rectangle x"/>
+ <arg name="y" type="fixed" summary="source rectangle y"/>
+ <arg name="width" type="fixed" summary="source rectangle width"/>
+ <arg name="height" type="fixed" summary="source rectangle height"/>
+ </request>
+
+ <request name="set_destination">
+ <description summary="set the surface size for scaling">
+ Set the destination size of the associated wl_surface. See
+ wp_viewport for the description, and relation to the wl_buffer
+ size.
+
+ If width is -1 and height is -1, the destination size is unset
+ instead. Any other pair of values for width and height that
+ contains zero or negative values raises the bad_value protocol
+ error.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+ </description>
+ <arg name="width" type="int" summary="surface width"/>
+ <arg name="height" type="int" summary="surface height"/>
+ </request>
+ </interface>
+
+</protocol>
diff --git a/gpu_display/protocol/xdg-shell-unstable-v6.xml b/gpu_display/protocol/xdg-shell-unstable-v6.xml
new file mode 100644
index 0000000..1c0f924
--- /dev/null
+++ b/gpu_display/protocol/xdg-shell-unstable-v6.xml
@@ -0,0 +1,1044 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="xdg_shell_unstable_v6">
+
+ <copyright>
+ Copyright © 2008-2013 Kristian Høgsberg
+ Copyright © 2013 Rafael Antognolli
+ Copyright © 2013 Jasper St. Pierre
+ Copyright © 2010-2013 Intel Corporation
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="zxdg_shell_v6" version="1">
+ <description summary="create desktop-style surfaces">
+ xdg_shell allows clients to turn a wl_surface into a "real window"
+ which can be dragged, resized, stacked, and moved around by the
+ user. Everything about this interface is suited towards traditional
+ desktop environments.
+ </description>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="given wl_surface has another role"/>
+ <entry name="defunct_surfaces" value="1"
+ summary="xdg_shell was destroyed before children"/>
+ <entry name="not_the_topmost_popup" value="2"
+ summary="the client tried to map or destroy a non-topmost popup"/>
+ <entry name="invalid_popup_parent" value="3"
+ summary="the client specified an invalid popup parent surface"/>
+ <entry name="invalid_surface_state" value="4"
+ summary="the client provided an invalid surface state"/>
+ <entry name="invalid_positioner" value="5"
+ summary="the client provided an invalid positioner"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy xdg_shell">
+ Destroy this xdg_shell object.
+
+ Destroying a bound xdg_shell object while there are surfaces
+ still alive created by this xdg_shell object instance is illegal
+ and will result in a protocol error.
+ </description>
+ </request>
+
+ <request name="create_positioner">
+ <description summary="create a positioner object">
+ Create a positioner object. A positioner object is used to position
+ surfaces relative to some parent surface. See the interface description
+ and xdg_surface.get_popup for details.
+ </description>
+ <arg name="id" type="new_id" interface="zxdg_positioner_v6"/>
+ </request>
+
+ <request name="get_xdg_surface">
+ <description summary="create a shell surface from a surface">
+ This creates an xdg_surface for the given surface. While xdg_surface
+ itself is not a role, the corresponding surface may only be assigned
+ a role extending xdg_surface, such as xdg_toplevel or xdg_popup.
+
+ This creates an xdg_surface for the given surface. An xdg_surface is
+ used as basis to define a role to a given surface, such as xdg_toplevel
+ or xdg_popup. It also manages functionality shared between xdg_surface
+ based surface roles.
+
+ See the documentation of xdg_surface for more details about what an
+ xdg_surface is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="zxdg_surface_v6"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="pong">
+ <description summary="respond to a ping event">
+ A client must respond to a ping event with a pong request or
+ the client may be deemed unresponsive. See xdg_shell.ping.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the ping event"/>
+ </request>
+
+ <event name="ping">
+ <description summary="check if the client is alive">
+ The ping event asks the client if it's still alive. Pass the
+ serial specified in the event back to the compositor by sending
+ a "pong" request back with the specified serial. See xdg_shell.ping.
+
+ Compositors can use this to determine if the client is still
+ alive. It's unspecified what will happen if the client doesn't
+ respond to the ping request, or in what timeframe. Clients should
+ try to respond in a reasonable amount of time.
+
+ A compositor is free to ping in any way it wants, but a client must
+ always respond to any xdg_shell object it created.
+ </description>
+ <arg name="serial" type="uint" summary="pass this to the pong request"/>
+ </event>
+ </interface>
+
+ <interface name="zxdg_positioner_v6" version="1">
+ <description summary="child surface positioner">
+ The xdg_positioner provides a collection of rules for the placement of a
+ child surface relative to a parent surface. Rules can be defined to ensure
+ the child surface remains within the visible area's borders, and to
+ specify how the child surface changes its position, such as sliding along
+ an axis, or flipping around a rectangle. These positioner-created rules are
+ constrained by the requirement that a child surface must intersect with or
+ be at least partially adjacent to its parent surface.
+
+ See the various requests for details about possible rules.
+
+ At the time of the request, the compositor makes a copy of the rules
+ specified by the xdg_positioner. Thus, after the request is complete the
+ xdg_positioner object can be destroyed or reused; further changes to the
+ object will have no effect on previous usages.
+
+ For an xdg_positioner object to be considered complete, it must have a
+ non-zero size set by set_size, and a non-zero anchor rectangle set by
+ set_anchor_rect. Passing an incomplete xdg_positioner object when
+ positioning a surface raises an error.
+ </description>
+
+ <enum name="error">
+ <entry name="invalid_input" value="0" summary="invalid input provided"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_positioner object">
+ Notify the compositor that the xdg_positioner will no longer be used.
+ </description>
+ </request>
+
+ <request name="set_size">
+ <description summary="set the size of the to-be positioned rectangle">
+ Set the size of the surface that is to be positioned with the positioner
+ object. The size is in surface-local coordinates and corresponds to the
+ window geometry. See xdg_surface.set_window_geometry.
+
+ If a zero or negative size is set the invalid_input error is raised.
+ </description>
+ <arg name="width" type="int" summary="width of positioned rectangle"/>
+ <arg name="height" type="int" summary="height of positioned rectangle"/>
+ </request>
+
+ <request name="set_anchor_rect">
+ <description summary="set the anchor rectangle within the parent surface">
+ Specify the anchor rectangle within the parent surface that the child
+ surface will be placed relative to. The rectangle is relative to the
+ window geometry as defined by xdg_surface.set_window_geometry of the
+ parent surface. The rectangle must be at least 1x1 large.
+
+ When the xdg_positioner object is used to position a child surface, the
+ anchor rectangle may not extend outside the window geometry of the
+ positioned child's parent surface.
+
+ If a zero or negative size is set the invalid_input error is raised.
+ </description>
+ <arg name="x" type="int" summary="x position of anchor rectangle"/>
+ <arg name="y" type="int" summary="y position of anchor rectangle"/>
+ <arg name="width" type="int" summary="width of anchor rectangle"/>
+ <arg name="height" type="int" summary="height of anchor rectangle"/>
+ </request>
+
+ <enum name="anchor" bitfield="true">
+ <entry name="none" value="0"
+ summary="the center of the anchor rectangle"/>
+ <entry name="top" value="1"
+ summary="the top edge of the anchor rectangle"/>
+ <entry name="bottom" value="2"
+ summary="the bottom edge of the anchor rectangle"/>
+ <entry name="left" value="4"
+ summary="the left edge of the anchor rectangle"/>
+ <entry name="right" value="8"
+ summary="the right edge of the anchor rectangle"/>
+ </enum>
+
+ <request name="set_anchor">
+ <description summary="set anchor rectangle anchor edges">
+ Defines a set of edges for the anchor rectangle. These are used to
+ derive an anchor point that the child surface will be positioned
+ relative to. If two orthogonal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left position of the rectangle); otherwise, the derived
+ anchor point will be centered on the specified edge, or in the center of
+ the anchor rectangle if no edge is specified.
+
+ If two parallel anchor edges are specified (e.g. 'left' and 'right'),
+ the invalid_input error is raised.
+ </description>
+ <arg name="anchor" type="uint" enum="anchor"
+ summary="bit mask of anchor edges"/>
+ </request>
+
+ <enum name="gravity" bitfield="true">
+ <entry name="none" value="0"
+ summary="center over the anchor edge"/>
+ <entry name="top" value="1"
+ summary="position above the anchor edge"/>
+ <entry name="bottom" value="2"
+ summary="position below the anchor edge"/>
+ <entry name="left" value="4"
+ summary="position to the left of the anchor edge"/>
+ <entry name="right" value="8"
+ summary="position to the right of the anchor edge"/>
+ </enum>
+
+ <request name="set_gravity">
+ <description summary="set child surface gravity">
+ Defines in what direction a surface should be positioned, relative to
+ the anchor point of the parent surface. If two orthogonal gravities are
+ specified (e.g. 'bottom' and 'right'), then the child surface will be
+ placed in the specified direction; otherwise, the child surface will be
+ centered over the anchor point on any axis that had no gravity
+ specified.
+
+ If two parallel gravities are specified (e.g. 'left' and 'right'), the
+ invalid_input error is raised.
+ </description>
+ <arg name="gravity" type="uint" enum="gravity"
+ summary="bit mask of gravity directions"/>
+ </request>
+
+ <enum name="constraint_adjustment" bitfield="true">
+ <description summary="constraint adjustments">
+ The constraint adjustment value define ways the compositor will adjust
+ the position of the surface, if the unadjusted position would result
+ in the surface being partly constrained.
+
+ Whether a surface is considered 'constrained' is left to the compositor
+ to determine. For example, the surface may be partly outside the
+ compositor's defined 'work area', thus necessitating the child surface's
+ position be adjusted until it is entirely inside the work area.
+
+ The adjustments can be combined, according to a defined precedence: 1)
+ Flip, 2) Slide, 3) Resize.
+ </description>
+ <entry name="none" value="0">
+ <description summary="don't move the child surface when constrained">
+ Don't alter the surface position even if it is constrained on some
+ axis, for example partially outside the edge of a monitor.
+ </description>
+ </entry>
+ <entry name="slide_x" value="1">
+ <description summary="move along the x axis until unconstrained">
+ Slide the surface along the x axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the x axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ x axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+ </description>
+ </entry>
+ <entry name="slide_y" value="2">
+ <description summary="move along the y axis until unconstrained">
+ Slide the surface along the y axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the y axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ y axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+ </description>
+ </entry>
+ <entry name="flip_x" value="4">
+ <description summary="invert the anchor and gravity on the x axis">
+ Invert the anchor and gravity on the x axis if the surface is
+ constrained on the x axis. For example, if the left edge of the
+ surface is constrained, the gravity is 'left' and the anchor is
+ 'left', change the gravity to 'right' and the anchor to 'right'.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_x adjustment will be the one before the
+ adjustment.
+ </description>
+ </entry>
+ <entry name="flip_y" value="8">
+ <description summary="invert the anchor and gravity on the y axis">
+ Invert the anchor and gravity on the y axis if the surface is
+ constrained on the y axis. For example, if the bottom edge of the
+ surface is constrained, the gravity is 'bottom' and the anchor is
+ 'bottom', change the gravity to 'top' and the anchor to 'top'.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_y adjustment will be the one before the
+ adjustment.
+ </description>
+ </entry>
+ <entry name="resize_x" value="16">
+ <description summary="horizontally resize the surface">
+ Resize the surface horizontally so that it is completely
+ unconstrained.
+ </description>
+ </entry>
+ <entry name="resize_y" value="32">
+ <description summary="vertically resize the surface">
+ Resize the surface vertically so that it is completely unconstrained.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_constraint_adjustment">
+ <description summary="set the adjustment to be done when constrained">
+ Specify how the window should be positioned if the originally intended
+ position caused the surface to be constrained, meaning at least
+ partially outside positioning boundaries set by the compositor. The
+ adjustment is set by constructing a bitmask describing the adjustment to
+ be made when the surface is constrained on that axis.
+
+ If no bit for one axis is set, the compositor will assume that the child
+ surface should not change its position on that axis when constrained.
+
+ If more than one bit for one axis is set, the order of how adjustments
+ are applied is specified in the corresponding adjustment descriptions.
+
+ The default adjustment is none.
+ </description>
+ <arg name="constraint_adjustment" type="uint"
+ summary="bit mask of constraint adjustments"/>
+ </request>
+
+ <request name="set_offset">
+ <description summary="set surface position offset">
+ Specify the surface position offset relative to the position of the
+ anchor on the anchor rectangle and the anchor on the surface. For
+ example if the anchor of the anchor rectangle is at (x, y), the surface
+ has the gravity bottom|right, and the offset is (ox, oy), the calculated
+ surface position will be (x + ox, y + oy). The offset position of the
+ surface is the one used for constraint testing. See
+ set_constraint_adjustment.
+
+ An example use case is placing a popup menu on top of a user interface
+ element, while aligning the user interface element of the parent surface
+ with some user interface element placed somewhere in the popup surface.
+ </description>
+ <arg name="x" type="int" summary="surface position x offset"/>
+ <arg name="y" type="int" summary="surface position y offset"/>
+ </request>
+ </interface>
+
+ <interface name="zxdg_surface_v6" version="1">
+ <description summary="desktop user interface surface base interface">
+ An interface that may be implemented by a wl_surface, for
+ implementations that provide a desktop-style user interface.
+
+ It provides a base set of functionality required to construct user
+ interface elements requiring management by the compositor, such as
+ toplevel windows, menus, etc. The types of functionality are split into
+ xdg_surface roles.
+
+ Creating an xdg_surface does not set the role for a wl_surface. In order
+ to map an xdg_surface, the client must create a role-specific object
+ using, e.g., get_toplevel, get_popup. The wl_surface for any given
+ xdg_surface can have at most one role, and may not be assigned any role
+ not based on xdg_surface.
+
+ A role must be assigned before any other requests are made to the
+ xdg_surface object.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_surface state to take effect.
+
+ Creating an xdg_surface from a wl_surface which has a buffer attached or
+ committed is a client error, and any attempts by a client to attach or
+ manipulate a buffer prior to the first xdg_surface.configure call must
+ also be treated as errors.
+
+ For a surface to be mapped by the compositor, the following conditions
+ must be met: (1) the client has assigned a xdg_surface based role to the
+ surface, (2) the client has set and committed the xdg_surface state and
+ the role dependent state to the surface and (3) the client has committed a
+ buffer to the surface.
+ </description>
+
+ <enum name="error">
+ <entry name="not_constructed" value="1"/>
+ <entry name="already_constructed" value="2"/>
+ <entry name="unconfigured_buffer" value="3"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_surface">
+ Destroy the xdg_surface object. An xdg_surface must only be destroyed
+ after its role object has been destroyed.
+ </description>
+ </request>
+
+ <request name="get_toplevel">
+ <description summary="assign the xdg_toplevel surface role">
+ This creates an xdg_toplevel object for the given xdg_surface and gives
+ the associated wl_surface the xdg_toplevel role.
+
+ See the documentation of xdg_toplevel for more details about what an
+ xdg_toplevel is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="zxdg_toplevel_v6"/>
+ </request>
+
+ <request name="get_popup">
+ <description summary="assign the xdg_popup surface role">
+ This creates an xdg_popup object for the given xdg_surface and gives the
+ associated wl_surface the xdg_popup role.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="zxdg_popup_v6"/>
+ <arg name="parent" type="object" interface="zxdg_surface_v6"/>
+ <arg name="positioner" type="object" interface="zxdg_positioner_v6"/>
+ </request>
+
+ <request name="set_window_geometry">
+ <description summary="set the new window geometry">
+ The window geometry of a surface is its "visible bounds" from the
+ user's perspective. Client-side decorations often have invisible
+ portions like drop-shadows which should be ignored for the
+ purposes of aligning, placing and constraining windows.
+
+ The window geometry is double buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ Once the window geometry of the surface is set, it is not possible to
+ unset it, and it will remain the same until set_window_geometry is
+ called again, even if a new subsurface or buffer is attached.
+
+ If never set, the value is the full bounds of the surface,
+ including any subsurfaces. This updates dynamically on every
+ commit. This unset is meant for extremely simple clients.
+
+ The arguments are given in the surface-local coordinate space of
+ the wl_surface associated with this xdg_surface.
+
+ The width and height must be greater than zero. Setting an invalid size
+ will raise an error. When applied, the effective window geometry will be
+ the set window geometry clamped to the bounding rectangle of the
+ combined geometry of the surface of the xdg_surface and the associated
+ subsurfaces.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="ack a configure event">
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ For instance, for toplevel surfaces the compositor might use this
+ information to move a surface to the top left only when the client has
+ drawn itself for the maximized or fullscreen state.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+ </description>
+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ The configure event marks the end of a configure sequence. A configure
+ sequence is a set of one or more events configuring the state of the
+ xdg_surface, including the final xdg_surface.configure event.
+
+ Where applicable, xdg_surface surface roles will during a configure
+ sequence extend this event as a latched state sent as events before the
+ xdg_surface.configure event. Such events should be considered to make up
+ a set of atomically applied configuration states, where the
+ xdg_surface.configure commits the accumulated state.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ If the client receives multiple configure events before it can respond
+ to one, it is free to discard all but the last event it received.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the configure event"/>
+ </event>
+ </interface>
+
+ <interface name="zxdg_toplevel_v6" version="1">
+ <description summary="toplevel surface">
+ This interface defines an xdg_surface role which allows a surface to,
+ among other things, set window-like properties such as maximize,
+ fullscreen, and minimize, set application-specific metadata like title and
+ id, and well as trigger user interactive operations such as interactive
+ resize and move.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_toplevel">
+ Unmap and destroy the window. The window will be effectively
+ hidden from the user's point of view, and all state like
+ maximization, fullscreen, and so on, will be lost.
+ </description>
+ </request>
+
+ <request name="set_parent">
+ <description summary="set the parent of this surface">
+ Set the "parent" of this surface. This window should be stacked
+ above a parent. The parent surface must be mapped as long as this
+ surface is mapped.
+
+ Parent windows should be set on dialogs, toolboxes, or other
+ "auxiliary" surfaces, so that the parent is raised when the dialog
+ is raised.
+ </description>
+ <arg name="parent" type="object" interface="zxdg_toplevel_v6" allow-null="true"/>
+ </request>
+
+ <request name="set_title">
+ <description summary="set surface title">
+ Set a short title for the surface.
+
+ This string may be used to identify the surface in a task bar,
+ window list, or other user interface elements provided by the
+ compositor.
+
+ The string must be encoded in UTF-8.
+ </description>
+ <arg name="title" type="string"/>
+ </request>
+
+ <request name="set_app_id">
+ <description summary="set application ID">
+ Set an application identifier for the surface.
+
+ The app ID identifies the general class of applications to which
+ the surface belongs. The compositor can use this to group multiple
+ surfaces together, or to determine how to launch a new application.
+
+ For D-Bus activatable applications, the app ID is used as the D-Bus
+ service name.
+
+ The compositor shell will try to group application surfaces together
+ by their app ID. As a best practice, it is suggested to select app
+ ID's that match the basename of the application's .desktop file.
+ For example, "org.freedesktop.FooViewer" where the .desktop file is
+ "org.freedesktop.FooViewer.desktop".
+
+ See the desktop-entry specification [0] for more details on
+ application identifiers and how they relate to well-known D-Bus
+ names and .desktop files.
+
+ [0] http://standards.freedesktop.org/desktop-entry-spec/
+ </description>
+ <arg name="app_id" type="string"/>
+ </request>
+
+ <request name="show_window_menu">
+ <description summary="show the window menu">
+ Clients implementing client-side decorations might want to show
+ a context menu when right-clicking on the decorations, giving the
+ user a menu that they can use to maximize or minimize the window.
+
+ This request asks the compositor to pop up such a window menu at
+ the given position, relative to the local surface coordinates of
+ the parent surface. There are no guarantees as to what menu items
+ the window menu contains.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
+ <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
+ </request>
+
+ <request name="move">
+ <description summary="start an interactive move">
+ Start an interactive, user-driven move of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive move (touch,
+ pointer, etc).
+
+ The server may ignore move requests depending on the state of
+ the surface (e.g. fullscreen or maximized), or if the passed serial
+ is no longer valid.
+
+ If triggered, the surface will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the move. It is up to the
+ compositor to visually indicate that the move is taking place, such as
+ updating a pointer cursor, during the move. There is no guarantee
+ that the device focus will return when the move is completed.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ </request>
+
+ <enum name="resize_edge">
+ <description summary="edge values for resizing">
+ These values are used to indicate which edge of a surface
+ is being dragged in a resize operation.
+ </description>
+ <entry name="none" value="0"/>
+ <entry name="top" value="1"/>
+ <entry name="bottom" value="2"/>
+ <entry name="left" value="4"/>
+ <entry name="top_left" value="5"/>
+ <entry name="bottom_left" value="6"/>
+ <entry name="right" value="8"/>
+ <entry name="top_right" value="9"/>
+ <entry name="bottom_right" value="10"/>
+ </enum>
+
+ <request name="resize">
+ <description summary="start an interactive resize">
+ Start a user-driven, interactive resize of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive resize (touch,
+ pointer, etc).
+
+ The server may ignore resize requests depending on the state of
+ the surface (e.g. fullscreen or maximized).
+
+ If triggered, the client will receive configure events with the
+ "resize" state enum value and the expected sizes. See the "resize"
+ enum value for more details about what is required. The client
+ must also acknowledge configure events using "ack_configure". After
+ the resize is completed, the client will receive another "configure"
+ event without the resize state.
+
+ If triggered, the surface also will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the resize. It is up to the
+ compositor to visually indicate that the resize is taking place,
+ such as updating a pointer cursor, during the resize. There is no
+ guarantee that the device focus will return when the resize is
+ completed.
+
+ The edges parameter specifies how the surface should be resized,
+ and is one of the values of the resize_edge enum. The compositor
+ may use this information to update the surface position for
+ example when dragging the top left corner. The compositor may also
+ use this information to adapt its behavior, e.g. choose an
+ appropriate cursor image.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ <arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
+ </request>
+
+ <enum name="state">
+ <description summary="types of state on the surface">
+ The different state values used on the surface. This is designed for
+ state values like maximized, fullscreen. It is paired with the
+ configure event to ensure that both the client and the compositor
+ setting the state can be synchronized.
+
+ States set in this way are double-buffered. They will get applied on
+ the next commit.
+ </description>
+ <entry name="maximized" value="1" summary="the surface is maximized">
+ <description summary="the surface is maximized">
+ The surface is maximized. The window geometry specified in the configure
+ event must be obeyed by the client.
+ </description>
+ </entry>
+ <entry name="fullscreen" value="2" summary="the surface is fullscreen">
+ <description summary="the surface is fullscreen">
+ The surface is fullscreen. The window geometry specified in the configure
+ event must be obeyed by the client.
+ </description>
+ </entry>
+ <entry name="resizing" value="3" summary="the surface is being resized">
+ <description summary="the surface is being resized">
+ The surface is being resized. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it.
+ Clients that have aspect ratio or cell sizing configuration can use
+ a smaller size, however.
+ </description>
+ </entry>
+ <entry name="activated" value="4" summary="the surface is now activated">
+ <description summary="the surface is now activated">
+ Client window decorations should be painted as if the window is
+ active. Do not assume this means that the window actually has
+ keyboard or pointer focus.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_max_size">
+ <description summary="set the maximum size">
+ Set a maximum size for the window.
+
+ The client can specify a maximum size so that the compositor does
+ not try to configure the window beyond this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered. They will get applied
+ on the next commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the maximum
+ size. The compositor may decide to ignore the values set by the
+ client and request a larger size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected maximum size in the given dimension.
+ As a result, a client wishing to reset the maximum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a maximum size to be smaller than the minimum size of
+ a surface is illegal and will result in a protocol error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width and height will result in a
+ protocol error.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="set_min_size">
+ <description summary="set the minimum size">
+ Set a minimum size for the window.
+
+ The client can specify a minimum size so that the compositor does
+ not try to configure the window below this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered. They will get applied
+ on the next commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the minimum
+ size. The compositor may decide to ignore the values set by the
+ client and request a smaller size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected minimum size in the given dimension.
+ As a result, a client wishing to reset the minimum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a minimum size to be larger than the maximum size of
+ a surface is illegal and will result in a protocol error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width and height will result in a
+ protocol error.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="set_maximized">
+ <description summary="maximize the window">
+ Maximize the surface.
+
+ After requesting that the surface should be maximized, the compositor
+ will respond by emitting a configure event with the "maximized" state
+ and the required window geometry. The client should then update its
+ content, drawing it in a maximized state, i.e. without shadow or other
+ decoration outside of the window geometry. The client must also
+ acknowledge the configure when committing the new content (see
+ ack_configure).
+
+ It is up to the compositor to decide how and where to maximize the
+ surface, for example which output and what region of the screen should
+ be used.
+
+ If the surface was already maximized, the compositor will still emit
+ a configure event with the "maximized" state.
+ </description>
+ </request>
+
+ <request name="unset_maximized">
+ <description summary="unmaximize the window">
+ Unmaximize the surface.
+
+ After requesting that the surface should be unmaximized, the compositor
+ will respond by emitting a configure event without the "maximized"
+ state. If available, the compositor will include the window geometry
+ dimensions the window had prior to being maximized in the configure
+ request. The client must then update its content, drawing it in a
+ regular state, i.e. potentially with shadow, etc. The client must also
+ acknowledge the configure when committing the new content (see
+ ack_configure).
+
+ It is up to the compositor to position the surface after it was
+ unmaximized; usually the position the surface had before maximizing, if
+ applicable.
+
+ If the surface was already not maximized, the compositor will still
+ emit a configure event without the "maximized" state.
+ </description>
+ </request>
+
+ <request name="set_fullscreen">
+ <description summary="set the window as fullscreen on a monitor">
+ Make the surface fullscreen.
+
+ You can specify an output that you would prefer to be fullscreen.
+ If this value is NULL, it's up to the compositor to choose which
+ display will be used to map this surface.
+
+ If the surface doesn't cover the whole output, the compositor will
+ position the surface in the center of the output and compensate with
+ black borders filling the rest of the output.
+ </description>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ </request>
+ <request name="unset_fullscreen" />
+
+ <request name="set_minimized">
+ <description summary="set the window as minimized">
+ Request that the compositor minimize your surface. There is no
+ way to know if the surface is currently minimized, nor is there
+ any way to unset minimization on this surface.
+
+ If you are looking to throttle redrawing when minimized, please
+ instead use the wl_surface.frame event for this, as this will
+ also work with live previews on windows in Alt-Tab, Expose or
+ similar compositor features.
+ </description>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ This configure event asks the client to resize its toplevel surface or
+ to change its state. The configured state should not be applied
+ immediately. See xdg_surface.configure for details.
+
+ The width and height arguments specify a hint to the window
+ about how its surface should be resized in window geometry
+ coordinates. See set_window_geometry.
+
+ If the width or height arguments are zero, it means the client
+ should decide its own window dimension. This may happen when the
+ compositor needs to configure the state of the surface but doesn't
+ have any information about any previous or expected dimension.
+
+ The states listed in the event specify how the width/height
+ arguments should be interpreted, and possibly how it should be
+ drawn.
+
+ Clients must send an ack_configure in response to this event. See
+ xdg_surface.configure and xdg_surface.ack_configure for details.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ <arg name="states" type="array"/>
+ </event>
+
+ <event name="close">
+ <description summary="surface wants to be closed">
+ The close event is sent by the compositor when the user
+ wants the surface to be closed. This should be equivalent to
+ the user clicking the close button in client-side decorations,
+ if your application has any.
+
+ This is only a request that the user intends to close the
+ window. The client may choose to ignore this request, or show
+ a dialog to ask the user to save their data, etc.
+ </description>
+ </event>
+ </interface>
+
+ <interface name="zxdg_popup_v6" version="1">
+ <description summary="short-lived, popup surfaces for menus">
+ A popup surface is a short-lived, temporary surface. It can be used to
+ implement for example menus, popovers, tooltips and other similar user
+ interface concepts.
+
+ A popup can be made to take an explicit grab. See xdg_popup.grab for
+ details.
+
+ When the popup is dismissed, a popup_done event will be sent out, and at
+ the same time the surface will be unmapped. See the xdg_popup.popup_done
+ event for details.
+
+ Explicitly destroying the xdg_popup object will also dismiss the popup and
+ unmap the surface. Clients that want to dismiss the popup when another
+ surface of their own is clicked should dismiss the popup using the destroy
+ request.
+
+ The parent surface must have either the xdg_toplevel or xdg_popup surface
+ role.
+
+ A newly created xdg_popup will be stacked on top of all previously created
+ xdg_popup surfaces associated with the same xdg_toplevel.
+
+ The parent of an xdg_popup must be mapped (see the xdg_surface
+ description) before the xdg_popup itself.
+
+ The x and y arguments passed when creating the popup object specify
+ where the top left of the popup should be placed, relative to the
+ local surface coordinates of the parent surface. See
+ xdg_surface.get_popup. An xdg_popup must intersect with or be at least
+ partially adjacent to its parent surface.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_popup state to take effect.
+ </description>
+
+ <enum name="error">
+ <entry name="invalid_grab" value="0"
+ summary="tried to grab after being mapped"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="remove xdg_popup interface">
+ This destroys the popup. Explicitly destroying the xdg_popup
+ object will also dismiss the popup, and unmap the surface.
+
+ If this xdg_popup is not the "topmost" popup, a protocol error
+ will be sent.
+ </description>
+ </request>
+
+ <request name="grab">
+ <description summary="make the popup take an explicit grab">
+ This request makes the created popup take an explicit grab. An explicit
+ grab will be dismissed when the user dismisses the popup, or when the
+ client destroys the xdg_popup. This can be done by the user clicking
+ outside the surface, using the keyboard, or even locking the screen
+ through closing the lid or a timeout.
+
+ If the compositor denies the grab, the popup will be immediately
+ dismissed.
+
+ This request must be used in response to some sort of user action like a
+ button press, key press, or touch down event. The serial number of the
+ event should be passed as 'serial'.
+
+ The parent of a grabbing popup must either be an xdg_toplevel surface or
+ another xdg_popup with an explicit grab. If the parent is another
+ xdg_popup it means that the popups are nested, with this popup now being
+ the topmost popup.
+
+ Nested popups must be destroyed in the reverse order they were created
+ in, e.g. the only popup you are allowed to destroy at all times is the
+ topmost one.
+
+ When compositors choose to dismiss a popup, they may dismiss every
+ nested grabbing popup as well. When a compositor dismisses popups, it
+ will follow the same dismissing order as required from the client.
+
+ The parent of a grabbing popup must either be another xdg_popup with an
+ active explicit grab, or an xdg_popup or xdg_toplevel, if there are no
+ explicit grabs already taken.
+
+ If the topmost grabbing popup is destroyed, the grab will be returned to
+ the parent of the popup, if that parent previously had an explicit grab.
+
+ If the parent is a grabbing popup which has already been dismissed, this
+ popup will be immediately dismissed. If the parent is a popup that did
+ not take an explicit grab, an error will be raised.
+
+ During a popup grab, the client owning the grab will receive pointer
+ and touch events for all their surfaces as normal (similar to an
+ "owner-events" grab in X11 parlance), while the top most grabbing popup
+ will always have keyboard focus.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat"
+ summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ </request>
+
+ <event name="configure">
+ <description summary="configure the popup surface">
+ This event asks the popup surface to configure itself given the
+ configuration. The configured state should not be applied immediately.
+ See xdg_surface.configure for details.
+
+ The x and y arguments represent the position the popup was placed at
+ given the xdg_positioner rule, relative to the upper left corner of the
+ window geometry of the parent surface.
+ </description>
+ <arg name="x" type="int"
+ summary="x position relative to parent surface window geometry"/>
+ <arg name="y" type="int"
+ summary="y position relative to parent surface window geometry"/>
+ <arg name="width" type="int" summary="window geometry width"/>
+ <arg name="height" type="int" summary="window geometry height"/>
+ </event>
+
+ <event name="popup_done">
+ <description summary="popup interaction is done">
+ The popup_done event is sent out when a popup is dismissed by the
+ compositor. The client should destroy the xdg_popup object at this
+ point.
+ </description>
+ </event>
+
+ </interface>
+</protocol>
diff --git a/gpu_display/src/display_wl.c b/gpu_display/src/display_wl.c
new file mode 100644
index 0000000..fce0ee0
--- /dev/null
+++ b/gpu_display/src/display_wl.c
@@ -0,0 +1,781 @@
+// Copyright 2018 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 <assert.h>
+#include <memory.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "aura-shell.h"
+#include "linux-dmabuf-unstable-v1.h"
+#include "viewporter.h"
+#include "xdg-shell-unstable-v6.h"
+#include <wayland-client-core.h>
+#include <wayland-client-protocol.h>
+#include <wayland-client.h>
+
+#define DEFAULT_SCALE 2
+#define MAX_BUFFER_COUNT 64
+
+struct dwl_context;
+
+struct interfaces {
+ struct dwl_context *context;
+ struct wl_compositor *compositor;
+ struct wl_subcompositor *subcompositor;
+ struct wl_shm *shm;
+ struct wl_shell *shell;
+ struct wl_seat *seat;
+ struct zaura_shell *aura; // optional
+ struct zwp_linux_dmabuf_v1 *linux_dmabuf;
+ struct zxdg_shell_v6 *xdg_shell;
+ struct wp_viewporter *viewporter; // optional
+};
+
+struct output {
+ struct wl_output *output;
+ struct zaura_output *aura_output;
+ struct dwl_context *context;
+ uint32_t id;
+ uint32_t current_scale;
+ uint32_t device_scale_factor;
+ bool internal;
+};
+
+struct dwl_context {
+ struct wl_display *display;
+ struct interfaces ifaces;
+ bool output_added;
+ struct output outputs[8];
+};
+
+#define outputs_for_each(context, pos, output) \
+ for (pos = 0, output = &context->outputs[pos]; \
+ pos < (sizeof(context->outputs) / sizeof(context->outputs[0])); \
+ pos++, output = &context->outputs[pos])
+
+struct dwl_dmabuf {
+ uint32_t width;
+ uint32_t height;
+ bool in_use;
+ struct wl_buffer *buffer;
+};
+
+struct dwl_surface {
+ struct dwl_context *context;
+ struct wl_surface *surface;
+ struct zaura_surface *aura;
+ struct zxdg_surface_v6 *xdg;
+ struct zxdg_toplevel_v6 *toplevel;
+ struct wp_viewport *viewport;
+ struct wl_subsurface *subsurface;
+ uint32_t width;
+ uint32_t height;
+ double scale;
+ bool close_requested;
+ size_t buffer_count;
+ uint64_t buffer_use_bit_mask;
+ struct wl_buffer *buffers[0];
+};
+
+static_assert(sizeof(((struct dwl_surface *)0)->buffer_use_bit_mask) * 8 >=
+ MAX_BUFFER_COUNT,
+ "not enough bits in buffer_use_bit_mask");
+
+static void output_geometry(void *data, struct wl_output *output, int x, int y,
+ int physical_width, int physical_height,
+ int subpixel, const char *make, const char *model,
+ int transform)
+{
+ (void)data;
+ (void)output;
+ (void)x;
+ (void)y;
+ (void)physical_width;
+ (void)physical_height;
+ (void)subpixel;
+ (void)make;
+ (void)model;
+ (void)transform;
+}
+
+static void output_mode(void *data, struct wl_output *output, uint32_t flags,
+ int width, int height, int refresh)
+{
+ (void)data;
+ (void)output;
+ (void)flags;
+ (void)width;
+ (void)height;
+ (void)refresh;
+}
+
+static void output_done(void *data, struct wl_output *output)
+{
+ (void)data;
+ (void)output;
+}
+
+static void output_scale(void *data, struct wl_output *wl_output,
+ int32_t scale_factor)
+{
+ (void)wl_output;
+ struct output *output = (struct output *)data;
+ struct dwl_context *context = output->context;
+
+ // If the aura interface is available, we prefer the scale factor
+ // reported by that.
+ if (context->ifaces.aura)
+ return;
+
+ output->current_scale = 1000 * scale_factor;
+}
+
+static const struct wl_output_listener output_listener = {
+ .geometry = output_geometry,
+ .mode = output_mode,
+ .done = output_done,
+ .scale = output_scale};
+
+static void aura_output_scale(void *data, struct zaura_output *aura_output,
+ uint32_t flags, uint32_t scale)
+{
+ (void)aura_output;
+ struct output *output = (struct output *)data;
+ if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT) {
+ output->current_scale = scale;
+ }
+}
+
+static void aura_output_connection(void *data, struct zaura_output *aura_output,
+ uint32_t connection)
+{
+ (void)aura_output;
+ struct output *output = (struct output *)data;
+ output->internal = connection == ZAURA_OUTPUT_CONNECTION_TYPE_INTERNAL;
+}
+
+static void aura_output_device_scale_factor(void *data,
+ struct zaura_output *aura_output,
+ uint32_t device_scale_factor)
+{
+ (void)aura_output;
+ struct output *output = (struct output *)data;
+ output->device_scale_factor = device_scale_factor;
+}
+
+static const struct zaura_output_listener aura_output_listener = {
+ .scale = aura_output_scale,
+ .connection = aura_output_connection,
+ .device_scale_factor = aura_output_device_scale_factor};
+
+static void dwl_context_output_add(struct dwl_context *context,
+ struct wl_output *wl_output, uint32_t id)
+{
+ size_t i;
+ struct output *output;
+ outputs_for_each(context, i, output)
+ {
+ if (output->output == NULL) {
+ context->output_added = true;
+ output->id = id;
+ output->output = wl_output;
+ output->context = context;
+ output->current_scale = 1000;
+ output->device_scale_factor = 1000;
+ // This is a fun little hack from reveman. The idea is
+ // that the first display will be internal and never get
+ // removed.
+ output->internal = i == 0;
+ wl_output_add_listener(output->output, &output_listener,
+ output);
+ return;
+ }
+ }
+}
+
+static void dwl_context_output_remove_destroy(struct dwl_context *context,
+ uint32_t id)
+{
+ size_t i;
+ struct output *output;
+ outputs_for_each(context, i, output)
+ {
+ if (output->id == id) {
+ if (output->aura_output)
+ zaura_output_destroy(output->aura_output);
+ wl_output_destroy(output->output);
+ memset(output, 0, sizeof(struct output));
+ return;
+ }
+ }
+}
+
+static void dwl_context_output_get_aura(struct dwl_context *context)
+{
+ if (!context->ifaces.aura)
+ return;
+
+ size_t i;
+ struct output *output;
+ outputs_for_each(context, i, output)
+ {
+ if (output->output != NULL && output->aura_output == NULL) {
+ output->aura_output = zaura_shell_get_aura_output(
+ context->ifaces.aura, output->output);
+ zaura_output_add_listener(
+ output->aura_output, &aura_output_listener, output);
+ }
+ }
+}
+
+static void registry_global(void *data, struct wl_registry *registry,
+ uint32_t id, const char *interface,
+ uint32_t version)
+{
+ (void)version;
+ struct interfaces *ifaces = (struct interfaces *)data;
+ if (strcmp(interface, "wl_compositor") == 0) {
+ ifaces->compositor = (struct wl_compositor *)wl_registry_bind(
+ registry, id, &wl_compositor_interface, 3);
+ } else if (strcmp(interface, "wl_subcompositor") == 0) {
+ ifaces->subcompositor =
+ (struct wl_subcompositor *)wl_registry_bind(
+ registry, id, &wl_subcompositor_interface, 1);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ ifaces->shm = (struct wl_shm *)wl_registry_bind(
+ registry, id, &wl_shm_interface, 1);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ ifaces->seat = (struct wl_seat *)wl_registry_bind(
+ registry, id, &wl_seat_interface, 5);
+ } else if (strcmp(interface, "wl_output") == 0) {
+ struct wl_output *output = (struct wl_output *)wl_registry_bind(
+ registry, id, &wl_output_interface, 2);
+ dwl_context_output_add(ifaces->context, output, id);
+ } else if (strcmp(interface, "zaura_shell") == 0 && version >= 6) {
+ ifaces->aura = (struct zaura_shell *)wl_registry_bind(
+ registry, id, &zaura_shell_interface, 6);
+ } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
+ ifaces->linux_dmabuf =
+ (struct zwp_linux_dmabuf_v1 *)wl_registry_bind(
+ registry, id, &zwp_linux_dmabuf_v1_interface, 1);
+ } else if (strcmp(interface, "zxdg_shell_v6") == 0) {
+ ifaces->xdg_shell = (struct zxdg_shell_v6 *)wl_registry_bind(
+ registry, id, &zxdg_shell_v6_interface, 1);
+ } else if (strcmp(interface, "wp_viewporter") == 0) {
+ ifaces->viewporter = (struct wp_viewporter *)wl_registry_bind(
+ registry, id, &wp_viewporter_interface, 1);
+ }
+}
+
+static void global_remove(void *data, struct wl_registry *registry, uint32_t id)
+{
+ (void)registry;
+
+ struct interfaces *ifaces = (struct interfaces *)data;
+ // If the ID matches any output, this will remove it. Otherwise, this is
+ // a no-op.
+ dwl_context_output_remove_destroy(ifaces->context, id);
+
+ if (ifaces->aura &&
+ wl_proxy_get_id((struct wl_proxy *)ifaces->aura) == id) {
+ zaura_shell_destroy(ifaces->aura);
+ ifaces->aura = NULL;
+ }
+
+ // TODO(zachr): deal with the removal of some of the required
+ // interfaces.
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = registry_global, .global_remove = global_remove};
+
+static void toplevel_configure(void *data,
+ struct zxdg_toplevel_v6 *zxdg_toplevel_v6,
+ int32_t width, int32_t height,
+ struct wl_array *states)
+{
+ (void)data;
+ (void)zxdg_toplevel_v6;
+ (void)width;
+ (void)height;
+ (void)states;
+}
+
+static void toplevel_close(void *data,
+ struct zxdg_toplevel_v6 *zxdg_toplevel_v6)
+{
+ (void)zxdg_toplevel_v6;
+ struct dwl_surface *surface = (struct dwl_surface *)data;
+ surface->close_requested = true;
+}
+
+static const struct zxdg_toplevel_v6_listener toplevel_listener = {
+ .configure = toplevel_configure, .close = toplevel_close};
+
+static void surface_enter(void *data, struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
+{
+ struct dwl_surface *surface = (struct dwl_surface *)data;
+
+ struct output *output =
+ (struct output *)wl_output_get_user_data(wl_output);
+
+ surface->scale = (output->device_scale_factor / 1000.0) *
+ (output->current_scale / 1000.0);
+
+ if (surface->viewport) {
+ wp_viewport_set_destination(
+ surface->viewport, ceil(surface->width / surface->scale),
+ ceil(surface->height / surface->scale));
+ } else {
+ wl_surface_set_buffer_scale(wl_surface, surface->scale);
+ }
+
+ wl_surface_commit(wl_surface);
+}
+
+static void surface_leave(void *data, struct wl_surface *wl_surface,
+ struct wl_output *output)
+{
+ (void)data;
+ (void)wl_surface;
+ (void)output;
+}
+
+static const struct wl_surface_listener surface_listener = {
+ .enter = surface_enter, .leave = surface_leave};
+
+struct dwl_context *dwl_context_new()
+{
+ struct dwl_context *ctx = calloc(1, sizeof(struct dwl_context));
+ ctx->ifaces.context = ctx;
+ return ctx;
+}
+
+void dwl_context_destroy(struct dwl_context **self)
+{
+ if ((*self)->display)
+ wl_display_disconnect((*self)->display);
+ free(*self);
+ *self = NULL;
+}
+
+bool dwl_context_setup(struct dwl_context *self, const char *socket_path)
+{
+ struct wl_display *display = wl_display_connect(socket_path);
+ if (!display) {
+ syslog(LOG_ERR, "failed to connect to display");
+ return false;
+ }
+ self->display = display;
+ wl_display_set_user_data(display, self);
+
+ struct wl_registry *registry = wl_display_get_registry(display);
+ if (!registry) {
+ syslog(LOG_ERR, "failed to get registry");
+ goto fail;
+ }
+
+ struct interfaces *ifaces = &self->ifaces;
+ wl_registry_add_listener(registry, ®istry_listener, ifaces);
+ wl_display_roundtrip(display);
+ dwl_context_output_get_aura(self);
+
+ if (!ifaces->shm) {
+ syslog(LOG_ERR, "missing interface shm");
+ goto fail;
+ }
+ if (!ifaces->compositor) {
+ syslog(LOG_ERR, "missing interface compositor");
+ goto fail;
+ }
+ if (!ifaces->subcompositor) {
+ syslog(LOG_ERR, "missing interface subcompositor");
+ goto fail;
+ }
+ if (!ifaces->seat) {
+ syslog(LOG_ERR, "missing interface seat");
+ goto fail;
+ }
+ if (!ifaces->linux_dmabuf) {
+ syslog(LOG_ERR, "missing interface linux_dmabuf");
+ goto fail;
+ }
+ if (!ifaces->xdg_shell) {
+ syslog(LOG_ERR, "missing interface xdg_shell");
+ goto fail;
+ }
+
+ return true;
+
+fail:
+ wl_display_disconnect(display);
+ return false;
+}
+
+int dwl_context_fd(struct dwl_context *self)
+{
+ return wl_display_get_fd(self->display);
+}
+
+void dwl_context_dispatch(struct dwl_context *self)
+{
+ wl_display_dispatch(self->display);
+ if (self->output_added) {
+ self->output_added = false;
+ dwl_context_output_get_aura(self);
+ wl_display_roundtrip(self->display);
+ }
+}
+
+static void linux_buffer_created(
+ void *data, struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1,
+ struct wl_buffer *buffer)
+{
+ (void)zwp_linux_buffer_params_v1;
+ struct dwl_dmabuf *dmabuf = (struct dwl_dmabuf *)data;
+ dmabuf->buffer = buffer;
+}
+
+static void linux_buffer_failed(
+ void *data, struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1)
+{
+ (void)data;
+ (void)zwp_linux_buffer_params_v1;
+}
+
+static const struct zwp_linux_buffer_params_v1_listener linux_buffer_listener =
+ {.created = linux_buffer_created, .failed = linux_buffer_failed};
+
+static void dmabuf_buffer_release(void *data, struct wl_buffer *buffer)
+{
+ struct dwl_dmabuf *dmabuf = (struct dwl_dmabuf *)data;
+ (void)buffer;
+
+ dmabuf->in_use = false;
+}
+
+static const struct wl_buffer_listener dmabuf_buffer_listener = {
+ .release = dmabuf_buffer_release};
+
+struct dwl_dmabuf *dwl_context_dmabuf_new(struct dwl_context *self, int fd,
+ uint32_t offset, uint32_t stride,
+ uint64_t modifiers, uint32_t width,
+ uint32_t height, uint32_t fourcc)
+{
+ struct dwl_dmabuf *dmabuf = calloc(1, sizeof(struct dwl_dmabuf));
+ if (!dmabuf) {
+ syslog(LOG_ERR, "failed to allocate dwl_dmabuf");
+ return NULL;
+ }
+ dmabuf->width = width;
+ dmabuf->height = height;
+
+ struct zwp_linux_buffer_params_v1 *params =
+ zwp_linux_dmabuf_v1_create_params(self->ifaces.linux_dmabuf);
+ if (!params) {
+ syslog(LOG_ERR,
+ "failed to allocate zwp_linux_buffer_params_v1");
+ free(dmabuf);
+ return NULL;
+ }
+
+ zwp_linux_buffer_params_v1_add_listener(params, &linux_buffer_listener,
+ dmabuf);
+ zwp_linux_buffer_params_v1_add(params, fd, 0 /* plane_idx */, offset,
+ stride, modifiers >> 32,
+ (uint32_t)modifiers);
+ zwp_linux_buffer_params_v1_create(params, width, height, fourcc, 0);
+ wl_display_roundtrip(self->display);
+ zwp_linux_buffer_params_v1_destroy(params);
+
+ if (!dmabuf->buffer) {
+ syslog(LOG_ERR, "failed to get wl_buffer for dmabuf");
+ free(dmabuf);
+ return NULL;
+ }
+
+ wl_buffer_add_listener(dmabuf->buffer, &dmabuf_buffer_listener, dmabuf);
+
+ return dmabuf;
+}
+
+void dwl_dmabuf_destroy(struct dwl_dmabuf **self)
+{
+ wl_buffer_destroy((*self)->buffer);
+ free(*self);
+ *self = NULL;
+}
+
+static void surface_buffer_release(void *data, struct wl_buffer *buffer)
+{
+ struct dwl_surface *surface = (struct dwl_surface *)data;
+ (void)buffer;
+
+ size_t i;
+ for (i = 0; i < surface->buffer_count; i++) {
+ if (buffer == surface->buffers[i]) {
+ surface->buffer_use_bit_mask &= ~(1 << i);
+ break;
+ }
+ }
+}
+
+static const struct wl_buffer_listener surface_buffer_listener = {
+ .release = surface_buffer_release};
+
+struct dwl_surface *dwl_context_surface_new(struct dwl_context *self,
+ struct dwl_surface *parent,
+ int shm_fd, size_t shm_size,
+ size_t buffer_size, uint32_t width,
+ uint32_t height, uint32_t stride)
+{
+ if (buffer_size == 0)
+ return NULL;
+ size_t buffer_count = shm_size / buffer_size;
+ if (buffer_count == 0)
+ return NULL;
+ if (buffer_count > MAX_BUFFER_COUNT)
+ return NULL;
+
+ struct dwl_surface *disp_surface =
+ calloc(1, sizeof(struct dwl_surface) +
+ sizeof(struct wl_buffer *) * buffer_count);
+ if (!disp_surface)
+ return NULL;
+ disp_surface->context = self;
+ disp_surface->width = width;
+ disp_surface->height = height;
+ disp_surface->scale = DEFAULT_SCALE;
+ disp_surface->buffer_count = buffer_count;
+
+ struct wl_region *region = NULL;
+ struct wl_shm_pool *shm_pool =
+ wl_shm_create_pool(self->ifaces.shm, shm_fd, shm_size);
+ if (!shm_pool) {
+ syslog(LOG_ERR, "failed to make shm pool");
+ goto fail;
+ }
+
+ size_t i;
+ for (i = 0; i < buffer_count; i++) {
+ struct wl_buffer *buffer = wl_shm_pool_create_buffer(
+ shm_pool, buffer_size * i, width, height, stride,
+ WL_SHM_FORMAT_ARGB8888);
+ if (!buffer) {
+ syslog(LOG_ERR, "failed to create buffer");
+ goto fail;
+ }
+ disp_surface->buffers[i] = buffer;
+ }
+
+ for (i = 0; i < buffer_count; i++)
+ wl_buffer_add_listener(disp_surface->buffers[i],
+ &surface_buffer_listener, disp_surface);
+
+ disp_surface->surface =
+ wl_compositor_create_surface(self->ifaces.compositor);
+ if (!disp_surface->surface) {
+ syslog(LOG_ERR, "failed to make surface");
+ goto fail;
+ }
+
+ wl_surface_add_listener(disp_surface->surface, &surface_listener,
+ disp_surface);
+
+ region = wl_compositor_create_region(self->ifaces.compositor);
+ if (!region) {
+ syslog(LOG_ERR, "failed to make region");
+ goto fail;
+ }
+ wl_region_add(region, 0, 0, width, height);
+ wl_surface_set_opaque_region(disp_surface->surface, region);
+
+ if (!parent) {
+ disp_surface->xdg = zxdg_shell_v6_get_xdg_surface(
+ self->ifaces.xdg_shell, disp_surface->surface);
+ if (!disp_surface->xdg) {
+ syslog(LOG_ERR, "failed to make xdg shell surface");
+ goto fail;
+ }
+
+ disp_surface->toplevel =
+ zxdg_surface_v6_get_toplevel(disp_surface->xdg);
+ if (!disp_surface->toplevel) {
+ syslog(LOG_ERR,
+ "failed to make toplevel xdg shell surface");
+ goto fail;
+ }
+ zxdg_toplevel_v6_set_title(disp_surface->toplevel, "crosvm");
+ zxdg_toplevel_v6_add_listener(disp_surface->toplevel,
+ &toplevel_listener, disp_surface);
+
+ if (self->ifaces.aura) {
+ disp_surface->aura = zaura_shell_get_aura_surface(
+ self->ifaces.aura, disp_surface->surface);
+ if (!disp_surface->aura) {
+ syslog(LOG_ERR, "failed to make aura surface");
+ goto fail;
+ }
+ zaura_surface_set_frame(
+ disp_surface->aura,
+ ZAURA_SURFACE_FRAME_TYPE_NORMAL);
+ }
+ } else {
+ disp_surface->subsurface = wl_subcompositor_get_subsurface(
+ self->ifaces.subcompositor, disp_surface->surface,
+ parent->surface);
+ if (!disp_surface->subsurface) {
+ syslog(LOG_ERR, "failed to make subsurface");
+ goto fail;
+ }
+ wl_subsurface_set_desync(disp_surface->subsurface);
+ }
+
+ if (self->ifaces.viewporter) {
+ disp_surface->viewport = wp_viewporter_get_viewport(
+ self->ifaces.viewporter, disp_surface->surface);
+ if (!disp_surface->viewport) {
+ syslog(LOG_ERR, "failed to make surface viewport");
+ goto fail;
+ }
+ }
+
+ wl_surface_attach(disp_surface->surface, disp_surface->buffers[0], 0,
+ 0);
+ wl_surface_damage(disp_surface->surface, 0, 0, width, height);
+ wl_region_destroy(region);
+ wl_shm_pool_destroy(shm_pool);
+
+ // Needed to get outputs before iterating them.
+ wl_display_roundtrip(self->display);
+
+ // Assuming that this surface will enter the internal output initially,
+ // trigger a surface enter for that output before doing the first
+ // surface commit. THis is to avoid unpleasant artifacts when the
+ // surface first appears.
+ struct output *output;
+ outputs_for_each(self, i, output)
+ {
+ if (output->internal) {
+ surface_enter(disp_surface, disp_surface->surface,
+ output->output);
+ }
+ }
+
+ wl_surface_commit(disp_surface->surface);
+ wl_display_flush(self->display);
+
+ return disp_surface;
+fail:
+ if (disp_surface->viewport)
+ wp_viewport_destroy(disp_surface->viewport);
+ if (disp_surface->subsurface)
+ wl_subsurface_destroy(disp_surface->subsurface);
+ if (disp_surface->toplevel)
+ zxdg_toplevel_v6_destroy(disp_surface->toplevel);
+ if (disp_surface->xdg)
+ zxdg_surface_v6_destroy(disp_surface->xdg);
+ if (disp_surface->aura)
+ zaura_surface_destroy(disp_surface->aura);
+ if (region)
+ wl_region_destroy(region);
+ if (disp_surface->surface)
+ wl_surface_destroy(disp_surface->surface);
+ for (i = 0; i < buffer_count; i++)
+ if (disp_surface->buffers[i])
+ wl_buffer_destroy(disp_surface->buffers[i]);
+ if (shm_pool)
+ wl_shm_pool_destroy(shm_pool);
+ free(disp_surface);
+ return NULL;
+}
+
+void dwl_surface_destroy(struct dwl_surface **self)
+{
+ size_t i;
+ if ((*self)->viewport)
+ wp_viewport_destroy((*self)->viewport);
+ if ((*self)->subsurface)
+ wl_subsurface_destroy((*self)->subsurface);
+ if ((*self)->toplevel)
+ zxdg_toplevel_v6_destroy((*self)->toplevel);
+ if ((*self)->xdg)
+ zxdg_surface_v6_destroy((*self)->xdg);
+ if ((*self)->aura)
+ zaura_surface_destroy((*self)->aura);
+ if ((*self)->surface)
+ wl_surface_destroy((*self)->surface);
+ for (i = 0; i < (*self)->buffer_count; i++)
+ wl_buffer_destroy((*self)->buffers[i]);
+ wl_display_flush((*self)->context->display);
+ free(*self);
+ *self = NULL;
+}
+
+void dwl_surface_commit(struct dwl_surface *self)
+{
+ // It is possible that we are committing frames faster than the
+ // compositor can put them on the screen. This may result in dropped
+ // frames, but this is acceptable considering there is no good way to
+ // apply back pressure to the guest gpu driver right now. The intention
+ // of this module is to help bootstrap gpu support, so it does not have
+ // to have artifact free rendering.
+ wl_surface_commit(self->surface);
+ wl_display_flush(self->context->display);
+}
+
+bool dwl_surface_buffer_in_use(struct dwl_surface *self, size_t buffer_index)
+{
+ return (self->buffer_use_bit_mask & (1 << buffer_index)) != 0;
+}
+
+void dwl_surface_flip(struct dwl_surface *self, size_t buffer_index)
+{
+ if (buffer_index >= self->buffer_count)
+ return;
+ wl_surface_attach(self->surface, self->buffers[buffer_index], 0, 0);
+ wl_surface_damage(self->surface, 0, 0, self->width, self->height);
+ dwl_surface_commit(self);
+ self->buffer_use_bit_mask |= 1 << buffer_index;
+}
+
+void dwl_surface_flip_to(struct dwl_surface *self, struct dwl_dmabuf *dmabuf)
+{
+ if (self->width != dmabuf->width || self->height != dmabuf->height)
+ return;
+ wl_surface_attach(self->surface, dmabuf->buffer, 0, 0);
+ wl_surface_damage(self->surface, 0, 0, self->width, self->height);
+ dwl_surface_commit(self);
+ dmabuf->in_use = true;
+}
+
+bool dwl_surface_close_requested(const struct dwl_surface *self)
+{
+ return self->close_requested;
+}
+
+void dwl_surface_set_position(struct dwl_surface *self, uint32_t x, uint32_t y)
+{
+ if (self->subsurface) {
+ wl_subsurface_set_position(self->subsurface, x / self->scale,
+ y / self->scale);
+ wl_surface_commit(self->surface);
+ wl_display_flush(self->context->display);
+ }
+}
diff --git a/gpu_display/src/dwl.rs b/gpu_display/src/dwl.rs
new file mode 100644
index 0000000..3f2d67d
--- /dev/null
+++ b/gpu_display/src/dwl.rs
@@ -0,0 +1,125 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+/// @page page_xdg_shell_unstable_v6 The xdg_shell_unstable_v6 protocol
+/// @section page_ifaces_xdg_shell_unstable_v6 Interfaces
+/// - @subpage page_iface_zxdg_shell_v6 - create desktop-style surfaces
+/// - @subpage page_iface_zxdg_positioner_v6 - child surface positioner
+/// - @subpage page_iface_zxdg_surface_v6 - desktop user interface surface base interface
+/// - @subpage page_iface_zxdg_toplevel_v6 - toplevel surface
+/// - @subpage page_iface_zxdg_popup_v6 - short-lived, popup surfaces for menus
+/// @section page_copyright_xdg_shell_unstable_v6 Copyright
+/// <pre>
+///
+/// Copyright © 2008-2013 Kristian Høgsberg
+/// Copyright © 2013 Rafael Antognolli
+/// Copyright © 2013 Jasper St. Pierre
+/// Copyright © 2010-2013 Intel Corporation
+///
+/// Permission is hereby granted, free of charge, to any person obtaining a
+/// copy of this software and associated documentation files (the "Software"),
+/// to deal in the Software without restriction, including without limitation
+/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+/// and/or sell copies of the Software, and to permit persons to whom the
+/// Software is furnished to do so, subject to the following conditions:
+///
+/// The above copyright notice and this permission notice (including the next
+/// paragraph) shall be included in all copies or substantial portions of the
+/// Software.
+///
+/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+/// DEALINGS IN THE SOFTWARE.
+/// </pre>
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct wl_output {
+ _unused: [u8; 0],
+}
+#[repr(C)]
+pub struct dwl_context {
+ pub _bindgen_opaque_blob: [u64; 52usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct dwl_dmabuf {
+ pub _bindgen_opaque_blob: [u64; 3usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct dwl_surface {
+ pub _bindgen_opaque_blob: [u64; 12usize],
+}
+extern "C" {
+ pub fn dwl_context_new() -> *mut dwl_context;
+}
+extern "C" {
+ pub fn dwl_context_destroy(self_: *mut *mut dwl_context);
+}
+extern "C" {
+ pub fn dwl_context_setup(
+ self_: *mut dwl_context,
+ socket_path: *const ::std::os::raw::c_char,
+ ) -> bool;
+}
+extern "C" {
+ pub fn dwl_context_fd(self_: *mut dwl_context) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn dwl_context_dispatch(self_: *mut dwl_context);
+}
+extern "C" {
+ pub fn dwl_context_dmabuf_new(
+ self_: *mut dwl_context,
+ fd: ::std::os::raw::c_int,
+ offset: u32,
+ stride: u32,
+ modifiers: u64,
+ width: u32,
+ height: u32,
+ fourcc: u32,
+ ) -> *mut dwl_dmabuf;
+}
+extern "C" {
+ pub fn dwl_dmabuf_destroy(self_: *mut *mut dwl_dmabuf);
+}
+extern "C" {
+ pub fn dwl_context_surface_new(
+ self_: *mut dwl_context,
+ parent: *mut dwl_surface,
+ shm_fd: ::std::os::raw::c_int,
+ shm_size: usize,
+ buffer_size: usize,
+ width: u32,
+ height: u32,
+ stride: u32,
+ ) -> *mut dwl_surface;
+}
+extern "C" {
+ pub fn dwl_surface_destroy(self_: *mut *mut dwl_surface);
+}
+extern "C" {
+ pub fn dwl_surface_commit(self_: *mut dwl_surface);
+}
+extern "C" {
+ pub fn dwl_surface_buffer_in_use(self_: *mut dwl_surface, buffer_index: usize) -> bool;
+}
+extern "C" {
+ pub fn dwl_surface_flip(self_: *mut dwl_surface, buffer_index: usize);
+}
+extern "C" {
+ pub fn dwl_surface_flip_to(self_: *mut dwl_surface, dmabuf: *mut dwl_dmabuf);
+}
+extern "C" {
+ pub fn dwl_surface_close_requested(self_: *const dwl_surface) -> bool;
+}
+extern "C" {
+ pub fn dwl_surface_set_position(self_: *mut dwl_surface, x: u32, y: u32);
+}
diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs
new file mode 100644
index 0000000..11a6703
--- /dev/null
+++ b/gpu_display/src/lib.rs
@@ -0,0 +1,399 @@
+// Copyright 2018 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.
+
+//! Crate for displaying simple surfaces and GPU buffers over wayland.
+
+mod dwl;
+
+use std::cell::Cell;
+use std::collections::HashMap;
+use std::ffi::{CStr, CString};
+use std::fmt::{self, Display};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::Path;
+use std::ptr::null_mut;
+
+use data_model::{VolatileMemory, VolatileSlice};
+use sys_util::{round_up_to_page_size, Error as SysError, MemoryMapping, SharedMemory};
+
+use crate::dwl::*;
+
+const BUFFER_COUNT: usize = 2;
+const BYTES_PER_PIXEL: u32 = 4;
+
+/// An error generated by `GpuDisplay`.
+#[derive(Debug)]
+pub enum GpuDisplayError {
+ /// An internal allocation failed.
+ Allocate,
+ /// Connecting to the compositor failed.
+ Connect,
+ /// Creating shared memory failed.
+ CreateShm(SysError),
+ /// Setting the size of shared memory failed.
+ SetSize(SysError),
+ /// Failed to create a surface on the compositor.
+ CreateSurface,
+ /// Failed to import a buffer to the compositor.
+ FailedImport,
+ /// The surface ID is invalid.
+ InvalidSurfaceId,
+ /// The path is invalid.
+ InvalidPath,
+}
+
+impl Display for GpuDisplayError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::GpuDisplayError::*;
+
+ match self {
+ Allocate => write!(f, "internal allocation failed"),
+ Connect => write!(f, "failed to connect to compositor"),
+ CreateShm(e) => write!(f, "failed to create shared memory: {}", e),
+ SetSize(e) => write!(f, "failed to set size of shared memory: {}", e),
+ CreateSurface => write!(f, "failed to crate surface on the compositor"),
+ FailedImport => write!(f, "failed to import a buffer to the compositor"),
+ InvalidSurfaceId => write!(f, "invalid surface ID"),
+ InvalidPath => write!(f, "invalid path"),
+ }
+ }
+}
+
+struct DwlContext(*mut dwl_context);
+impl Drop for DwlContext {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ // Safe given that we checked the pointer for non-null and it should always be of the
+ // correct type.
+ unsafe {
+ dwl_context_destroy(&mut self.0);
+ }
+ }
+ }
+}
+
+struct DwlDmabuf(*mut dwl_dmabuf);
+impl Drop for DwlDmabuf {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ // Safe given that we checked the pointer for non-null and it should always be of the
+ // correct type.
+ unsafe {
+ dwl_dmabuf_destroy(&mut self.0);
+ }
+ }
+ }
+}
+
+struct DwlSurface(*mut dwl_surface);
+impl Drop for DwlSurface {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ // Safe given that we checked the pointer for non-null and it should always be of the
+ // correct type.
+ unsafe {
+ dwl_surface_destroy(&mut self.0);
+ }
+ }
+ }
+}
+
+struct GpuDisplaySurface {
+ surface: DwlSurface,
+ buffer_size: usize,
+ buffer_index: Cell<usize>,
+ buffer_mem: MemoryMapping,
+}
+
+impl GpuDisplaySurface {
+ fn surface(&self) -> *mut dwl_surface {
+ self.surface.0
+ }
+}
+
+/// 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
+/// descriptor. When the connection is readable, `dispatch_events` can be called to process it.
+pub struct GpuDisplay {
+ ctx: DwlContext,
+ dmabufs: HashMap<u32, DwlDmabuf>,
+ dmabuf_next_id: u32,
+ surfaces: HashMap<u32, GpuDisplaySurface>,
+ surface_next_id: u32,
+}
+
+impl GpuDisplay {
+ /// Opens a fresh connection to the compositor.
+ pub fn new<P: AsRef<Path>>(wayland_path: P) -> Result<GpuDisplay, GpuDisplayError> {
+ // The dwl_context_new call should always be safe to call, and we check its result.
+ let ctx = DwlContext(unsafe { dwl_context_new() });
+ if ctx.0.is_null() {
+ return Err(GpuDisplayError::Allocate);
+ }
+
+ // The dwl_context_setup call is always safe to call given that the supplied context is
+ // valid. and we check its result.
+ let cstr_path = match wayland_path.as_ref().as_os_str().to_str() {
+ Some(str) => match CString::new(str) {
+ Ok(cstr) => cstr,
+ Err(_) => return Err(GpuDisplayError::InvalidPath),
+ },
+ None => return Err(GpuDisplayError::InvalidPath),
+ };
+ let setup_success = unsafe { dwl_context_setup(ctx.0, cstr_path.as_ptr()) };
+ if !setup_success {
+ return Err(GpuDisplayError::Connect);
+ }
+
+ Ok(GpuDisplay {
+ ctx,
+ dmabufs: Default::default(),
+ dmabuf_next_id: 0,
+ surfaces: Default::default(),
+ surface_next_id: 0,
+ })
+ }
+
+ fn ctx(&self) -> *mut dwl_context {
+ self.ctx.0
+ }
+
+ fn get_surface(&self, surface_id: u32) -> Option<&GpuDisplaySurface> {
+ self.surfaces.get(&surface_id)
+ }
+
+ /// 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,
+ offset: u32,
+ stride: u32,
+ modifiers: u64,
+ width: u32,
+ height: u32,
+ fourcc: u32,
+ ) -> Result<u32, GpuDisplayError> {
+ // Safe given that the context pointer is valid. Any other invalid parameters would be
+ // rejected by dwl_context_dmabuf_new safely. We check that the resulting dmabuf is valid
+ // before filing it away.
+ let dmabuf = DwlDmabuf(unsafe {
+ dwl_context_dmabuf_new(
+ self.ctx(),
+ fd,
+ offset,
+ stride,
+ modifiers,
+ width,
+ height,
+ fourcc,
+ )
+ });
+ if dmabuf.0.is_null() {
+ return Err(GpuDisplayError::FailedImport);
+ }
+
+ let next_id = self.dmabuf_next_id;
+ self.dmabufs.insert(next_id, dmabuf);
+ self.dmabuf_next_id += 1;
+ Ok(next_id)
+ }
+
+ /// Releases a previously imported dmabuf identified by the given handle.
+ pub fn release_import(&mut self, import_id: u32) {
+ self.dmabufs.remove(&import_id);
+ }
+
+ /// Dispatches internal events that were received from the compositor since the last call to
+ /// `dispatch_events`.
+ pub fn dispatch_events(&mut self) {
+ // Safe given that the context pointer is valid.
+ unsafe {
+ dwl_context_dispatch(self.ctx());
+ }
+ }
+
+ /// Creates a surface on the the compositor as either a top level window, or child of another
+ /// surface, returning a handle to the new surface.
+ pub fn create_surface(
+ &mut self,
+ parent_surface_id: Option<u32>,
+ width: u32,
+ height: u32,
+ ) -> Result<u32, GpuDisplayError> {
+ let parent_ptr = match parent_surface_id {
+ Some(id) => match self.get_surface(id).map(|p| p.surface()) {
+ Some(ptr) => ptr,
+ None => return Err(GpuDisplayError::InvalidSurfaceId),
+ },
+ None => null_mut(),
+ };
+ let row_size = width * BYTES_PER_PIXEL;
+ let fb_size = row_size * height;
+ let buffer_size = round_up_to_page_size(fb_size as usize * BUFFER_COUNT);
+ let mut buffer_shm = SharedMemory::new(Some(
+ CStr::from_bytes_with_nul(b"GpuDisplaySurface\0").unwrap(),
+ ))
+ .map_err(GpuDisplayError::CreateShm)?;
+ buffer_shm
+ .set_size(buffer_size as u64)
+ .map_err(GpuDisplayError::SetSize)?;
+ let buffer_mem = MemoryMapping::from_fd(&buffer_shm, buffer_size).unwrap();
+
+ // Safe because only a valid context, parent pointer (if not None), and buffer FD are used.
+ // The returned surface is checked for validity before being filed away.
+ let surface = DwlSurface(unsafe {
+ dwl_context_surface_new(
+ self.ctx(),
+ parent_ptr,
+ buffer_shm.as_raw_fd(),
+ buffer_size,
+ fb_size as usize,
+ width,
+ height,
+ row_size,
+ )
+ });
+
+ if surface.0.is_null() {
+ return Err(GpuDisplayError::CreateSurface);
+ }
+
+ let next_id = self.surface_next_id;
+ self.surfaces.insert(
+ next_id,
+ GpuDisplaySurface {
+ surface,
+ buffer_size: fb_size as usize,
+ buffer_index: Cell::new(0),
+ buffer_mem,
+ },
+ );
+
+ self.surface_next_id += 1;
+ Ok(next_id)
+ }
+
+ /// Releases a previously created surface identified by the given handle.
+ pub fn release_surface(&mut self, surface_id: u32) {
+ self.surfaces.remove(&surface_id);
+ }
+
+ /// Gets a reference to an unused framebuffer for the identified surface.
+ pub fn framebuffer_memory(&self, surface_id: u32) -> Option<VolatileSlice> {
+ let surface = self.get_surface(surface_id)?;
+ let buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
+ surface
+ .buffer_mem
+ .get_slice(
+ (buffer_index * surface.buffer_size) as u64,
+ surface.buffer_size as u64,
+ )
+ .ok()
+ }
+
+ /// Commits any pending state for the identified surface.
+ pub fn commit(&self, surface_id: u32) {
+ match self.get_surface(surface_id) {
+ Some(surface) => {
+ // Safe because only a valid surface is used.
+ unsafe {
+ dwl_surface_commit(surface.surface());
+ }
+ }
+ None => debug_assert!(false, "invalid surface_id {}", surface_id),
+ }
+ }
+
+ /// Returns true if the next buffer in the buffer queue for the given surface is currently in
+ /// use.
+ ///
+ /// If the next buffer is in use, the memory returned from `framebuffer_memory` should not be
+ /// written to.
+ pub fn next_buffer_in_use(&self, surface_id: u32) -> bool {
+ match self.get_surface(surface_id) {
+ Some(surface) => {
+ let next_buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
+ // Safe because only a valid surface and buffer index is used.
+ unsafe { dwl_surface_buffer_in_use(surface.surface(), next_buffer_index) }
+ }
+ None => {
+ debug_assert!(false, "invalid surface_id {}", surface_id);
+ false
+ }
+ }
+ }
+
+ /// Changes the visible contents of the identified surface to the contents of the framebuffer
+ /// last returned by `framebuffer_memory` for this surface.
+ pub fn flip(&self, surface_id: u32) {
+ match self.get_surface(surface_id) {
+ Some(surface) => {
+ surface
+ .buffer_index
+ .set((surface.buffer_index.get() + 1) % BUFFER_COUNT);
+ // Safe because only a valid surface and buffer index is used.
+ unsafe {
+ dwl_surface_flip(surface.surface(), surface.buffer_index.get());
+ }
+ }
+ None => debug_assert!(false, "invalid surface_id {}", surface_id),
+ }
+ }
+
+ /// Changes the visible contents of the identified surface to that of the identified imported
+ /// buffer.
+ pub fn flip_to(&self, surface_id: u32, import_id: u32) {
+ match self.get_surface(surface_id) {
+ Some(surface) => {
+ match self.dmabufs.get(&import_id) {
+ // Safe because only a valid surface and dmabuf is used.
+ Some(dmabuf) => unsafe { dwl_surface_flip_to(surface.surface(), dmabuf.0) },
+ None => debug_assert!(false, "invalid import_id {}", import_id),
+ }
+ }
+ None => debug_assert!(false, "invalid surface_id {}", surface_id),
+ }
+ }
+
+ /// Returns true if the identified top level surface has been told to close by the compositor,
+ /// and by extension the user.
+ pub fn close_requested(&self, surface_id: u32) -> bool {
+ match self.get_surface(surface_id) {
+ Some(surface) =>
+ // Safe because only a valid surface is used.
+ unsafe { dwl_surface_close_requested(surface.surface()) }
+ None => false,
+ }
+ }
+
+ /// Sets the position of the identified subsurface relative to its parent.
+ ///
+ /// The change in position will not be visible until `commit` is called for the parent surface.
+ pub fn set_position(&self, surface_id: u32, x: u32, y: u32) {
+ match self.get_surface(surface_id) {
+ Some(surface) => {
+ // Safe because only a valid surface is used.
+ unsafe {
+ dwl_surface_set_position(surface.surface(), x, y);
+ }
+ }
+ None => debug_assert!(false, "invalid surface_id {}", surface_id),
+ }
+ }
+}
+
+impl Drop for GpuDisplay {
+ fn drop(&mut self) {
+ // Safe given that the context pointer is valid.
+ unsafe { dwl_context_destroy(&mut self.ctx.0) }
+ }
+}
+
+impl AsRawFd for GpuDisplay {
+ fn as_raw_fd(&self) -> RawFd {
+ // Safe given that the context pointer is valid.
+ unsafe { dwl_context_fd(self.ctx.0) }
+ }
+}
diff --git a/gpu_renderer/Cargo.toml b/gpu_renderer/Cargo.toml
new file mode 100644
index 0000000..6fcd22e
--- /dev/null
+++ b/gpu_renderer/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "gpu_renderer"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+data_model = { path = "../data_model" }
+libc = "*"
+sys_util = { path = "../sys_util" }
diff --git a/gpu_renderer/src/command_buffer.rs b/gpu_renderer/src/command_buffer.rs
new file mode 100644
index 0000000..b6c7005
--- /dev/null
+++ b/gpu_renderer/src/command_buffer.rs
@@ -0,0 +1,140 @@
+// Copyright 2018 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::mem::size_of;
+use std::os::raw::c_void;
+use std::slice::{from_raw_parts, from_raw_parts_mut};
+
+use crate::generated::virgl_protocol::*;
+use crate::Resource;
+
+/// Helper struct for making a virgl command buffer.
+#[derive(Default)]
+pub struct CommandBufferBuilder {
+ cbuf: Vec<u32>,
+}
+
+impl AsRef<[u8]> for CommandBufferBuilder {
+ fn as_ref(&self) -> &[u8] {
+ // Safe because the returned slice is a trivial reinterpretation of the same number of
+ // bytes.
+ unsafe {
+ from_raw_parts(
+ self.cbuf.as_ptr() as *const u8,
+ self.cbuf.len() * size_of::<u32>(),
+ )
+ }
+ }
+}
+
+impl AsMut<[u8]> for CommandBufferBuilder {
+ fn as_mut(&mut self) -> &mut [u8] {
+ // Safe because the returned slice is a trivial reinterpretation of the same number of
+ // bytes.
+ unsafe {
+ from_raw_parts_mut(
+ self.cbuf.as_mut_ptr() as *mut u8,
+ self.cbuf.len() * size_of::<u32>(),
+ )
+ }
+ }
+}
+
+impl CommandBufferBuilder {
+ /// Constructs an empty command
+ pub fn new() -> CommandBufferBuilder {
+ Default::default()
+ }
+
+ fn push(&mut self, dw: u32) {
+ self.cbuf.push(dw);
+ }
+
+ fn push_qw(&mut self, qw: u64) {
+ self.cbuf.push(qw as u32);
+ self.cbuf.push((qw >> 32) as u32);
+ }
+
+ fn push_cmd(&mut self, cmd: u32, obj_type: u32, len: u32) {
+ self.cbuf
+ .push((cmd & 0xff) | ((obj_type & 0xff) << 8) | ((len & 0xffff) << 16));
+ }
+
+ /// Gets the command buffer as a pointer to the beginning.
+ pub fn as_mut_ptr(&mut self) -> *mut c_void {
+ self.cbuf.as_mut_ptr() as *mut c_void
+ }
+
+ /// Gets the size of the command buffer content in dwords.
+ pub fn dword_count(&self) -> usize {
+ self.cbuf.len()
+ }
+
+ /// Clears the command buffer content.
+ pub fn clear(&mut self) {
+ self.cbuf.clear();
+ }
+
+ /// Checks that the command buffer is well formed.
+ pub fn is_valid(&self) -> bool {
+ let mut i = 0;
+ while i < self.cbuf.len() {
+ i += 1 + (self.cbuf[i] >> 16) as usize;
+ }
+ i == self.cbuf.len()
+ }
+
+ /// Pushes a clear command to this command buffer.
+ pub fn e_clear(&mut self, buffers: u32, color: [f32; 4], depth: f64, stencil: u32) {
+ self.push_cmd(VIRGL_CCMD_CLEAR, 0, VIRGL_OBJ_CLEAR_SIZE);
+ self.push(buffers);
+ for c in &color {
+ self.push(c.to_bits())
+ }
+ self.push_qw(depth.to_bits());
+ self.push(stencil);
+ assert!(self.is_valid());
+ }
+
+ /// Pushes a create surface command to this command buffer.
+ pub fn e_create_surface(
+ &mut self,
+ new_id: u32,
+ res: &Resource,
+ format: u32,
+ level: u32,
+ first_layer: u32,
+ last_layer: u32,
+ ) {
+ self.push_cmd(
+ VIRGL_CCMD_CREATE_OBJECT,
+ VIRGL_OBJECT_SURFACE,
+ VIRGL_OBJ_SURFACE_SIZE,
+ );
+ self.push(new_id);
+ self.push(res.id());
+ self.push(format);
+ self.push(level);
+ self.push(first_layer | (last_layer << 16));
+ assert!(self.is_valid());
+ }
+
+ /// Pushes a set framebuffer state command to this command buffer.
+ pub fn e_set_fb_state(&mut self, surface_handles: &[u32], zbuf: Option<u32>) {
+ fn cmd_set_fb_state_size(surface_count: u32) -> u32 {
+ 2 + surface_count
+ }
+ self.push_cmd(
+ VIRGL_CCMD_SET_FRAMEBUFFER_STATE,
+ 0,
+ cmd_set_fb_state_size(surface_handles.len() as u32),
+ );
+ self.push(surface_handles.len() as u32);
+ self.push(zbuf.unwrap_or(0));
+ for &surface_handle in surface_handles {
+ self.push(surface_handle);
+ }
+ assert!(self.is_valid());
+ }
+}
diff --git a/gpu_renderer/src/generated/epoxy_egl.rs b/gpu_renderer/src/generated/epoxy_egl.rs
new file mode 100644
index 0000000..b998b06
--- /dev/null
+++ b/gpu_renderer/src/generated/epoxy_egl.rs
@@ -0,0 +1,31436 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[link(name = "epoxy")]
+extern "C" {}
+
+pub const GL_ES_VERSION_2_0: u32 = 1;
+pub const GL_ES_VERSION_3_0: u32 = 1;
+pub const GL_ES_VERSION_3_1: u32 = 1;
+pub const GL_ES_VERSION_3_2: u32 = 1;
+pub const GL_SC_VERSION_2_0: u32 = 1;
+pub const GL_VERSION_1_0: u32 = 1;
+pub const GL_VERSION_1_1: u32 = 1;
+pub const GL_VERSION_1_2: u32 = 1;
+pub const GL_VERSION_1_3: u32 = 1;
+pub const GL_VERSION_1_4: u32 = 1;
+pub const GL_VERSION_1_5: u32 = 1;
+pub const GL_VERSION_2_0: u32 = 1;
+pub const GL_VERSION_2_1: u32 = 1;
+pub const GL_VERSION_3_0: u32 = 1;
+pub const GL_VERSION_3_1: u32 = 1;
+pub const GL_VERSION_3_2: u32 = 1;
+pub const GL_VERSION_3_3: u32 = 1;
+pub const GL_VERSION_4_0: u32 = 1;
+pub const GL_VERSION_4_1: u32 = 1;
+pub const GL_VERSION_4_2: u32 = 1;
+pub const GL_VERSION_4_3: u32 = 1;
+pub const GL_VERSION_4_4: u32 = 1;
+pub const GL_VERSION_4_5: u32 = 1;
+pub const GL_VERSION_ES_CM_1_0: u32 = 1;
+pub const GL_3DFX_multisample: u32 = 1;
+pub const GL_3DFX_tbuffer: u32 = 1;
+pub const GL_3DFX_texture_compression_FXT1: u32 = 1;
+pub const GL_AMD_blend_minmax_factor: u32 = 1;
+pub const GL_AMD_compressed_3DC_texture: u32 = 1;
+pub const GL_AMD_compressed_ATC_texture: u32 = 1;
+pub const GL_AMD_conservative_depth: u32 = 1;
+pub const GL_AMD_debug_output: u32 = 1;
+pub const GL_AMD_depth_clamp_separate: u32 = 1;
+pub const GL_AMD_draw_buffers_blend: u32 = 1;
+pub const GL_AMD_framebuffer_sample_positions: u32 = 1;
+pub const GL_AMD_gcn_shader: u32 = 1;
+pub const GL_AMD_gpu_shader_half_float: u32 = 1;
+pub const GL_AMD_gpu_shader_int64: u32 = 1;
+pub const GL_AMD_interleaved_elements: u32 = 1;
+pub const GL_AMD_multi_draw_indirect: u32 = 1;
+pub const GL_AMD_name_gen_delete: u32 = 1;
+pub const GL_AMD_occlusion_query_event: u32 = 1;
+pub const GL_AMD_performance_monitor: u32 = 1;
+pub const GL_AMD_pinned_memory: u32 = 1;
+pub const GL_AMD_program_binary_Z400: u32 = 1;
+pub const GL_AMD_query_buffer_object: u32 = 1;
+pub const GL_AMD_sample_positions: u32 = 1;
+pub const GL_AMD_seamless_cubemap_per_texture: u32 = 1;
+pub const GL_AMD_shader_atomic_counter_ops: u32 = 1;
+pub const GL_AMD_shader_ballot: u32 = 1;
+pub const GL_AMD_shader_explicit_vertex_parameter: u32 = 1;
+pub const GL_AMD_shader_stencil_export: u32 = 1;
+pub const GL_AMD_shader_trinary_minmax: u32 = 1;
+pub const GL_AMD_sparse_texture: u32 = 1;
+pub const GL_AMD_stencil_operation_extended: u32 = 1;
+pub const GL_AMD_texture_texture4: u32 = 1;
+pub const GL_AMD_transform_feedback3_lines_triangles: u32 = 1;
+pub const GL_AMD_transform_feedback4: u32 = 1;
+pub const GL_AMD_vertex_shader_layer: u32 = 1;
+pub const GL_AMD_vertex_shader_tessellator: u32 = 1;
+pub const GL_AMD_vertex_shader_viewport_index: u32 = 1;
+pub const GL_ANDROID_extension_pack_es31a: u32 = 1;
+pub const GL_ANGLE_depth_texture: u32 = 1;
+pub const GL_ANGLE_framebuffer_blit: u32 = 1;
+pub const GL_ANGLE_framebuffer_multisample: u32 = 1;
+pub const GL_ANGLE_instanced_arrays: u32 = 1;
+pub const GL_ANGLE_pack_reverse_row_order: u32 = 1;
+pub const GL_ANGLE_program_binary: u32 = 1;
+pub const GL_ANGLE_texture_compression_dxt3: u32 = 1;
+pub const GL_ANGLE_texture_compression_dxt5: u32 = 1;
+pub const GL_ANGLE_texture_usage: u32 = 1;
+pub const GL_ANGLE_translated_shader_source: u32 = 1;
+pub const GL_APPLE_aux_depth_stencil: u32 = 1;
+pub const GL_APPLE_client_storage: u32 = 1;
+pub const GL_APPLE_clip_distance: u32 = 1;
+pub const GL_APPLE_color_buffer_packed_float: u32 = 1;
+pub const GL_APPLE_copy_texture_levels: u32 = 1;
+pub const GL_APPLE_element_array: u32 = 1;
+pub const GL_APPLE_fence: u32 = 1;
+pub const GL_APPLE_float_pixels: u32 = 1;
+pub const GL_APPLE_flush_buffer_range: u32 = 1;
+pub const GL_APPLE_framebuffer_multisample: u32 = 1;
+pub const GL_APPLE_object_purgeable: u32 = 1;
+pub const GL_APPLE_rgb_422: u32 = 1;
+pub const GL_APPLE_row_bytes: u32 = 1;
+pub const GL_APPLE_specular_vector: u32 = 1;
+pub const GL_APPLE_sync: u32 = 1;
+pub const GL_APPLE_texture_2D_limited_npot: u32 = 1;
+pub const GL_APPLE_texture_format_BGRA8888: u32 = 1;
+pub const GL_APPLE_texture_max_level: u32 = 1;
+pub const GL_APPLE_texture_packed_float: u32 = 1;
+pub const GL_APPLE_texture_range: u32 = 1;
+pub const GL_APPLE_transform_hint: u32 = 1;
+pub const GL_APPLE_vertex_array_object: u32 = 1;
+pub const GL_APPLE_vertex_array_range: u32 = 1;
+pub const GL_APPLE_vertex_program_evaluators: u32 = 1;
+pub const GL_APPLE_ycbcr_422: u32 = 1;
+pub const GL_ARB_ES2_compatibility: u32 = 1;
+pub const GL_ARB_ES3_1_compatibility: u32 = 1;
+pub const GL_ARB_ES3_2_compatibility: u32 = 1;
+pub const GL_ARB_ES3_compatibility: u32 = 1;
+pub const GL_ARB_arrays_of_arrays: u32 = 1;
+pub const GL_ARB_base_instance: u32 = 1;
+pub const GL_ARB_bindless_texture: u32 = 1;
+pub const GL_ARB_blend_func_extended: u32 = 1;
+pub const GL_ARB_buffer_storage: u32 = 1;
+pub const GL_ARB_cl_event: u32 = 1;
+pub const GL_ARB_clear_buffer_object: u32 = 1;
+pub const GL_ARB_clear_texture: u32 = 1;
+pub const GL_ARB_clip_control: u32 = 1;
+pub const GL_ARB_color_buffer_float: u32 = 1;
+pub const GL_ARB_compatibility: u32 = 1;
+pub const GL_ARB_compressed_texture_pixel_storage: u32 = 1;
+pub const GL_ARB_compute_shader: u32 = 1;
+pub const GL_ARB_compute_variable_group_size: u32 = 1;
+pub const GL_ARB_conditional_render_inverted: u32 = 1;
+pub const GL_ARB_conservative_depth: u32 = 1;
+pub const GL_ARB_copy_buffer: u32 = 1;
+pub const GL_ARB_copy_image: u32 = 1;
+pub const GL_ARB_cull_distance: u32 = 1;
+pub const GL_ARB_debug_output: u32 = 1;
+pub const GL_ARB_depth_buffer_float: u32 = 1;
+pub const GL_ARB_depth_clamp: u32 = 1;
+pub const GL_ARB_depth_texture: u32 = 1;
+pub const GL_ARB_derivative_control: u32 = 1;
+pub const GL_ARB_direct_state_access: u32 = 1;
+pub const GL_ARB_draw_buffers: u32 = 1;
+pub const GL_ARB_draw_buffers_blend: u32 = 1;
+pub const GL_ARB_draw_elements_base_vertex: u32 = 1;
+pub const GL_ARB_draw_indirect: u32 = 1;
+pub const GL_ARB_draw_instanced: u32 = 1;
+pub const GL_ARB_enhanced_layouts: u32 = 1;
+pub const GL_ARB_explicit_attrib_location: u32 = 1;
+pub const GL_ARB_explicit_uniform_location: u32 = 1;
+pub const GL_ARB_fragment_coord_conventions: u32 = 1;
+pub const GL_ARB_fragment_layer_viewport: u32 = 1;
+pub const GL_ARB_fragment_program: u32 = 1;
+pub const GL_ARB_fragment_program_shadow: u32 = 1;
+pub const GL_ARB_fragment_shader: u32 = 1;
+pub const GL_ARB_fragment_shader_interlock: u32 = 1;
+pub const GL_ARB_framebuffer_no_attachments: u32 = 1;
+pub const GL_ARB_framebuffer_object: u32 = 1;
+pub const GL_ARB_framebuffer_sRGB: u32 = 1;
+pub const GL_ARB_geometry_shader4: u32 = 1;
+pub const GL_ARB_get_program_binary: u32 = 1;
+pub const GL_ARB_get_texture_sub_image: u32 = 1;
+pub const GL_ARB_gpu_shader5: u32 = 1;
+pub const GL_ARB_gpu_shader_fp64: u32 = 1;
+pub const GL_ARB_gpu_shader_int64: u32 = 1;
+pub const GL_ARB_half_float_pixel: u32 = 1;
+pub const GL_ARB_half_float_vertex: u32 = 1;
+pub const GL_ARB_imaging: u32 = 1;
+pub const GL_ARB_indirect_parameters: u32 = 1;
+pub const GL_ARB_instanced_arrays: u32 = 1;
+pub const GL_ARB_internalformat_query: u32 = 1;
+pub const GL_ARB_internalformat_query2: u32 = 1;
+pub const GL_ARB_invalidate_subdata: u32 = 1;
+pub const GL_ARB_map_buffer_alignment: u32 = 1;
+pub const GL_ARB_map_buffer_range: u32 = 1;
+pub const GL_ARB_matrix_palette: u32 = 1;
+pub const GL_ARB_multi_bind: u32 = 1;
+pub const GL_ARB_multi_draw_indirect: u32 = 1;
+pub const GL_ARB_multisample: u32 = 1;
+pub const GL_ARB_multitexture: u32 = 1;
+pub const GL_ARB_occlusion_query: u32 = 1;
+pub const GL_ARB_occlusion_query2: u32 = 1;
+pub const GL_ARB_parallel_shader_compile: u32 = 1;
+pub const GL_ARB_pipeline_statistics_query: u32 = 1;
+pub const GL_ARB_pixel_buffer_object: u32 = 1;
+pub const GL_ARB_point_parameters: u32 = 1;
+pub const GL_ARB_point_sprite: u32 = 1;
+pub const GL_ARB_post_depth_coverage: u32 = 1;
+pub const GL_ARB_program_interface_query: u32 = 1;
+pub const GL_ARB_provoking_vertex: u32 = 1;
+pub const GL_ARB_query_buffer_object: u32 = 1;
+pub const GL_ARB_robust_buffer_access_behavior: u32 = 1;
+pub const GL_ARB_robustness: u32 = 1;
+pub const GL_ARB_robustness_isolation: u32 = 1;
+pub const GL_ARB_sample_locations: u32 = 1;
+pub const GL_ARB_sample_shading: u32 = 1;
+pub const GL_ARB_sampler_objects: u32 = 1;
+pub const GL_ARB_seamless_cube_map: u32 = 1;
+pub const GL_ARB_seamless_cubemap_per_texture: u32 = 1;
+pub const GL_ARB_separate_shader_objects: u32 = 1;
+pub const GL_ARB_shader_atomic_counter_ops: u32 = 1;
+pub const GL_ARB_shader_atomic_counters: u32 = 1;
+pub const GL_ARB_shader_ballot: u32 = 1;
+pub const GL_ARB_shader_bit_encoding: u32 = 1;
+pub const GL_ARB_shader_clock: u32 = 1;
+pub const GL_ARB_shader_draw_parameters: u32 = 1;
+pub const GL_ARB_shader_group_vote: u32 = 1;
+pub const GL_ARB_shader_image_load_store: u32 = 1;
+pub const GL_ARB_shader_image_size: u32 = 1;
+pub const GL_ARB_shader_objects: u32 = 1;
+pub const GL_ARB_shader_precision: u32 = 1;
+pub const GL_ARB_shader_stencil_export: u32 = 1;
+pub const GL_ARB_shader_storage_buffer_object: u32 = 1;
+pub const GL_ARB_shader_subroutine: u32 = 1;
+pub const GL_ARB_shader_texture_image_samples: u32 = 1;
+pub const GL_ARB_shader_texture_lod: u32 = 1;
+pub const GL_ARB_shader_viewport_layer_array: u32 = 1;
+pub const GL_ARB_shading_language_100: u32 = 1;
+pub const GL_ARB_shading_language_420pack: u32 = 1;
+pub const GL_ARB_shading_language_include: u32 = 1;
+pub const GL_ARB_shading_language_packing: u32 = 1;
+pub const GL_ARB_shadow: u32 = 1;
+pub const GL_ARB_shadow_ambient: u32 = 1;
+pub const GL_ARB_sparse_buffer: u32 = 1;
+pub const GL_ARB_sparse_texture: u32 = 1;
+pub const GL_ARB_sparse_texture2: u32 = 1;
+pub const GL_ARB_sparse_texture_clamp: u32 = 1;
+pub const GL_ARB_stencil_texturing: u32 = 1;
+pub const GL_ARB_sync: u32 = 1;
+pub const GL_ARB_tessellation_shader: u32 = 1;
+pub const GL_ARB_texture_barrier: u32 = 1;
+pub const GL_ARB_texture_border_clamp: u32 = 1;
+pub const GL_ARB_texture_buffer_object: u32 = 1;
+pub const GL_ARB_texture_buffer_object_rgb32: u32 = 1;
+pub const GL_ARB_texture_buffer_range: u32 = 1;
+pub const GL_ARB_texture_compression: u32 = 1;
+pub const GL_ARB_texture_compression_bptc: u32 = 1;
+pub const GL_ARB_texture_compression_rgtc: u32 = 1;
+pub const GL_ARB_texture_cube_map: u32 = 1;
+pub const GL_ARB_texture_cube_map_array: u32 = 1;
+pub const GL_ARB_texture_env_add: u32 = 1;
+pub const GL_ARB_texture_env_combine: u32 = 1;
+pub const GL_ARB_texture_env_crossbar: u32 = 1;
+pub const GL_ARB_texture_env_dot3: u32 = 1;
+pub const GL_ARB_texture_filter_minmax: u32 = 1;
+pub const GL_ARB_texture_float: u32 = 1;
+pub const GL_ARB_texture_gather: u32 = 1;
+pub const GL_ARB_texture_mirror_clamp_to_edge: u32 = 1;
+pub const GL_ARB_texture_mirrored_repeat: u32 = 1;
+pub const GL_ARB_texture_multisample: u32 = 1;
+pub const GL_ARB_texture_non_power_of_two: u32 = 1;
+pub const GL_ARB_texture_query_levels: u32 = 1;
+pub const GL_ARB_texture_query_lod: u32 = 1;
+pub const GL_ARB_texture_rectangle: u32 = 1;
+pub const GL_ARB_texture_rg: u32 = 1;
+pub const GL_ARB_texture_rgb10_a2ui: u32 = 1;
+pub const GL_ARB_texture_stencil8: u32 = 1;
+pub const GL_ARB_texture_storage: u32 = 1;
+pub const GL_ARB_texture_storage_multisample: u32 = 1;
+pub const GL_ARB_texture_swizzle: u32 = 1;
+pub const GL_ARB_texture_view: u32 = 1;
+pub const GL_ARB_timer_query: u32 = 1;
+pub const GL_ARB_transform_feedback2: u32 = 1;
+pub const GL_ARB_transform_feedback3: u32 = 1;
+pub const GL_ARB_transform_feedback_instanced: u32 = 1;
+pub const GL_ARB_transform_feedback_overflow_query: u32 = 1;
+pub const GL_ARB_transpose_matrix: u32 = 1;
+pub const GL_ARB_uniform_buffer_object: u32 = 1;
+pub const GL_ARB_vertex_array_bgra: u32 = 1;
+pub const GL_ARB_vertex_array_object: u32 = 1;
+pub const GL_ARB_vertex_attrib_64bit: u32 = 1;
+pub const GL_ARB_vertex_attrib_binding: u32 = 1;
+pub const GL_ARB_vertex_blend: u32 = 1;
+pub const GL_ARB_vertex_buffer_object: u32 = 1;
+pub const GL_ARB_vertex_program: u32 = 1;
+pub const GL_ARB_vertex_shader: u32 = 1;
+pub const GL_ARB_vertex_type_10f_11f_11f_rev: u32 = 1;
+pub const GL_ARB_vertex_type_2_10_10_10_rev: u32 = 1;
+pub const GL_ARB_viewport_array: u32 = 1;
+pub const GL_ARB_window_pos: u32 = 1;
+pub const GL_ARM_mali_program_binary: u32 = 1;
+pub const GL_ARM_mali_shader_binary: u32 = 1;
+pub const GL_ARM_rgba8: u32 = 1;
+pub const GL_ARM_shader_framebuffer_fetch: u32 = 1;
+pub const GL_ARM_shader_framebuffer_fetch_depth_stencil: u32 = 1;
+pub const GL_ATI_draw_buffers: u32 = 1;
+pub const GL_ATI_element_array: u32 = 1;
+pub const GL_ATI_envmap_bumpmap: u32 = 1;
+pub const GL_ATI_fragment_shader: u32 = 1;
+pub const GL_ATI_map_object_buffer: u32 = 1;
+pub const GL_ATI_meminfo: u32 = 1;
+pub const GL_ATI_pixel_format_float: u32 = 1;
+pub const GL_ATI_pn_triangles: u32 = 1;
+pub const GL_ATI_separate_stencil: u32 = 1;
+pub const GL_ATI_text_fragment_shader: u32 = 1;
+pub const GL_ATI_texture_env_combine3: u32 = 1;
+pub const GL_ATI_texture_float: u32 = 1;
+pub const GL_ATI_texture_mirror_once: u32 = 1;
+pub const GL_ATI_vertex_array_object: u32 = 1;
+pub const GL_ATI_vertex_attrib_array_object: u32 = 1;
+pub const GL_ATI_vertex_streams: u32 = 1;
+pub const GL_DMP_program_binary: u32 = 1;
+pub const GL_DMP_shader_binary: u32 = 1;
+pub const GL_EXT_422_pixels: u32 = 1;
+pub const GL_EXT_YUV_target: u32 = 1;
+pub const GL_EXT_abgr: u32 = 1;
+pub const GL_EXT_base_instance: u32 = 1;
+pub const GL_EXT_bgra: u32 = 1;
+pub const GL_EXT_bindable_uniform: u32 = 1;
+pub const GL_EXT_blend_color: u32 = 1;
+pub const GL_EXT_blend_equation_separate: u32 = 1;
+pub const GL_EXT_blend_func_extended: u32 = 1;
+pub const GL_EXT_blend_func_separate: u32 = 1;
+pub const GL_EXT_blend_logic_op: u32 = 1;
+pub const GL_EXT_blend_minmax: u32 = 1;
+pub const GL_EXT_blend_subtract: u32 = 1;
+pub const GL_EXT_buffer_storage: u32 = 1;
+pub const GL_EXT_clear_texture: u32 = 1;
+pub const GL_EXT_clip_cull_distance: u32 = 1;
+pub const GL_EXT_clip_volume_hint: u32 = 1;
+pub const GL_EXT_cmyka: u32 = 1;
+pub const GL_EXT_color_buffer_float: u32 = 1;
+pub const GL_EXT_color_buffer_half_float: u32 = 1;
+pub const GL_EXT_color_subtable: u32 = 1;
+pub const GL_EXT_compiled_vertex_array: u32 = 1;
+pub const GL_EXT_conservative_depth: u32 = 1;
+pub const GL_EXT_convolution: u32 = 1;
+pub const GL_EXT_coordinate_frame: u32 = 1;
+pub const GL_EXT_copy_image: u32 = 1;
+pub const GL_EXT_copy_texture: u32 = 1;
+pub const GL_EXT_cull_vertex: u32 = 1;
+pub const GL_EXT_debug_label: u32 = 1;
+pub const GL_EXT_debug_marker: u32 = 1;
+pub const GL_EXT_depth_bounds_test: u32 = 1;
+pub const GL_EXT_direct_state_access: u32 = 1;
+pub const GL_EXT_discard_framebuffer: u32 = 1;
+pub const GL_EXT_disjoint_timer_query: u32 = 1;
+pub const GL_EXT_draw_buffers: u32 = 1;
+pub const GL_EXT_draw_buffers2: u32 = 1;
+pub const GL_EXT_draw_buffers_indexed: u32 = 1;
+pub const GL_EXT_draw_elements_base_vertex: u32 = 1;
+pub const GL_EXT_draw_instanced: u32 = 1;
+pub const GL_EXT_draw_range_elements: u32 = 1;
+pub const GL_EXT_draw_transform_feedback: u32 = 1;
+pub const GL_EXT_float_blend: u32 = 1;
+pub const GL_EXT_fog_coord: u32 = 1;
+pub const GL_EXT_framebuffer_blit: u32 = 1;
+pub const GL_EXT_framebuffer_multisample: u32 = 1;
+pub const GL_EXT_framebuffer_multisample_blit_scaled: u32 = 1;
+pub const GL_EXT_framebuffer_object: u32 = 1;
+pub const GL_EXT_framebuffer_sRGB: u32 = 1;
+pub const GL_EXT_geometry_point_size: u32 = 1;
+pub const GL_EXT_geometry_shader: u32 = 1;
+pub const GL_EXT_geometry_shader4: u32 = 1;
+pub const GL_EXT_gpu_program_parameters: u32 = 1;
+pub const GL_EXT_gpu_shader4: u32 = 1;
+pub const GL_EXT_gpu_shader5: u32 = 1;
+pub const GL_EXT_histogram: u32 = 1;
+pub const GL_EXT_index_array_formats: u32 = 1;
+pub const GL_EXT_index_func: u32 = 1;
+pub const GL_EXT_index_material: u32 = 1;
+pub const GL_EXT_index_texture: u32 = 1;
+pub const GL_EXT_instanced_arrays: u32 = 1;
+pub const GL_EXT_light_texture: u32 = 1;
+pub const GL_EXT_map_buffer_range: u32 = 1;
+pub const GL_EXT_misc_attribute: u32 = 1;
+pub const GL_EXT_multi_draw_arrays: u32 = 1;
+pub const GL_EXT_multi_draw_indirect: u32 = 1;
+pub const GL_EXT_multisample: u32 = 1;
+pub const GL_EXT_multisampled_compatibility: u32 = 1;
+pub const GL_EXT_multisampled_render_to_texture: u32 = 1;
+pub const GL_EXT_multiview_draw_buffers: u32 = 1;
+pub const GL_EXT_occlusion_query_boolean: u32 = 1;
+pub const GL_EXT_packed_depth_stencil: u32 = 1;
+pub const GL_EXT_packed_float: u32 = 1;
+pub const GL_EXT_packed_pixels: u32 = 1;
+pub const GL_EXT_paletted_texture: u32 = 1;
+pub const GL_EXT_pixel_buffer_object: u32 = 1;
+pub const GL_EXT_pixel_transform: u32 = 1;
+pub const GL_EXT_pixel_transform_color_table: u32 = 1;
+pub const GL_EXT_point_parameters: u32 = 1;
+pub const GL_EXT_polygon_offset: u32 = 1;
+pub const GL_EXT_polygon_offset_clamp: u32 = 1;
+pub const GL_EXT_post_depth_coverage: u32 = 1;
+pub const GL_EXT_primitive_bounding_box: u32 = 1;
+pub const GL_EXT_protected_textures: u32 = 1;
+pub const GL_EXT_provoking_vertex: u32 = 1;
+pub const GL_EXT_pvrtc_sRGB: u32 = 1;
+pub const GL_EXT_raster_multisample: u32 = 1;
+pub const GL_EXT_read_format_bgra: u32 = 1;
+pub const GL_EXT_render_snorm: u32 = 1;
+pub const GL_EXT_rescale_normal: u32 = 1;
+pub const GL_EXT_robustness: u32 = 1;
+pub const GL_EXT_sRGB: u32 = 1;
+pub const GL_EXT_sRGB_write_control: u32 = 1;
+pub const GL_EXT_secondary_color: u32 = 1;
+pub const GL_EXT_separate_shader_objects: u32 = 1;
+pub const GL_EXT_separate_specular_color: u32 = 1;
+pub const GL_EXT_shader_framebuffer_fetch: u32 = 1;
+pub const GL_EXT_shader_group_vote: u32 = 1;
+pub const GL_EXT_shader_image_load_formatted: u32 = 1;
+pub const GL_EXT_shader_image_load_store: u32 = 1;
+pub const GL_EXT_shader_implicit_conversions: u32 = 1;
+pub const GL_EXT_shader_integer_mix: u32 = 1;
+pub const GL_EXT_shader_io_blocks: u32 = 1;
+pub const GL_EXT_shader_non_constant_global_initializers: u32 = 1;
+pub const GL_EXT_shader_pixel_local_storage: u32 = 1;
+pub const GL_EXT_shader_pixel_local_storage2: u32 = 1;
+pub const GL_EXT_shader_texture_lod: u32 = 1;
+pub const GL_EXT_shadow_funcs: u32 = 1;
+pub const GL_EXT_shadow_samplers: u32 = 1;
+pub const GL_EXT_shared_texture_palette: u32 = 1;
+pub const GL_EXT_sparse_texture: u32 = 1;
+pub const GL_EXT_sparse_texture2: u32 = 1;
+pub const GL_EXT_stencil_clear_tag: u32 = 1;
+pub const GL_EXT_stencil_two_side: u32 = 1;
+pub const GL_EXT_stencil_wrap: u32 = 1;
+pub const GL_EXT_subtexture: u32 = 1;
+pub const GL_EXT_tessellation_point_size: u32 = 1;
+pub const GL_EXT_tessellation_shader: u32 = 1;
+pub const GL_EXT_texture: u32 = 1;
+pub const GL_EXT_texture3D: u32 = 1;
+pub const GL_EXT_texture_array: u32 = 1;
+pub const GL_EXT_texture_border_clamp: u32 = 1;
+pub const GL_EXT_texture_buffer: u32 = 1;
+pub const GL_EXT_texture_buffer_object: u32 = 1;
+pub const GL_EXT_texture_compression_dxt1: u32 = 1;
+pub const GL_EXT_texture_compression_latc: u32 = 1;
+pub const GL_EXT_texture_compression_rgtc: u32 = 1;
+pub const GL_EXT_texture_compression_s3tc: u32 = 1;
+pub const GL_EXT_texture_cube_map: u32 = 1;
+pub const GL_EXT_texture_cube_map_array: u32 = 1;
+pub const GL_EXT_texture_env_add: u32 = 1;
+pub const GL_EXT_texture_env_combine: u32 = 1;
+pub const GL_EXT_texture_env_dot3: u32 = 1;
+pub const GL_EXT_texture_filter_anisotropic: u32 = 1;
+pub const GL_EXT_texture_filter_minmax: u32 = 1;
+pub const GL_EXT_texture_format_BGRA8888: u32 = 1;
+pub const GL_EXT_texture_integer: u32 = 1;
+pub const GL_EXT_texture_lod_bias: u32 = 1;
+pub const GL_EXT_texture_mirror_clamp: u32 = 1;
+pub const GL_EXT_texture_norm16: u32 = 1;
+pub const GL_EXT_texture_object: u32 = 1;
+pub const GL_EXT_texture_perturb_normal: u32 = 1;
+pub const GL_EXT_texture_rg: u32 = 1;
+pub const GL_EXT_texture_sRGB: u32 = 1;
+pub const GL_EXT_texture_sRGB_R8: u32 = 1;
+pub const GL_EXT_texture_sRGB_RG8: u32 = 1;
+pub const GL_EXT_texture_sRGB_decode: u32 = 1;
+pub const GL_EXT_texture_shared_exponent: u32 = 1;
+pub const GL_EXT_texture_snorm: u32 = 1;
+pub const GL_EXT_texture_storage: u32 = 1;
+pub const GL_EXT_texture_swizzle: u32 = 1;
+pub const GL_EXT_texture_type_2_10_10_10_REV: u32 = 1;
+pub const GL_EXT_texture_view: u32 = 1;
+pub const GL_EXT_timer_query: u32 = 1;
+pub const GL_EXT_transform_feedback: u32 = 1;
+pub const GL_EXT_unpack_subimage: u32 = 1;
+pub const GL_EXT_vertex_array: u32 = 1;
+pub const GL_EXT_vertex_array_bgra: u32 = 1;
+pub const GL_EXT_vertex_attrib_64bit: u32 = 1;
+pub const GL_EXT_vertex_shader: u32 = 1;
+pub const GL_EXT_vertex_weighting: u32 = 1;
+pub const GL_EXT_window_rectangles: u32 = 1;
+pub const GL_EXT_x11_sync_object: u32 = 1;
+pub const GL_FJ_shader_binary_GCCSO: u32 = 1;
+pub const GL_GREMEDY_frame_terminator: u32 = 1;
+pub const GL_GREMEDY_string_marker: u32 = 1;
+pub const GL_HP_convolution_border_modes: u32 = 1;
+pub const GL_HP_image_transform: u32 = 1;
+pub const GL_HP_occlusion_test: u32 = 1;
+pub const GL_HP_texture_lighting: u32 = 1;
+pub const GL_IBM_cull_vertex: u32 = 1;
+pub const GL_IBM_multimode_draw_arrays: u32 = 1;
+pub const GL_IBM_rasterpos_clip: u32 = 1;
+pub const GL_IBM_static_data: u32 = 1;
+pub const GL_IBM_texture_mirrored_repeat: u32 = 1;
+pub const GL_IBM_vertex_array_lists: u32 = 1;
+pub const GL_IMG_bindless_texture: u32 = 1;
+pub const GL_IMG_framebuffer_downsample: u32 = 1;
+pub const GL_IMG_multisampled_render_to_texture: u32 = 1;
+pub const GL_IMG_program_binary: u32 = 1;
+pub const GL_IMG_read_format: u32 = 1;
+pub const GL_IMG_shader_binary: u32 = 1;
+pub const GL_IMG_texture_compression_pvrtc: u32 = 1;
+pub const GL_IMG_texture_compression_pvrtc2: u32 = 1;
+pub const GL_IMG_texture_env_enhanced_fixed_function: u32 = 1;
+pub const GL_IMG_texture_filter_cubic: u32 = 1;
+pub const GL_IMG_user_clip_plane: u32 = 1;
+pub const GL_INGR_blend_func_separate: u32 = 1;
+pub const GL_INGR_color_clamp: u32 = 1;
+pub const GL_INGR_interlace_read: u32 = 1;
+pub const GL_INTEL_conservative_rasterization: u32 = 1;
+pub const GL_INTEL_fragment_shader_ordering: u32 = 1;
+pub const GL_INTEL_framebuffer_CMAA: u32 = 1;
+pub const GL_INTEL_map_texture: u32 = 1;
+pub const GL_INTEL_parallel_arrays: u32 = 1;
+pub const GL_INTEL_performance_query: u32 = 1;
+pub const GL_KHR_blend_equation_advanced: u32 = 1;
+pub const GL_KHR_blend_equation_advanced_coherent: u32 = 1;
+pub const GL_KHR_context_flush_control: u32 = 1;
+pub const GL_KHR_debug: u32 = 1;
+pub const GL_KHR_no_error: u32 = 1;
+pub const GL_KHR_robust_buffer_access_behavior: u32 = 1;
+pub const GL_KHR_robustness: u32 = 1;
+pub const GL_KHR_texture_compression_astc_hdr: u32 = 1;
+pub const GL_KHR_texture_compression_astc_ldr: u32 = 1;
+pub const GL_KHR_texture_compression_astc_sliced_3d: u32 = 1;
+pub const GL_MESAX_texture_stack: u32 = 1;
+pub const GL_MESA_pack_invert: u32 = 1;
+pub const GL_MESA_resize_buffers: u32 = 1;
+pub const GL_MESA_window_pos: u32 = 1;
+pub const GL_MESA_ycbcr_texture: u32 = 1;
+pub const GL_NVX_conditional_render: u32 = 1;
+pub const GL_NVX_gpu_memory_info: u32 = 1;
+pub const GL_NV_bindless_multi_draw_indirect: u32 = 1;
+pub const GL_NV_bindless_multi_draw_indirect_count: u32 = 1;
+pub const GL_NV_bindless_texture: u32 = 1;
+pub const GL_NV_blend_equation_advanced: u32 = 1;
+pub const GL_NV_blend_equation_advanced_coherent: u32 = 1;
+pub const GL_NV_blend_square: u32 = 1;
+pub const GL_NV_clip_space_w_scaling: u32 = 1;
+pub const GL_NV_command_list: u32 = 1;
+pub const GL_NV_compute_program5: u32 = 1;
+pub const GL_NV_conditional_render: u32 = 1;
+pub const GL_NV_conservative_raster: u32 = 1;
+pub const GL_NV_conservative_raster_dilate: u32 = 1;
+pub const GL_NV_conservative_raster_pre_snap_triangles: u32 = 1;
+pub const GL_NV_copy_buffer: u32 = 1;
+pub const GL_NV_copy_depth_to_color: u32 = 1;
+pub const GL_NV_copy_image: u32 = 1;
+pub const GL_NV_coverage_sample: u32 = 1;
+pub const GL_NV_deep_texture3D: u32 = 1;
+pub const GL_NV_depth_buffer_float: u32 = 1;
+pub const GL_NV_depth_clamp: u32 = 1;
+pub const GL_NV_depth_nonlinear: u32 = 1;
+pub const GL_NV_draw_buffers: u32 = 1;
+pub const GL_NV_draw_instanced: u32 = 1;
+pub const GL_NV_draw_texture: u32 = 1;
+pub const GL_NV_evaluators: u32 = 1;
+pub const GL_NV_explicit_attrib_location: u32 = 1;
+pub const GL_NV_explicit_multisample: u32 = 1;
+pub const GL_NV_fbo_color_attachments: u32 = 1;
+pub const GL_NV_fence: u32 = 1;
+pub const GL_NV_fill_rectangle: u32 = 1;
+pub const GL_NV_float_buffer: u32 = 1;
+pub const GL_NV_fog_distance: u32 = 1;
+pub const GL_NV_fragment_coverage_to_color: u32 = 1;
+pub const GL_NV_fragment_program: u32 = 1;
+pub const GL_NV_fragment_program2: u32 = 1;
+pub const GL_NV_fragment_program4: u32 = 1;
+pub const GL_NV_fragment_program_option: u32 = 1;
+pub const GL_NV_fragment_shader_interlock: u32 = 1;
+pub const GL_NV_framebuffer_blit: u32 = 1;
+pub const GL_NV_framebuffer_mixed_samples: u32 = 1;
+pub const GL_NV_framebuffer_multisample: u32 = 1;
+pub const GL_NV_framebuffer_multisample_coverage: u32 = 1;
+pub const GL_NV_generate_mipmap_sRGB: u32 = 1;
+pub const GL_NV_geometry_program4: u32 = 1;
+pub const GL_NV_geometry_shader4: u32 = 1;
+pub const GL_NV_geometry_shader_passthrough: u32 = 1;
+pub const GL_NV_gpu_program4: u32 = 1;
+pub const GL_NV_gpu_program5: u32 = 1;
+pub const GL_NV_gpu_program5_mem_extended: u32 = 1;
+pub const GL_NV_gpu_shader5: u32 = 1;
+pub const GL_NV_half_float: u32 = 1;
+pub const GL_NV_image_formats: u32 = 1;
+pub const GL_NV_instanced_arrays: u32 = 1;
+pub const GL_NV_internalformat_sample_query: u32 = 1;
+pub const GL_NV_light_max_exponent: u32 = 1;
+pub const GL_NV_multisample_coverage: u32 = 1;
+pub const GL_NV_multisample_filter_hint: u32 = 1;
+pub const GL_NV_non_square_matrices: u32 = 1;
+pub const GL_NV_occlusion_query: u32 = 1;
+pub const GL_NV_packed_depth_stencil: u32 = 1;
+pub const GL_NV_parameter_buffer_object: u32 = 1;
+pub const GL_NV_parameter_buffer_object2: u32 = 1;
+pub const GL_NV_path_rendering: u32 = 1;
+pub const GL_NV_path_rendering_shared_edge: u32 = 1;
+pub const GL_NV_pixel_data_range: u32 = 1;
+pub const GL_NV_point_sprite: u32 = 1;
+pub const GL_NV_polygon_mode: u32 = 1;
+pub const GL_NV_present_video: u32 = 1;
+pub const GL_NV_primitive_restart: u32 = 1;
+pub const GL_NV_read_buffer: u32 = 1;
+pub const GL_NV_read_buffer_front: u32 = 1;
+pub const GL_NV_read_depth: u32 = 1;
+pub const GL_NV_read_depth_stencil: u32 = 1;
+pub const GL_NV_read_stencil: u32 = 1;
+pub const GL_NV_register_combiners: u32 = 1;
+pub const GL_NV_register_combiners2: u32 = 1;
+pub const GL_NV_robustness_video_memory_purge: u32 = 1;
+pub const GL_NV_sRGB_formats: u32 = 1;
+pub const GL_NV_sample_locations: u32 = 1;
+pub const GL_NV_sample_mask_override_coverage: u32 = 1;
+pub const GL_NV_shader_atomic_counters: u32 = 1;
+pub const GL_NV_shader_atomic_float: u32 = 1;
+pub const GL_NV_shader_atomic_float64: u32 = 1;
+pub const GL_NV_shader_atomic_fp16_vector: u32 = 1;
+pub const GL_NV_shader_atomic_int64: u32 = 1;
+pub const GL_NV_shader_buffer_load: u32 = 1;
+pub const GL_NV_shader_buffer_store: u32 = 1;
+pub const GL_NV_shader_noperspective_interpolation: u32 = 1;
+pub const GL_NV_shader_storage_buffer_object: u32 = 1;
+pub const GL_NV_shader_thread_group: u32 = 1;
+pub const GL_NV_shader_thread_shuffle: u32 = 1;
+pub const GL_NV_shadow_samplers_array: u32 = 1;
+pub const GL_NV_shadow_samplers_cube: u32 = 1;
+pub const GL_NV_stereo_view_rendering: u32 = 1;
+pub const GL_NV_tessellation_program5: u32 = 1;
+pub const GL_NV_texgen_emboss: u32 = 1;
+pub const GL_NV_texgen_reflection: u32 = 1;
+pub const GL_NV_texture_barrier: u32 = 1;
+pub const GL_NV_texture_border_clamp: u32 = 1;
+pub const GL_NV_texture_compression_s3tc_update: u32 = 1;
+pub const GL_NV_texture_compression_vtc: u32 = 1;
+pub const GL_NV_texture_env_combine4: u32 = 1;
+pub const GL_NV_texture_expand_normal: u32 = 1;
+pub const GL_NV_texture_multisample: u32 = 1;
+pub const GL_NV_texture_npot_2D_mipmap: u32 = 1;
+pub const GL_NV_texture_rectangle: u32 = 1;
+pub const GL_NV_texture_shader: u32 = 1;
+pub const GL_NV_texture_shader2: u32 = 1;
+pub const GL_NV_texture_shader3: u32 = 1;
+pub const GL_NV_transform_feedback: u32 = 1;
+pub const GL_NV_transform_feedback2: u32 = 1;
+pub const GL_NV_uniform_buffer_unified_memory: u32 = 1;
+pub const GL_NV_vdpau_interop: u32 = 1;
+pub const GL_NV_vertex_array_range: u32 = 1;
+pub const GL_NV_vertex_array_range2: u32 = 1;
+pub const GL_NV_vertex_attrib_integer_64bit: u32 = 1;
+pub const GL_NV_vertex_buffer_unified_memory: u32 = 1;
+pub const GL_NV_vertex_program: u32 = 1;
+pub const GL_NV_vertex_program1_1: u32 = 1;
+pub const GL_NV_vertex_program2: u32 = 1;
+pub const GL_NV_vertex_program2_option: u32 = 1;
+pub const GL_NV_vertex_program3: u32 = 1;
+pub const GL_NV_vertex_program4: u32 = 1;
+pub const GL_NV_video_capture: u32 = 1;
+pub const GL_NV_viewport_array: u32 = 1;
+pub const GL_NV_viewport_array2: u32 = 1;
+pub const GL_NV_viewport_swizzle: u32 = 1;
+pub const GL_OES_EGL_image: u32 = 1;
+pub const GL_OES_EGL_image_external: u32 = 1;
+pub const GL_OES_EGL_image_external_essl3: u32 = 1;
+pub const GL_OES_blend_equation_separate: u32 = 1;
+pub const GL_OES_blend_func_separate: u32 = 1;
+pub const GL_OES_blend_subtract: u32 = 1;
+pub const GL_OES_byte_coordinates: u32 = 1;
+pub const GL_OES_compressed_ETC1_RGB8_sub_texture: u32 = 1;
+pub const GL_OES_compressed_ETC1_RGB8_texture: u32 = 1;
+pub const GL_OES_compressed_paletted_texture: u32 = 1;
+pub const GL_OES_copy_image: u32 = 1;
+pub const GL_OES_depth24: u32 = 1;
+pub const GL_OES_depth32: u32 = 1;
+pub const GL_OES_depth_texture: u32 = 1;
+pub const GL_OES_draw_buffers_indexed: u32 = 1;
+pub const GL_OES_draw_elements_base_vertex: u32 = 1;
+pub const GL_OES_draw_texture: u32 = 1;
+pub const GL_OES_element_index_uint: u32 = 1;
+pub const GL_OES_extended_matrix_palette: u32 = 1;
+pub const GL_OES_fbo_render_mipmap: u32 = 1;
+pub const GL_OES_fixed_point: u32 = 1;
+pub const GL_OES_fragment_precision_high: u32 = 1;
+pub const GL_OES_framebuffer_object: u32 = 1;
+pub const GL_OES_geometry_point_size: u32 = 1;
+pub const GL_OES_geometry_shader: u32 = 1;
+pub const GL_OES_get_program_binary: u32 = 1;
+pub const GL_OES_gpu_shader5: u32 = 1;
+pub const GL_OES_mapbuffer: u32 = 1;
+pub const GL_OES_matrix_get: u32 = 1;
+pub const GL_OES_matrix_palette: u32 = 1;
+pub const GL_OES_packed_depth_stencil: u32 = 1;
+pub const GL_OES_point_size_array: u32 = 1;
+pub const GL_OES_point_sprite: u32 = 1;
+pub const GL_OES_primitive_bounding_box: u32 = 1;
+pub const GL_OES_query_matrix: u32 = 1;
+pub const GL_OES_read_format: u32 = 1;
+pub const GL_OES_required_internalformat: u32 = 1;
+pub const GL_OES_rgb8_rgba8: u32 = 1;
+pub const GL_OES_sample_shading: u32 = 1;
+pub const GL_OES_sample_variables: u32 = 1;
+pub const GL_OES_shader_image_atomic: u32 = 1;
+pub const GL_OES_shader_io_blocks: u32 = 1;
+pub const GL_OES_shader_multisample_interpolation: u32 = 1;
+pub const GL_OES_single_precision: u32 = 1;
+pub const GL_OES_standard_derivatives: u32 = 1;
+pub const GL_OES_stencil1: u32 = 1;
+pub const GL_OES_stencil4: u32 = 1;
+pub const GL_OES_stencil8: u32 = 1;
+pub const GL_OES_stencil_wrap: u32 = 1;
+pub const GL_OES_surfaceless_context: u32 = 1;
+pub const GL_OES_tessellation_point_size: u32 = 1;
+pub const GL_OES_tessellation_shader: u32 = 1;
+pub const GL_OES_texture_3D: u32 = 1;
+pub const GL_OES_texture_border_clamp: u32 = 1;
+pub const GL_OES_texture_buffer: u32 = 1;
+pub const GL_OES_texture_compression_astc: u32 = 1;
+pub const GL_OES_texture_cube_map: u32 = 1;
+pub const GL_OES_texture_cube_map_array: u32 = 1;
+pub const GL_OES_texture_env_crossbar: u32 = 1;
+pub const GL_OES_texture_float: u32 = 1;
+pub const GL_OES_texture_float_linear: u32 = 1;
+pub const GL_OES_texture_half_float: u32 = 1;
+pub const GL_OES_texture_half_float_linear: u32 = 1;
+pub const GL_OES_texture_mirrored_repeat: u32 = 1;
+pub const GL_OES_texture_npot: u32 = 1;
+pub const GL_OES_texture_stencil8: u32 = 1;
+pub const GL_OES_texture_storage_multisample_2d_array: u32 = 1;
+pub const GL_OES_texture_view: u32 = 1;
+pub const GL_OES_vertex_array_object: u32 = 1;
+pub const GL_OES_vertex_half_float: u32 = 1;
+pub const GL_OES_vertex_type_10_10_10_2: u32 = 1;
+pub const GL_OES_viewport_array: u32 = 1;
+pub const GL_OML_interlace: u32 = 1;
+pub const GL_OML_resample: u32 = 1;
+pub const GL_OML_subsample: u32 = 1;
+pub const GL_OVR_multiview: u32 = 1;
+pub const GL_OVR_multiview2: u32 = 1;
+pub const GL_OVR_multiview_multisampled_render_to_texture: u32 = 1;
+pub const GL_PGI_misc_hints: u32 = 1;
+pub const GL_PGI_vertex_hints: u32 = 1;
+pub const GL_QCOM_alpha_test: u32 = 1;
+pub const GL_QCOM_binning_control: u32 = 1;
+pub const GL_QCOM_driver_control: u32 = 1;
+pub const GL_QCOM_extended_get: u32 = 1;
+pub const GL_QCOM_extended_get2: u32 = 1;
+pub const GL_QCOM_perfmon_global_mode: u32 = 1;
+pub const GL_QCOM_tiled_rendering: u32 = 1;
+pub const GL_QCOM_writeonly_rendering: u32 = 1;
+pub const GL_REND_screen_coordinates: u32 = 1;
+pub const GL_S3_s3tc: u32 = 1;
+pub const GL_SGIS_detail_texture: u32 = 1;
+pub const GL_SGIS_fog_function: u32 = 1;
+pub const GL_SGIS_generate_mipmap: u32 = 1;
+pub const GL_SGIS_multisample: u32 = 1;
+pub const GL_SGIS_pixel_texture: u32 = 1;
+pub const GL_SGIS_point_line_texgen: u32 = 1;
+pub const GL_SGIS_point_parameters: u32 = 1;
+pub const GL_SGIS_sharpen_texture: u32 = 1;
+pub const GL_SGIS_texture4D: u32 = 1;
+pub const GL_SGIS_texture_border_clamp: u32 = 1;
+pub const GL_SGIS_texture_color_mask: u32 = 1;
+pub const GL_SGIS_texture_edge_clamp: u32 = 1;
+pub const GL_SGIS_texture_filter4: u32 = 1;
+pub const GL_SGIS_texture_lod: u32 = 1;
+pub const GL_SGIS_texture_select: u32 = 1;
+pub const GL_SGIX_async: u32 = 1;
+pub const GL_SGIX_async_histogram: u32 = 1;
+pub const GL_SGIX_async_pixel: u32 = 1;
+pub const GL_SGIX_blend_alpha_minmax: u32 = 1;
+pub const GL_SGIX_calligraphic_fragment: u32 = 1;
+pub const GL_SGIX_clipmap: u32 = 1;
+pub const GL_SGIX_convolution_accuracy: u32 = 1;
+pub const GL_SGIX_depth_pass_instrument: u32 = 1;
+pub const GL_SGIX_depth_texture: u32 = 1;
+pub const GL_SGIX_flush_raster: u32 = 1;
+pub const GL_SGIX_fog_offset: u32 = 1;
+pub const GL_SGIX_fragment_lighting: u32 = 1;
+pub const GL_SGIX_framezoom: u32 = 1;
+pub const GL_SGIX_igloo_interface: u32 = 1;
+pub const GL_SGIX_instruments: u32 = 1;
+pub const GL_SGIX_interlace: u32 = 1;
+pub const GL_SGIX_ir_instrument1: u32 = 1;
+pub const GL_SGIX_list_priority: u32 = 1;
+pub const GL_SGIX_pixel_texture: u32 = 1;
+pub const GL_SGIX_pixel_tiles: u32 = 1;
+pub const GL_SGIX_polynomial_ffd: u32 = 1;
+pub const GL_SGIX_reference_plane: u32 = 1;
+pub const GL_SGIX_resample: u32 = 1;
+pub const GL_SGIX_scalebias_hint: u32 = 1;
+pub const GL_SGIX_shadow: u32 = 1;
+pub const GL_SGIX_shadow_ambient: u32 = 1;
+pub const GL_SGIX_sprite: u32 = 1;
+pub const GL_SGIX_subsample: u32 = 1;
+pub const GL_SGIX_tag_sample_buffer: u32 = 1;
+pub const GL_SGIX_texture_add_env: u32 = 1;
+pub const GL_SGIX_texture_coordinate_clamp: u32 = 1;
+pub const GL_SGIX_texture_lod_bias: u32 = 1;
+pub const GL_SGIX_texture_multi_buffer: u32 = 1;
+pub const GL_SGIX_texture_scale_bias: u32 = 1;
+pub const GL_SGIX_vertex_preclip: u32 = 1;
+pub const GL_SGIX_ycrcb: u32 = 1;
+pub const GL_SGIX_ycrcb_subsample: u32 = 1;
+pub const GL_SGIX_ycrcba: u32 = 1;
+pub const GL_SGI_color_matrix: u32 = 1;
+pub const GL_SGI_color_table: u32 = 1;
+pub const GL_SGI_texture_color_table: u32 = 1;
+pub const GL_SUNX_constant_data: u32 = 1;
+pub const GL_SUN_convolution_border_modes: u32 = 1;
+pub const GL_SUN_global_alpha: u32 = 1;
+pub const GL_SUN_mesh_array: u32 = 1;
+pub const GL_SUN_slice_accum: u32 = 1;
+pub const GL_SUN_triangle_list: u32 = 1;
+pub const GL_SUN_vertex: u32 = 1;
+pub const GL_VIV_shader_binary: u32 = 1;
+pub const GL_WIN_phong_shading: u32 = 1;
+pub const GL_WIN_specular_fog: u32 = 1;
+pub const GL_NEXT_BUFFER_NV: i32 = -2;
+pub const GL_SKIP_COMPONENTS4_NV: i32 = -3;
+pub const GL_SKIP_COMPONENTS3_NV: i32 = -4;
+pub const GL_SKIP_COMPONENTS2_NV: i32 = -5;
+pub const GL_SKIP_COMPONENTS1_NV: i32 = -6;
+pub const GL_FALSE: u32 = 0;
+pub const GL_LAYOUT_DEFAULT_INTEL: u32 = 0;
+pub const GL_NONE: u32 = 0;
+pub const GL_NONE_OES: u32 = 0;
+pub const GL_NO_ERROR: u32 = 0;
+pub const GL_ZERO: u32 = 0;
+pub const GL_CLOSE_PATH_NV: u32 = 0;
+pub const GL_POINTS: u32 = 0;
+pub const GL_TERMINATE_SEQUENCE_COMMAND_NV: u32 = 0;
+pub const GL_PERFQUERY_SINGLE_CONTEXT_INTEL: u32 = 0;
+pub const GL_2X_BIT_ATI: u32 = 1;
+pub const GL_CLIENT_PIXEL_STORE_BIT: u32 = 1;
+pub const GL_COLOR_BUFFER_BIT0_QCOM: u32 = 1;
+pub const GL_CONTEXT_CORE_PROFILE_BIT: u32 = 1;
+pub const GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT: u32 = 1;
+pub const GL_CURRENT_BIT: u32 = 1;
+pub const GL_PERFQUERY_GLOBAL_CONTEXT_INTEL: u32 = 1;
+pub const GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD: u32 = 1;
+pub const GL_RED_BIT_ATI: u32 = 1;
+pub const GL_SYNC_FLUSH_COMMANDS_BIT: u32 = 1;
+pub const GL_SYNC_FLUSH_COMMANDS_BIT_APPLE: u32 = 1;
+pub const GL_TEXTURE_DEFORMATION_BIT_SGIX: u32 = 1;
+pub const GL_TEXTURE_STORAGE_SPARSE_BIT_AMD: u32 = 1;
+pub const GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT: u32 = 1;
+pub const GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT: u32 = 1;
+pub const GL_VERTEX_SHADER_BIT: u32 = 1;
+pub const GL_VERTEX_SHADER_BIT_EXT: u32 = 1;
+pub const GL_4X_BIT_ATI: u32 = 2;
+pub const GL_CLIENT_VERTEX_ARRAY_BIT: u32 = 2;
+pub const GL_COLOR_BUFFER_BIT1_QCOM: u32 = 2;
+pub const GL_COMP_BIT_ATI: u32 = 2;
+pub const GL_CONTEXT_COMPATIBILITY_PROFILE_BIT: u32 = 2;
+pub const GL_CONTEXT_FLAG_DEBUG_BIT: u32 = 2;
+pub const GL_CONTEXT_FLAG_DEBUG_BIT_KHR: u32 = 2;
+pub const GL_ELEMENT_ARRAY_BARRIER_BIT: u32 = 2;
+pub const GL_ELEMENT_ARRAY_BARRIER_BIT_EXT: u32 = 2;
+pub const GL_FRAGMENT_SHADER_BIT: u32 = 2;
+pub const GL_FRAGMENT_SHADER_BIT_EXT: u32 = 2;
+pub const GL_GEOMETRY_DEFORMATION_BIT_SGIX: u32 = 2;
+pub const GL_GREEN_BIT_ATI: u32 = 2;
+pub const GL_POINT_BIT: u32 = 2;
+pub const GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD: u32 = 2;
+pub const GL_8X_BIT_ATI: u32 = 4;
+pub const GL_BLUE_BIT_ATI: u32 = 4;
+pub const GL_COLOR_BUFFER_BIT2_QCOM: u32 = 4;
+pub const GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT: u32 = 4;
+pub const GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB: u32 = 4;
+pub const GL_GEOMETRY_SHADER_BIT: u32 = 4;
+pub const GL_GEOMETRY_SHADER_BIT_EXT: u32 = 4;
+pub const GL_GEOMETRY_SHADER_BIT_OES: u32 = 4;
+pub const GL_LINE_BIT: u32 = 4;
+pub const GL_NEGATE_BIT_ATI: u32 = 4;
+pub const GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD: u32 = 4;
+pub const GL_UNIFORM_BARRIER_BIT: u32 = 4;
+pub const GL_UNIFORM_BARRIER_BIT_EXT: u32 = 4;
+pub const GL_VERTEX23_BIT_PGI: u32 = 4;
+pub const GL_BIAS_BIT_ATI: u32 = 8;
+pub const GL_COLOR_BUFFER_BIT3_QCOM: u32 = 8;
+pub const GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR: u32 = 8;
+pub const GL_HALF_BIT_ATI: u32 = 8;
+pub const GL_POLYGON_BIT: u32 = 8;
+pub const GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD: u32 = 8;
+pub const GL_TESS_CONTROL_SHADER_BIT: u32 = 8;
+pub const GL_TESS_CONTROL_SHADER_BIT_EXT: u32 = 8;
+pub const GL_TESS_CONTROL_SHADER_BIT_OES: u32 = 8;
+pub const GL_TEXTURE_FETCH_BARRIER_BIT: u32 = 8;
+pub const GL_TEXTURE_FETCH_BARRIER_BIT_EXT: u32 = 8;
+pub const GL_VERTEX4_BIT_PGI: u32 = 8;
+pub const GL_COLOR_BUFFER_BIT4_QCOM: u32 = 16;
+pub const GL_CONTEXT_FLAG_PROTECTED_CONTENT_BIT_EXT: u32 = 16;
+pub const GL_POLYGON_STIPPLE_BIT: u32 = 16;
+pub const GL_QUARTER_BIT_ATI: u32 = 16;
+pub const GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV: u32 = 16;
+pub const GL_TESS_EVALUATION_SHADER_BIT: u32 = 16;
+pub const GL_TESS_EVALUATION_SHADER_BIT_EXT: u32 = 16;
+pub const GL_TESS_EVALUATION_SHADER_BIT_OES: u32 = 16;
+pub const GL_COLOR_BUFFER_BIT5_QCOM: u32 = 32;
+pub const GL_COMPUTE_SHADER_BIT: u32 = 32;
+pub const GL_EIGHTH_BIT_ATI: u32 = 32;
+pub const GL_PIXEL_MODE_BIT: u32 = 32;
+pub const GL_SHADER_IMAGE_ACCESS_BARRIER_BIT: u32 = 32;
+pub const GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT: u32 = 32;
+pub const GL_COLOR_BUFFER_BIT6_QCOM: u32 = 64;
+pub const GL_COMMAND_BARRIER_BIT: u32 = 64;
+pub const GL_COMMAND_BARRIER_BIT_EXT: u32 = 64;
+pub const GL_LIGHTING_BIT: u32 = 64;
+pub const GL_SATURATE_BIT_ATI: u32 = 64;
+pub const GL_COLOR_BUFFER_BIT7_QCOM: u32 = 128;
+pub const GL_FOG_BIT: u32 = 128;
+pub const GL_PIXEL_BUFFER_BARRIER_BIT: u32 = 128;
+pub const GL_PIXEL_BUFFER_BARRIER_BIT_EXT: u32 = 128;
+pub const GL_DEPTH_BUFFER_BIT: u32 = 256;
+pub const GL_DEPTH_BUFFER_BIT0_QCOM: u32 = 256;
+pub const GL_TEXTURE_UPDATE_BARRIER_BIT: u32 = 256;
+pub const GL_TEXTURE_UPDATE_BARRIER_BIT_EXT: u32 = 256;
+pub const GL_ACCUM_BUFFER_BIT: u32 = 512;
+pub const GL_BUFFER_UPDATE_BARRIER_BIT: u32 = 512;
+pub const GL_BUFFER_UPDATE_BARRIER_BIT_EXT: u32 = 512;
+pub const GL_DEPTH_BUFFER_BIT1_QCOM: u32 = 512;
+pub const GL_DEPTH_BUFFER_BIT2_QCOM: u32 = 1024;
+pub const GL_FRAMEBUFFER_BARRIER_BIT: u32 = 1024;
+pub const GL_FRAMEBUFFER_BARRIER_BIT_EXT: u32 = 1024;
+pub const GL_STENCIL_BUFFER_BIT: u32 = 1024;
+pub const GL_DEPTH_BUFFER_BIT3_QCOM: u32 = 2048;
+pub const GL_TRANSFORM_FEEDBACK_BARRIER_BIT: u32 = 2048;
+pub const GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT: u32 = 2048;
+pub const GL_VIEWPORT_BIT: u32 = 2048;
+pub const GL_ATOMIC_COUNTER_BARRIER_BIT: u32 = 4096;
+pub const GL_ATOMIC_COUNTER_BARRIER_BIT_EXT: u32 = 4096;
+pub const GL_DEPTH_BUFFER_BIT4_QCOM: u32 = 4096;
+pub const GL_TRANSFORM_BIT: u32 = 4096;
+pub const GL_DEPTH_BUFFER_BIT5_QCOM: u32 = 8192;
+pub const GL_ENABLE_BIT: u32 = 8192;
+pub const GL_SHADER_STORAGE_BARRIER_BIT: u32 = 8192;
+pub const GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT: u32 = 16384;
+pub const GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT: u32 = 16384;
+pub const GL_COLOR_BUFFER_BIT: u32 = 16384;
+pub const GL_DEPTH_BUFFER_BIT6_QCOM: u32 = 16384;
+pub const GL_COVERAGE_BUFFER_BIT_NV: u32 = 32768;
+pub const GL_DEPTH_BUFFER_BIT7_QCOM: u32 = 32768;
+pub const GL_HINT_BIT: u32 = 32768;
+pub const GL_QUERY_BUFFER_BARRIER_BIT: u32 = 32768;
+pub const GL_LINES: u32 = 1;
+pub const GL_MAP_READ_BIT: u32 = 1;
+pub const GL_MAP_READ_BIT_EXT: u32 = 1;
+pub const GL_NOP_COMMAND_NV: u32 = 1;
+pub const GL_RESTART_SUN: u32 = 1;
+pub const GL_TRACE_OPERATIONS_BIT_MESA: u32 = 1;
+pub const GL_COLOR3_BIT_PGI: u32 = 65536;
+pub const GL_EVAL_BIT: u32 = 65536;
+pub const GL_FONT_X_MIN_BOUNDS_BIT_NV: u32 = 65536;
+pub const GL_STENCIL_BUFFER_BIT0_QCOM: u32 = 65536;
+pub const GL_DRAW_ELEMENTS_COMMAND_NV: u32 = 2;
+pub const GL_LINE_LOOP: u32 = 2;
+pub const GL_MAP_WRITE_BIT: u32 = 2;
+pub const GL_MAP_WRITE_BIT_EXT: u32 = 2;
+pub const GL_REPLACE_MIDDLE_SUN: u32 = 2;
+pub const GL_TRACE_PRIMITIVES_BIT_MESA: u32 = 2;
+pub const GL_COLOR4_BIT_PGI: u32 = 131072;
+pub const GL_FONT_Y_MIN_BOUNDS_BIT_NV: u32 = 131072;
+pub const GL_LIST_BIT: u32 = 131072;
+pub const GL_STENCIL_BUFFER_BIT1_QCOM: u32 = 131072;
+pub const GL_DRAW_ARRAYS_COMMAND_NV: u32 = 3;
+pub const GL_LINE_STRIP: u32 = 3;
+pub const GL_REPLACE_OLDEST_SUN: u32 = 3;
+pub const GL_DRAW_ELEMENTS_STRIP_COMMAND_NV: u32 = 4;
+pub const GL_MAP_INVALIDATE_RANGE_BIT: u32 = 4;
+pub const GL_MAP_INVALIDATE_RANGE_BIT_EXT: u32 = 4;
+pub const GL_TRACE_ARRAYS_BIT_MESA: u32 = 4;
+pub const GL_TRIANGLES: u32 = 4;
+pub const GL_EDGEFLAG_BIT_PGI: u32 = 262144;
+pub const GL_FONT_X_MAX_BOUNDS_BIT_NV: u32 = 262144;
+pub const GL_STENCIL_BUFFER_BIT2_QCOM: u32 = 262144;
+pub const GL_TEXTURE_BIT: u32 = 262144;
+pub const GL_DRAW_ARRAYS_STRIP_COMMAND_NV: u32 = 5;
+pub const GL_TRIANGLE_STRIP: u32 = 5;
+pub const GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV: u32 = 6;
+pub const GL_TRIANGLE_FAN: u32 = 6;
+pub const GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV: u32 = 7;
+pub const GL_QUADS: u32 = 7;
+pub const GL_QUADS_EXT: u32 = 7;
+pub const GL_QUADS_OES: u32 = 7;
+pub const GL_ELEMENT_ADDRESS_COMMAND_NV: u32 = 8;
+pub const GL_MAP_INVALIDATE_BUFFER_BIT: u32 = 8;
+pub const GL_MAP_INVALIDATE_BUFFER_BIT_EXT: u32 = 8;
+pub const GL_QUAD_STRIP: u32 = 8;
+pub const GL_TRACE_TEXTURES_BIT_MESA: u32 = 8;
+pub const GL_FONT_Y_MAX_BOUNDS_BIT_NV: u32 = 524288;
+pub const GL_INDEX_BIT_PGI: u32 = 524288;
+pub const GL_SCISSOR_BIT: u32 = 524288;
+pub const GL_STENCIL_BUFFER_BIT3_QCOM: u32 = 524288;
+pub const GL_ATTRIBUTE_ADDRESS_COMMAND_NV: u32 = 9;
+pub const GL_POLYGON: u32 = 9;
+pub const GL_LINES_ADJACENCY: u32 = 10;
+pub const GL_LINES_ADJACENCY_ARB: u32 = 10;
+pub const GL_LINES_ADJACENCY_EXT: u32 = 10;
+pub const GL_LINES_ADJACENCY_OES: u32 = 10;
+pub const GL_UNIFORM_ADDRESS_COMMAND_NV: u32 = 10;
+pub const GL_BLEND_COLOR_COMMAND_NV: u32 = 11;
+pub const GL_LINE_STRIP_ADJACENCY: u32 = 11;
+pub const GL_LINE_STRIP_ADJACENCY_ARB: u32 = 11;
+pub const GL_LINE_STRIP_ADJACENCY_EXT: u32 = 11;
+pub const GL_LINE_STRIP_ADJACENCY_OES: u32 = 11;
+pub const GL_STENCIL_REF_COMMAND_NV: u32 = 12;
+pub const GL_TRIANGLES_ADJACENCY: u32 = 12;
+pub const GL_TRIANGLES_ADJACENCY_ARB: u32 = 12;
+pub const GL_TRIANGLES_ADJACENCY_EXT: u32 = 12;
+pub const GL_TRIANGLES_ADJACENCY_OES: u32 = 12;
+pub const GL_LINE_WIDTH_COMMAND_NV: u32 = 13;
+pub const GL_TRIANGLE_STRIP_ADJACENCY: u32 = 13;
+pub const GL_TRIANGLE_STRIP_ADJACENCY_ARB: u32 = 13;
+pub const GL_TRIANGLE_STRIP_ADJACENCY_EXT: u32 = 13;
+pub const GL_TRIANGLE_STRIP_ADJACENCY_OES: u32 = 13;
+pub const GL_PATCHES: u32 = 14;
+pub const GL_PATCHES_EXT: u32 = 14;
+pub const GL_PATCHES_OES: u32 = 14;
+pub const GL_POLYGON_OFFSET_COMMAND_NV: u32 = 14;
+pub const GL_ALPHA_REF_COMMAND_NV: u32 = 15;
+pub const GL_ALL_ATTRIB_BITS: u32 = 1048575;
+pub const GL_MAP_FLUSH_EXPLICIT_BIT: u32 = 16;
+pub const GL_MAP_FLUSH_EXPLICIT_BIT_EXT: u32 = 16;
+pub const GL_TRACE_PIXELS_BIT_MESA: u32 = 16;
+pub const GL_VIEWPORT_COMMAND_NV: u32 = 16;
+pub const GL_FONT_UNITS_PER_EM_BIT_NV: u32 = 1048576;
+pub const GL_MAT_AMBIENT_BIT_PGI: u32 = 1048576;
+pub const GL_STENCIL_BUFFER_BIT4_QCOM: u32 = 1048576;
+pub const GL_SCISSOR_COMMAND_NV: u32 = 17;
+pub const GL_FRONT_FACE_COMMAND_NV: u32 = 18;
+pub const GL_MAP_UNSYNCHRONIZED_BIT: u32 = 32;
+pub const GL_MAP_UNSYNCHRONIZED_BIT_EXT: u32 = 32;
+pub const GL_TRACE_ERRORS_BIT_MESA: u32 = 32;
+pub const GL_FONT_ASCENDER_BIT_NV: u32 = 2097152;
+pub const GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI: u32 = 2097152;
+pub const GL_STENCIL_BUFFER_BIT5_QCOM: u32 = 2097152;
+pub const GL_MAP_PERSISTENT_BIT: u32 = 64;
+pub const GL_MAP_PERSISTENT_BIT_EXT: u32 = 64;
+pub const GL_FONT_DESCENDER_BIT_NV: u32 = 4194304;
+pub const GL_MAT_DIFFUSE_BIT_PGI: u32 = 4194304;
+pub const GL_STENCIL_BUFFER_BIT6_QCOM: u32 = 4194304;
+pub const GL_MAP_COHERENT_BIT: u32 = 128;
+pub const GL_MAP_COHERENT_BIT_EXT: u32 = 128;
+pub const GL_FONT_HEIGHT_BIT_NV: u32 = 8388608;
+pub const GL_MAT_EMISSION_BIT_PGI: u32 = 8388608;
+pub const GL_STENCIL_BUFFER_BIT7_QCOM: u32 = 8388608;
+pub const GL_BOLD_BIT_NV: u32 = 1;
+pub const GL_GLYPH_WIDTH_BIT_NV: u32 = 1;
+pub const GL_ACCUM: u32 = 256;
+pub const GL_DYNAMIC_STORAGE_BIT: u32 = 256;
+pub const GL_DYNAMIC_STORAGE_BIT_EXT: u32 = 256;
+pub const GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV: u32 = 16777216;
+pub const GL_MAT_COLOR_INDEXES_BIT_PGI: u32 = 16777216;
+pub const GL_MULTISAMPLE_BUFFER_BIT0_QCOM: u32 = 16777216;
+pub const GL_LOAD: u32 = 257;
+pub const GL_RETURN: u32 = 258;
+pub const GL_MULT: u32 = 259;
+pub const GL_ADD: u32 = 260;
+pub const GL_GLYPH_HEIGHT_BIT_NV: u32 = 2;
+pub const GL_ITALIC_BIT_NV: u32 = 2;
+pub const GL_MOVE_TO_NV: u32 = 2;
+pub const GL_CLIENT_STORAGE_BIT: u32 = 512;
+pub const GL_CLIENT_STORAGE_BIT_EXT: u32 = 512;
+pub const GL_NEVER: u32 = 512;
+pub const GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV: u32 = 33554432;
+pub const GL_MAT_SHININESS_BIT_PGI: u32 = 33554432;
+pub const GL_MULTISAMPLE_BUFFER_BIT1_QCOM: u32 = 33554432;
+pub const GL_LESS: u32 = 513;
+pub const GL_EQUAL: u32 = 514;
+pub const GL_LEQUAL: u32 = 515;
+pub const GL_GREATER: u32 = 516;
+pub const GL_NOTEQUAL: u32 = 517;
+pub const GL_GEQUAL: u32 = 518;
+pub const GL_ALWAYS: u32 = 519;
+pub const GL_RELATIVE_MOVE_TO_NV: u32 = 3;
+pub const GL_SRC_COLOR: u32 = 768;
+pub const GL_ONE_MINUS_SRC_COLOR: u32 = 769;
+pub const GL_SRC_ALPHA: u32 = 770;
+pub const GL_ONE_MINUS_SRC_ALPHA: u32 = 771;
+pub const GL_DST_ALPHA: u32 = 772;
+pub const GL_ONE_MINUS_DST_ALPHA: u32 = 773;
+pub const GL_DST_COLOR: u32 = 774;
+pub const GL_ONE_MINUS_DST_COLOR: u32 = 775;
+pub const GL_SRC_ALPHA_SATURATE: u32 = 776;
+pub const GL_SRC_ALPHA_SATURATE_EXT: u32 = 776;
+pub const GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV: u32 = 4;
+pub const GL_LINE_TO_NV: u32 = 4;
+pub const GL_FRONT_LEFT: u32 = 1024;
+pub const GL_SPARSE_STORAGE_BIT_ARB: u32 = 1024;
+pub const GL_FONT_UNDERLINE_POSITION_BIT_NV: u32 = 67108864;
+pub const GL_MAT_SPECULAR_BIT_PGI: u32 = 67108864;
+pub const GL_MULTISAMPLE_BUFFER_BIT2_QCOM: u32 = 67108864;
+pub const GL_FRONT_RIGHT: u32 = 1025;
+pub const GL_BACK_LEFT: u32 = 1026;
+pub const GL_BACK_RIGHT: u32 = 1027;
+pub const GL_FRONT: u32 = 1028;
+pub const GL_BACK: u32 = 1029;
+pub const GL_LEFT: u32 = 1030;
+pub const GL_RIGHT: u32 = 1031;
+pub const GL_FRONT_AND_BACK: u32 = 1032;
+pub const GL_AUX0: u32 = 1033;
+pub const GL_AUX1: u32 = 1034;
+pub const GL_AUX2: u32 = 1035;
+pub const GL_AUX3: u32 = 1036;
+pub const GL_RELATIVE_LINE_TO_NV: u32 = 5;
+pub const GL_INVALID_ENUM: u32 = 1280;
+pub const GL_INVALID_VALUE: u32 = 1281;
+pub const GL_INVALID_OPERATION: u32 = 1282;
+pub const GL_STACK_OVERFLOW: u32 = 1283;
+pub const GL_STACK_OVERFLOW_KHR: u32 = 1283;
+pub const GL_STACK_UNDERFLOW: u32 = 1284;
+pub const GL_STACK_UNDERFLOW_KHR: u32 = 1284;
+pub const GL_OUT_OF_MEMORY: u32 = 1285;
+pub const GL_INVALID_FRAMEBUFFER_OPERATION: u32 = 1286;
+pub const GL_INVALID_FRAMEBUFFER_OPERATION_EXT: u32 = 1286;
+pub const GL_INVALID_FRAMEBUFFER_OPERATION_OES: u32 = 1286;
+pub const GL_CONTEXT_LOST: u32 = 1287;
+pub const GL_CONTEXT_LOST_KHR: u32 = 1287;
+pub const GL_HORIZONTAL_LINE_TO_NV: u32 = 6;
+pub const GL_2D: u32 = 1536;
+pub const GL_3D: u32 = 1537;
+pub const GL_3D_COLOR: u32 = 1538;
+pub const GL_3D_COLOR_TEXTURE: u32 = 1539;
+pub const GL_4D_COLOR_TEXTURE: u32 = 1540;
+pub const GL_RELATIVE_HORIZONTAL_LINE_TO_NV: u32 = 7;
+pub const GL_PASS_THROUGH_TOKEN: u32 = 1792;
+pub const GL_POINT_TOKEN: u32 = 1793;
+pub const GL_LINE_TOKEN: u32 = 1794;
+pub const GL_POLYGON_TOKEN: u32 = 1795;
+pub const GL_BITMAP_TOKEN: u32 = 1796;
+pub const GL_DRAW_PIXEL_TOKEN: u32 = 1797;
+pub const GL_COPY_PIXEL_TOKEN: u32 = 1798;
+pub const GL_LINE_RESET_TOKEN: u32 = 1799;
+pub const GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV: u32 = 8;
+pub const GL_VERTICAL_LINE_TO_NV: u32 = 8;
+pub const GL_EXP: u32 = 2048;
+pub const GL_FONT_UNDERLINE_THICKNESS_BIT_NV: u32 = 134217728;
+pub const GL_MULTISAMPLE_BUFFER_BIT3_QCOM: u32 = 134217728;
+pub const GL_NORMAL_BIT_PGI: u32 = 134217728;
+pub const GL_EXP2: u32 = 2049;
+pub const GL_RELATIVE_VERTICAL_LINE_TO_NV: u32 = 9;
+pub const GL_CW: u32 = 2304;
+pub const GL_CCW: u32 = 2305;
+pub const GL_QUADRATIC_CURVE_TO_NV: u32 = 10;
+pub const GL_COEFF: u32 = 2560;
+pub const GL_ORDER: u32 = 2561;
+pub const GL_DOMAIN: u32 = 2562;
+pub const GL_RELATIVE_QUADRATIC_CURVE_TO_NV: u32 = 11;
+pub const GL_CURRENT_COLOR: u32 = 2816;
+pub const GL_CURRENT_INDEX: u32 = 2817;
+pub const GL_CURRENT_NORMAL: u32 = 2818;
+pub const GL_CURRENT_TEXTURE_COORDS: u32 = 2819;
+pub const GL_CURRENT_RASTER_COLOR: u32 = 2820;
+pub const GL_CURRENT_RASTER_INDEX: u32 = 2821;
+pub const GL_CURRENT_RASTER_TEXTURE_COORDS: u32 = 2822;
+pub const GL_CURRENT_RASTER_POSITION: u32 = 2823;
+pub const GL_CURRENT_RASTER_POSITION_VALID: u32 = 2824;
+pub const GL_CURRENT_RASTER_DISTANCE: u32 = 2825;
+pub const GL_POINT_SMOOTH: u32 = 2832;
+pub const GL_POINT_SIZE: u32 = 2833;
+pub const GL_POINT_SIZE_RANGE: u32 = 2834;
+pub const GL_SMOOTH_POINT_SIZE_RANGE: u32 = 2834;
+pub const GL_POINT_SIZE_GRANULARITY: u32 = 2835;
+pub const GL_SMOOTH_POINT_SIZE_GRANULARITY: u32 = 2835;
+pub const GL_LINE_SMOOTH: u32 = 2848;
+pub const GL_LINE_WIDTH: u32 = 2849;
+pub const GL_LINE_WIDTH_RANGE: u32 = 2850;
+pub const GL_SMOOTH_LINE_WIDTH_RANGE: u32 = 2850;
+pub const GL_LINE_WIDTH_GRANULARITY: u32 = 2851;
+pub const GL_SMOOTH_LINE_WIDTH_GRANULARITY: u32 = 2851;
+pub const GL_LINE_STIPPLE: u32 = 2852;
+pub const GL_LINE_STIPPLE_PATTERN: u32 = 2853;
+pub const GL_LINE_STIPPLE_REPEAT: u32 = 2854;
+pub const GL_LIST_MODE: u32 = 2864;
+pub const GL_MAX_LIST_NESTING: u32 = 2865;
+pub const GL_LIST_BASE: u32 = 2866;
+pub const GL_LIST_INDEX: u32 = 2867;
+pub const GL_POLYGON_MODE: u32 = 2880;
+pub const GL_POLYGON_MODE_NV: u32 = 2880;
+pub const GL_POLYGON_SMOOTH: u32 = 2881;
+pub const GL_POLYGON_STIPPLE: u32 = 2882;
+pub const GL_EDGE_FLAG: u32 = 2883;
+pub const GL_CULL_FACE: u32 = 2884;
+pub const GL_CULL_FACE_MODE: u32 = 2885;
+pub const GL_FRONT_FACE: u32 = 2886;
+pub const GL_LIGHTING: u32 = 2896;
+pub const GL_LIGHT_MODEL_LOCAL_VIEWER: u32 = 2897;
+pub const GL_LIGHT_MODEL_TWO_SIDE: u32 = 2898;
+pub const GL_LIGHT_MODEL_AMBIENT: u32 = 2899;
+pub const GL_SHADE_MODEL: u32 = 2900;
+pub const GL_COLOR_MATERIAL_FACE: u32 = 2901;
+pub const GL_COLOR_MATERIAL_PARAMETER: u32 = 2902;
+pub const GL_COLOR_MATERIAL: u32 = 2903;
+pub const GL_FOG: u32 = 2912;
+pub const GL_FOG_INDEX: u32 = 2913;
+pub const GL_FOG_DENSITY: u32 = 2914;
+pub const GL_FOG_START: u32 = 2915;
+pub const GL_FOG_END: u32 = 2916;
+pub const GL_FOG_MODE: u32 = 2917;
+pub const GL_FOG_COLOR: u32 = 2918;
+pub const GL_DEPTH_RANGE: u32 = 2928;
+pub const GL_DEPTH_TEST: u32 = 2929;
+pub const GL_DEPTH_WRITEMASK: u32 = 2930;
+pub const GL_DEPTH_CLEAR_VALUE: u32 = 2931;
+pub const GL_DEPTH_FUNC: u32 = 2932;
+pub const GL_ACCUM_CLEAR_VALUE: u32 = 2944;
+pub const GL_STENCIL_TEST: u32 = 2960;
+pub const GL_STENCIL_CLEAR_VALUE: u32 = 2961;
+pub const GL_STENCIL_FUNC: u32 = 2962;
+pub const GL_STENCIL_VALUE_MASK: u32 = 2963;
+pub const GL_STENCIL_FAIL: u32 = 2964;
+pub const GL_STENCIL_PASS_DEPTH_FAIL: u32 = 2965;
+pub const GL_STENCIL_PASS_DEPTH_PASS: u32 = 2966;
+pub const GL_STENCIL_REF: u32 = 2967;
+pub const GL_STENCIL_WRITEMASK: u32 = 2968;
+pub const GL_MATRIX_MODE: u32 = 2976;
+pub const GL_NORMALIZE: u32 = 2977;
+pub const GL_VIEWPORT: u32 = 2978;
+pub const GL_MODELVIEW0_STACK_DEPTH_EXT: u32 = 2979;
+pub const GL_MODELVIEW_STACK_DEPTH: u32 = 2979;
+pub const GL_PATH_MODELVIEW_STACK_DEPTH_NV: u32 = 2979;
+pub const GL_PATH_PROJECTION_STACK_DEPTH_NV: u32 = 2980;
+pub const GL_PROJECTION_STACK_DEPTH: u32 = 2980;
+pub const GL_TEXTURE_STACK_DEPTH: u32 = 2981;
+pub const GL_MODELVIEW0_MATRIX_EXT: u32 = 2982;
+pub const GL_MODELVIEW_MATRIX: u32 = 2982;
+pub const GL_PATH_MODELVIEW_MATRIX_NV: u32 = 2982;
+pub const GL_PATH_PROJECTION_MATRIX_NV: u32 = 2983;
+pub const GL_PROJECTION_MATRIX: u32 = 2983;
+pub const GL_TEXTURE_MATRIX: u32 = 2984;
+pub const GL_ATTRIB_STACK_DEPTH: u32 = 2992;
+pub const GL_CLIENT_ATTRIB_STACK_DEPTH: u32 = 2993;
+pub const GL_ALPHA_TEST: u32 = 3008;
+pub const GL_ALPHA_TEST_QCOM: u32 = 3008;
+pub const GL_ALPHA_TEST_FUNC: u32 = 3009;
+pub const GL_ALPHA_TEST_FUNC_QCOM: u32 = 3009;
+pub const GL_ALPHA_TEST_REF: u32 = 3010;
+pub const GL_ALPHA_TEST_REF_QCOM: u32 = 3010;
+pub const GL_DITHER: u32 = 3024;
+pub const GL_BLEND_DST: u32 = 3040;
+pub const GL_BLEND_SRC: u32 = 3041;
+pub const GL_BLEND: u32 = 3042;
+pub const GL_LOGIC_OP_MODE: u32 = 3056;
+pub const GL_INDEX_LOGIC_OP: u32 = 3057;
+pub const GL_LOGIC_OP: u32 = 3057;
+pub const GL_COLOR_LOGIC_OP: u32 = 3058;
+pub const GL_CUBIC_CURVE_TO_NV: u32 = 12;
+pub const GL_AUX_BUFFERS: u32 = 3072;
+pub const GL_DRAW_BUFFER: u32 = 3073;
+pub const GL_DRAW_BUFFER_EXT: u32 = 3073;
+pub const GL_READ_BUFFER: u32 = 3074;
+pub const GL_READ_BUFFER_EXT: u32 = 3074;
+pub const GL_READ_BUFFER_NV: u32 = 3074;
+pub const GL_SCISSOR_BOX: u32 = 3088;
+pub const GL_SCISSOR_TEST: u32 = 3089;
+pub const GL_INDEX_CLEAR_VALUE: u32 = 3104;
+pub const GL_INDEX_WRITEMASK: u32 = 3105;
+pub const GL_COLOR_CLEAR_VALUE: u32 = 3106;
+pub const GL_COLOR_WRITEMASK: u32 = 3107;
+pub const GL_INDEX_MODE: u32 = 3120;
+pub const GL_RGBA_MODE: u32 = 3121;
+pub const GL_DOUBLEBUFFER: u32 = 3122;
+pub const GL_STEREO: u32 = 3123;
+pub const GL_RENDER_MODE: u32 = 3136;
+pub const GL_PERSPECTIVE_CORRECTION_HINT: u32 = 3152;
+pub const GL_POINT_SMOOTH_HINT: u32 = 3153;
+pub const GL_LINE_SMOOTH_HINT: u32 = 3154;
+pub const GL_POLYGON_SMOOTH_HINT: u32 = 3155;
+pub const GL_FOG_HINT: u32 = 3156;
+pub const GL_TEXTURE_GEN_S: u32 = 3168;
+pub const GL_TEXTURE_GEN_T: u32 = 3169;
+pub const GL_TEXTURE_GEN_R: u32 = 3170;
+pub const GL_TEXTURE_GEN_Q: u32 = 3171;
+pub const GL_PIXEL_MAP_I_TO_I: u32 = 3184;
+pub const GL_PIXEL_MAP_S_TO_S: u32 = 3185;
+pub const GL_PIXEL_MAP_I_TO_R: u32 = 3186;
+pub const GL_PIXEL_MAP_I_TO_G: u32 = 3187;
+pub const GL_PIXEL_MAP_I_TO_B: u32 = 3188;
+pub const GL_PIXEL_MAP_I_TO_A: u32 = 3189;
+pub const GL_PIXEL_MAP_R_TO_R: u32 = 3190;
+pub const GL_PIXEL_MAP_G_TO_G: u32 = 3191;
+pub const GL_PIXEL_MAP_B_TO_B: u32 = 3192;
+pub const GL_PIXEL_MAP_A_TO_A: u32 = 3193;
+pub const GL_PIXEL_MAP_I_TO_I_SIZE: u32 = 3248;
+pub const GL_PIXEL_MAP_S_TO_S_SIZE: u32 = 3249;
+pub const GL_PIXEL_MAP_I_TO_R_SIZE: u32 = 3250;
+pub const GL_PIXEL_MAP_I_TO_G_SIZE: u32 = 3251;
+pub const GL_PIXEL_MAP_I_TO_B_SIZE: u32 = 3252;
+pub const GL_PIXEL_MAP_I_TO_A_SIZE: u32 = 3253;
+pub const GL_PIXEL_MAP_R_TO_R_SIZE: u32 = 3254;
+pub const GL_PIXEL_MAP_G_TO_G_SIZE: u32 = 3255;
+pub const GL_PIXEL_MAP_B_TO_B_SIZE: u32 = 3256;
+pub const GL_PIXEL_MAP_A_TO_A_SIZE: u32 = 3257;
+pub const GL_UNPACK_SWAP_BYTES: u32 = 3312;
+pub const GL_UNPACK_LSB_FIRST: u32 = 3313;
+pub const GL_UNPACK_ROW_LENGTH: u32 = 3314;
+pub const GL_UNPACK_ROW_LENGTH_EXT: u32 = 3314;
+pub const GL_UNPACK_SKIP_ROWS: u32 = 3315;
+pub const GL_UNPACK_SKIP_ROWS_EXT: u32 = 3315;
+pub const GL_UNPACK_SKIP_PIXELS: u32 = 3316;
+pub const GL_UNPACK_SKIP_PIXELS_EXT: u32 = 3316;
+pub const GL_UNPACK_ALIGNMENT: u32 = 3317;
+pub const GL_RELATIVE_CUBIC_CURVE_TO_NV: u32 = 13;
+pub const GL_PACK_SWAP_BYTES: u32 = 3328;
+pub const GL_PACK_LSB_FIRST: u32 = 3329;
+pub const GL_PACK_ROW_LENGTH: u32 = 3330;
+pub const GL_PACK_SKIP_ROWS: u32 = 3331;
+pub const GL_PACK_SKIP_PIXELS: u32 = 3332;
+pub const GL_PACK_ALIGNMENT: u32 = 3333;
+pub const GL_MAP_COLOR: u32 = 3344;
+pub const GL_MAP_STENCIL: u32 = 3345;
+pub const GL_INDEX_SHIFT: u32 = 3346;
+pub const GL_INDEX_OFFSET: u32 = 3347;
+pub const GL_RED_SCALE: u32 = 3348;
+pub const GL_RED_BIAS: u32 = 3349;
+pub const GL_ZOOM_X: u32 = 3350;
+pub const GL_ZOOM_Y: u32 = 3351;
+pub const GL_GREEN_SCALE: u32 = 3352;
+pub const GL_GREEN_BIAS: u32 = 3353;
+pub const GL_BLUE_SCALE: u32 = 3354;
+pub const GL_BLUE_BIAS: u32 = 3355;
+pub const GL_ALPHA_SCALE: u32 = 3356;
+pub const GL_ALPHA_BIAS: u32 = 3357;
+pub const GL_DEPTH_SCALE: u32 = 3358;
+pub const GL_DEPTH_BIAS: u32 = 3359;
+pub const GL_MAX_EVAL_ORDER: u32 = 3376;
+pub const GL_MAX_LIGHTS: u32 = 3377;
+pub const GL_MAX_CLIP_DISTANCES: u32 = 3378;
+pub const GL_MAX_CLIP_DISTANCES_APPLE: u32 = 3378;
+pub const GL_MAX_CLIP_DISTANCES_EXT: u32 = 3378;
+pub const GL_MAX_CLIP_PLANES: u32 = 3378;
+pub const GL_MAX_CLIP_PLANES_IMG: u32 = 3378;
+pub const GL_MAX_TEXTURE_SIZE: u32 = 3379;
+pub const GL_MAX_PIXEL_MAP_TABLE: u32 = 3380;
+pub const GL_MAX_ATTRIB_STACK_DEPTH: u32 = 3381;
+pub const GL_MAX_MODELVIEW_STACK_DEPTH: u32 = 3382;
+pub const GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV: u32 = 3382;
+pub const GL_MAX_NAME_STACK_DEPTH: u32 = 3383;
+pub const GL_MAX_PROJECTION_STACK_DEPTH: u32 = 3384;
+pub const GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV: u32 = 3384;
+pub const GL_MAX_TEXTURE_STACK_DEPTH: u32 = 3385;
+pub const GL_MAX_VIEWPORT_DIMS: u32 = 3386;
+pub const GL_MAX_CLIENT_ATTRIB_STACK_DEPTH: u32 = 3387;
+pub const GL_SUBPIXEL_BITS: u32 = 3408;
+pub const GL_INDEX_BITS: u32 = 3409;
+pub const GL_RED_BITS: u32 = 3410;
+pub const GL_GREEN_BITS: u32 = 3411;
+pub const GL_BLUE_BITS: u32 = 3412;
+pub const GL_ALPHA_BITS: u32 = 3413;
+pub const GL_DEPTH_BITS: u32 = 3414;
+pub const GL_STENCIL_BITS: u32 = 3415;
+pub const GL_ACCUM_RED_BITS: u32 = 3416;
+pub const GL_ACCUM_GREEN_BITS: u32 = 3417;
+pub const GL_ACCUM_BLUE_BITS: u32 = 3418;
+pub const GL_ACCUM_ALPHA_BITS: u32 = 3419;
+pub const GL_NAME_STACK_DEPTH: u32 = 3440;
+pub const GL_AUTO_NORMAL: u32 = 3456;
+pub const GL_MAP1_COLOR_4: u32 = 3472;
+pub const GL_MAP1_INDEX: u32 = 3473;
+pub const GL_MAP1_NORMAL: u32 = 3474;
+pub const GL_MAP1_TEXTURE_COORD_1: u32 = 3475;
+pub const GL_MAP1_TEXTURE_COORD_2: u32 = 3476;
+pub const GL_MAP1_TEXTURE_COORD_3: u32 = 3477;
+pub const GL_MAP1_TEXTURE_COORD_4: u32 = 3478;
+pub const GL_MAP1_VERTEX_3: u32 = 3479;
+pub const GL_MAP1_VERTEX_4: u32 = 3480;
+pub const GL_MAP2_COLOR_4: u32 = 3504;
+pub const GL_MAP2_INDEX: u32 = 3505;
+pub const GL_MAP2_NORMAL: u32 = 3506;
+pub const GL_MAP2_TEXTURE_COORD_1: u32 = 3507;
+pub const GL_MAP2_TEXTURE_COORD_2: u32 = 3508;
+pub const GL_MAP2_TEXTURE_COORD_3: u32 = 3509;
+pub const GL_MAP2_TEXTURE_COORD_4: u32 = 3510;
+pub const GL_MAP2_VERTEX_3: u32 = 3511;
+pub const GL_MAP2_VERTEX_4: u32 = 3512;
+pub const GL_MAP1_GRID_DOMAIN: u32 = 3536;
+pub const GL_MAP1_GRID_SEGMENTS: u32 = 3537;
+pub const GL_MAP2_GRID_DOMAIN: u32 = 3538;
+pub const GL_MAP2_GRID_SEGMENTS: u32 = 3539;
+pub const GL_TEXTURE_1D: u32 = 3552;
+pub const GL_TEXTURE_2D: u32 = 3553;
+pub const GL_FEEDBACK_BUFFER_POINTER: u32 = 3568;
+pub const GL_FEEDBACK_BUFFER_SIZE: u32 = 3569;
+pub const GL_FEEDBACK_BUFFER_TYPE: u32 = 3570;
+pub const GL_SELECTION_BUFFER_POINTER: u32 = 3571;
+pub const GL_SELECTION_BUFFER_SIZE: u32 = 3572;
+pub const GL_SMOOTH_QUADRATIC_CURVE_TO_NV: u32 = 14;
+pub const GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV: u32 = 15;
+pub const GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV: u32 = 16;
+pub const GL_SMOOTH_CUBIC_CURVE_TO_NV: u32 = 16;
+pub const GL_GLYPH_HAS_KERNING_BIT_NV: u32 = 256;
+pub const GL_TEXTURE_WIDTH: u32 = 4096;
+pub const GL_FONT_HAS_KERNING_BIT_NV: u32 = 268435456;
+pub const GL_MULTISAMPLE_BUFFER_BIT4_QCOM: u32 = 268435456;
+pub const GL_TEXCOORD1_BIT_PGI: u32 = 268435456;
+pub const GL_TEXTURE_HEIGHT: u32 = 4097;
+pub const GL_TEXTURE_COMPONENTS: u32 = 4099;
+pub const GL_TEXTURE_INTERNAL_FORMAT: u32 = 4099;
+pub const GL_TEXTURE_BORDER_COLOR: u32 = 4100;
+pub const GL_TEXTURE_BORDER_COLOR_EXT: u32 = 4100;
+pub const GL_TEXTURE_BORDER_COLOR_NV: u32 = 4100;
+pub const GL_TEXTURE_BORDER_COLOR_OES: u32 = 4100;
+pub const GL_TEXTURE_BORDER: u32 = 4101;
+pub const GL_TEXTURE_TARGET: u32 = 4102;
+pub const GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV: u32 = 17;
+pub const GL_DONT_CARE: u32 = 4352;
+pub const GL_FASTEST: u32 = 4353;
+pub const GL_NICEST: u32 = 4354;
+pub const GL_SMALL_CCW_ARC_TO_NV: u32 = 18;
+pub const GL_AMBIENT: u32 = 4608;
+pub const GL_DIFFUSE: u32 = 4609;
+pub const GL_SPECULAR: u32 = 4610;
+pub const GL_POSITION: u32 = 4611;
+pub const GL_SPOT_DIRECTION: u32 = 4612;
+pub const GL_SPOT_EXPONENT: u32 = 4613;
+pub const GL_SPOT_CUTOFF: u32 = 4614;
+pub const GL_CONSTANT_ATTENUATION: u32 = 4615;
+pub const GL_LINEAR_ATTENUATION: u32 = 4616;
+pub const GL_QUADRATIC_ATTENUATION: u32 = 4617;
+pub const GL_RELATIVE_SMALL_CCW_ARC_TO_NV: u32 = 19;
+pub const GL_COMPILE: u32 = 4864;
+pub const GL_COMPILE_AND_EXECUTE: u32 = 4865;
+pub const GL_SMALL_CW_ARC_TO_NV: u32 = 20;
+pub const GL_BYTE: u32 = 5120;
+pub const GL_UNSIGNED_BYTE: u32 = 5121;
+pub const GL_SHORT: u32 = 5122;
+pub const GL_UNSIGNED_SHORT: u32 = 5123;
+pub const GL_INT: u32 = 5124;
+pub const GL_UNSIGNED_INT: u32 = 5125;
+pub const GL_FLOAT: u32 = 5126;
+pub const GL_2_BYTES: u32 = 5127;
+pub const GL_2_BYTES_NV: u32 = 5127;
+pub const GL_3_BYTES: u32 = 5128;
+pub const GL_3_BYTES_NV: u32 = 5128;
+pub const GL_4_BYTES: u32 = 5129;
+pub const GL_4_BYTES_NV: u32 = 5129;
+pub const GL_DOUBLE: u32 = 5130;
+pub const GL_DOUBLE_EXT: u32 = 5130;
+pub const GL_HALF_APPLE: u32 = 5131;
+pub const GL_HALF_FLOAT: u32 = 5131;
+pub const GL_HALF_FLOAT_ARB: u32 = 5131;
+pub const GL_HALF_FLOAT_NV: u32 = 5131;
+pub const GL_FIXED: u32 = 5132;
+pub const GL_FIXED_OES: u32 = 5132;
+pub const GL_INT64_ARB: u32 = 5134;
+pub const GL_INT64_NV: u32 = 5134;
+pub const GL_UNSIGNED_INT64_ARB: u32 = 5135;
+pub const GL_UNSIGNED_INT64_NV: u32 = 5135;
+pub const GL_RELATIVE_SMALL_CW_ARC_TO_NV: u32 = 21;
+pub const GL_CLEAR: u32 = 5376;
+pub const GL_AND: u32 = 5377;
+pub const GL_AND_REVERSE: u32 = 5378;
+pub const GL_COPY: u32 = 5379;
+pub const GL_AND_INVERTED: u32 = 5380;
+pub const GL_NOOP: u32 = 5381;
+pub const GL_XOR: u32 = 5382;
+pub const GL_XOR_NV: u32 = 5382;
+pub const GL_OR: u32 = 5383;
+pub const GL_NOR: u32 = 5384;
+pub const GL_EQUIV: u32 = 5385;
+pub const GL_INVERT: u32 = 5386;
+pub const GL_OR_REVERSE: u32 = 5387;
+pub const GL_COPY_INVERTED: u32 = 5388;
+pub const GL_OR_INVERTED: u32 = 5389;
+pub const GL_NAND: u32 = 5390;
+pub const GL_SET: u32 = 5391;
+pub const GL_LARGE_CCW_ARC_TO_NV: u32 = 22;
+pub const GL_EMISSION: u32 = 5632;
+pub const GL_SHININESS: u32 = 5633;
+pub const GL_AMBIENT_AND_DIFFUSE: u32 = 5634;
+pub const GL_COLOR_INDEXES: u32 = 5635;
+pub const GL_RELATIVE_LARGE_CCW_ARC_TO_NV: u32 = 23;
+pub const GL_MODELVIEW: u32 = 5888;
+pub const GL_MODELVIEW0_ARB: u32 = 5888;
+pub const GL_MODELVIEW0_EXT: u32 = 5888;
+pub const GL_PATH_MODELVIEW_NV: u32 = 5888;
+pub const GL_PATH_PROJECTION_NV: u32 = 5889;
+pub const GL_PROJECTION: u32 = 5889;
+pub const GL_TEXTURE: u32 = 5890;
+pub const GL_LARGE_CW_ARC_TO_NV: u32 = 24;
+pub const GL_COLOR: u32 = 6144;
+pub const GL_COLOR_EXT: u32 = 6144;
+pub const GL_DEPTH: u32 = 6145;
+pub const GL_DEPTH_EXT: u32 = 6145;
+pub const GL_STENCIL: u32 = 6146;
+pub const GL_STENCIL_EXT: u32 = 6146;
+pub const GL_RELATIVE_LARGE_CW_ARC_TO_NV: u32 = 25;
+pub const GL_COLOR_INDEX: u32 = 6400;
+pub const GL_STENCIL_INDEX: u32 = 6401;
+pub const GL_STENCIL_INDEX_OES: u32 = 6401;
+pub const GL_DEPTH_COMPONENT: u32 = 6402;
+pub const GL_RED: u32 = 6403;
+pub const GL_RED_EXT: u32 = 6403;
+pub const GL_RED_NV: u32 = 6403;
+pub const GL_GREEN: u32 = 6404;
+pub const GL_GREEN_NV: u32 = 6404;
+pub const GL_BLUE: u32 = 6405;
+pub const GL_BLUE_NV: u32 = 6405;
+pub const GL_ALPHA: u32 = 6406;
+pub const GL_RGB: u32 = 6407;
+pub const GL_RGBA: u32 = 6408;
+pub const GL_LUMINANCE: u32 = 6409;
+pub const GL_LUMINANCE_ALPHA: u32 = 6410;
+pub const GL_RASTER_POSITION_UNCLIPPED_IBM: u32 = 103010;
+pub const GL_CONIC_CURVE_TO_NV: u32 = 26;
+pub const GL_BITMAP: u32 = 6656;
+pub const GL_PREFER_DOUBLEBUFFER_HINT_PGI: u32 = 107000;
+pub const GL_CONSERVE_MEMORY_HINT_PGI: u32 = 107005;
+pub const GL_RECLAIM_MEMORY_HINT_PGI: u32 = 107006;
+pub const GL_NATIVE_GRAPHICS_HANDLE_PGI: u32 = 107010;
+pub const GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI: u32 = 107011;
+pub const GL_NATIVE_GRAPHICS_END_HINT_PGI: u32 = 107012;
+pub const GL_ALWAYS_FAST_HINT_PGI: u32 = 107020;
+pub const GL_ALWAYS_SOFT_HINT_PGI: u32 = 107021;
+pub const GL_ALLOW_DRAW_OBJ_HINT_PGI: u32 = 107022;
+pub const GL_ALLOW_DRAW_WIN_HINT_PGI: u32 = 107023;
+pub const GL_ALLOW_DRAW_FRG_HINT_PGI: u32 = 107024;
+pub const GL_ALLOW_DRAW_MEM_HINT_PGI: u32 = 107025;
+pub const GL_STRICT_DEPTHFUNC_HINT_PGI: u32 = 107030;
+pub const GL_STRICT_LIGHTING_HINT_PGI: u32 = 107031;
+pub const GL_STRICT_SCISSOR_HINT_PGI: u32 = 107032;
+pub const GL_FULL_STIPPLE_HINT_PGI: u32 = 107033;
+pub const GL_CLIP_NEAR_HINT_PGI: u32 = 107040;
+pub const GL_CLIP_FAR_HINT_PGI: u32 = 107041;
+pub const GL_WIDE_LINE_HINT_PGI: u32 = 107042;
+pub const GL_BACK_NORMALS_HINT_PGI: u32 = 107043;
+pub const GL_VERTEX_DATA_HINT_PGI: u32 = 107050;
+pub const GL_VERTEX_CONSISTENT_HINT_PGI: u32 = 107051;
+pub const GL_MATERIAL_SIDE_HINT_PGI: u32 = 107052;
+pub const GL_MAX_VERTEX_HINT_PGI: u32 = 107053;
+pub const GL_RELATIVE_CONIC_CURVE_TO_NV: u32 = 27;
+pub const GL_POINT: u32 = 6912;
+pub const GL_POINT_NV: u32 = 6912;
+pub const GL_LINE: u32 = 6913;
+pub const GL_LINE_NV: u32 = 6913;
+pub const GL_FILL: u32 = 6914;
+pub const GL_FILL_NV: u32 = 6914;
+pub const GL_RENDER: u32 = 7168;
+pub const GL_FEEDBACK: u32 = 7169;
+pub const GL_SELECT: u32 = 7170;
+pub const GL_FLAT: u32 = 7424;
+pub const GL_SMOOTH: u32 = 7425;
+pub const GL_KEEP: u32 = 7680;
+pub const GL_REPLACE: u32 = 7681;
+pub const GL_INCR: u32 = 7682;
+pub const GL_DECR: u32 = 7683;
+pub const GL_VENDOR: u32 = 7936;
+pub const GL_RENDERER: u32 = 7937;
+pub const GL_VERSION: u32 = 7938;
+pub const GL_EXTENSIONS: u32 = 7939;
+pub const GL_GLYPH_VERTICAL_BEARING_X_BIT_NV: u32 = 32;
+pub const GL_S: u32 = 8192;
+pub const GL_FONT_NUM_GLYPH_INDICES_BIT_NV: u32 = 536870912;
+pub const GL_MULTISAMPLE_BIT: u32 = 536870912;
+pub const GL_MULTISAMPLE_BIT_3DFX: u32 = 536870912;
+pub const GL_MULTISAMPLE_BIT_ARB: u32 = 536870912;
+pub const GL_MULTISAMPLE_BIT_EXT: u32 = 536870912;
+pub const GL_MULTISAMPLE_BUFFER_BIT5_QCOM: u32 = 536870912;
+pub const GL_TEXCOORD2_BIT_PGI: u32 = 536870912;
+pub const GL_T: u32 = 8193;
+pub const GL_R: u32 = 8194;
+pub const GL_Q: u32 = 8195;
+pub const GL_MODULATE: u32 = 8448;
+pub const GL_DECAL: u32 = 8449;
+pub const GL_TEXTURE_ENV_MODE: u32 = 8704;
+pub const GL_TEXTURE_ENV_COLOR: u32 = 8705;
+pub const GL_TEXTURE_ENV: u32 = 8960;
+pub const GL_EYE_LINEAR: u32 = 9216;
+pub const GL_EYE_LINEAR_NV: u32 = 9216;
+pub const GL_OBJECT_LINEAR: u32 = 9217;
+pub const GL_OBJECT_LINEAR_NV: u32 = 9217;
+pub const GL_SPHERE_MAP: u32 = 9218;
+pub const GL_TEXTURE_GEN_MODE: u32 = 9472;
+pub const GL_TEXTURE_GEN_MODE_OES: u32 = 9472;
+pub const GL_OBJECT_PLANE: u32 = 9473;
+pub const GL_EYE_PLANE: u32 = 9474;
+pub const GL_NEAREST: u32 = 9728;
+pub const GL_LINEAR: u32 = 9729;
+pub const GL_NEAREST_MIPMAP_NEAREST: u32 = 9984;
+pub const GL_LINEAR_MIPMAP_NEAREST: u32 = 9985;
+pub const GL_NEAREST_MIPMAP_LINEAR: u32 = 9986;
+pub const GL_LINEAR_MIPMAP_LINEAR: u32 = 9987;
+pub const GL_TEXTURE_MAG_FILTER: u32 = 10240;
+pub const GL_TEXTURE_MIN_FILTER: u32 = 10241;
+pub const GL_TEXTURE_WRAP_S: u32 = 10242;
+pub const GL_TEXTURE_WRAP_T: u32 = 10243;
+pub const GL_CLAMP: u32 = 10496;
+pub const GL_REPEAT: u32 = 10497;
+pub const GL_POLYGON_OFFSET_UNITS: u32 = 10752;
+pub const GL_POLYGON_OFFSET_POINT: u32 = 10753;
+pub const GL_POLYGON_OFFSET_POINT_NV: u32 = 10753;
+pub const GL_POLYGON_OFFSET_LINE: u32 = 10754;
+pub const GL_POLYGON_OFFSET_LINE_NV: u32 = 10754;
+pub const GL_R3_G3_B2: u32 = 10768;
+pub const GL_V2F: u32 = 10784;
+pub const GL_V3F: u32 = 10785;
+pub const GL_C4UB_V2F: u32 = 10786;
+pub const GL_C4UB_V3F: u32 = 10787;
+pub const GL_C3F_V3F: u32 = 10788;
+pub const GL_N3F_V3F: u32 = 10789;
+pub const GL_C4F_N3F_V3F: u32 = 10790;
+pub const GL_T2F_V3F: u32 = 10791;
+pub const GL_T4F_V4F: u32 = 10792;
+pub const GL_T2F_C4UB_V3F: u32 = 10793;
+pub const GL_T2F_C3F_V3F: u32 = 10794;
+pub const GL_T2F_N3F_V3F: u32 = 10795;
+pub const GL_T2F_C4F_N3F_V3F: u32 = 10796;
+pub const GL_T4F_C4F_N3F_V4F: u32 = 10797;
+pub const GL_CLIP_DISTANCE0: u32 = 12288;
+pub const GL_CLIP_DISTANCE0_APPLE: u32 = 12288;
+pub const GL_CLIP_DISTANCE0_EXT: u32 = 12288;
+pub const GL_CLIP_PLANE0: u32 = 12288;
+pub const GL_CLIP_PLANE0_IMG: u32 = 12288;
+pub const GL_CLIP_DISTANCE1: u32 = 12289;
+pub const GL_CLIP_DISTANCE1_APPLE: u32 = 12289;
+pub const GL_CLIP_DISTANCE1_EXT: u32 = 12289;
+pub const GL_CLIP_PLANE1: u32 = 12289;
+pub const GL_CLIP_PLANE1_IMG: u32 = 12289;
+pub const GL_CLIP_DISTANCE2: u32 = 12290;
+pub const GL_CLIP_DISTANCE2_APPLE: u32 = 12290;
+pub const GL_CLIP_DISTANCE2_EXT: u32 = 12290;
+pub const GL_CLIP_PLANE2: u32 = 12290;
+pub const GL_CLIP_PLANE2_IMG: u32 = 12290;
+pub const GL_CLIP_DISTANCE3: u32 = 12291;
+pub const GL_CLIP_DISTANCE3_APPLE: u32 = 12291;
+pub const GL_CLIP_DISTANCE3_EXT: u32 = 12291;
+pub const GL_CLIP_PLANE3: u32 = 12291;
+pub const GL_CLIP_PLANE3_IMG: u32 = 12291;
+pub const GL_CLIP_DISTANCE4: u32 = 12292;
+pub const GL_CLIP_DISTANCE4_APPLE: u32 = 12292;
+pub const GL_CLIP_DISTANCE4_EXT: u32 = 12292;
+pub const GL_CLIP_PLANE4: u32 = 12292;
+pub const GL_CLIP_PLANE4_IMG: u32 = 12292;
+pub const GL_CLIP_DISTANCE5: u32 = 12293;
+pub const GL_CLIP_DISTANCE5_APPLE: u32 = 12293;
+pub const GL_CLIP_DISTANCE5_EXT: u32 = 12293;
+pub const GL_CLIP_PLANE5: u32 = 12293;
+pub const GL_CLIP_PLANE5_IMG: u32 = 12293;
+pub const GL_CLIP_DISTANCE6: u32 = 12294;
+pub const GL_CLIP_DISTANCE6_APPLE: u32 = 12294;
+pub const GL_CLIP_DISTANCE6_EXT: u32 = 12294;
+pub const GL_CLIP_DISTANCE7: u32 = 12295;
+pub const GL_CLIP_DISTANCE7_APPLE: u32 = 12295;
+pub const GL_CLIP_DISTANCE7_EXT: u32 = 12295;
+pub const GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV: u32 = 64;
+pub const GL_LIGHT0: u32 = 16384;
+pub const GL_MULTISAMPLE_BUFFER_BIT6_QCOM: u32 = 1073741824;
+pub const GL_TEXCOORD3_BIT_PGI: u32 = 1073741824;
+pub const GL_LIGHT1: u32 = 16385;
+pub const GL_LIGHT2: u32 = 16386;
+pub const GL_LIGHT3: u32 = 16387;
+pub const GL_LIGHT4: u32 = 16388;
+pub const GL_LIGHT5: u32 = 16389;
+pub const GL_LIGHT6: u32 = 16390;
+pub const GL_LIGHT7: u32 = 16391;
+pub const GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV: u32 = 128;
+pub const GL_ABGR_EXT: u32 = 32768;
+pub const GL_MULTISAMPLE_BUFFER_BIT7_QCOM: u32 = 2147483648;
+pub const GL_TEXCOORD4_BIT_PGI: u32 = 2147483648;
+pub const GL_CONSTANT_COLOR: u32 = 32769;
+pub const GL_CONSTANT_COLOR_EXT: u32 = 32769;
+pub const GL_ONE_MINUS_CONSTANT_COLOR: u32 = 32770;
+pub const GL_ONE_MINUS_CONSTANT_COLOR_EXT: u32 = 32770;
+pub const GL_CONSTANT_ALPHA: u32 = 32771;
+pub const GL_CONSTANT_ALPHA_EXT: u32 = 32771;
+pub const GL_ONE_MINUS_CONSTANT_ALPHA: u32 = 32772;
+pub const GL_ONE_MINUS_CONSTANT_ALPHA_EXT: u32 = 32772;
+pub const GL_BLEND_COLOR: u32 = 32773;
+pub const GL_BLEND_COLOR_EXT: u32 = 32773;
+pub const GL_FUNC_ADD: u32 = 32774;
+pub const GL_FUNC_ADD_EXT: u32 = 32774;
+pub const GL_FUNC_ADD_OES: u32 = 32774;
+pub const GL_MIN: u32 = 32775;
+pub const GL_MIN_EXT: u32 = 32775;
+pub const GL_MAX: u32 = 32776;
+pub const GL_MAX_EXT: u32 = 32776;
+pub const GL_BLEND_EQUATION: u32 = 32777;
+pub const GL_BLEND_EQUATION_EXT: u32 = 32777;
+pub const GL_BLEND_EQUATION_OES: u32 = 32777;
+pub const GL_BLEND_EQUATION_RGB: u32 = 32777;
+pub const GL_BLEND_EQUATION_RGB_EXT: u32 = 32777;
+pub const GL_BLEND_EQUATION_RGB_OES: u32 = 32777;
+pub const GL_FUNC_SUBTRACT: u32 = 32778;
+pub const GL_FUNC_SUBTRACT_EXT: u32 = 32778;
+pub const GL_FUNC_SUBTRACT_OES: u32 = 32778;
+pub const GL_FUNC_REVERSE_SUBTRACT: u32 = 32779;
+pub const GL_FUNC_REVERSE_SUBTRACT_EXT: u32 = 32779;
+pub const GL_FUNC_REVERSE_SUBTRACT_OES: u32 = 32779;
+pub const GL_CMYK_EXT: u32 = 32780;
+pub const GL_CMYKA_EXT: u32 = 32781;
+pub const GL_PACK_CMYK_HINT_EXT: u32 = 32782;
+pub const GL_UNPACK_CMYK_HINT_EXT: u32 = 32783;
+pub const GL_CONVOLUTION_1D: u32 = 32784;
+pub const GL_CONVOLUTION_1D_EXT: u32 = 32784;
+pub const GL_CONVOLUTION_2D: u32 = 32785;
+pub const GL_CONVOLUTION_2D_EXT: u32 = 32785;
+pub const GL_SEPARABLE_2D: u32 = 32786;
+pub const GL_SEPARABLE_2D_EXT: u32 = 32786;
+pub const GL_CONVOLUTION_BORDER_MODE: u32 = 32787;
+pub const GL_CONVOLUTION_BORDER_MODE_EXT: u32 = 32787;
+pub const GL_CONVOLUTION_FILTER_SCALE: u32 = 32788;
+pub const GL_CONVOLUTION_FILTER_SCALE_EXT: u32 = 32788;
+pub const GL_CONVOLUTION_FILTER_BIAS: u32 = 32789;
+pub const GL_CONVOLUTION_FILTER_BIAS_EXT: u32 = 32789;
+pub const GL_REDUCE: u32 = 32790;
+pub const GL_REDUCE_EXT: u32 = 32790;
+pub const GL_CONVOLUTION_FORMAT: u32 = 32791;
+pub const GL_CONVOLUTION_FORMAT_EXT: u32 = 32791;
+pub const GL_CONVOLUTION_WIDTH: u32 = 32792;
+pub const GL_CONVOLUTION_WIDTH_EXT: u32 = 32792;
+pub const GL_CONVOLUTION_HEIGHT: u32 = 32793;
+pub const GL_CONVOLUTION_HEIGHT_EXT: u32 = 32793;
+pub const GL_MAX_CONVOLUTION_WIDTH: u32 = 32794;
+pub const GL_MAX_CONVOLUTION_WIDTH_EXT: u32 = 32794;
+pub const GL_MAX_CONVOLUTION_HEIGHT: u32 = 32795;
+pub const GL_MAX_CONVOLUTION_HEIGHT_EXT: u32 = 32795;
+pub const GL_POST_CONVOLUTION_RED_SCALE: u32 = 32796;
+pub const GL_POST_CONVOLUTION_RED_SCALE_EXT: u32 = 32796;
+pub const GL_POST_CONVOLUTION_GREEN_SCALE: u32 = 32797;
+pub const GL_POST_CONVOLUTION_GREEN_SCALE_EXT: u32 = 32797;
+pub const GL_POST_CONVOLUTION_BLUE_SCALE: u32 = 32798;
+pub const GL_POST_CONVOLUTION_BLUE_SCALE_EXT: u32 = 32798;
+pub const GL_POST_CONVOLUTION_ALPHA_SCALE: u32 = 32799;
+pub const GL_POST_CONVOLUTION_ALPHA_SCALE_EXT: u32 = 32799;
+pub const GL_POST_CONVOLUTION_RED_BIAS: u32 = 32800;
+pub const GL_POST_CONVOLUTION_RED_BIAS_EXT: u32 = 32800;
+pub const GL_POST_CONVOLUTION_GREEN_BIAS: u32 = 32801;
+pub const GL_POST_CONVOLUTION_GREEN_BIAS_EXT: u32 = 32801;
+pub const GL_POST_CONVOLUTION_BLUE_BIAS: u32 = 32802;
+pub const GL_POST_CONVOLUTION_BLUE_BIAS_EXT: u32 = 32802;
+pub const GL_POST_CONVOLUTION_ALPHA_BIAS: u32 = 32803;
+pub const GL_POST_CONVOLUTION_ALPHA_BIAS_EXT: u32 = 32803;
+pub const GL_HISTOGRAM: u32 = 32804;
+pub const GL_HISTOGRAM_EXT: u32 = 32804;
+pub const GL_PROXY_HISTOGRAM: u32 = 32805;
+pub const GL_PROXY_HISTOGRAM_EXT: u32 = 32805;
+pub const GL_HISTOGRAM_WIDTH: u32 = 32806;
+pub const GL_HISTOGRAM_WIDTH_EXT: u32 = 32806;
+pub const GL_HISTOGRAM_FORMAT: u32 = 32807;
+pub const GL_HISTOGRAM_FORMAT_EXT: u32 = 32807;
+pub const GL_HISTOGRAM_RED_SIZE: u32 = 32808;
+pub const GL_HISTOGRAM_RED_SIZE_EXT: u32 = 32808;
+pub const GL_HISTOGRAM_GREEN_SIZE: u32 = 32809;
+pub const GL_HISTOGRAM_GREEN_SIZE_EXT: u32 = 32809;
+pub const GL_HISTOGRAM_BLUE_SIZE: u32 = 32810;
+pub const GL_HISTOGRAM_BLUE_SIZE_EXT: u32 = 32810;
+pub const GL_HISTOGRAM_ALPHA_SIZE: u32 = 32811;
+pub const GL_HISTOGRAM_ALPHA_SIZE_EXT: u32 = 32811;
+pub const GL_HISTOGRAM_LUMINANCE_SIZE: u32 = 32812;
+pub const GL_HISTOGRAM_LUMINANCE_SIZE_EXT: u32 = 32812;
+pub const GL_HISTOGRAM_SINK: u32 = 32813;
+pub const GL_HISTOGRAM_SINK_EXT: u32 = 32813;
+pub const GL_MINMAX: u32 = 32814;
+pub const GL_MINMAX_EXT: u32 = 32814;
+pub const GL_MINMAX_FORMAT: u32 = 32815;
+pub const GL_MINMAX_FORMAT_EXT: u32 = 32815;
+pub const GL_MINMAX_SINK: u32 = 32816;
+pub const GL_MINMAX_SINK_EXT: u32 = 32816;
+pub const GL_TABLE_TOO_LARGE: u32 = 32817;
+pub const GL_TABLE_TOO_LARGE_EXT: u32 = 32817;
+pub const GL_UNSIGNED_BYTE_3_3_2: u32 = 32818;
+pub const GL_UNSIGNED_BYTE_3_3_2_EXT: u32 = 32818;
+pub const GL_UNSIGNED_SHORT_4_4_4_4: u32 = 32819;
+pub const GL_UNSIGNED_SHORT_4_4_4_4_EXT: u32 = 32819;
+pub const GL_UNSIGNED_SHORT_5_5_5_1: u32 = 32820;
+pub const GL_UNSIGNED_SHORT_5_5_5_1_EXT: u32 = 32820;
+pub const GL_UNSIGNED_INT_8_8_8_8: u32 = 32821;
+pub const GL_UNSIGNED_INT_8_8_8_8_EXT: u32 = 32821;
+pub const GL_UNSIGNED_INT_10_10_10_2: u32 = 32822;
+pub const GL_UNSIGNED_INT_10_10_10_2_EXT: u32 = 32822;
+pub const GL_POLYGON_OFFSET_EXT: u32 = 32823;
+pub const GL_POLYGON_OFFSET_FILL: u32 = 32823;
+pub const GL_POLYGON_OFFSET_FACTOR: u32 = 32824;
+pub const GL_POLYGON_OFFSET_FACTOR_EXT: u32 = 32824;
+pub const GL_POLYGON_OFFSET_BIAS_EXT: u32 = 32825;
+pub const GL_RESCALE_NORMAL: u32 = 32826;
+pub const GL_RESCALE_NORMAL_EXT: u32 = 32826;
+pub const GL_ALPHA4: u32 = 32827;
+pub const GL_ALPHA4_EXT: u32 = 32827;
+pub const GL_ALPHA8: u32 = 32828;
+pub const GL_ALPHA8_EXT: u32 = 32828;
+pub const GL_ALPHA8_OES: u32 = 32828;
+pub const GL_ALPHA12: u32 = 32829;
+pub const GL_ALPHA12_EXT: u32 = 32829;
+pub const GL_ALPHA16: u32 = 32830;
+pub const GL_ALPHA16_EXT: u32 = 32830;
+pub const GL_LUMINANCE4: u32 = 32831;
+pub const GL_LUMINANCE4_EXT: u32 = 32831;
+pub const GL_LUMINANCE8: u32 = 32832;
+pub const GL_LUMINANCE8_EXT: u32 = 32832;
+pub const GL_LUMINANCE8_OES: u32 = 32832;
+pub const GL_LUMINANCE12: u32 = 32833;
+pub const GL_LUMINANCE12_EXT: u32 = 32833;
+pub const GL_LUMINANCE16: u32 = 32834;
+pub const GL_LUMINANCE16_EXT: u32 = 32834;
+pub const GL_LUMINANCE4_ALPHA4: u32 = 32835;
+pub const GL_LUMINANCE4_ALPHA4_EXT: u32 = 32835;
+pub const GL_LUMINANCE4_ALPHA4_OES: u32 = 32835;
+pub const GL_LUMINANCE6_ALPHA2: u32 = 32836;
+pub const GL_LUMINANCE6_ALPHA2_EXT: u32 = 32836;
+pub const GL_LUMINANCE8_ALPHA8: u32 = 32837;
+pub const GL_LUMINANCE8_ALPHA8_EXT: u32 = 32837;
+pub const GL_LUMINANCE8_ALPHA8_OES: u32 = 32837;
+pub const GL_LUMINANCE12_ALPHA4: u32 = 32838;
+pub const GL_LUMINANCE12_ALPHA4_EXT: u32 = 32838;
+pub const GL_LUMINANCE12_ALPHA12: u32 = 32839;
+pub const GL_LUMINANCE12_ALPHA12_EXT: u32 = 32839;
+pub const GL_LUMINANCE16_ALPHA16: u32 = 32840;
+pub const GL_LUMINANCE16_ALPHA16_EXT: u32 = 32840;
+pub const GL_INTENSITY: u32 = 32841;
+pub const GL_INTENSITY_EXT: u32 = 32841;
+pub const GL_INTENSITY4: u32 = 32842;
+pub const GL_INTENSITY4_EXT: u32 = 32842;
+pub const GL_INTENSITY8: u32 = 32843;
+pub const GL_INTENSITY8_EXT: u32 = 32843;
+pub const GL_INTENSITY12: u32 = 32844;
+pub const GL_INTENSITY12_EXT: u32 = 32844;
+pub const GL_INTENSITY16: u32 = 32845;
+pub const GL_INTENSITY16_EXT: u32 = 32845;
+pub const GL_RGB2_EXT: u32 = 32846;
+pub const GL_RGB4: u32 = 32847;
+pub const GL_RGB4_EXT: u32 = 32847;
+pub const GL_RGB5: u32 = 32848;
+pub const GL_RGB5_EXT: u32 = 32848;
+pub const GL_RGB8: u32 = 32849;
+pub const GL_RGB8_EXT: u32 = 32849;
+pub const GL_RGB8_OES: u32 = 32849;
+pub const GL_RGB10: u32 = 32850;
+pub const GL_RGB10_EXT: u32 = 32850;
+pub const GL_RGB12: u32 = 32851;
+pub const GL_RGB12_EXT: u32 = 32851;
+pub const GL_RGB16: u32 = 32852;
+pub const GL_RGB16_EXT: u32 = 32852;
+pub const GL_RGBA2: u32 = 32853;
+pub const GL_RGBA2_EXT: u32 = 32853;
+pub const GL_RGBA4: u32 = 32854;
+pub const GL_RGBA4_EXT: u32 = 32854;
+pub const GL_RGBA4_OES: u32 = 32854;
+pub const GL_RGB5_A1: u32 = 32855;
+pub const GL_RGB5_A1_EXT: u32 = 32855;
+pub const GL_RGB5_A1_OES: u32 = 32855;
+pub const GL_RGBA8: u32 = 32856;
+pub const GL_RGBA8_EXT: u32 = 32856;
+pub const GL_RGBA8_OES: u32 = 32856;
+pub const GL_RGB10_A2: u32 = 32857;
+pub const GL_RGB10_A2_EXT: u32 = 32857;
+pub const GL_RGBA12: u32 = 32858;
+pub const GL_RGBA12_EXT: u32 = 32858;
+pub const GL_RGBA16: u32 = 32859;
+pub const GL_RGBA16_EXT: u32 = 32859;
+pub const GL_TEXTURE_RED_SIZE: u32 = 32860;
+pub const GL_TEXTURE_RED_SIZE_EXT: u32 = 32860;
+pub const GL_TEXTURE_GREEN_SIZE: u32 = 32861;
+pub const GL_TEXTURE_GREEN_SIZE_EXT: u32 = 32861;
+pub const GL_TEXTURE_BLUE_SIZE: u32 = 32862;
+pub const GL_TEXTURE_BLUE_SIZE_EXT: u32 = 32862;
+pub const GL_TEXTURE_ALPHA_SIZE: u32 = 32863;
+pub const GL_TEXTURE_ALPHA_SIZE_EXT: u32 = 32863;
+pub const GL_TEXTURE_LUMINANCE_SIZE: u32 = 32864;
+pub const GL_TEXTURE_LUMINANCE_SIZE_EXT: u32 = 32864;
+pub const GL_TEXTURE_INTENSITY_SIZE: u32 = 32865;
+pub const GL_TEXTURE_INTENSITY_SIZE_EXT: u32 = 32865;
+pub const GL_REPLACE_EXT: u32 = 32866;
+pub const GL_PROXY_TEXTURE_1D: u32 = 32867;
+pub const GL_PROXY_TEXTURE_1D_EXT: u32 = 32867;
+pub const GL_PROXY_TEXTURE_2D: u32 = 32868;
+pub const GL_PROXY_TEXTURE_2D_EXT: u32 = 32868;
+pub const GL_TEXTURE_TOO_LARGE_EXT: u32 = 32869;
+pub const GL_TEXTURE_PRIORITY: u32 = 32870;
+pub const GL_TEXTURE_PRIORITY_EXT: u32 = 32870;
+pub const GL_TEXTURE_RESIDENT: u32 = 32871;
+pub const GL_TEXTURE_RESIDENT_EXT: u32 = 32871;
+pub const GL_TEXTURE_1D_BINDING_EXT: u32 = 32872;
+pub const GL_TEXTURE_BINDING_1D: u32 = 32872;
+pub const GL_TEXTURE_2D_BINDING_EXT: u32 = 32873;
+pub const GL_TEXTURE_BINDING_2D: u32 = 32873;
+pub const GL_TEXTURE_3D_BINDING_EXT: u32 = 32874;
+pub const GL_TEXTURE_3D_BINDING_OES: u32 = 32874;
+pub const GL_TEXTURE_BINDING_3D: u32 = 32874;
+pub const GL_TEXTURE_BINDING_3D_OES: u32 = 32874;
+pub const GL_PACK_SKIP_IMAGES: u32 = 32875;
+pub const GL_PACK_SKIP_IMAGES_EXT: u32 = 32875;
+pub const GL_PACK_IMAGE_HEIGHT: u32 = 32876;
+pub const GL_PACK_IMAGE_HEIGHT_EXT: u32 = 32876;
+pub const GL_UNPACK_SKIP_IMAGES: u32 = 32877;
+pub const GL_UNPACK_SKIP_IMAGES_EXT: u32 = 32877;
+pub const GL_UNPACK_IMAGE_HEIGHT: u32 = 32878;
+pub const GL_UNPACK_IMAGE_HEIGHT_EXT: u32 = 32878;
+pub const GL_TEXTURE_3D: u32 = 32879;
+pub const GL_TEXTURE_3D_EXT: u32 = 32879;
+pub const GL_TEXTURE_3D_OES: u32 = 32879;
+pub const GL_PROXY_TEXTURE_3D: u32 = 32880;
+pub const GL_PROXY_TEXTURE_3D_EXT: u32 = 32880;
+pub const GL_TEXTURE_DEPTH: u32 = 32881;
+pub const GL_TEXTURE_DEPTH_EXT: u32 = 32881;
+pub const GL_TEXTURE_WRAP_R: u32 = 32882;
+pub const GL_TEXTURE_WRAP_R_EXT: u32 = 32882;
+pub const GL_TEXTURE_WRAP_R_OES: u32 = 32882;
+pub const GL_MAX_3D_TEXTURE_SIZE: u32 = 32883;
+pub const GL_MAX_3D_TEXTURE_SIZE_EXT: u32 = 32883;
+pub const GL_MAX_3D_TEXTURE_SIZE_OES: u32 = 32883;
+pub const GL_VERTEX_ARRAY: u32 = 32884;
+pub const GL_VERTEX_ARRAY_EXT: u32 = 32884;
+pub const GL_VERTEX_ARRAY_KHR: u32 = 32884;
+pub const GL_NORMAL_ARRAY: u32 = 32885;
+pub const GL_NORMAL_ARRAY_EXT: u32 = 32885;
+pub const GL_COLOR_ARRAY: u32 = 32886;
+pub const GL_COLOR_ARRAY_EXT: u32 = 32886;
+pub const GL_INDEX_ARRAY: u32 = 32887;
+pub const GL_INDEX_ARRAY_EXT: u32 = 32887;
+pub const GL_TEXTURE_COORD_ARRAY: u32 = 32888;
+pub const GL_TEXTURE_COORD_ARRAY_EXT: u32 = 32888;
+pub const GL_EDGE_FLAG_ARRAY: u32 = 32889;
+pub const GL_EDGE_FLAG_ARRAY_EXT: u32 = 32889;
+pub const GL_VERTEX_ARRAY_SIZE: u32 = 32890;
+pub const GL_VERTEX_ARRAY_SIZE_EXT: u32 = 32890;
+pub const GL_VERTEX_ARRAY_TYPE: u32 = 32891;
+pub const GL_VERTEX_ARRAY_TYPE_EXT: u32 = 32891;
+pub const GL_VERTEX_ARRAY_STRIDE: u32 = 32892;
+pub const GL_VERTEX_ARRAY_STRIDE_EXT: u32 = 32892;
+pub const GL_VERTEX_ARRAY_COUNT_EXT: u32 = 32893;
+pub const GL_NORMAL_ARRAY_TYPE: u32 = 32894;
+pub const GL_NORMAL_ARRAY_TYPE_EXT: u32 = 32894;
+pub const GL_NORMAL_ARRAY_STRIDE: u32 = 32895;
+pub const GL_NORMAL_ARRAY_STRIDE_EXT: u32 = 32895;
+pub const GL_NORMAL_ARRAY_COUNT_EXT: u32 = 32896;
+pub const GL_COLOR_ARRAY_SIZE: u32 = 32897;
+pub const GL_COLOR_ARRAY_SIZE_EXT: u32 = 32897;
+pub const GL_COLOR_ARRAY_TYPE: u32 = 32898;
+pub const GL_COLOR_ARRAY_TYPE_EXT: u32 = 32898;
+pub const GL_COLOR_ARRAY_STRIDE: u32 = 32899;
+pub const GL_COLOR_ARRAY_STRIDE_EXT: u32 = 32899;
+pub const GL_COLOR_ARRAY_COUNT_EXT: u32 = 32900;
+pub const GL_INDEX_ARRAY_TYPE: u32 = 32901;
+pub const GL_INDEX_ARRAY_TYPE_EXT: u32 = 32901;
+pub const GL_INDEX_ARRAY_STRIDE: u32 = 32902;
+pub const GL_INDEX_ARRAY_STRIDE_EXT: u32 = 32902;
+pub const GL_INDEX_ARRAY_COUNT_EXT: u32 = 32903;
+pub const GL_TEXTURE_COORD_ARRAY_SIZE: u32 = 32904;
+pub const GL_TEXTURE_COORD_ARRAY_SIZE_EXT: u32 = 32904;
+pub const GL_TEXTURE_COORD_ARRAY_TYPE: u32 = 32905;
+pub const GL_TEXTURE_COORD_ARRAY_TYPE_EXT: u32 = 32905;
+pub const GL_TEXTURE_COORD_ARRAY_STRIDE: u32 = 32906;
+pub const GL_TEXTURE_COORD_ARRAY_STRIDE_EXT: u32 = 32906;
+pub const GL_TEXTURE_COORD_ARRAY_COUNT_EXT: u32 = 32907;
+pub const GL_EDGE_FLAG_ARRAY_STRIDE: u32 = 32908;
+pub const GL_EDGE_FLAG_ARRAY_STRIDE_EXT: u32 = 32908;
+pub const GL_EDGE_FLAG_ARRAY_COUNT_EXT: u32 = 32909;
+pub const GL_VERTEX_ARRAY_POINTER: u32 = 32910;
+pub const GL_VERTEX_ARRAY_POINTER_EXT: u32 = 32910;
+pub const GL_NORMAL_ARRAY_POINTER: u32 = 32911;
+pub const GL_NORMAL_ARRAY_POINTER_EXT: u32 = 32911;
+pub const GL_COLOR_ARRAY_POINTER: u32 = 32912;
+pub const GL_COLOR_ARRAY_POINTER_EXT: u32 = 32912;
+pub const GL_INDEX_ARRAY_POINTER: u32 = 32913;
+pub const GL_INDEX_ARRAY_POINTER_EXT: u32 = 32913;
+pub const GL_TEXTURE_COORD_ARRAY_POINTER: u32 = 32914;
+pub const GL_TEXTURE_COORD_ARRAY_POINTER_EXT: u32 = 32914;
+pub const GL_EDGE_FLAG_ARRAY_POINTER: u32 = 32915;
+pub const GL_EDGE_FLAG_ARRAY_POINTER_EXT: u32 = 32915;
+pub const GL_INTERLACE_SGIX: u32 = 32916;
+pub const GL_DETAIL_TEXTURE_2D_SGIS: u32 = 32917;
+pub const GL_DETAIL_TEXTURE_2D_BINDING_SGIS: u32 = 32918;
+pub const GL_LINEAR_DETAIL_SGIS: u32 = 32919;
+pub const GL_LINEAR_DETAIL_ALPHA_SGIS: u32 = 32920;
+pub const GL_LINEAR_DETAIL_COLOR_SGIS: u32 = 32921;
+pub const GL_DETAIL_TEXTURE_LEVEL_SGIS: u32 = 32922;
+pub const GL_DETAIL_TEXTURE_MODE_SGIS: u32 = 32923;
+pub const GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS: u32 = 32924;
+pub const GL_MULTISAMPLE: u32 = 32925;
+pub const GL_MULTISAMPLE_ARB: u32 = 32925;
+pub const GL_MULTISAMPLE_EXT: u32 = 32925;
+pub const GL_MULTISAMPLE_SGIS: u32 = 32925;
+pub const GL_SAMPLE_ALPHA_TO_COVERAGE: u32 = 32926;
+pub const GL_SAMPLE_ALPHA_TO_COVERAGE_ARB: u32 = 32926;
+pub const GL_SAMPLE_ALPHA_TO_MASK_EXT: u32 = 32926;
+pub const GL_SAMPLE_ALPHA_TO_MASK_SGIS: u32 = 32926;
+pub const GL_SAMPLE_ALPHA_TO_ONE: u32 = 32927;
+pub const GL_SAMPLE_ALPHA_TO_ONE_ARB: u32 = 32927;
+pub const GL_SAMPLE_ALPHA_TO_ONE_EXT: u32 = 32927;
+pub const GL_SAMPLE_ALPHA_TO_ONE_SGIS: u32 = 32927;
+pub const GL_SAMPLE_COVERAGE: u32 = 32928;
+pub const GL_SAMPLE_COVERAGE_ARB: u32 = 32928;
+pub const GL_SAMPLE_MASK_EXT: u32 = 32928;
+pub const GL_SAMPLE_MASK_SGIS: u32 = 32928;
+pub const GL_1PASS_EXT: u32 = 32929;
+pub const GL_1PASS_SGIS: u32 = 32929;
+pub const GL_2PASS_0_EXT: u32 = 32930;
+pub const GL_2PASS_0_SGIS: u32 = 32930;
+pub const GL_2PASS_1_EXT: u32 = 32931;
+pub const GL_2PASS_1_SGIS: u32 = 32931;
+pub const GL_4PASS_0_EXT: u32 = 32932;
+pub const GL_4PASS_0_SGIS: u32 = 32932;
+pub const GL_4PASS_1_EXT: u32 = 32933;
+pub const GL_4PASS_1_SGIS: u32 = 32933;
+pub const GL_4PASS_2_EXT: u32 = 32934;
+pub const GL_4PASS_2_SGIS: u32 = 32934;
+pub const GL_4PASS_3_EXT: u32 = 32935;
+pub const GL_4PASS_3_SGIS: u32 = 32935;
+pub const GL_SAMPLE_BUFFERS: u32 = 32936;
+pub const GL_SAMPLE_BUFFERS_ARB: u32 = 32936;
+pub const GL_SAMPLE_BUFFERS_EXT: u32 = 32936;
+pub const GL_SAMPLE_BUFFERS_SGIS: u32 = 32936;
+pub const GL_SAMPLES: u32 = 32937;
+pub const GL_SAMPLES_ARB: u32 = 32937;
+pub const GL_SAMPLES_EXT: u32 = 32937;
+pub const GL_SAMPLES_SGIS: u32 = 32937;
+pub const GL_SAMPLE_COVERAGE_VALUE: u32 = 32938;
+pub const GL_SAMPLE_COVERAGE_VALUE_ARB: u32 = 32938;
+pub const GL_SAMPLE_MASK_VALUE_EXT: u32 = 32938;
+pub const GL_SAMPLE_MASK_VALUE_SGIS: u32 = 32938;
+pub const GL_SAMPLE_COVERAGE_INVERT: u32 = 32939;
+pub const GL_SAMPLE_COVERAGE_INVERT_ARB: u32 = 32939;
+pub const GL_SAMPLE_MASK_INVERT_EXT: u32 = 32939;
+pub const GL_SAMPLE_MASK_INVERT_SGIS: u32 = 32939;
+pub const GL_SAMPLE_PATTERN_EXT: u32 = 32940;
+pub const GL_SAMPLE_PATTERN_SGIS: u32 = 32940;
+pub const GL_LINEAR_SHARPEN_SGIS: u32 = 32941;
+pub const GL_LINEAR_SHARPEN_ALPHA_SGIS: u32 = 32942;
+pub const GL_LINEAR_SHARPEN_COLOR_SGIS: u32 = 32943;
+pub const GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS: u32 = 32944;
+pub const GL_COLOR_MATRIX: u32 = 32945;
+pub const GL_COLOR_MATRIX_SGI: u32 = 32945;
+pub const GL_COLOR_MATRIX_STACK_DEPTH: u32 = 32946;
+pub const GL_COLOR_MATRIX_STACK_DEPTH_SGI: u32 = 32946;
+pub const GL_MAX_COLOR_MATRIX_STACK_DEPTH: u32 = 32947;
+pub const GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI: u32 = 32947;
+pub const GL_POST_COLOR_MATRIX_RED_SCALE: u32 = 32948;
+pub const GL_POST_COLOR_MATRIX_RED_SCALE_SGI: u32 = 32948;
+pub const GL_POST_COLOR_MATRIX_GREEN_SCALE: u32 = 32949;
+pub const GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI: u32 = 32949;
+pub const GL_POST_COLOR_MATRIX_BLUE_SCALE: u32 = 32950;
+pub const GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI: u32 = 32950;
+pub const GL_POST_COLOR_MATRIX_ALPHA_SCALE: u32 = 32951;
+pub const GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI: u32 = 32951;
+pub const GL_POST_COLOR_MATRIX_RED_BIAS: u32 = 32952;
+pub const GL_POST_COLOR_MATRIX_RED_BIAS_SGI: u32 = 32952;
+pub const GL_POST_COLOR_MATRIX_GREEN_BIAS: u32 = 32953;
+pub const GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI: u32 = 32953;
+pub const GL_POST_COLOR_MATRIX_BLUE_BIAS: u32 = 32954;
+pub const GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI: u32 = 32954;
+pub const GL_POST_COLOR_MATRIX_ALPHA_BIAS: u32 = 32955;
+pub const GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI: u32 = 32955;
+pub const GL_TEXTURE_COLOR_TABLE_SGI: u32 = 32956;
+pub const GL_PROXY_TEXTURE_COLOR_TABLE_SGI: u32 = 32957;
+pub const GL_TEXTURE_ENV_BIAS_SGIX: u32 = 32958;
+pub const GL_SHADOW_AMBIENT_SGIX: u32 = 32959;
+pub const GL_TEXTURE_COMPARE_FAIL_VALUE_ARB: u32 = 32959;
+pub const GL_BLEND_DST_RGB: u32 = 32968;
+pub const GL_BLEND_DST_RGB_EXT: u32 = 32968;
+pub const GL_BLEND_DST_RGB_OES: u32 = 32968;
+pub const GL_BLEND_SRC_RGB: u32 = 32969;
+pub const GL_BLEND_SRC_RGB_EXT: u32 = 32969;
+pub const GL_BLEND_SRC_RGB_OES: u32 = 32969;
+pub const GL_BLEND_DST_ALPHA: u32 = 32970;
+pub const GL_BLEND_DST_ALPHA_EXT: u32 = 32970;
+pub const GL_BLEND_DST_ALPHA_OES: u32 = 32970;
+pub const GL_BLEND_SRC_ALPHA: u32 = 32971;
+pub const GL_BLEND_SRC_ALPHA_EXT: u32 = 32971;
+pub const GL_BLEND_SRC_ALPHA_OES: u32 = 32971;
+pub const GL_422_EXT: u32 = 32972;
+pub const GL_422_REV_EXT: u32 = 32973;
+pub const GL_422_AVERAGE_EXT: u32 = 32974;
+pub const GL_422_REV_AVERAGE_EXT: u32 = 32975;
+pub const GL_COLOR_TABLE: u32 = 32976;
+pub const GL_COLOR_TABLE_SGI: u32 = 32976;
+pub const GL_POST_CONVOLUTION_COLOR_TABLE: u32 = 32977;
+pub const GL_POST_CONVOLUTION_COLOR_TABLE_SGI: u32 = 32977;
+pub const GL_POST_COLOR_MATRIX_COLOR_TABLE: u32 = 32978;
+pub const GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI: u32 = 32978;
+pub const GL_PROXY_COLOR_TABLE: u32 = 32979;
+pub const GL_PROXY_COLOR_TABLE_SGI: u32 = 32979;
+pub const GL_PROXY_POST_CONVOLUTION_COLOR_TABLE: u32 = 32980;
+pub const GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI: u32 = 32980;
+pub const GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE: u32 = 32981;
+pub const GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI: u32 = 32981;
+pub const GL_COLOR_TABLE_SCALE: u32 = 32982;
+pub const GL_COLOR_TABLE_SCALE_SGI: u32 = 32982;
+pub const GL_COLOR_TABLE_BIAS: u32 = 32983;
+pub const GL_COLOR_TABLE_BIAS_SGI: u32 = 32983;
+pub const GL_COLOR_TABLE_FORMAT: u32 = 32984;
+pub const GL_COLOR_TABLE_FORMAT_SGI: u32 = 32984;
+pub const GL_COLOR_TABLE_WIDTH: u32 = 32985;
+pub const GL_COLOR_TABLE_WIDTH_SGI: u32 = 32985;
+pub const GL_COLOR_TABLE_RED_SIZE: u32 = 32986;
+pub const GL_COLOR_TABLE_RED_SIZE_SGI: u32 = 32986;
+pub const GL_COLOR_TABLE_GREEN_SIZE: u32 = 32987;
+pub const GL_COLOR_TABLE_GREEN_SIZE_SGI: u32 = 32987;
+pub const GL_COLOR_TABLE_BLUE_SIZE: u32 = 32988;
+pub const GL_COLOR_TABLE_BLUE_SIZE_SGI: u32 = 32988;
+pub const GL_COLOR_TABLE_ALPHA_SIZE: u32 = 32989;
+pub const GL_COLOR_TABLE_ALPHA_SIZE_SGI: u32 = 32989;
+pub const GL_COLOR_TABLE_LUMINANCE_SIZE: u32 = 32990;
+pub const GL_COLOR_TABLE_LUMINANCE_SIZE_SGI: u32 = 32990;
+pub const GL_COLOR_TABLE_INTENSITY_SIZE: u32 = 32991;
+pub const GL_COLOR_TABLE_INTENSITY_SIZE_SGI: u32 = 32991;
+pub const GL_BGR: u32 = 32992;
+pub const GL_BGR_EXT: u32 = 32992;
+pub const GL_BGRA: u32 = 32993;
+pub const GL_BGRA_EXT: u32 = 32993;
+pub const GL_BGRA_IMG: u32 = 32993;
+pub const GL_COLOR_INDEX1_EXT: u32 = 32994;
+pub const GL_COLOR_INDEX2_EXT: u32 = 32995;
+pub const GL_COLOR_INDEX4_EXT: u32 = 32996;
+pub const GL_COLOR_INDEX8_EXT: u32 = 32997;
+pub const GL_COLOR_INDEX12_EXT: u32 = 32998;
+pub const GL_COLOR_INDEX16_EXT: u32 = 32999;
+pub const GL_MAX_ELEMENTS_VERTICES: u32 = 33000;
+pub const GL_MAX_ELEMENTS_VERTICES_EXT: u32 = 33000;
+pub const GL_MAX_ELEMENTS_INDICES: u32 = 33001;
+pub const GL_MAX_ELEMENTS_INDICES_EXT: u32 = 33001;
+pub const GL_PHONG_WIN: u32 = 33002;
+pub const GL_PHONG_HINT_WIN: u32 = 33003;
+pub const GL_FOG_SPECULAR_TEXTURE_WIN: u32 = 33004;
+pub const GL_TEXTURE_INDEX_SIZE_EXT: u32 = 33005;
+pub const GL_PARAMETER_BUFFER_ARB: u32 = 33006;
+pub const GL_PARAMETER_BUFFER_BINDING_ARB: u32 = 33007;
+pub const GL_CLIP_VOLUME_CLIPPING_HINT_EXT: u32 = 33008;
+pub const GL_DUAL_ALPHA4_SGIS: u32 = 33040;
+pub const GL_DUAL_ALPHA8_SGIS: u32 = 33041;
+pub const GL_DUAL_ALPHA12_SGIS: u32 = 33042;
+pub const GL_DUAL_ALPHA16_SGIS: u32 = 33043;
+pub const GL_DUAL_LUMINANCE4_SGIS: u32 = 33044;
+pub const GL_DUAL_LUMINANCE8_SGIS: u32 = 33045;
+pub const GL_DUAL_LUMINANCE12_SGIS: u32 = 33046;
+pub const GL_DUAL_LUMINANCE16_SGIS: u32 = 33047;
+pub const GL_DUAL_INTENSITY4_SGIS: u32 = 33048;
+pub const GL_DUAL_INTENSITY8_SGIS: u32 = 33049;
+pub const GL_DUAL_INTENSITY12_SGIS: u32 = 33050;
+pub const GL_DUAL_INTENSITY16_SGIS: u32 = 33051;
+pub const GL_DUAL_LUMINANCE_ALPHA4_SGIS: u32 = 33052;
+pub const GL_DUAL_LUMINANCE_ALPHA8_SGIS: u32 = 33053;
+pub const GL_QUAD_ALPHA4_SGIS: u32 = 33054;
+pub const GL_QUAD_ALPHA8_SGIS: u32 = 33055;
+pub const GL_QUAD_LUMINANCE4_SGIS: u32 = 33056;
+pub const GL_QUAD_LUMINANCE8_SGIS: u32 = 33057;
+pub const GL_QUAD_INTENSITY4_SGIS: u32 = 33058;
+pub const GL_QUAD_INTENSITY8_SGIS: u32 = 33059;
+pub const GL_DUAL_TEXTURE_SELECT_SGIS: u32 = 33060;
+pub const GL_QUAD_TEXTURE_SELECT_SGIS: u32 = 33061;
+pub const GL_POINT_SIZE_MIN: u32 = 33062;
+pub const GL_POINT_SIZE_MIN_ARB: u32 = 33062;
+pub const GL_POINT_SIZE_MIN_EXT: u32 = 33062;
+pub const GL_POINT_SIZE_MIN_SGIS: u32 = 33062;
+pub const GL_POINT_SIZE_MAX: u32 = 33063;
+pub const GL_POINT_SIZE_MAX_ARB: u32 = 33063;
+pub const GL_POINT_SIZE_MAX_EXT: u32 = 33063;
+pub const GL_POINT_SIZE_MAX_SGIS: u32 = 33063;
+pub const GL_POINT_FADE_THRESHOLD_SIZE: u32 = 33064;
+pub const GL_POINT_FADE_THRESHOLD_SIZE_ARB: u32 = 33064;
+pub const GL_POINT_FADE_THRESHOLD_SIZE_EXT: u32 = 33064;
+pub const GL_POINT_FADE_THRESHOLD_SIZE_SGIS: u32 = 33064;
+pub const GL_DISTANCE_ATTENUATION_EXT: u32 = 33065;
+pub const GL_DISTANCE_ATTENUATION_SGIS: u32 = 33065;
+pub const GL_POINT_DISTANCE_ATTENUATION: u32 = 33065;
+pub const GL_POINT_DISTANCE_ATTENUATION_ARB: u32 = 33065;
+pub const GL_FOG_FUNC_SGIS: u32 = 33066;
+pub const GL_FOG_FUNC_POINTS_SGIS: u32 = 33067;
+pub const GL_MAX_FOG_FUNC_POINTS_SGIS: u32 = 33068;
+pub const GL_CLAMP_TO_BORDER: u32 = 33069;
+pub const GL_CLAMP_TO_BORDER_ARB: u32 = 33069;
+pub const GL_CLAMP_TO_BORDER_EXT: u32 = 33069;
+pub const GL_CLAMP_TO_BORDER_NV: u32 = 33069;
+pub const GL_CLAMP_TO_BORDER_OES: u32 = 33069;
+pub const GL_CLAMP_TO_BORDER_SGIS: u32 = 33069;
+pub const GL_TEXTURE_MULTI_BUFFER_HINT_SGIX: u32 = 33070;
+pub const GL_CLAMP_TO_EDGE: u32 = 33071;
+pub const GL_CLAMP_TO_EDGE_SGIS: u32 = 33071;
+pub const GL_PACK_SKIP_VOLUMES_SGIS: u32 = 33072;
+pub const GL_PACK_IMAGE_DEPTH_SGIS: u32 = 33073;
+pub const GL_UNPACK_SKIP_VOLUMES_SGIS: u32 = 33074;
+pub const GL_UNPACK_IMAGE_DEPTH_SGIS: u32 = 33075;
+pub const GL_TEXTURE_4D_SGIS: u32 = 33076;
+pub const GL_PROXY_TEXTURE_4D_SGIS: u32 = 33077;
+pub const GL_TEXTURE_4DSIZE_SGIS: u32 = 33078;
+pub const GL_TEXTURE_WRAP_Q_SGIS: u32 = 33079;
+pub const GL_MAX_4D_TEXTURE_SIZE_SGIS: u32 = 33080;
+pub const GL_PIXEL_TEX_GEN_SGIX: u32 = 33081;
+pub const GL_TEXTURE_MIN_LOD: u32 = 33082;
+pub const GL_TEXTURE_MIN_LOD_SGIS: u32 = 33082;
+pub const GL_TEXTURE_MAX_LOD: u32 = 33083;
+pub const GL_TEXTURE_MAX_LOD_SGIS: u32 = 33083;
+pub const GL_TEXTURE_BASE_LEVEL: u32 = 33084;
+pub const GL_TEXTURE_BASE_LEVEL_SGIS: u32 = 33084;
+pub const GL_TEXTURE_MAX_LEVEL: u32 = 33085;
+pub const GL_TEXTURE_MAX_LEVEL_APPLE: u32 = 33085;
+pub const GL_TEXTURE_MAX_LEVEL_SGIS: u32 = 33085;
+pub const GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX: u32 = 33086;
+pub const GL_PIXEL_TILE_CACHE_INCREMENT_SGIX: u32 = 33087;
+pub const GL_PIXEL_TILE_WIDTH_SGIX: u32 = 33088;
+pub const GL_PIXEL_TILE_HEIGHT_SGIX: u32 = 33089;
+pub const GL_PIXEL_TILE_GRID_WIDTH_SGIX: u32 = 33090;
+pub const GL_PIXEL_TILE_GRID_HEIGHT_SGIX: u32 = 33091;
+pub const GL_PIXEL_TILE_GRID_DEPTH_SGIX: u32 = 33092;
+pub const GL_PIXEL_TILE_CACHE_SIZE_SGIX: u32 = 33093;
+pub const GL_FILTER4_SGIS: u32 = 33094;
+pub const GL_TEXTURE_FILTER4_SIZE_SGIS: u32 = 33095;
+pub const GL_SPRITE_SGIX: u32 = 33096;
+pub const GL_SPRITE_MODE_SGIX: u32 = 33097;
+pub const GL_SPRITE_AXIS_SGIX: u32 = 33098;
+pub const GL_SPRITE_TRANSLATION_SGIX: u32 = 33099;
+pub const GL_SPRITE_AXIAL_SGIX: u32 = 33100;
+pub const GL_SPRITE_OBJECT_ALIGNED_SGIX: u32 = 33101;
+pub const GL_SPRITE_EYE_ALIGNED_SGIX: u32 = 33102;
+pub const GL_TEXTURE_4D_BINDING_SGIS: u32 = 33103;
+pub const GL_IGNORE_BORDER_HP: u32 = 33104;
+pub const GL_CONSTANT_BORDER: u32 = 33105;
+pub const GL_CONSTANT_BORDER_HP: u32 = 33105;
+pub const GL_REPLICATE_BORDER: u32 = 33107;
+pub const GL_REPLICATE_BORDER_HP: u32 = 33107;
+pub const GL_CONVOLUTION_BORDER_COLOR: u32 = 33108;
+pub const GL_CONVOLUTION_BORDER_COLOR_HP: u32 = 33108;
+pub const GL_IMAGE_SCALE_X_HP: u32 = 33109;
+pub const GL_IMAGE_SCALE_Y_HP: u32 = 33110;
+pub const GL_IMAGE_TRANSLATE_X_HP: u32 = 33111;
+pub const GL_IMAGE_TRANSLATE_Y_HP: u32 = 33112;
+pub const GL_IMAGE_ROTATE_ANGLE_HP: u32 = 33113;
+pub const GL_IMAGE_ROTATE_ORIGIN_X_HP: u32 = 33114;
+pub const GL_IMAGE_ROTATE_ORIGIN_Y_HP: u32 = 33115;
+pub const GL_IMAGE_MAG_FILTER_HP: u32 = 33116;
+pub const GL_IMAGE_MIN_FILTER_HP: u32 = 33117;
+pub const GL_IMAGE_CUBIC_WEIGHT_HP: u32 = 33118;
+pub const GL_CUBIC_HP: u32 = 33119;
+pub const GL_AVERAGE_HP: u32 = 33120;
+pub const GL_IMAGE_TRANSFORM_2D_HP: u32 = 33121;
+pub const GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP: u32 = 33122;
+pub const GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP: u32 = 33123;
+pub const GL_OCCLUSION_TEST_HP: u32 = 33125;
+pub const GL_OCCLUSION_TEST_RESULT_HP: u32 = 33126;
+pub const GL_TEXTURE_LIGHTING_MODE_HP: u32 = 33127;
+pub const GL_TEXTURE_POST_SPECULAR_HP: u32 = 33128;
+pub const GL_TEXTURE_PRE_SPECULAR_HP: u32 = 33129;
+pub const GL_LINEAR_CLIPMAP_LINEAR_SGIX: u32 = 33136;
+pub const GL_TEXTURE_CLIPMAP_CENTER_SGIX: u32 = 33137;
+pub const GL_TEXTURE_CLIPMAP_FRAME_SGIX: u32 = 33138;
+pub const GL_TEXTURE_CLIPMAP_OFFSET_SGIX: u32 = 33139;
+pub const GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX: u32 = 33140;
+pub const GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX: u32 = 33141;
+pub const GL_TEXTURE_CLIPMAP_DEPTH_SGIX: u32 = 33142;
+pub const GL_MAX_CLIPMAP_DEPTH_SGIX: u32 = 33143;
+pub const GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX: u32 = 33144;
+pub const GL_POST_TEXTURE_FILTER_BIAS_SGIX: u32 = 33145;
+pub const GL_POST_TEXTURE_FILTER_SCALE_SGIX: u32 = 33146;
+pub const GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX: u32 = 33147;
+pub const GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX: u32 = 33148;
+pub const GL_REFERENCE_PLANE_SGIX: u32 = 33149;
+pub const GL_REFERENCE_PLANE_EQUATION_SGIX: u32 = 33150;
+pub const GL_IR_INSTRUMENT1_SGIX: u32 = 33151;
+pub const GL_INSTRUMENT_BUFFER_POINTER_SGIX: u32 = 33152;
+pub const GL_INSTRUMENT_MEASUREMENTS_SGIX: u32 = 33153;
+pub const GL_LIST_PRIORITY_SGIX: u32 = 33154;
+pub const GL_CALLIGRAPHIC_FRAGMENT_SGIX: u32 = 33155;
+pub const GL_PIXEL_TEX_GEN_Q_CEILING_SGIX: u32 = 33156;
+pub const GL_PIXEL_TEX_GEN_Q_ROUND_SGIX: u32 = 33157;
+pub const GL_PIXEL_TEX_GEN_Q_FLOOR_SGIX: u32 = 33158;
+pub const GL_PIXEL_TEX_GEN_ALPHA_REPLACE_SGIX: u32 = 33159;
+pub const GL_PIXEL_TEX_GEN_ALPHA_NO_REPLACE_SGIX: u32 = 33160;
+pub const GL_PIXEL_TEX_GEN_ALPHA_LS_SGIX: u32 = 33161;
+pub const GL_PIXEL_TEX_GEN_ALPHA_MS_SGIX: u32 = 33162;
+pub const GL_FRAMEZOOM_SGIX: u32 = 33163;
+pub const GL_FRAMEZOOM_FACTOR_SGIX: u32 = 33164;
+pub const GL_MAX_FRAMEZOOM_FACTOR_SGIX: u32 = 33165;
+pub const GL_TEXTURE_LOD_BIAS_S_SGIX: u32 = 33166;
+pub const GL_TEXTURE_LOD_BIAS_T_SGIX: u32 = 33167;
+pub const GL_TEXTURE_LOD_BIAS_R_SGIX: u32 = 33168;
+pub const GL_GENERATE_MIPMAP: u32 = 33169;
+pub const GL_GENERATE_MIPMAP_SGIS: u32 = 33169;
+pub const GL_GENERATE_MIPMAP_HINT: u32 = 33170;
+pub const GL_GENERATE_MIPMAP_HINT_SGIS: u32 = 33170;
+pub const GL_GEOMETRY_DEFORMATION_SGIX: u32 = 33172;
+pub const GL_TEXTURE_DEFORMATION_SGIX: u32 = 33173;
+pub const GL_DEFORMATIONS_MASK_SGIX: u32 = 33174;
+pub const GL_MAX_DEFORMATION_ORDER_SGIX: u32 = 33175;
+pub const GL_FOG_OFFSET_SGIX: u32 = 33176;
+pub const GL_FOG_OFFSET_VALUE_SGIX: u32 = 33177;
+pub const GL_TEXTURE_COMPARE_SGIX: u32 = 33178;
+pub const GL_TEXTURE_COMPARE_OPERATOR_SGIX: u32 = 33179;
+pub const GL_TEXTURE_LEQUAL_R_SGIX: u32 = 33180;
+pub const GL_TEXTURE_GEQUAL_R_SGIX: u32 = 33181;
+pub const GL_DEPTH_COMPONENT16: u32 = 33189;
+pub const GL_DEPTH_COMPONENT16_ARB: u32 = 33189;
+pub const GL_DEPTH_COMPONENT16_OES: u32 = 33189;
+pub const GL_DEPTH_COMPONENT16_SGIX: u32 = 33189;
+pub const GL_DEPTH_COMPONENT24: u32 = 33190;
+pub const GL_DEPTH_COMPONENT24_ARB: u32 = 33190;
+pub const GL_DEPTH_COMPONENT24_OES: u32 = 33190;
+pub const GL_DEPTH_COMPONENT24_SGIX: u32 = 33190;
+pub const GL_DEPTH_COMPONENT32: u32 = 33191;
+pub const GL_DEPTH_COMPONENT32_ARB: u32 = 33191;
+pub const GL_DEPTH_COMPONENT32_OES: u32 = 33191;
+pub const GL_DEPTH_COMPONENT32_SGIX: u32 = 33191;
+pub const GL_ARRAY_ELEMENT_LOCK_FIRST_EXT: u32 = 33192;
+pub const GL_ARRAY_ELEMENT_LOCK_COUNT_EXT: u32 = 33193;
+pub const GL_CULL_VERTEX_EXT: u32 = 33194;
+pub const GL_CULL_VERTEX_EYE_POSITION_EXT: u32 = 33195;
+pub const GL_CULL_VERTEX_OBJECT_POSITION_EXT: u32 = 33196;
+pub const GL_IUI_V2F_EXT: u32 = 33197;
+pub const GL_IUI_V3F_EXT: u32 = 33198;
+pub const GL_IUI_N3F_V2F_EXT: u32 = 33199;
+pub const GL_IUI_N3F_V3F_EXT: u32 = 33200;
+pub const GL_T2F_IUI_V2F_EXT: u32 = 33201;
+pub const GL_T2F_IUI_V3F_EXT: u32 = 33202;
+pub const GL_T2F_IUI_N3F_V2F_EXT: u32 = 33203;
+pub const GL_T2F_IUI_N3F_V3F_EXT: u32 = 33204;
+pub const GL_INDEX_TEST_EXT: u32 = 33205;
+pub const GL_INDEX_TEST_FUNC_EXT: u32 = 33206;
+pub const GL_INDEX_TEST_REF_EXT: u32 = 33207;
+pub const GL_INDEX_MATERIAL_EXT: u32 = 33208;
+pub const GL_INDEX_MATERIAL_PARAMETER_EXT: u32 = 33209;
+pub const GL_INDEX_MATERIAL_FACE_EXT: u32 = 33210;
+pub const GL_YCRCB_422_SGIX: u32 = 33211;
+pub const GL_YCRCB_444_SGIX: u32 = 33212;
+pub const GL_WRAP_BORDER_SUN: u32 = 33236;
+pub const GL_UNPACK_CONSTANT_DATA_SUNX: u32 = 33237;
+pub const GL_TEXTURE_CONSTANT_DATA_SUNX: u32 = 33238;
+pub const GL_TRIANGLE_LIST_SUN: u32 = 33239;
+pub const GL_REPLACEMENT_CODE_SUN: u32 = 33240;
+pub const GL_GLOBAL_ALPHA_SUN: u32 = 33241;
+pub const GL_GLOBAL_ALPHA_FACTOR_SUN: u32 = 33242;
+pub const GL_TEXTURE_COLOR_WRITEMASK_SGIS: u32 = 33263;
+pub const GL_EYE_DISTANCE_TO_POINT_SGIS: u32 = 33264;
+pub const GL_OBJECT_DISTANCE_TO_POINT_SGIS: u32 = 33265;
+pub const GL_EYE_DISTANCE_TO_LINE_SGIS: u32 = 33266;
+pub const GL_OBJECT_DISTANCE_TO_LINE_SGIS: u32 = 33267;
+pub const GL_EYE_POINT_SGIS: u32 = 33268;
+pub const GL_OBJECT_POINT_SGIS: u32 = 33269;
+pub const GL_EYE_LINE_SGIS: u32 = 33270;
+pub const GL_OBJECT_LINE_SGIS: u32 = 33271;
+pub const GL_LIGHT_MODEL_COLOR_CONTROL: u32 = 33272;
+pub const GL_LIGHT_MODEL_COLOR_CONTROL_EXT: u32 = 33272;
+pub const GL_SINGLE_COLOR: u32 = 33273;
+pub const GL_SINGLE_COLOR_EXT: u32 = 33273;
+pub const GL_SEPARATE_SPECULAR_COLOR: u32 = 33274;
+pub const GL_SEPARATE_SPECULAR_COLOR_EXT: u32 = 33274;
+pub const GL_SHARED_TEXTURE_PALETTE_EXT: u32 = 33275;
+pub const GL_TEXT_FRAGMENT_SHADER_ATI: u32 = 33280;
+pub const GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: u32 = 33296;
+pub const GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT: u32 = 33296;
+pub const GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: u32 = 33297;
+pub const GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: u32 = 33297;
+pub const GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: u32 = 33298;
+pub const GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: u32 = 33299;
+pub const GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: u32 = 33300;
+pub const GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: u32 = 33301;
+pub const GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: u32 = 33302;
+pub const GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: u32 = 33303;
+pub const GL_FRAMEBUFFER_DEFAULT: u32 = 33304;
+pub const GL_FRAMEBUFFER_UNDEFINED: u32 = 33305;
+pub const GL_FRAMEBUFFER_UNDEFINED_OES: u32 = 33305;
+pub const GL_DEPTH_STENCIL_ATTACHMENT: u32 = 33306;
+pub const GL_MAJOR_VERSION: u32 = 33307;
+pub const GL_MINOR_VERSION: u32 = 33308;
+pub const GL_NUM_EXTENSIONS: u32 = 33309;
+pub const GL_CONTEXT_FLAGS: u32 = 33310;
+pub const GL_BUFFER_IMMUTABLE_STORAGE: u32 = 33311;
+pub const GL_BUFFER_IMMUTABLE_STORAGE_EXT: u32 = 33311;
+pub const GL_BUFFER_STORAGE_FLAGS: u32 = 33312;
+pub const GL_BUFFER_STORAGE_FLAGS_EXT: u32 = 33312;
+pub const GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED: u32 = 33313;
+pub const GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED_OES: u32 = 33313;
+pub const GL_INDEX: u32 = 33314;
+pub const GL_COMPRESSED_RED: u32 = 33317;
+pub const GL_COMPRESSED_RG: u32 = 33318;
+pub const GL_RG: u32 = 33319;
+pub const GL_RG_EXT: u32 = 33319;
+pub const GL_RG_INTEGER: u32 = 33320;
+pub const GL_R8: u32 = 33321;
+pub const GL_R8_EXT: u32 = 33321;
+pub const GL_R16: u32 = 33322;
+pub const GL_R16_EXT: u32 = 33322;
+pub const GL_RG8: u32 = 33323;
+pub const GL_RG8_EXT: u32 = 33323;
+pub const GL_RG16: u32 = 33324;
+pub const GL_RG16_EXT: u32 = 33324;
+pub const GL_R16F: u32 = 33325;
+pub const GL_R16F_EXT: u32 = 33325;
+pub const GL_R32F: u32 = 33326;
+pub const GL_R32F_EXT: u32 = 33326;
+pub const GL_RG16F: u32 = 33327;
+pub const GL_RG16F_EXT: u32 = 33327;
+pub const GL_RG32F: u32 = 33328;
+pub const GL_RG32F_EXT: u32 = 33328;
+pub const GL_R8I: u32 = 33329;
+pub const GL_R8UI: u32 = 33330;
+pub const GL_R16I: u32 = 33331;
+pub const GL_R16UI: u32 = 33332;
+pub const GL_R32I: u32 = 33333;
+pub const GL_R32UI: u32 = 33334;
+pub const GL_RG8I: u32 = 33335;
+pub const GL_RG8UI: u32 = 33336;
+pub const GL_RG16I: u32 = 33337;
+pub const GL_RG16UI: u32 = 33338;
+pub const GL_RG32I: u32 = 33339;
+pub const GL_RG32UI: u32 = 33340;
+pub const GL_SYNC_CL_EVENT_ARB: u32 = 33344;
+pub const GL_SYNC_CL_EVENT_COMPLETE_ARB: u32 = 33345;
+pub const GL_DEBUG_OUTPUT_SYNCHRONOUS: u32 = 33346;
+pub const GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB: u32 = 33346;
+pub const GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR: u32 = 33346;
+pub const GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH: u32 = 33347;
+pub const GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB: u32 = 33347;
+pub const GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR: u32 = 33347;
+pub const GL_DEBUG_CALLBACK_FUNCTION: u32 = 33348;
+pub const GL_DEBUG_CALLBACK_FUNCTION_ARB: u32 = 33348;
+pub const GL_DEBUG_CALLBACK_FUNCTION_KHR: u32 = 33348;
+pub const GL_DEBUG_CALLBACK_USER_PARAM: u32 = 33349;
+pub const GL_DEBUG_CALLBACK_USER_PARAM_ARB: u32 = 33349;
+pub const GL_DEBUG_CALLBACK_USER_PARAM_KHR: u32 = 33349;
+pub const GL_DEBUG_SOURCE_API: u32 = 33350;
+pub const GL_DEBUG_SOURCE_API_ARB: u32 = 33350;
+pub const GL_DEBUG_SOURCE_API_KHR: u32 = 33350;
+pub const GL_DEBUG_SOURCE_WINDOW_SYSTEM: u32 = 33351;
+pub const GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: u32 = 33351;
+pub const GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR: u32 = 33351;
+pub const GL_DEBUG_SOURCE_SHADER_COMPILER: u32 = 33352;
+pub const GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: u32 = 33352;
+pub const GL_DEBUG_SOURCE_SHADER_COMPILER_KHR: u32 = 33352;
+pub const GL_DEBUG_SOURCE_THIRD_PARTY: u32 = 33353;
+pub const GL_DEBUG_SOURCE_THIRD_PARTY_ARB: u32 = 33353;
+pub const GL_DEBUG_SOURCE_THIRD_PARTY_KHR: u32 = 33353;
+pub const GL_DEBUG_SOURCE_APPLICATION: u32 = 33354;
+pub const GL_DEBUG_SOURCE_APPLICATION_ARB: u32 = 33354;
+pub const GL_DEBUG_SOURCE_APPLICATION_KHR: u32 = 33354;
+pub const GL_DEBUG_SOURCE_OTHER: u32 = 33355;
+pub const GL_DEBUG_SOURCE_OTHER_ARB: u32 = 33355;
+pub const GL_DEBUG_SOURCE_OTHER_KHR: u32 = 33355;
+pub const GL_DEBUG_TYPE_ERROR: u32 = 33356;
+pub const GL_DEBUG_TYPE_ERROR_ARB: u32 = 33356;
+pub const GL_DEBUG_TYPE_ERROR_KHR: u32 = 33356;
+pub const GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: u32 = 33357;
+pub const GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: u32 = 33357;
+pub const GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR: u32 = 33357;
+pub const GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: u32 = 33358;
+pub const GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: u32 = 33358;
+pub const GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR: u32 = 33358;
+pub const GL_DEBUG_TYPE_PORTABILITY: u32 = 33359;
+pub const GL_DEBUG_TYPE_PORTABILITY_ARB: u32 = 33359;
+pub const GL_DEBUG_TYPE_PORTABILITY_KHR: u32 = 33359;
+pub const GL_DEBUG_TYPE_PERFORMANCE: u32 = 33360;
+pub const GL_DEBUG_TYPE_PERFORMANCE_ARB: u32 = 33360;
+pub const GL_DEBUG_TYPE_PERFORMANCE_KHR: u32 = 33360;
+pub const GL_DEBUG_TYPE_OTHER: u32 = 33361;
+pub const GL_DEBUG_TYPE_OTHER_ARB: u32 = 33361;
+pub const GL_DEBUG_TYPE_OTHER_KHR: u32 = 33361;
+pub const GL_LOSE_CONTEXT_ON_RESET: u32 = 33362;
+pub const GL_LOSE_CONTEXT_ON_RESET_ARB: u32 = 33362;
+pub const GL_LOSE_CONTEXT_ON_RESET_EXT: u32 = 33362;
+pub const GL_LOSE_CONTEXT_ON_RESET_KHR: u32 = 33362;
+pub const GL_GUILTY_CONTEXT_RESET: u32 = 33363;
+pub const GL_GUILTY_CONTEXT_RESET_ARB: u32 = 33363;
+pub const GL_GUILTY_CONTEXT_RESET_EXT: u32 = 33363;
+pub const GL_GUILTY_CONTEXT_RESET_KHR: u32 = 33363;
+pub const GL_INNOCENT_CONTEXT_RESET: u32 = 33364;
+pub const GL_INNOCENT_CONTEXT_RESET_ARB: u32 = 33364;
+pub const GL_INNOCENT_CONTEXT_RESET_EXT: u32 = 33364;
+pub const GL_INNOCENT_CONTEXT_RESET_KHR: u32 = 33364;
+pub const GL_UNKNOWN_CONTEXT_RESET: u32 = 33365;
+pub const GL_UNKNOWN_CONTEXT_RESET_ARB: u32 = 33365;
+pub const GL_UNKNOWN_CONTEXT_RESET_EXT: u32 = 33365;
+pub const GL_UNKNOWN_CONTEXT_RESET_KHR: u32 = 33365;
+pub const GL_RESET_NOTIFICATION_STRATEGY: u32 = 33366;
+pub const GL_RESET_NOTIFICATION_STRATEGY_ARB: u32 = 33366;
+pub const GL_RESET_NOTIFICATION_STRATEGY_EXT: u32 = 33366;
+pub const GL_RESET_NOTIFICATION_STRATEGY_KHR: u32 = 33366;
+pub const GL_PROGRAM_BINARY_RETRIEVABLE_HINT: u32 = 33367;
+pub const GL_PROGRAM_SEPARABLE: u32 = 33368;
+pub const GL_PROGRAM_SEPARABLE_EXT: u32 = 33368;
+pub const GL_ACTIVE_PROGRAM: u32 = 33369;
+pub const GL_PROGRAM_PIPELINE_BINDING: u32 = 33370;
+pub const GL_PROGRAM_PIPELINE_BINDING_EXT: u32 = 33370;
+pub const GL_MAX_VIEWPORTS: u32 = 33371;
+pub const GL_MAX_VIEWPORTS_NV: u32 = 33371;
+pub const GL_MAX_VIEWPORTS_OES: u32 = 33371;
+pub const GL_VIEWPORT_SUBPIXEL_BITS: u32 = 33372;
+pub const GL_VIEWPORT_SUBPIXEL_BITS_EXT: u32 = 33372;
+pub const GL_VIEWPORT_SUBPIXEL_BITS_NV: u32 = 33372;
+pub const GL_VIEWPORT_SUBPIXEL_BITS_OES: u32 = 33372;
+pub const GL_VIEWPORT_BOUNDS_RANGE: u32 = 33373;
+pub const GL_VIEWPORT_BOUNDS_RANGE_EXT: u32 = 33373;
+pub const GL_VIEWPORT_BOUNDS_RANGE_NV: u32 = 33373;
+pub const GL_VIEWPORT_BOUNDS_RANGE_OES: u32 = 33373;
+pub const GL_LAYER_PROVOKING_VERTEX: u32 = 33374;
+pub const GL_LAYER_PROVOKING_VERTEX_EXT: u32 = 33374;
+pub const GL_LAYER_PROVOKING_VERTEX_OES: u32 = 33374;
+pub const GL_VIEWPORT_INDEX_PROVOKING_VERTEX: u32 = 33375;
+pub const GL_VIEWPORT_INDEX_PROVOKING_VERTEX_EXT: u32 = 33375;
+pub const GL_VIEWPORT_INDEX_PROVOKING_VERTEX_NV: u32 = 33375;
+pub const GL_VIEWPORT_INDEX_PROVOKING_VERTEX_OES: u32 = 33375;
+pub const GL_UNDEFINED_VERTEX: u32 = 33376;
+pub const GL_UNDEFINED_VERTEX_EXT: u32 = 33376;
+pub const GL_UNDEFINED_VERTEX_OES: u32 = 33376;
+pub const GL_NO_RESET_NOTIFICATION: u32 = 33377;
+pub const GL_NO_RESET_NOTIFICATION_ARB: u32 = 33377;
+pub const GL_NO_RESET_NOTIFICATION_EXT: u32 = 33377;
+pub const GL_NO_RESET_NOTIFICATION_KHR: u32 = 33377;
+pub const GL_MAX_COMPUTE_SHARED_MEMORY_SIZE: u32 = 33378;
+pub const GL_MAX_COMPUTE_UNIFORM_COMPONENTS: u32 = 33379;
+pub const GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS: u32 = 33380;
+pub const GL_MAX_COMPUTE_ATOMIC_COUNTERS: u32 = 33381;
+pub const GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS: u32 = 33382;
+pub const GL_COMPUTE_WORK_GROUP_SIZE: u32 = 33383;
+pub const GL_DEBUG_TYPE_MARKER: u32 = 33384;
+pub const GL_DEBUG_TYPE_MARKER_KHR: u32 = 33384;
+pub const GL_DEBUG_TYPE_PUSH_GROUP: u32 = 33385;
+pub const GL_DEBUG_TYPE_PUSH_GROUP_KHR: u32 = 33385;
+pub const GL_DEBUG_TYPE_POP_GROUP: u32 = 33386;
+pub const GL_DEBUG_TYPE_POP_GROUP_KHR: u32 = 33386;
+pub const GL_DEBUG_SEVERITY_NOTIFICATION: u32 = 33387;
+pub const GL_DEBUG_SEVERITY_NOTIFICATION_KHR: u32 = 33387;
+pub const GL_MAX_DEBUG_GROUP_STACK_DEPTH: u32 = 33388;
+pub const GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR: u32 = 33388;
+pub const GL_DEBUG_GROUP_STACK_DEPTH: u32 = 33389;
+pub const GL_DEBUG_GROUP_STACK_DEPTH_KHR: u32 = 33389;
+pub const GL_MAX_UNIFORM_LOCATIONS: u32 = 33390;
+pub const GL_INTERNALFORMAT_SUPPORTED: u32 = 33391;
+pub const GL_INTERNALFORMAT_PREFERRED: u32 = 33392;
+pub const GL_INTERNALFORMAT_RED_SIZE: u32 = 33393;
+pub const GL_INTERNALFORMAT_GREEN_SIZE: u32 = 33394;
+pub const GL_INTERNALFORMAT_BLUE_SIZE: u32 = 33395;
+pub const GL_INTERNALFORMAT_ALPHA_SIZE: u32 = 33396;
+pub const GL_INTERNALFORMAT_DEPTH_SIZE: u32 = 33397;
+pub const GL_INTERNALFORMAT_STENCIL_SIZE: u32 = 33398;
+pub const GL_INTERNALFORMAT_SHARED_SIZE: u32 = 33399;
+pub const GL_INTERNALFORMAT_RED_TYPE: u32 = 33400;
+pub const GL_INTERNALFORMAT_GREEN_TYPE: u32 = 33401;
+pub const GL_INTERNALFORMAT_BLUE_TYPE: u32 = 33402;
+pub const GL_INTERNALFORMAT_ALPHA_TYPE: u32 = 33403;
+pub const GL_INTERNALFORMAT_DEPTH_TYPE: u32 = 33404;
+pub const GL_INTERNALFORMAT_STENCIL_TYPE: u32 = 33405;
+pub const GL_MAX_WIDTH: u32 = 33406;
+pub const GL_MAX_HEIGHT: u32 = 33407;
+pub const GL_MAX_DEPTH: u32 = 33408;
+pub const GL_MAX_LAYERS: u32 = 33409;
+pub const GL_MAX_COMBINED_DIMENSIONS: u32 = 33410;
+pub const GL_COLOR_COMPONENTS: u32 = 33411;
+pub const GL_DEPTH_COMPONENTS: u32 = 33412;
+pub const GL_STENCIL_COMPONENTS: u32 = 33413;
+pub const GL_COLOR_RENDERABLE: u32 = 33414;
+pub const GL_DEPTH_RENDERABLE: u32 = 33415;
+pub const GL_STENCIL_RENDERABLE: u32 = 33416;
+pub const GL_FRAMEBUFFER_RENDERABLE: u32 = 33417;
+pub const GL_FRAMEBUFFER_RENDERABLE_LAYERED: u32 = 33418;
+pub const GL_FRAMEBUFFER_BLEND: u32 = 33419;
+pub const GL_READ_PIXELS: u32 = 33420;
+pub const GL_READ_PIXELS_FORMAT: u32 = 33421;
+pub const GL_READ_PIXELS_TYPE: u32 = 33422;
+pub const GL_TEXTURE_IMAGE_FORMAT: u32 = 33423;
+pub const GL_TEXTURE_IMAGE_TYPE: u32 = 33424;
+pub const GL_GET_TEXTURE_IMAGE_FORMAT: u32 = 33425;
+pub const GL_GET_TEXTURE_IMAGE_TYPE: u32 = 33426;
+pub const GL_MIPMAP: u32 = 33427;
+pub const GL_MANUAL_GENERATE_MIPMAP: u32 = 33428;
+pub const GL_AUTO_GENERATE_MIPMAP: u32 = 33429;
+pub const GL_COLOR_ENCODING: u32 = 33430;
+pub const GL_SRGB_READ: u32 = 33431;
+pub const GL_SRGB_WRITE: u32 = 33432;
+pub const GL_SRGB_DECODE_ARB: u32 = 33433;
+pub const GL_FILTER: u32 = 33434;
+pub const GL_VERTEX_TEXTURE: u32 = 33435;
+pub const GL_TESS_CONTROL_TEXTURE: u32 = 33436;
+pub const GL_TESS_EVALUATION_TEXTURE: u32 = 33437;
+pub const GL_GEOMETRY_TEXTURE: u32 = 33438;
+pub const GL_FRAGMENT_TEXTURE: u32 = 33439;
+pub const GL_COMPUTE_TEXTURE: u32 = 33440;
+pub const GL_TEXTURE_SHADOW: u32 = 33441;
+pub const GL_TEXTURE_GATHER: u32 = 33442;
+pub const GL_TEXTURE_GATHER_SHADOW: u32 = 33443;
+pub const GL_SHADER_IMAGE_LOAD: u32 = 33444;
+pub const GL_SHADER_IMAGE_STORE: u32 = 33445;
+pub const GL_SHADER_IMAGE_ATOMIC: u32 = 33446;
+pub const GL_IMAGE_TEXEL_SIZE: u32 = 33447;
+pub const GL_IMAGE_COMPATIBILITY_CLASS: u32 = 33448;
+pub const GL_IMAGE_PIXEL_FORMAT: u32 = 33449;
+pub const GL_IMAGE_PIXEL_TYPE: u32 = 33450;
+pub const GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST: u32 = 33452;
+pub const GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST: u32 = 33453;
+pub const GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE: u32 = 33454;
+pub const GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE: u32 = 33455;
+pub const GL_TEXTURE_COMPRESSED_BLOCK_WIDTH: u32 = 33457;
+pub const GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT: u32 = 33458;
+pub const GL_TEXTURE_COMPRESSED_BLOCK_SIZE: u32 = 33459;
+pub const GL_CLEAR_BUFFER: u32 = 33460;
+pub const GL_TEXTURE_VIEW: u32 = 33461;
+pub const GL_VIEW_COMPATIBILITY_CLASS: u32 = 33462;
+pub const GL_FULL_SUPPORT: u32 = 33463;
+pub const GL_CAVEAT_SUPPORT: u32 = 33464;
+pub const GL_IMAGE_CLASS_4_X_32: u32 = 33465;
+pub const GL_IMAGE_CLASS_2_X_32: u32 = 33466;
+pub const GL_IMAGE_CLASS_1_X_32: u32 = 33467;
+pub const GL_IMAGE_CLASS_4_X_16: u32 = 33468;
+pub const GL_IMAGE_CLASS_2_X_16: u32 = 33469;
+pub const GL_IMAGE_CLASS_1_X_16: u32 = 33470;
+pub const GL_IMAGE_CLASS_4_X_8: u32 = 33471;
+pub const GL_IMAGE_CLASS_2_X_8: u32 = 33472;
+pub const GL_IMAGE_CLASS_1_X_8: u32 = 33473;
+pub const GL_IMAGE_CLASS_11_11_10: u32 = 33474;
+pub const GL_IMAGE_CLASS_10_10_10_2: u32 = 33475;
+pub const GL_VIEW_CLASS_128_BITS: u32 = 33476;
+pub const GL_VIEW_CLASS_96_BITS: u32 = 33477;
+pub const GL_VIEW_CLASS_64_BITS: u32 = 33478;
+pub const GL_VIEW_CLASS_48_BITS: u32 = 33479;
+pub const GL_VIEW_CLASS_32_BITS: u32 = 33480;
+pub const GL_VIEW_CLASS_24_BITS: u32 = 33481;
+pub const GL_VIEW_CLASS_16_BITS: u32 = 33482;
+pub const GL_VIEW_CLASS_8_BITS: u32 = 33483;
+pub const GL_VIEW_CLASS_S3TC_DXT1_RGB: u32 = 33484;
+pub const GL_VIEW_CLASS_S3TC_DXT1_RGBA: u32 = 33485;
+pub const GL_VIEW_CLASS_S3TC_DXT3_RGBA: u32 = 33486;
+pub const GL_VIEW_CLASS_S3TC_DXT5_RGBA: u32 = 33487;
+pub const GL_VIEW_CLASS_RGTC1_RED: u32 = 33488;
+pub const GL_VIEW_CLASS_RGTC2_RG: u32 = 33489;
+pub const GL_VIEW_CLASS_BPTC_UNORM: u32 = 33490;
+pub const GL_VIEW_CLASS_BPTC_FLOAT: u32 = 33491;
+pub const GL_VERTEX_ATTRIB_BINDING: u32 = 33492;
+pub const GL_VERTEX_ATTRIB_RELATIVE_OFFSET: u32 = 33493;
+pub const GL_VERTEX_BINDING_DIVISOR: u32 = 33494;
+pub const GL_VERTEX_BINDING_OFFSET: u32 = 33495;
+pub const GL_VERTEX_BINDING_STRIDE: u32 = 33496;
+pub const GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET: u32 = 33497;
+pub const GL_MAX_VERTEX_ATTRIB_BINDINGS: u32 = 33498;
+pub const GL_TEXTURE_VIEW_MIN_LEVEL: u32 = 33499;
+pub const GL_TEXTURE_VIEW_MIN_LEVEL_EXT: u32 = 33499;
+pub const GL_TEXTURE_VIEW_MIN_LEVEL_OES: u32 = 33499;
+pub const GL_TEXTURE_VIEW_NUM_LEVELS: u32 = 33500;
+pub const GL_TEXTURE_VIEW_NUM_LEVELS_EXT: u32 = 33500;
+pub const GL_TEXTURE_VIEW_NUM_LEVELS_OES: u32 = 33500;
+pub const GL_TEXTURE_VIEW_MIN_LAYER: u32 = 33501;
+pub const GL_TEXTURE_VIEW_MIN_LAYER_EXT: u32 = 33501;
+pub const GL_TEXTURE_VIEW_MIN_LAYER_OES: u32 = 33501;
+pub const GL_TEXTURE_VIEW_NUM_LAYERS: u32 = 33502;
+pub const GL_TEXTURE_VIEW_NUM_LAYERS_EXT: u32 = 33502;
+pub const GL_TEXTURE_VIEW_NUM_LAYERS_OES: u32 = 33502;
+pub const GL_TEXTURE_IMMUTABLE_LEVELS: u32 = 33503;
+pub const GL_BUFFER: u32 = 33504;
+pub const GL_BUFFER_KHR: u32 = 33504;
+pub const GL_SHADER: u32 = 33505;
+pub const GL_SHADER_KHR: u32 = 33505;
+pub const GL_PROGRAM: u32 = 33506;
+pub const GL_PROGRAM_KHR: u32 = 33506;
+pub const GL_QUERY: u32 = 33507;
+pub const GL_QUERY_KHR: u32 = 33507;
+pub const GL_PROGRAM_PIPELINE: u32 = 33508;
+pub const GL_PROGRAM_PIPELINE_KHR: u32 = 33508;
+pub const GL_MAX_VERTEX_ATTRIB_STRIDE: u32 = 33509;
+pub const GL_SAMPLER: u32 = 33510;
+pub const GL_SAMPLER_KHR: u32 = 33510;
+pub const GL_DISPLAY_LIST: u32 = 33511;
+pub const GL_MAX_LABEL_LENGTH: u32 = 33512;
+pub const GL_MAX_LABEL_LENGTH_KHR: u32 = 33512;
+pub const GL_NUM_SHADING_LANGUAGE_VERSIONS: u32 = 33513;
+pub const GL_QUERY_TARGET: u32 = 33514;
+pub const GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB: u32 = 33516;
+pub const GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB: u32 = 33517;
+pub const GL_VERTICES_SUBMITTED_ARB: u32 = 33518;
+pub const GL_PRIMITIVES_SUBMITTED_ARB: u32 = 33519;
+pub const GL_VERTEX_SHADER_INVOCATIONS_ARB: u32 = 33520;
+pub const GL_TESS_CONTROL_SHADER_PATCHES_ARB: u32 = 33521;
+pub const GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: u32 = 33522;
+pub const GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: u32 = 33523;
+pub const GL_FRAGMENT_SHADER_INVOCATIONS_ARB: u32 = 33524;
+pub const GL_COMPUTE_SHADER_INVOCATIONS_ARB: u32 = 33525;
+pub const GL_CLIPPING_INPUT_PRIMITIVES_ARB: u32 = 33526;
+pub const GL_CLIPPING_OUTPUT_PRIMITIVES_ARB: u32 = 33527;
+pub const GL_SPARSE_BUFFER_PAGE_SIZE_ARB: u32 = 33528;
+pub const GL_MAX_CULL_DISTANCES: u32 = 33529;
+pub const GL_MAX_CULL_DISTANCES_EXT: u32 = 33529;
+pub const GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES: u32 = 33530;
+pub const GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT: u32 = 33530;
+pub const GL_CONTEXT_RELEASE_BEHAVIOR: u32 = 33531;
+pub const GL_CONTEXT_RELEASE_BEHAVIOR_KHR: u32 = 33531;
+pub const GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH: u32 = 33532;
+pub const GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR: u32 = 33532;
+pub const GL_DEPTH_PASS_INSTRUMENT_SGIX: u32 = 33552;
+pub const GL_DEPTH_PASS_INSTRUMENT_COUNTERS_SGIX: u32 = 33553;
+pub const GL_DEPTH_PASS_INSTRUMENT_MAX_SGIX: u32 = 33554;
+pub const GL_FRAGMENTS_INSTRUMENT_SGIX: u32 = 33555;
+pub const GL_FRAGMENTS_INSTRUMENT_COUNTERS_SGIX: u32 = 33556;
+pub const GL_FRAGMENTS_INSTRUMENT_MAX_SGIX: u32 = 33557;
+pub const GL_CONVOLUTION_HINT_SGIX: u32 = 33558;
+pub const GL_YCRCB_SGIX: u32 = 33560;
+pub const GL_YCRCBA_SGIX: u32 = 33561;
+pub const GL_UNPACK_COMPRESSED_SIZE_SGIX: u32 = 33562;
+pub const GL_PACK_MAX_COMPRESSED_SIZE_SGIX: u32 = 33563;
+pub const GL_PACK_COMPRESSED_SIZE_SGIX: u32 = 33564;
+pub const GL_SLIM8U_SGIX: u32 = 33565;
+pub const GL_SLIM10U_SGIX: u32 = 33566;
+pub const GL_SLIM12S_SGIX: u32 = 33567;
+pub const GL_ALPHA_MIN_SGIX: u32 = 33568;
+pub const GL_ALPHA_MAX_SGIX: u32 = 33569;
+pub const GL_SCALEBIAS_HINT_SGIX: u32 = 33570;
+pub const GL_ASYNC_MARKER_SGIX: u32 = 33577;
+pub const GL_PIXEL_TEX_GEN_MODE_SGIX: u32 = 33579;
+pub const GL_ASYNC_HISTOGRAM_SGIX: u32 = 33580;
+pub const GL_MAX_ASYNC_HISTOGRAM_SGIX: u32 = 33581;
+pub const GL_PIXEL_TRANSFORM_2D_EXT: u32 = 33584;
+pub const GL_PIXEL_MAG_FILTER_EXT: u32 = 33585;
+pub const GL_PIXEL_MIN_FILTER_EXT: u32 = 33586;
+pub const GL_PIXEL_CUBIC_WEIGHT_EXT: u32 = 33587;
+pub const GL_CUBIC_EXT: u32 = 33588;
+pub const GL_AVERAGE_EXT: u32 = 33589;
+pub const GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT: u32 = 33590;
+pub const GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT: u32 = 33591;
+pub const GL_PIXEL_TRANSFORM_2D_MATRIX_EXT: u32 = 33592;
+pub const GL_FRAGMENT_MATERIAL_EXT: u32 = 33609;
+pub const GL_FRAGMENT_NORMAL_EXT: u32 = 33610;
+pub const GL_FRAGMENT_COLOR_EXT: u32 = 33612;
+pub const GL_ATTENUATION_EXT: u32 = 33613;
+pub const GL_SHADOW_ATTENUATION_EXT: u32 = 33614;
+pub const GL_TEXTURE_APPLICATION_MODE_EXT: u32 = 33615;
+pub const GL_TEXTURE_LIGHT_EXT: u32 = 33616;
+pub const GL_TEXTURE_MATERIAL_FACE_EXT: u32 = 33617;
+pub const GL_TEXTURE_MATERIAL_PARAMETER_EXT: u32 = 33618;
+pub const GL_PIXEL_TEXTURE_SGIS: u32 = 33619;
+pub const GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS: u32 = 33620;
+pub const GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS: u32 = 33621;
+pub const GL_PIXEL_GROUP_COLOR_SGIS: u32 = 33622;
+pub const GL_LINE_QUALITY_HINT_SGIX: u32 = 33627;
+pub const GL_ASYNC_TEX_IMAGE_SGIX: u32 = 33628;
+pub const GL_ASYNC_DRAW_PIXELS_SGIX: u32 = 33629;
+pub const GL_ASYNC_READ_PIXELS_SGIX: u32 = 33630;
+pub const GL_MAX_ASYNC_TEX_IMAGE_SGIX: u32 = 33631;
+pub const GL_MAX_ASYNC_DRAW_PIXELS_SGIX: u32 = 33632;
+pub const GL_MAX_ASYNC_READ_PIXELS_SGIX: u32 = 33633;
+pub const GL_UNSIGNED_BYTE_2_3_3_REV: u32 = 33634;
+pub const GL_UNSIGNED_BYTE_2_3_3_REV_EXT: u32 = 33634;
+pub const GL_UNSIGNED_SHORT_5_6_5: u32 = 33635;
+pub const GL_UNSIGNED_SHORT_5_6_5_EXT: u32 = 33635;
+pub const GL_UNSIGNED_SHORT_5_6_5_REV: u32 = 33636;
+pub const GL_UNSIGNED_SHORT_5_6_5_REV_EXT: u32 = 33636;
+pub const GL_UNSIGNED_SHORT_4_4_4_4_REV: u32 = 33637;
+pub const GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT: u32 = 33637;
+pub const GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG: u32 = 33637;
+pub const GL_UNSIGNED_SHORT_1_5_5_5_REV: u32 = 33638;
+pub const GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT: u32 = 33638;
+pub const GL_UNSIGNED_INT_8_8_8_8_REV: u32 = 33639;
+pub const GL_UNSIGNED_INT_8_8_8_8_REV_EXT: u32 = 33639;
+pub const GL_UNSIGNED_INT_2_10_10_10_REV: u32 = 33640;
+pub const GL_UNSIGNED_INT_2_10_10_10_REV_EXT: u32 = 33640;
+pub const GL_TEXTURE_MAX_CLAMP_S_SGIX: u32 = 33641;
+pub const GL_TEXTURE_MAX_CLAMP_T_SGIX: u32 = 33642;
+pub const GL_TEXTURE_MAX_CLAMP_R_SGIX: u32 = 33643;
+pub const GL_MIRRORED_REPEAT: u32 = 33648;
+pub const GL_MIRRORED_REPEAT_ARB: u32 = 33648;
+pub const GL_MIRRORED_REPEAT_IBM: u32 = 33648;
+pub const GL_MIRRORED_REPEAT_OES: u32 = 33648;
+pub const GL_RGB_S3TC: u32 = 33696;
+pub const GL_RGB4_S3TC: u32 = 33697;
+pub const GL_RGBA_S3TC: u32 = 33698;
+pub const GL_RGBA4_S3TC: u32 = 33699;
+pub const GL_RGBA_DXT5_S3TC: u32 = 33700;
+pub const GL_RGBA4_DXT5_S3TC: u32 = 33701;
+pub const GL_VERTEX_PRECLIP_SGIX: u32 = 33774;
+pub const GL_VERTEX_PRECLIP_HINT_SGIX: u32 = 33775;
+pub const GL_COMPRESSED_RGB_S3TC_DXT1_EXT: u32 = 33776;
+pub const GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: u32 = 33777;
+pub const GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: u32 = 33778;
+pub const GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: u32 = 33778;
+pub const GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: u32 = 33779;
+pub const GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: u32 = 33779;
+pub const GL_PARALLEL_ARRAYS_INTEL: u32 = 33780;
+pub const GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL: u32 = 33781;
+pub const GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL: u32 = 33782;
+pub const GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL: u32 = 33783;
+pub const GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL: u32 = 33784;
+pub const GL_PERFQUERY_DONOT_FLUSH_INTEL: u32 = 33785;
+pub const GL_PERFQUERY_FLUSH_INTEL: u32 = 33786;
+pub const GL_PERFQUERY_WAIT_INTEL: u32 = 33787;
+pub const GL_CONSERVATIVE_RASTERIZATION_INTEL: u32 = 33790;
+pub const GL_TEXTURE_MEMORY_LAYOUT_INTEL: u32 = 33791;
+pub const GL_FRAGMENT_LIGHTING_SGIX: u32 = 33792;
+pub const GL_FRAGMENT_COLOR_MATERIAL_SGIX: u32 = 33793;
+pub const GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX: u32 = 33794;
+pub const GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX: u32 = 33795;
+pub const GL_MAX_FRAGMENT_LIGHTS_SGIX: u32 = 33796;
+pub const GL_MAX_ACTIVE_LIGHTS_SGIX: u32 = 33797;
+pub const GL_CURRENT_RASTER_NORMAL_SGIX: u32 = 33798;
+pub const GL_LIGHT_ENV_MODE_SGIX: u32 = 33799;
+pub const GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX: u32 = 33800;
+pub const GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX: u32 = 33801;
+pub const GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX: u32 = 33802;
+pub const GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX: u32 = 33803;
+pub const GL_FRAGMENT_LIGHT0_SGIX: u32 = 33804;
+pub const GL_FRAGMENT_LIGHT1_SGIX: u32 = 33805;
+pub const GL_FRAGMENT_LIGHT2_SGIX: u32 = 33806;
+pub const GL_FRAGMENT_LIGHT3_SGIX: u32 = 33807;
+pub const GL_FRAGMENT_LIGHT4_SGIX: u32 = 33808;
+pub const GL_FRAGMENT_LIGHT5_SGIX: u32 = 33809;
+pub const GL_FRAGMENT_LIGHT6_SGIX: u32 = 33810;
+pub const GL_FRAGMENT_LIGHT7_SGIX: u32 = 33811;
+pub const GL_PACK_RESAMPLE_SGIX: u32 = 33838;
+pub const GL_UNPACK_RESAMPLE_SGIX: u32 = 33839;
+pub const GL_RESAMPLE_DECIMATE_SGIX: u32 = 33840;
+pub const GL_RESAMPLE_REPLICATE_SGIX: u32 = 33843;
+pub const GL_RESAMPLE_ZERO_FILL_SGIX: u32 = 33844;
+pub const GL_TANGENT_ARRAY_EXT: u32 = 33849;
+pub const GL_BINORMAL_ARRAY_EXT: u32 = 33850;
+pub const GL_CURRENT_TANGENT_EXT: u32 = 33851;
+pub const GL_CURRENT_BINORMAL_EXT: u32 = 33852;
+pub const GL_TANGENT_ARRAY_TYPE_EXT: u32 = 33854;
+pub const GL_TANGENT_ARRAY_STRIDE_EXT: u32 = 33855;
+pub const GL_BINORMAL_ARRAY_TYPE_EXT: u32 = 33856;
+pub const GL_BINORMAL_ARRAY_STRIDE_EXT: u32 = 33857;
+pub const GL_TANGENT_ARRAY_POINTER_EXT: u32 = 33858;
+pub const GL_BINORMAL_ARRAY_POINTER_EXT: u32 = 33859;
+pub const GL_MAP1_TANGENT_EXT: u32 = 33860;
+pub const GL_MAP2_TANGENT_EXT: u32 = 33861;
+pub const GL_MAP1_BINORMAL_EXT: u32 = 33862;
+pub const GL_MAP2_BINORMAL_EXT: u32 = 33863;
+pub const GL_NEAREST_CLIPMAP_NEAREST_SGIX: u32 = 33869;
+pub const GL_NEAREST_CLIPMAP_LINEAR_SGIX: u32 = 33870;
+pub const GL_LINEAR_CLIPMAP_NEAREST_SGIX: u32 = 33871;
+pub const GL_FOG_COORDINATE_SOURCE: u32 = 33872;
+pub const GL_FOG_COORDINATE_SOURCE_EXT: u32 = 33872;
+pub const GL_FOG_COORD_SRC: u32 = 33872;
+pub const GL_FOG_COORD: u32 = 33873;
+pub const GL_FOG_COORDINATE: u32 = 33873;
+pub const GL_FOG_COORDINATE_EXT: u32 = 33873;
+pub const GL_FRAGMENT_DEPTH: u32 = 33874;
+pub const GL_FRAGMENT_DEPTH_EXT: u32 = 33874;
+pub const GL_CURRENT_FOG_COORD: u32 = 33875;
+pub const GL_CURRENT_FOG_COORDINATE: u32 = 33875;
+pub const GL_CURRENT_FOG_COORDINATE_EXT: u32 = 33875;
+pub const GL_FOG_COORDINATE_ARRAY_TYPE: u32 = 33876;
+pub const GL_FOG_COORDINATE_ARRAY_TYPE_EXT: u32 = 33876;
+pub const GL_FOG_COORD_ARRAY_TYPE: u32 = 33876;
+pub const GL_FOG_COORDINATE_ARRAY_STRIDE: u32 = 33877;
+pub const GL_FOG_COORDINATE_ARRAY_STRIDE_EXT: u32 = 33877;
+pub const GL_FOG_COORD_ARRAY_STRIDE: u32 = 33877;
+pub const GL_FOG_COORDINATE_ARRAY_POINTER: u32 = 33878;
+pub const GL_FOG_COORDINATE_ARRAY_POINTER_EXT: u32 = 33878;
+pub const GL_FOG_COORD_ARRAY_POINTER: u32 = 33878;
+pub const GL_FOG_COORDINATE_ARRAY: u32 = 33879;
+pub const GL_FOG_COORDINATE_ARRAY_EXT: u32 = 33879;
+pub const GL_FOG_COORD_ARRAY: u32 = 33879;
+pub const GL_COLOR_SUM: u32 = 33880;
+pub const GL_COLOR_SUM_ARB: u32 = 33880;
+pub const GL_COLOR_SUM_EXT: u32 = 33880;
+pub const GL_CURRENT_SECONDARY_COLOR: u32 = 33881;
+pub const GL_CURRENT_SECONDARY_COLOR_EXT: u32 = 33881;
+pub const GL_SECONDARY_COLOR_ARRAY_SIZE: u32 = 33882;
+pub const GL_SECONDARY_COLOR_ARRAY_SIZE_EXT: u32 = 33882;
+pub const GL_SECONDARY_COLOR_ARRAY_TYPE: u32 = 33883;
+pub const GL_SECONDARY_COLOR_ARRAY_TYPE_EXT: u32 = 33883;
+pub const GL_SECONDARY_COLOR_ARRAY_STRIDE: u32 = 33884;
+pub const GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT: u32 = 33884;
+pub const GL_SECONDARY_COLOR_ARRAY_POINTER: u32 = 33885;
+pub const GL_SECONDARY_COLOR_ARRAY_POINTER_EXT: u32 = 33885;
+pub const GL_SECONDARY_COLOR_ARRAY: u32 = 33886;
+pub const GL_SECONDARY_COLOR_ARRAY_EXT: u32 = 33886;
+pub const GL_CURRENT_RASTER_SECONDARY_COLOR: u32 = 33887;
+pub const GL_ALIASED_POINT_SIZE_RANGE: u32 = 33901;
+pub const GL_ALIASED_LINE_WIDTH_RANGE: u32 = 33902;
+pub const GL_SCREEN_COORDINATES_REND: u32 = 33936;
+pub const GL_INVERTED_SCREEN_W_REND: u32 = 33937;
+pub const GL_TEXTURE0: u32 = 33984;
+pub const GL_TEXTURE0_ARB: u32 = 33984;
+pub const GL_TEXTURE1: u32 = 33985;
+pub const GL_TEXTURE1_ARB: u32 = 33985;
+pub const GL_TEXTURE2: u32 = 33986;
+pub const GL_TEXTURE2_ARB: u32 = 33986;
+pub const GL_TEXTURE3: u32 = 33987;
+pub const GL_TEXTURE3_ARB: u32 = 33987;
+pub const GL_TEXTURE4: u32 = 33988;
+pub const GL_TEXTURE4_ARB: u32 = 33988;
+pub const GL_TEXTURE5: u32 = 33989;
+pub const GL_TEXTURE5_ARB: u32 = 33989;
+pub const GL_TEXTURE6: u32 = 33990;
+pub const GL_TEXTURE6_ARB: u32 = 33990;
+pub const GL_TEXTURE7: u32 = 33991;
+pub const GL_TEXTURE7_ARB: u32 = 33991;
+pub const GL_TEXTURE8: u32 = 33992;
+pub const GL_TEXTURE8_ARB: u32 = 33992;
+pub const GL_TEXTURE9: u32 = 33993;
+pub const GL_TEXTURE9_ARB: u32 = 33993;
+pub const GL_TEXTURE10: u32 = 33994;
+pub const GL_TEXTURE10_ARB: u32 = 33994;
+pub const GL_TEXTURE11: u32 = 33995;
+pub const GL_TEXTURE11_ARB: u32 = 33995;
+pub const GL_TEXTURE12: u32 = 33996;
+pub const GL_TEXTURE12_ARB: u32 = 33996;
+pub const GL_TEXTURE13: u32 = 33997;
+pub const GL_TEXTURE13_ARB: u32 = 33997;
+pub const GL_TEXTURE14: u32 = 33998;
+pub const GL_TEXTURE14_ARB: u32 = 33998;
+pub const GL_TEXTURE15: u32 = 33999;
+pub const GL_TEXTURE15_ARB: u32 = 33999;
+pub const GL_TEXTURE16: u32 = 34000;
+pub const GL_TEXTURE16_ARB: u32 = 34000;
+pub const GL_TEXTURE17: u32 = 34001;
+pub const GL_TEXTURE17_ARB: u32 = 34001;
+pub const GL_TEXTURE18: u32 = 34002;
+pub const GL_TEXTURE18_ARB: u32 = 34002;
+pub const GL_TEXTURE19: u32 = 34003;
+pub const GL_TEXTURE19_ARB: u32 = 34003;
+pub const GL_TEXTURE20: u32 = 34004;
+pub const GL_TEXTURE20_ARB: u32 = 34004;
+pub const GL_TEXTURE21: u32 = 34005;
+pub const GL_TEXTURE21_ARB: u32 = 34005;
+pub const GL_TEXTURE22: u32 = 34006;
+pub const GL_TEXTURE22_ARB: u32 = 34006;
+pub const GL_TEXTURE23: u32 = 34007;
+pub const GL_TEXTURE23_ARB: u32 = 34007;
+pub const GL_TEXTURE24: u32 = 34008;
+pub const GL_TEXTURE24_ARB: u32 = 34008;
+pub const GL_TEXTURE25: u32 = 34009;
+pub const GL_TEXTURE25_ARB: u32 = 34009;
+pub const GL_TEXTURE26: u32 = 34010;
+pub const GL_TEXTURE26_ARB: u32 = 34010;
+pub const GL_TEXTURE27: u32 = 34011;
+pub const GL_TEXTURE27_ARB: u32 = 34011;
+pub const GL_TEXTURE28: u32 = 34012;
+pub const GL_TEXTURE28_ARB: u32 = 34012;
+pub const GL_TEXTURE29: u32 = 34013;
+pub const GL_TEXTURE29_ARB: u32 = 34013;
+pub const GL_TEXTURE30: u32 = 34014;
+pub const GL_TEXTURE30_ARB: u32 = 34014;
+pub const GL_TEXTURE31: u32 = 34015;
+pub const GL_TEXTURE31_ARB: u32 = 34015;
+pub const GL_ACTIVE_TEXTURE: u32 = 34016;
+pub const GL_ACTIVE_TEXTURE_ARB: u32 = 34016;
+pub const GL_CLIENT_ACTIVE_TEXTURE: u32 = 34017;
+pub const GL_CLIENT_ACTIVE_TEXTURE_ARB: u32 = 34017;
+pub const GL_MAX_TEXTURE_UNITS: u32 = 34018;
+pub const GL_MAX_TEXTURE_UNITS_ARB: u32 = 34018;
+pub const GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV: u32 = 34019;
+pub const GL_TRANSPOSE_MODELVIEW_MATRIX: u32 = 34019;
+pub const GL_TRANSPOSE_MODELVIEW_MATRIX_ARB: u32 = 34019;
+pub const GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV: u32 = 34020;
+pub const GL_TRANSPOSE_PROJECTION_MATRIX: u32 = 34020;
+pub const GL_TRANSPOSE_PROJECTION_MATRIX_ARB: u32 = 34020;
+pub const GL_TRANSPOSE_TEXTURE_MATRIX: u32 = 34021;
+pub const GL_TRANSPOSE_TEXTURE_MATRIX_ARB: u32 = 34021;
+pub const GL_TRANSPOSE_COLOR_MATRIX: u32 = 34022;
+pub const GL_TRANSPOSE_COLOR_MATRIX_ARB: u32 = 34022;
+pub const GL_SUBTRACT: u32 = 34023;
+pub const GL_SUBTRACT_ARB: u32 = 34023;
+pub const GL_MAX_RENDERBUFFER_SIZE: u32 = 34024;
+pub const GL_MAX_RENDERBUFFER_SIZE_EXT: u32 = 34024;
+pub const GL_MAX_RENDERBUFFER_SIZE_OES: u32 = 34024;
+pub const GL_COMPRESSED_ALPHA: u32 = 34025;
+pub const GL_COMPRESSED_ALPHA_ARB: u32 = 34025;
+pub const GL_COMPRESSED_LUMINANCE: u32 = 34026;
+pub const GL_COMPRESSED_LUMINANCE_ARB: u32 = 34026;
+pub const GL_COMPRESSED_LUMINANCE_ALPHA: u32 = 34027;
+pub const GL_COMPRESSED_LUMINANCE_ALPHA_ARB: u32 = 34027;
+pub const GL_COMPRESSED_INTENSITY: u32 = 34028;
+pub const GL_COMPRESSED_INTENSITY_ARB: u32 = 34028;
+pub const GL_COMPRESSED_RGB: u32 = 34029;
+pub const GL_COMPRESSED_RGB_ARB: u32 = 34029;
+pub const GL_COMPRESSED_RGBA: u32 = 34030;
+pub const GL_COMPRESSED_RGBA_ARB: u32 = 34030;
+pub const GL_TEXTURE_COMPRESSION_HINT: u32 = 34031;
+pub const GL_TEXTURE_COMPRESSION_HINT_ARB: u32 = 34031;
+pub const GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER: u32 = 34032;
+pub const GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER: u32 = 34033;
+pub const GL_ALL_COMPLETED_NV: u32 = 34034;
+pub const GL_FENCE_STATUS_NV: u32 = 34035;
+pub const GL_FENCE_CONDITION_NV: u32 = 34036;
+pub const GL_TEXTURE_RECTANGLE: u32 = 34037;
+pub const GL_TEXTURE_RECTANGLE_ARB: u32 = 34037;
+pub const GL_TEXTURE_RECTANGLE_NV: u32 = 34037;
+pub const GL_TEXTURE_BINDING_RECTANGLE: u32 = 34038;
+pub const GL_TEXTURE_BINDING_RECTANGLE_ARB: u32 = 34038;
+pub const GL_TEXTURE_BINDING_RECTANGLE_NV: u32 = 34038;
+pub const GL_PROXY_TEXTURE_RECTANGLE: u32 = 34039;
+pub const GL_PROXY_TEXTURE_RECTANGLE_ARB: u32 = 34039;
+pub const GL_PROXY_TEXTURE_RECTANGLE_NV: u32 = 34039;
+pub const GL_MAX_RECTANGLE_TEXTURE_SIZE: u32 = 34040;
+pub const GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB: u32 = 34040;
+pub const GL_MAX_RECTANGLE_TEXTURE_SIZE_NV: u32 = 34040;
+pub const GL_DEPTH_STENCIL: u32 = 34041;
+pub const GL_DEPTH_STENCIL_EXT: u32 = 34041;
+pub const GL_DEPTH_STENCIL_NV: u32 = 34041;
+pub const GL_DEPTH_STENCIL_OES: u32 = 34041;
+pub const GL_UNSIGNED_INT_24_8: u32 = 34042;
+pub const GL_UNSIGNED_INT_24_8_EXT: u32 = 34042;
+pub const GL_UNSIGNED_INT_24_8_NV: u32 = 34042;
+pub const GL_UNSIGNED_INT_24_8_OES: u32 = 34042;
+pub const GL_MAX_TEXTURE_LOD_BIAS: u32 = 34045;
+pub const GL_MAX_TEXTURE_LOD_BIAS_EXT: u32 = 34045;
+pub const GL_TEXTURE_MAX_ANISOTROPY_EXT: u32 = 34046;
+pub const GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: u32 = 34047;
+pub const GL_TEXTURE_FILTER_CONTROL: u32 = 34048;
+pub const GL_TEXTURE_FILTER_CONTROL_EXT: u32 = 34048;
+pub const GL_TEXTURE_LOD_BIAS: u32 = 34049;
+pub const GL_TEXTURE_LOD_BIAS_EXT: u32 = 34049;
+pub const GL_MODELVIEW1_STACK_DEPTH_EXT: u32 = 34050;
+pub const GL_COMBINE4_NV: u32 = 34051;
+pub const GL_MAX_SHININESS_NV: u32 = 34052;
+pub const GL_MAX_SPOT_EXPONENT_NV: u32 = 34053;
+pub const GL_MODELVIEW1_MATRIX_EXT: u32 = 34054;
+pub const GL_INCR_WRAP: u32 = 34055;
+pub const GL_INCR_WRAP_EXT: u32 = 34055;
+pub const GL_INCR_WRAP_OES: u32 = 34055;
+pub const GL_DECR_WRAP: u32 = 34056;
+pub const GL_DECR_WRAP_EXT: u32 = 34056;
+pub const GL_DECR_WRAP_OES: u32 = 34056;
+pub const GL_VERTEX_WEIGHTING_EXT: u32 = 34057;
+pub const GL_MODELVIEW1_ARB: u32 = 34058;
+pub const GL_MODELVIEW1_EXT: u32 = 34058;
+pub const GL_CURRENT_VERTEX_WEIGHT_EXT: u32 = 34059;
+pub const GL_VERTEX_WEIGHT_ARRAY_EXT: u32 = 34060;
+pub const GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT: u32 = 34061;
+pub const GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT: u32 = 34062;
+pub const GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT: u32 = 34063;
+pub const GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT: u32 = 34064;
+pub const GL_NORMAL_MAP: u32 = 34065;
+pub const GL_NORMAL_MAP_ARB: u32 = 34065;
+pub const GL_NORMAL_MAP_EXT: u32 = 34065;
+pub const GL_NORMAL_MAP_NV: u32 = 34065;
+pub const GL_NORMAL_MAP_OES: u32 = 34065;
+pub const GL_REFLECTION_MAP: u32 = 34066;
+pub const GL_REFLECTION_MAP_ARB: u32 = 34066;
+pub const GL_REFLECTION_MAP_EXT: u32 = 34066;
+pub const GL_REFLECTION_MAP_NV: u32 = 34066;
+pub const GL_REFLECTION_MAP_OES: u32 = 34066;
+pub const GL_TEXTURE_CUBE_MAP: u32 = 34067;
+pub const GL_TEXTURE_CUBE_MAP_ARB: u32 = 34067;
+pub const GL_TEXTURE_CUBE_MAP_EXT: u32 = 34067;
+pub const GL_TEXTURE_CUBE_MAP_OES: u32 = 34067;
+pub const GL_TEXTURE_BINDING_CUBE_MAP: u32 = 34068;
+pub const GL_TEXTURE_BINDING_CUBE_MAP_ARB: u32 = 34068;
+pub const GL_TEXTURE_BINDING_CUBE_MAP_EXT: u32 = 34068;
+pub const GL_TEXTURE_BINDING_CUBE_MAP_OES: u32 = 34068;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_X: u32 = 34069;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB: u32 = 34069;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT: u32 = 34069;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES: u32 = 34069;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_X: u32 = 34070;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB: u32 = 34070;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT: u32 = 34070;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES: u32 = 34070;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Y: u32 = 34071;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB: u32 = 34071;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT: u32 = 34071;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES: u32 = 34071;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: u32 = 34072;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB: u32 = 34072;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT: u32 = 34072;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES: u32 = 34072;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Z: u32 = 34073;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB: u32 = 34073;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT: u32 = 34073;
+pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES: u32 = 34073;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: u32 = 34074;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB: u32 = 34074;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT: u32 = 34074;
+pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES: u32 = 34074;
+pub const GL_PROXY_TEXTURE_CUBE_MAP: u32 = 34075;
+pub const GL_PROXY_TEXTURE_CUBE_MAP_ARB: u32 = 34075;
+pub const GL_PROXY_TEXTURE_CUBE_MAP_EXT: u32 = 34075;
+pub const GL_MAX_CUBE_MAP_TEXTURE_SIZE: u32 = 34076;
+pub const GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB: u32 = 34076;
+pub const GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT: u32 = 34076;
+pub const GL_MAX_CUBE_MAP_TEXTURE_SIZE_OES: u32 = 34076;
+pub const GL_VERTEX_ARRAY_RANGE_APPLE: u32 = 34077;
+pub const GL_VERTEX_ARRAY_RANGE_NV: u32 = 34077;
+pub const GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE: u32 = 34078;
+pub const GL_VERTEX_ARRAY_RANGE_LENGTH_NV: u32 = 34078;
+pub const GL_VERTEX_ARRAY_RANGE_VALID_NV: u32 = 34079;
+pub const GL_VERTEX_ARRAY_STORAGE_HINT_APPLE: u32 = 34079;
+pub const GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV: u32 = 34080;
+pub const GL_VERTEX_ARRAY_RANGE_POINTER_APPLE: u32 = 34081;
+pub const GL_VERTEX_ARRAY_RANGE_POINTER_NV: u32 = 34081;
+pub const GL_REGISTER_COMBINERS_NV: u32 = 34082;
+pub const GL_VARIABLE_A_NV: u32 = 34083;
+pub const GL_VARIABLE_B_NV: u32 = 34084;
+pub const GL_VARIABLE_C_NV: u32 = 34085;
+pub const GL_VARIABLE_D_NV: u32 = 34086;
+pub const GL_VARIABLE_E_NV: u32 = 34087;
+pub const GL_VARIABLE_F_NV: u32 = 34088;
+pub const GL_VARIABLE_G_NV: u32 = 34089;
+pub const GL_CONSTANT_COLOR0_NV: u32 = 34090;
+pub const GL_CONSTANT_COLOR1_NV: u32 = 34091;
+pub const GL_PRIMARY_COLOR_NV: u32 = 34092;
+pub const GL_SECONDARY_COLOR_NV: u32 = 34093;
+pub const GL_SPARE0_NV: u32 = 34094;
+pub const GL_SPARE1_NV: u32 = 34095;
+pub const GL_DISCARD_NV: u32 = 34096;
+pub const GL_E_TIMES_F_NV: u32 = 34097;
+pub const GL_SPARE0_PLUS_SECONDARY_COLOR_NV: u32 = 34098;
+pub const GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV: u32 = 34099;
+pub const GL_MULTISAMPLE_FILTER_HINT_NV: u32 = 34100;
+pub const GL_PER_STAGE_CONSTANTS_NV: u32 = 34101;
+pub const GL_UNSIGNED_IDENTITY_NV: u32 = 34102;
+pub const GL_UNSIGNED_INVERT_NV: u32 = 34103;
+pub const GL_EXPAND_NORMAL_NV: u32 = 34104;
+pub const GL_EXPAND_NEGATE_NV: u32 = 34105;
+pub const GL_HALF_BIAS_NORMAL_NV: u32 = 34106;
+pub const GL_HALF_BIAS_NEGATE_NV: u32 = 34107;
+pub const GL_SIGNED_IDENTITY_NV: u32 = 34108;
+pub const GL_SIGNED_NEGATE_NV: u32 = 34109;
+pub const GL_SCALE_BY_TWO_NV: u32 = 34110;
+pub const GL_SCALE_BY_FOUR_NV: u32 = 34111;
+pub const GL_SCALE_BY_ONE_HALF_NV: u32 = 34112;
+pub const GL_BIAS_BY_NEGATIVE_ONE_HALF_NV: u32 = 34113;
+pub const GL_COMBINER_INPUT_NV: u32 = 34114;
+pub const GL_COMBINER_MAPPING_NV: u32 = 34115;
+pub const GL_COMBINER_COMPONENT_USAGE_NV: u32 = 34116;
+pub const GL_COMBINER_AB_DOT_PRODUCT_NV: u32 = 34117;
+pub const GL_COMBINER_CD_DOT_PRODUCT_NV: u32 = 34118;
+pub const GL_COMBINER_MUX_SUM_NV: u32 = 34119;
+pub const GL_COMBINER_SCALE_NV: u32 = 34120;
+pub const GL_COMBINER_BIAS_NV: u32 = 34121;
+pub const GL_COMBINER_AB_OUTPUT_NV: u32 = 34122;
+pub const GL_COMBINER_CD_OUTPUT_NV: u32 = 34123;
+pub const GL_COMBINER_SUM_OUTPUT_NV: u32 = 34124;
+pub const GL_MAX_GENERAL_COMBINERS_NV: u32 = 34125;
+pub const GL_NUM_GENERAL_COMBINERS_NV: u32 = 34126;
+pub const GL_COLOR_SUM_CLAMP_NV: u32 = 34127;
+pub const GL_COMBINER0_NV: u32 = 34128;
+pub const GL_COMBINER1_NV: u32 = 34129;
+pub const GL_COMBINER2_NV: u32 = 34130;
+pub const GL_COMBINER3_NV: u32 = 34131;
+pub const GL_COMBINER4_NV: u32 = 34132;
+pub const GL_COMBINER5_NV: u32 = 34133;
+pub const GL_COMBINER6_NV: u32 = 34134;
+pub const GL_COMBINER7_NV: u32 = 34135;
+pub const GL_PRIMITIVE_RESTART_NV: u32 = 34136;
+pub const GL_PRIMITIVE_RESTART_INDEX_NV: u32 = 34137;
+pub const GL_FOG_DISTANCE_MODE_NV: u32 = 34138;
+pub const GL_EYE_RADIAL_NV: u32 = 34139;
+pub const GL_EYE_PLANE_ABSOLUTE_NV: u32 = 34140;
+pub const GL_EMBOSS_LIGHT_NV: u32 = 34141;
+pub const GL_EMBOSS_CONSTANT_NV: u32 = 34142;
+pub const GL_EMBOSS_MAP_NV: u32 = 34143;
+pub const GL_RED_MIN_CLAMP_INGR: u32 = 34144;
+pub const GL_GREEN_MIN_CLAMP_INGR: u32 = 34145;
+pub const GL_BLUE_MIN_CLAMP_INGR: u32 = 34146;
+pub const GL_ALPHA_MIN_CLAMP_INGR: u32 = 34147;
+pub const GL_RED_MAX_CLAMP_INGR: u32 = 34148;
+pub const GL_GREEN_MAX_CLAMP_INGR: u32 = 34149;
+pub const GL_BLUE_MAX_CLAMP_INGR: u32 = 34150;
+pub const GL_ALPHA_MAX_CLAMP_INGR: u32 = 34151;
+pub const GL_INTERLACE_READ_INGR: u32 = 34152;
+pub const GL_COMBINE: u32 = 34160;
+pub const GL_COMBINE_ARB: u32 = 34160;
+pub const GL_COMBINE_EXT: u32 = 34160;
+pub const GL_COMBINE_RGB: u32 = 34161;
+pub const GL_COMBINE_RGB_ARB: u32 = 34161;
+pub const GL_COMBINE_RGB_EXT: u32 = 34161;
+pub const GL_COMBINE_ALPHA: u32 = 34162;
+pub const GL_COMBINE_ALPHA_ARB: u32 = 34162;
+pub const GL_COMBINE_ALPHA_EXT: u32 = 34162;
+pub const GL_RGB_SCALE: u32 = 34163;
+pub const GL_RGB_SCALE_ARB: u32 = 34163;
+pub const GL_RGB_SCALE_EXT: u32 = 34163;
+pub const GL_ADD_SIGNED: u32 = 34164;
+pub const GL_ADD_SIGNED_ARB: u32 = 34164;
+pub const GL_ADD_SIGNED_EXT: u32 = 34164;
+pub const GL_INTERPOLATE: u32 = 34165;
+pub const GL_INTERPOLATE_ARB: u32 = 34165;
+pub const GL_INTERPOLATE_EXT: u32 = 34165;
+pub const GL_CONSTANT: u32 = 34166;
+pub const GL_CONSTANT_ARB: u32 = 34166;
+pub const GL_CONSTANT_EXT: u32 = 34166;
+pub const GL_CONSTANT_NV: u32 = 34166;
+pub const GL_PRIMARY_COLOR: u32 = 34167;
+pub const GL_PRIMARY_COLOR_ARB: u32 = 34167;
+pub const GL_PRIMARY_COLOR_EXT: u32 = 34167;
+pub const GL_PREVIOUS: u32 = 34168;
+pub const GL_PREVIOUS_ARB: u32 = 34168;
+pub const GL_PREVIOUS_EXT: u32 = 34168;
+pub const GL_SOURCE0_RGB: u32 = 34176;
+pub const GL_SOURCE0_RGB_ARB: u32 = 34176;
+pub const GL_SOURCE0_RGB_EXT: u32 = 34176;
+pub const GL_SRC0_RGB: u32 = 34176;
+pub const GL_SOURCE1_RGB: u32 = 34177;
+pub const GL_SOURCE1_RGB_ARB: u32 = 34177;
+pub const GL_SOURCE1_RGB_EXT: u32 = 34177;
+pub const GL_SRC1_RGB: u32 = 34177;
+pub const GL_SOURCE2_RGB: u32 = 34178;
+pub const GL_SOURCE2_RGB_ARB: u32 = 34178;
+pub const GL_SOURCE2_RGB_EXT: u32 = 34178;
+pub const GL_SRC2_RGB: u32 = 34178;
+pub const GL_SOURCE3_RGB_NV: u32 = 34179;
+pub const GL_SOURCE0_ALPHA: u32 = 34184;
+pub const GL_SOURCE0_ALPHA_ARB: u32 = 34184;
+pub const GL_SOURCE0_ALPHA_EXT: u32 = 34184;
+pub const GL_SRC0_ALPHA: u32 = 34184;
+pub const GL_SOURCE1_ALPHA: u32 = 34185;
+pub const GL_SOURCE1_ALPHA_ARB: u32 = 34185;
+pub const GL_SOURCE1_ALPHA_EXT: u32 = 34185;
+pub const GL_SRC1_ALPHA: u32 = 34185;
+pub const GL_SRC1_ALPHA_EXT: u32 = 34185;
+pub const GL_SOURCE2_ALPHA: u32 = 34186;
+pub const GL_SOURCE2_ALPHA_ARB: u32 = 34186;
+pub const GL_SOURCE2_ALPHA_EXT: u32 = 34186;
+pub const GL_SRC2_ALPHA: u32 = 34186;
+pub const GL_SOURCE3_ALPHA_NV: u32 = 34187;
+pub const GL_OPERAND0_RGB: u32 = 34192;
+pub const GL_OPERAND0_RGB_ARB: u32 = 34192;
+pub const GL_OPERAND0_RGB_EXT: u32 = 34192;
+pub const GL_OPERAND1_RGB: u32 = 34193;
+pub const GL_OPERAND1_RGB_ARB: u32 = 34193;
+pub const GL_OPERAND1_RGB_EXT: u32 = 34193;
+pub const GL_OPERAND2_RGB: u32 = 34194;
+pub const GL_OPERAND2_RGB_ARB: u32 = 34194;
+pub const GL_OPERAND2_RGB_EXT: u32 = 34194;
+pub const GL_OPERAND3_RGB_NV: u32 = 34195;
+pub const GL_OPERAND0_ALPHA: u32 = 34200;
+pub const GL_OPERAND0_ALPHA_ARB: u32 = 34200;
+pub const GL_OPERAND0_ALPHA_EXT: u32 = 34200;
+pub const GL_OPERAND1_ALPHA: u32 = 34201;
+pub const GL_OPERAND1_ALPHA_ARB: u32 = 34201;
+pub const GL_OPERAND1_ALPHA_EXT: u32 = 34201;
+pub const GL_OPERAND2_ALPHA: u32 = 34202;
+pub const GL_OPERAND2_ALPHA_ARB: u32 = 34202;
+pub const GL_OPERAND2_ALPHA_EXT: u32 = 34202;
+pub const GL_OPERAND3_ALPHA_NV: u32 = 34203;
+pub const GL_PACK_SUBSAMPLE_RATE_SGIX: u32 = 34208;
+pub const GL_UNPACK_SUBSAMPLE_RATE_SGIX: u32 = 34209;
+pub const GL_PIXEL_SUBSAMPLE_4444_SGIX: u32 = 34210;
+pub const GL_PIXEL_SUBSAMPLE_2424_SGIX: u32 = 34211;
+pub const GL_PIXEL_SUBSAMPLE_4242_SGIX: u32 = 34212;
+pub const GL_PERTURB_EXT: u32 = 34222;
+pub const GL_TEXTURE_NORMAL_EXT: u32 = 34223;
+pub const GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE: u32 = 34224;
+pub const GL_TRANSFORM_HINT_APPLE: u32 = 34225;
+pub const GL_UNPACK_CLIENT_STORAGE_APPLE: u32 = 34226;
+pub const GL_BUFFER_OBJECT_APPLE: u32 = 34227;
+pub const GL_STORAGE_CLIENT_APPLE: u32 = 34228;
+pub const GL_VERTEX_ARRAY_BINDING: u32 = 34229;
+pub const GL_VERTEX_ARRAY_BINDING_APPLE: u32 = 34229;
+pub const GL_VERTEX_ARRAY_BINDING_OES: u32 = 34229;
+pub const GL_TEXTURE_RANGE_LENGTH_APPLE: u32 = 34231;
+pub const GL_TEXTURE_RANGE_POINTER_APPLE: u32 = 34232;
+pub const GL_YCBCR_422_APPLE: u32 = 34233;
+pub const GL_UNSIGNED_SHORT_8_8_APPLE: u32 = 34234;
+pub const GL_UNSIGNED_SHORT_8_8_MESA: u32 = 34234;
+pub const GL_UNSIGNED_SHORT_8_8_REV_APPLE: u32 = 34235;
+pub const GL_UNSIGNED_SHORT_8_8_REV_MESA: u32 = 34235;
+pub const GL_TEXTURE_STORAGE_HINT_APPLE: u32 = 34236;
+pub const GL_STORAGE_PRIVATE_APPLE: u32 = 34237;
+pub const GL_STORAGE_CACHED_APPLE: u32 = 34238;
+pub const GL_STORAGE_SHARED_APPLE: u32 = 34239;
+pub const GL_REPLACEMENT_CODE_ARRAY_SUN: u32 = 34240;
+pub const GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN: u32 = 34241;
+pub const GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN: u32 = 34242;
+pub const GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN: u32 = 34243;
+pub const GL_R1UI_V3F_SUN: u32 = 34244;
+pub const GL_R1UI_C4UB_V3F_SUN: u32 = 34245;
+pub const GL_R1UI_C3F_V3F_SUN: u32 = 34246;
+pub const GL_R1UI_N3F_V3F_SUN: u32 = 34247;
+pub const GL_R1UI_C4F_N3F_V3F_SUN: u32 = 34248;
+pub const GL_R1UI_T2F_V3F_SUN: u32 = 34249;
+pub const GL_R1UI_T2F_N3F_V3F_SUN: u32 = 34250;
+pub const GL_R1UI_T2F_C4F_N3F_V3F_SUN: u32 = 34251;
+pub const GL_SLICE_ACCUM_SUN: u32 = 34252;
+pub const GL_QUAD_MESH_SUN: u32 = 34324;
+pub const GL_TRIANGLE_MESH_SUN: u32 = 34325;
+pub const GL_VERTEX_PROGRAM_ARB: u32 = 34336;
+pub const GL_VERTEX_PROGRAM_NV: u32 = 34336;
+pub const GL_VERTEX_STATE_PROGRAM_NV: u32 = 34337;
+pub const GL_VERTEX_ATTRIB_ARRAY_ENABLED: u32 = 34338;
+pub const GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB: u32 = 34338;
+pub const GL_ATTRIB_ARRAY_SIZE_NV: u32 = 34339;
+pub const GL_VERTEX_ATTRIB_ARRAY_SIZE: u32 = 34339;
+pub const GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB: u32 = 34339;
+pub const GL_ATTRIB_ARRAY_STRIDE_NV: u32 = 34340;
+pub const GL_VERTEX_ATTRIB_ARRAY_STRIDE: u32 = 34340;
+pub const GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB: u32 = 34340;
+pub const GL_ATTRIB_ARRAY_TYPE_NV: u32 = 34341;
+pub const GL_VERTEX_ATTRIB_ARRAY_TYPE: u32 = 34341;
+pub const GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB: u32 = 34341;
+pub const GL_CURRENT_ATTRIB_NV: u32 = 34342;
+pub const GL_CURRENT_VERTEX_ATTRIB: u32 = 34342;
+pub const GL_CURRENT_VERTEX_ATTRIB_ARB: u32 = 34342;
+pub const GL_PROGRAM_LENGTH_ARB: u32 = 34343;
+pub const GL_PROGRAM_LENGTH_NV: u32 = 34343;
+pub const GL_PROGRAM_STRING_ARB: u32 = 34344;
+pub const GL_PROGRAM_STRING_NV: u32 = 34344;
+pub const GL_MODELVIEW_PROJECTION_NV: u32 = 34345;
+pub const GL_IDENTITY_NV: u32 = 34346;
+pub const GL_INVERSE_NV: u32 = 34347;
+pub const GL_TRANSPOSE_NV: u32 = 34348;
+pub const GL_INVERSE_TRANSPOSE_NV: u32 = 34349;
+pub const GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB: u32 = 34350;
+pub const GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV: u32 = 34350;
+pub const GL_MAX_PROGRAM_MATRICES_ARB: u32 = 34351;
+pub const GL_MAX_TRACK_MATRICES_NV: u32 = 34351;
+pub const GL_MATRIX0_NV: u32 = 34352;
+pub const GL_MATRIX1_NV: u32 = 34353;
+pub const GL_MATRIX2_NV: u32 = 34354;
+pub const GL_MATRIX3_NV: u32 = 34355;
+pub const GL_MATRIX4_NV: u32 = 34356;
+pub const GL_MATRIX5_NV: u32 = 34357;
+pub const GL_MATRIX6_NV: u32 = 34358;
+pub const GL_MATRIX7_NV: u32 = 34359;
+pub const GL_CURRENT_MATRIX_STACK_DEPTH_ARB: u32 = 34368;
+pub const GL_CURRENT_MATRIX_STACK_DEPTH_NV: u32 = 34368;
+pub const GL_CURRENT_MATRIX_ARB: u32 = 34369;
+pub const GL_CURRENT_MATRIX_NV: u32 = 34369;
+pub const GL_PROGRAM_POINT_SIZE: u32 = 34370;
+pub const GL_PROGRAM_POINT_SIZE_ARB: u32 = 34370;
+pub const GL_PROGRAM_POINT_SIZE_EXT: u32 = 34370;
+pub const GL_VERTEX_PROGRAM_POINT_SIZE: u32 = 34370;
+pub const GL_VERTEX_PROGRAM_POINT_SIZE_ARB: u32 = 34370;
+pub const GL_VERTEX_PROGRAM_POINT_SIZE_NV: u32 = 34370;
+pub const GL_VERTEX_PROGRAM_TWO_SIDE: u32 = 34371;
+pub const GL_VERTEX_PROGRAM_TWO_SIDE_ARB: u32 = 34371;
+pub const GL_VERTEX_PROGRAM_TWO_SIDE_NV: u32 = 34371;
+pub const GL_PROGRAM_PARAMETER_NV: u32 = 34372;
+pub const GL_ATTRIB_ARRAY_POINTER_NV: u32 = 34373;
+pub const GL_VERTEX_ATTRIB_ARRAY_POINTER: u32 = 34373;
+pub const GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB: u32 = 34373;
+pub const GL_PROGRAM_TARGET_NV: u32 = 34374;
+pub const GL_PROGRAM_RESIDENT_NV: u32 = 34375;
+pub const GL_TRACK_MATRIX_NV: u32 = 34376;
+pub const GL_TRACK_MATRIX_TRANSFORM_NV: u32 = 34377;
+pub const GL_VERTEX_PROGRAM_BINDING_NV: u32 = 34378;
+pub const GL_PROGRAM_ERROR_POSITION_ARB: u32 = 34379;
+pub const GL_PROGRAM_ERROR_POSITION_NV: u32 = 34379;
+pub const GL_OFFSET_TEXTURE_RECTANGLE_NV: u32 = 34380;
+pub const GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV: u32 = 34381;
+pub const GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV: u32 = 34382;
+pub const GL_DEPTH_CLAMP: u32 = 34383;
+pub const GL_DEPTH_CLAMP_NV: u32 = 34383;
+pub const GL_VERTEX_ATTRIB_ARRAY0_NV: u32 = 34384;
+pub const GL_VERTEX_ATTRIB_ARRAY1_NV: u32 = 34385;
+pub const GL_VERTEX_ATTRIB_ARRAY2_NV: u32 = 34386;
+pub const GL_VERTEX_ATTRIB_ARRAY3_NV: u32 = 34387;
+pub const GL_VERTEX_ATTRIB_ARRAY4_NV: u32 = 34388;
+pub const GL_VERTEX_ATTRIB_ARRAY5_NV: u32 = 34389;
+pub const GL_VERTEX_ATTRIB_ARRAY6_NV: u32 = 34390;
+pub const GL_VERTEX_ATTRIB_ARRAY7_NV: u32 = 34391;
+pub const GL_VERTEX_ATTRIB_ARRAY8_NV: u32 = 34392;
+pub const GL_VERTEX_ATTRIB_ARRAY9_NV: u32 = 34393;
+pub const GL_VERTEX_ATTRIB_ARRAY10_NV: u32 = 34394;
+pub const GL_VERTEX_ATTRIB_ARRAY11_NV: u32 = 34395;
+pub const GL_VERTEX_ATTRIB_ARRAY12_NV: u32 = 34396;
+pub const GL_VERTEX_ATTRIB_ARRAY13_NV: u32 = 34397;
+pub const GL_VERTEX_ATTRIB_ARRAY14_NV: u32 = 34398;
+pub const GL_VERTEX_ATTRIB_ARRAY15_NV: u32 = 34399;
+pub const GL_MAP1_VERTEX_ATTRIB0_4_NV: u32 = 34400;
+pub const GL_MAP1_VERTEX_ATTRIB1_4_NV: u32 = 34401;
+pub const GL_MAP1_VERTEX_ATTRIB2_4_NV: u32 = 34402;
+pub const GL_MAP1_VERTEX_ATTRIB3_4_NV: u32 = 34403;
+pub const GL_MAP1_VERTEX_ATTRIB4_4_NV: u32 = 34404;
+pub const GL_MAP1_VERTEX_ATTRIB5_4_NV: u32 = 34405;
+pub const GL_MAP1_VERTEX_ATTRIB6_4_NV: u32 = 34406;
+pub const GL_MAP1_VERTEX_ATTRIB7_4_NV: u32 = 34407;
+pub const GL_MAP1_VERTEX_ATTRIB8_4_NV: u32 = 34408;
+pub const GL_MAP1_VERTEX_ATTRIB9_4_NV: u32 = 34409;
+pub const GL_MAP1_VERTEX_ATTRIB10_4_NV: u32 = 34410;
+pub const GL_MAP1_VERTEX_ATTRIB11_4_NV: u32 = 34411;
+pub const GL_MAP1_VERTEX_ATTRIB12_4_NV: u32 = 34412;
+pub const GL_MAP1_VERTEX_ATTRIB13_4_NV: u32 = 34413;
+pub const GL_MAP1_VERTEX_ATTRIB14_4_NV: u32 = 34414;
+pub const GL_MAP1_VERTEX_ATTRIB15_4_NV: u32 = 34415;
+pub const GL_MAP2_VERTEX_ATTRIB0_4_NV: u32 = 34416;
+pub const GL_MAP2_VERTEX_ATTRIB1_4_NV: u32 = 34417;
+pub const GL_MAP2_VERTEX_ATTRIB2_4_NV: u32 = 34418;
+pub const GL_MAP2_VERTEX_ATTRIB3_4_NV: u32 = 34419;
+pub const GL_MAP2_VERTEX_ATTRIB4_4_NV: u32 = 34420;
+pub const GL_MAP2_VERTEX_ATTRIB5_4_NV: u32 = 34421;
+pub const GL_MAP2_VERTEX_ATTRIB6_4_NV: u32 = 34422;
+pub const GL_MAP2_VERTEX_ATTRIB7_4_NV: u32 = 34423;
+pub const GL_PROGRAM_BINDING_ARB: u32 = 34423;
+pub const GL_MAP2_VERTEX_ATTRIB8_4_NV: u32 = 34424;
+pub const GL_MAP2_VERTEX_ATTRIB9_4_NV: u32 = 34425;
+pub const GL_MAP2_VERTEX_ATTRIB10_4_NV: u32 = 34426;
+pub const GL_MAP2_VERTEX_ATTRIB11_4_NV: u32 = 34427;
+pub const GL_MAP2_VERTEX_ATTRIB12_4_NV: u32 = 34428;
+pub const GL_MAP2_VERTEX_ATTRIB13_4_NV: u32 = 34429;
+pub const GL_MAP2_VERTEX_ATTRIB14_4_NV: u32 = 34430;
+pub const GL_MAP2_VERTEX_ATTRIB15_4_NV: u32 = 34431;
+pub const GL_TEXTURE_COMPRESSED_IMAGE_SIZE: u32 = 34464;
+pub const GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB: u32 = 34464;
+pub const GL_TEXTURE_COMPRESSED: u32 = 34465;
+pub const GL_TEXTURE_COMPRESSED_ARB: u32 = 34465;
+pub const GL_NUM_COMPRESSED_TEXTURE_FORMATS: u32 = 34466;
+pub const GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB: u32 = 34466;
+pub const GL_COMPRESSED_TEXTURE_FORMATS: u32 = 34467;
+pub const GL_COMPRESSED_TEXTURE_FORMATS_ARB: u32 = 34467;
+pub const GL_MAX_VERTEX_UNITS_ARB: u32 = 34468;
+pub const GL_MAX_VERTEX_UNITS_OES: u32 = 34468;
+pub const GL_ACTIVE_VERTEX_UNITS_ARB: u32 = 34469;
+pub const GL_WEIGHT_SUM_UNITY_ARB: u32 = 34470;
+pub const GL_VERTEX_BLEND_ARB: u32 = 34471;
+pub const GL_CURRENT_WEIGHT_ARB: u32 = 34472;
+pub const GL_WEIGHT_ARRAY_TYPE_ARB: u32 = 34473;
+pub const GL_WEIGHT_ARRAY_TYPE_OES: u32 = 34473;
+pub const GL_WEIGHT_ARRAY_STRIDE_ARB: u32 = 34474;
+pub const GL_WEIGHT_ARRAY_STRIDE_OES: u32 = 34474;
+pub const GL_WEIGHT_ARRAY_SIZE_ARB: u32 = 34475;
+pub const GL_WEIGHT_ARRAY_SIZE_OES: u32 = 34475;
+pub const GL_WEIGHT_ARRAY_POINTER_ARB: u32 = 34476;
+pub const GL_WEIGHT_ARRAY_POINTER_OES: u32 = 34476;
+pub const GL_WEIGHT_ARRAY_ARB: u32 = 34477;
+pub const GL_WEIGHT_ARRAY_OES: u32 = 34477;
+pub const GL_DOT3_RGB: u32 = 34478;
+pub const GL_DOT3_RGB_ARB: u32 = 34478;
+pub const GL_DOT3_RGBA: u32 = 34479;
+pub const GL_DOT3_RGBA_ARB: u32 = 34479;
+pub const GL_DOT3_RGBA_IMG: u32 = 34479;
+pub const GL_COMPRESSED_RGB_FXT1_3DFX: u32 = 34480;
+pub const GL_COMPRESSED_RGBA_FXT1_3DFX: u32 = 34481;
+pub const GL_MULTISAMPLE_3DFX: u32 = 34482;
+pub const GL_SAMPLE_BUFFERS_3DFX: u32 = 34483;
+pub const GL_SAMPLES_3DFX: u32 = 34484;
+pub const GL_EVAL_2D_NV: u32 = 34496;
+pub const GL_EVAL_TRIANGULAR_2D_NV: u32 = 34497;
+pub const GL_MAP_TESSELLATION_NV: u32 = 34498;
+pub const GL_MAP_ATTRIB_U_ORDER_NV: u32 = 34499;
+pub const GL_MAP_ATTRIB_V_ORDER_NV: u32 = 34500;
+pub const GL_EVAL_FRACTIONAL_TESSELLATION_NV: u32 = 34501;
+pub const GL_EVAL_VERTEX_ATTRIB0_NV: u32 = 34502;
+pub const GL_EVAL_VERTEX_ATTRIB1_NV: u32 = 34503;
+pub const GL_EVAL_VERTEX_ATTRIB2_NV: u32 = 34504;
+pub const GL_EVAL_VERTEX_ATTRIB3_NV: u32 = 34505;
+pub const GL_EVAL_VERTEX_ATTRIB4_NV: u32 = 34506;
+pub const GL_EVAL_VERTEX_ATTRIB5_NV: u32 = 34507;
+pub const GL_EVAL_VERTEX_ATTRIB6_NV: u32 = 34508;
+pub const GL_EVAL_VERTEX_ATTRIB7_NV: u32 = 34509;
+pub const GL_EVAL_VERTEX_ATTRIB8_NV: u32 = 34510;
+pub const GL_EVAL_VERTEX_ATTRIB9_NV: u32 = 34511;
+pub const GL_EVAL_VERTEX_ATTRIB10_NV: u32 = 34512;
+pub const GL_EVAL_VERTEX_ATTRIB11_NV: u32 = 34513;
+pub const GL_EVAL_VERTEX_ATTRIB12_NV: u32 = 34514;
+pub const GL_EVAL_VERTEX_ATTRIB13_NV: u32 = 34515;
+pub const GL_EVAL_VERTEX_ATTRIB14_NV: u32 = 34516;
+pub const GL_EVAL_VERTEX_ATTRIB15_NV: u32 = 34517;
+pub const GL_MAX_MAP_TESSELLATION_NV: u32 = 34518;
+pub const GL_MAX_RATIONAL_EVAL_ORDER_NV: u32 = 34519;
+pub const GL_MAX_PROGRAM_PATCH_ATTRIBS_NV: u32 = 34520;
+pub const GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV: u32 = 34521;
+pub const GL_UNSIGNED_INT_S8_S8_8_8_NV: u32 = 34522;
+pub const GL_UNSIGNED_INT_8_8_S8_S8_REV_NV: u32 = 34523;
+pub const GL_DSDT_MAG_INTENSITY_NV: u32 = 34524;
+pub const GL_SHADER_CONSISTENT_NV: u32 = 34525;
+pub const GL_TEXTURE_SHADER_NV: u32 = 34526;
+pub const GL_SHADER_OPERATION_NV: u32 = 34527;
+pub const GL_CULL_MODES_NV: u32 = 34528;
+pub const GL_OFFSET_TEXTURE_2D_MATRIX_NV: u32 = 34529;
+pub const GL_OFFSET_TEXTURE_MATRIX_NV: u32 = 34529;
+pub const GL_OFFSET_TEXTURE_2D_SCALE_NV: u32 = 34530;
+pub const GL_OFFSET_TEXTURE_SCALE_NV: u32 = 34530;
+pub const GL_OFFSET_TEXTURE_2D_BIAS_NV: u32 = 34531;
+pub const GL_OFFSET_TEXTURE_BIAS_NV: u32 = 34531;
+pub const GL_PREVIOUS_TEXTURE_INPUT_NV: u32 = 34532;
+pub const GL_CONST_EYE_NV: u32 = 34533;
+pub const GL_PASS_THROUGH_NV: u32 = 34534;
+pub const GL_CULL_FRAGMENT_NV: u32 = 34535;
+pub const GL_OFFSET_TEXTURE_2D_NV: u32 = 34536;
+pub const GL_DEPENDENT_AR_TEXTURE_2D_NV: u32 = 34537;
+pub const GL_DEPENDENT_GB_TEXTURE_2D_NV: u32 = 34538;
+pub const GL_SURFACE_STATE_NV: u32 = 34539;
+pub const GL_DOT_PRODUCT_NV: u32 = 34540;
+pub const GL_DOT_PRODUCT_DEPTH_REPLACE_NV: u32 = 34541;
+pub const GL_DOT_PRODUCT_TEXTURE_2D_NV: u32 = 34542;
+pub const GL_DOT_PRODUCT_TEXTURE_3D_NV: u32 = 34543;
+pub const GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV: u32 = 34544;
+pub const GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV: u32 = 34545;
+pub const GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV: u32 = 34546;
+pub const GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV: u32 = 34547;
+pub const GL_HILO_NV: u32 = 34548;
+pub const GL_DSDT_NV: u32 = 34549;
+pub const GL_DSDT_MAG_NV: u32 = 34550;
+pub const GL_DSDT_MAG_VIB_NV: u32 = 34551;
+pub const GL_HILO16_NV: u32 = 34552;
+pub const GL_SIGNED_HILO_NV: u32 = 34553;
+pub const GL_SIGNED_HILO16_NV: u32 = 34554;
+pub const GL_SIGNED_RGBA_NV: u32 = 34555;
+pub const GL_SIGNED_RGBA8_NV: u32 = 34556;
+pub const GL_SURFACE_REGISTERED_NV: u32 = 34557;
+pub const GL_SIGNED_RGB_NV: u32 = 34558;
+pub const GL_SIGNED_RGB8_NV: u32 = 34559;
+pub const GL_SURFACE_MAPPED_NV: u32 = 34560;
+pub const GL_SIGNED_LUMINANCE_NV: u32 = 34561;
+pub const GL_SIGNED_LUMINANCE8_NV: u32 = 34562;
+pub const GL_SIGNED_LUMINANCE_ALPHA_NV: u32 = 34563;
+pub const GL_SIGNED_LUMINANCE8_ALPHA8_NV: u32 = 34564;
+pub const GL_SIGNED_ALPHA_NV: u32 = 34565;
+pub const GL_SIGNED_ALPHA8_NV: u32 = 34566;
+pub const GL_SIGNED_INTENSITY_NV: u32 = 34567;
+pub const GL_SIGNED_INTENSITY8_NV: u32 = 34568;
+pub const GL_DSDT8_NV: u32 = 34569;
+pub const GL_DSDT8_MAG8_NV: u32 = 34570;
+pub const GL_DSDT8_MAG8_INTENSITY8_NV: u32 = 34571;
+pub const GL_SIGNED_RGB_UNSIGNED_ALPHA_NV: u32 = 34572;
+pub const GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV: u32 = 34573;
+pub const GL_HI_SCALE_NV: u32 = 34574;
+pub const GL_LO_SCALE_NV: u32 = 34575;
+pub const GL_DS_SCALE_NV: u32 = 34576;
+pub const GL_DT_SCALE_NV: u32 = 34577;
+pub const GL_MAGNITUDE_SCALE_NV: u32 = 34578;
+pub const GL_VIBRANCE_SCALE_NV: u32 = 34579;
+pub const GL_HI_BIAS_NV: u32 = 34580;
+pub const GL_LO_BIAS_NV: u32 = 34581;
+pub const GL_DS_BIAS_NV: u32 = 34582;
+pub const GL_DT_BIAS_NV: u32 = 34583;
+pub const GL_MAGNITUDE_BIAS_NV: u32 = 34584;
+pub const GL_VIBRANCE_BIAS_NV: u32 = 34585;
+pub const GL_TEXTURE_BORDER_VALUES_NV: u32 = 34586;
+pub const GL_TEXTURE_HI_SIZE_NV: u32 = 34587;
+pub const GL_TEXTURE_LO_SIZE_NV: u32 = 34588;
+pub const GL_TEXTURE_DS_SIZE_NV: u32 = 34589;
+pub const GL_TEXTURE_DT_SIZE_NV: u32 = 34590;
+pub const GL_TEXTURE_MAG_SIZE_NV: u32 = 34591;
+pub const GL_MODELVIEW2_ARB: u32 = 34594;
+pub const GL_MODELVIEW3_ARB: u32 = 34595;
+pub const GL_MODELVIEW4_ARB: u32 = 34596;
+pub const GL_MODELVIEW5_ARB: u32 = 34597;
+pub const GL_MODELVIEW6_ARB: u32 = 34598;
+pub const GL_MODELVIEW7_ARB: u32 = 34599;
+pub const GL_MODELVIEW8_ARB: u32 = 34600;
+pub const GL_MODELVIEW9_ARB: u32 = 34601;
+pub const GL_MODELVIEW10_ARB: u32 = 34602;
+pub const GL_MODELVIEW11_ARB: u32 = 34603;
+pub const GL_MODELVIEW12_ARB: u32 = 34604;
+pub const GL_MODELVIEW13_ARB: u32 = 34605;
+pub const GL_MODELVIEW14_ARB: u32 = 34606;
+pub const GL_MODELVIEW15_ARB: u32 = 34607;
+pub const GL_MODELVIEW16_ARB: u32 = 34608;
+pub const GL_MODELVIEW17_ARB: u32 = 34609;
+pub const GL_MODELVIEW18_ARB: u32 = 34610;
+pub const GL_MODELVIEW19_ARB: u32 = 34611;
+pub const GL_MODELVIEW20_ARB: u32 = 34612;
+pub const GL_MODELVIEW21_ARB: u32 = 34613;
+pub const GL_MODELVIEW22_ARB: u32 = 34614;
+pub const GL_MODELVIEW23_ARB: u32 = 34615;
+pub const GL_MODELVIEW24_ARB: u32 = 34616;
+pub const GL_MODELVIEW25_ARB: u32 = 34617;
+pub const GL_MODELVIEW26_ARB: u32 = 34618;
+pub const GL_MODELVIEW27_ARB: u32 = 34619;
+pub const GL_MODELVIEW28_ARB: u32 = 34620;
+pub const GL_MODELVIEW29_ARB: u32 = 34621;
+pub const GL_MODELVIEW30_ARB: u32 = 34622;
+pub const GL_MODELVIEW31_ARB: u32 = 34623;
+pub const GL_DOT3_RGB_EXT: u32 = 34624;
+pub const GL_Z400_BINARY_AMD: u32 = 34624;
+pub const GL_DOT3_RGBA_EXT: u32 = 34625;
+pub const GL_PROGRAM_BINARY_LENGTH: u32 = 34625;
+pub const GL_PROGRAM_BINARY_LENGTH_OES: u32 = 34625;
+pub const GL_MIRROR_CLAMP_ATI: u32 = 34626;
+pub const GL_MIRROR_CLAMP_EXT: u32 = 34626;
+pub const GL_MIRROR_CLAMP_TO_EDGE: u32 = 34627;
+pub const GL_MIRROR_CLAMP_TO_EDGE_ATI: u32 = 34627;
+pub const GL_MIRROR_CLAMP_TO_EDGE_EXT: u32 = 34627;
+pub const GL_MODULATE_ADD_ATI: u32 = 34628;
+pub const GL_MODULATE_SIGNED_ADD_ATI: u32 = 34629;
+pub const GL_MODULATE_SUBTRACT_ATI: u32 = 34630;
+pub const GL_SET_AMD: u32 = 34634;
+pub const GL_REPLACE_VALUE_AMD: u32 = 34635;
+pub const GL_STENCIL_OP_VALUE_AMD: u32 = 34636;
+pub const GL_STENCIL_BACK_OP_VALUE_AMD: u32 = 34637;
+pub const GL_VERTEX_ATTRIB_ARRAY_LONG: u32 = 34638;
+pub const GL_OCCLUSION_QUERY_EVENT_MASK_AMD: u32 = 34639;
+pub const GL_DEPTH_STENCIL_MESA: u32 = 34640;
+pub const GL_UNSIGNED_INT_24_8_MESA: u32 = 34641;
+pub const GL_UNSIGNED_INT_8_24_REV_MESA: u32 = 34642;
+pub const GL_UNSIGNED_SHORT_15_1_MESA: u32 = 34643;
+pub const GL_UNSIGNED_SHORT_1_15_REV_MESA: u32 = 34644;
+pub const GL_TRACE_MASK_MESA: u32 = 34645;
+pub const GL_TRACE_NAME_MESA: u32 = 34646;
+pub const GL_YCBCR_MESA: u32 = 34647;
+pub const GL_PACK_INVERT_MESA: u32 = 34648;
+pub const GL_DEBUG_OBJECT_MESA: u32 = 34649;
+pub const GL_TEXTURE_1D_STACK_MESAX: u32 = 34649;
+pub const GL_DEBUG_PRINT_MESA: u32 = 34650;
+pub const GL_TEXTURE_2D_STACK_MESAX: u32 = 34650;
+pub const GL_DEBUG_ASSERT_MESA: u32 = 34651;
+pub const GL_PROXY_TEXTURE_1D_STACK_MESAX: u32 = 34651;
+pub const GL_PROXY_TEXTURE_2D_STACK_MESAX: u32 = 34652;
+pub const GL_TEXTURE_1D_STACK_BINDING_MESAX: u32 = 34653;
+pub const GL_TEXTURE_2D_STACK_BINDING_MESAX: u32 = 34654;
+pub const GL_STATIC_ATI: u32 = 34656;
+pub const GL_DYNAMIC_ATI: u32 = 34657;
+pub const GL_PRESERVE_ATI: u32 = 34658;
+pub const GL_DISCARD_ATI: u32 = 34659;
+pub const GL_BUFFER_SIZE: u32 = 34660;
+pub const GL_BUFFER_SIZE_ARB: u32 = 34660;
+pub const GL_OBJECT_BUFFER_SIZE_ATI: u32 = 34660;
+pub const GL_BUFFER_USAGE: u32 = 34661;
+pub const GL_BUFFER_USAGE_ARB: u32 = 34661;
+pub const GL_OBJECT_BUFFER_USAGE_ATI: u32 = 34661;
+pub const GL_ARRAY_OBJECT_BUFFER_ATI: u32 = 34662;
+pub const GL_ARRAY_OBJECT_OFFSET_ATI: u32 = 34663;
+pub const GL_ELEMENT_ARRAY_ATI: u32 = 34664;
+pub const GL_ELEMENT_ARRAY_TYPE_ATI: u32 = 34665;
+pub const GL_ELEMENT_ARRAY_POINTER_ATI: u32 = 34666;
+pub const GL_MAX_VERTEX_STREAMS_ATI: u32 = 34667;
+pub const GL_VERTEX_STREAM0_ATI: u32 = 34668;
+pub const GL_VERTEX_STREAM1_ATI: u32 = 34669;
+pub const GL_VERTEX_STREAM2_ATI: u32 = 34670;
+pub const GL_VERTEX_STREAM3_ATI: u32 = 34671;
+pub const GL_VERTEX_STREAM4_ATI: u32 = 34672;
+pub const GL_VERTEX_STREAM5_ATI: u32 = 34673;
+pub const GL_VERTEX_STREAM6_ATI: u32 = 34674;
+pub const GL_VERTEX_STREAM7_ATI: u32 = 34675;
+pub const GL_VERTEX_SOURCE_ATI: u32 = 34676;
+pub const GL_BUMP_ROT_MATRIX_ATI: u32 = 34677;
+pub const GL_BUMP_ROT_MATRIX_SIZE_ATI: u32 = 34678;
+pub const GL_BUMP_NUM_TEX_UNITS_ATI: u32 = 34679;
+pub const GL_BUMP_TEX_UNITS_ATI: u32 = 34680;
+pub const GL_DUDV_ATI: u32 = 34681;
+pub const GL_DU8DV8_ATI: u32 = 34682;
+pub const GL_BUMP_ENVMAP_ATI: u32 = 34683;
+pub const GL_BUMP_TARGET_ATI: u32 = 34684;
+pub const GL_VERTEX_SHADER_EXT: u32 = 34688;
+pub const GL_VERTEX_SHADER_BINDING_EXT: u32 = 34689;
+pub const GL_OP_INDEX_EXT: u32 = 34690;
+pub const GL_OP_NEGATE_EXT: u32 = 34691;
+pub const GL_OP_DOT3_EXT: u32 = 34692;
+pub const GL_OP_DOT4_EXT: u32 = 34693;
+pub const GL_OP_MUL_EXT: u32 = 34694;
+pub const GL_OP_ADD_EXT: u32 = 34695;
+pub const GL_OP_MADD_EXT: u32 = 34696;
+pub const GL_OP_FRAC_EXT: u32 = 34697;
+pub const GL_OP_MAX_EXT: u32 = 34698;
+pub const GL_OP_MIN_EXT: u32 = 34699;
+pub const GL_OP_SET_GE_EXT: u32 = 34700;
+pub const GL_OP_SET_LT_EXT: u32 = 34701;
+pub const GL_OP_CLAMP_EXT: u32 = 34702;
+pub const GL_OP_FLOOR_EXT: u32 = 34703;
+pub const GL_OP_ROUND_EXT: u32 = 34704;
+pub const GL_OP_EXP_BASE_2_EXT: u32 = 34705;
+pub const GL_OP_LOG_BASE_2_EXT: u32 = 34706;
+pub const GL_OP_POWER_EXT: u32 = 34707;
+pub const GL_OP_RECIP_EXT: u32 = 34708;
+pub const GL_OP_RECIP_SQRT_EXT: u32 = 34709;
+pub const GL_OP_SUB_EXT: u32 = 34710;
+pub const GL_OP_CROSS_PRODUCT_EXT: u32 = 34711;
+pub const GL_OP_MULTIPLY_MATRIX_EXT: u32 = 34712;
+pub const GL_OP_MOV_EXT: u32 = 34713;
+pub const GL_OUTPUT_VERTEX_EXT: u32 = 34714;
+pub const GL_OUTPUT_COLOR0_EXT: u32 = 34715;
+pub const GL_OUTPUT_COLOR1_EXT: u32 = 34716;
+pub const GL_OUTPUT_TEXTURE_COORD0_EXT: u32 = 34717;
+pub const GL_OUTPUT_TEXTURE_COORD1_EXT: u32 = 34718;
+pub const GL_OUTPUT_TEXTURE_COORD2_EXT: u32 = 34719;
+pub const GL_OUTPUT_TEXTURE_COORD3_EXT: u32 = 34720;
+pub const GL_OUTPUT_TEXTURE_COORD4_EXT: u32 = 34721;
+pub const GL_OUTPUT_TEXTURE_COORD5_EXT: u32 = 34722;
+pub const GL_OUTPUT_TEXTURE_COORD6_EXT: u32 = 34723;
+pub const GL_OUTPUT_TEXTURE_COORD7_EXT: u32 = 34724;
+pub const GL_OUTPUT_TEXTURE_COORD8_EXT: u32 = 34725;
+pub const GL_OUTPUT_TEXTURE_COORD9_EXT: u32 = 34726;
+pub const GL_OUTPUT_TEXTURE_COORD10_EXT: u32 = 34727;
+pub const GL_OUTPUT_TEXTURE_COORD11_EXT: u32 = 34728;
+pub const GL_OUTPUT_TEXTURE_COORD12_EXT: u32 = 34729;
+pub const GL_OUTPUT_TEXTURE_COORD13_EXT: u32 = 34730;
+pub const GL_OUTPUT_TEXTURE_COORD14_EXT: u32 = 34731;
+pub const GL_OUTPUT_TEXTURE_COORD15_EXT: u32 = 34732;
+pub const GL_OUTPUT_TEXTURE_COORD16_EXT: u32 = 34733;
+pub const GL_OUTPUT_TEXTURE_COORD17_EXT: u32 = 34734;
+pub const GL_OUTPUT_TEXTURE_COORD18_EXT: u32 = 34735;
+pub const GL_OUTPUT_TEXTURE_COORD19_EXT: u32 = 34736;
+pub const GL_OUTPUT_TEXTURE_COORD20_EXT: u32 = 34737;
+pub const GL_OUTPUT_TEXTURE_COORD21_EXT: u32 = 34738;
+pub const GL_OUTPUT_TEXTURE_COORD22_EXT: u32 = 34739;
+pub const GL_OUTPUT_TEXTURE_COORD23_EXT: u32 = 34740;
+pub const GL_OUTPUT_TEXTURE_COORD24_EXT: u32 = 34741;
+pub const GL_OUTPUT_TEXTURE_COORD25_EXT: u32 = 34742;
+pub const GL_OUTPUT_TEXTURE_COORD26_EXT: u32 = 34743;
+pub const GL_OUTPUT_TEXTURE_COORD27_EXT: u32 = 34744;
+pub const GL_OUTPUT_TEXTURE_COORD28_EXT: u32 = 34745;
+pub const GL_OUTPUT_TEXTURE_COORD29_EXT: u32 = 34746;
+pub const GL_OUTPUT_TEXTURE_COORD30_EXT: u32 = 34747;
+pub const GL_OUTPUT_TEXTURE_COORD31_EXT: u32 = 34748;
+pub const GL_OUTPUT_FOG_EXT: u32 = 34749;
+pub const GL_SCALAR_EXT: u32 = 34750;
+pub const GL_VECTOR_EXT: u32 = 34751;
+pub const GL_MATRIX_EXT: u32 = 34752;
+pub const GL_VARIANT_EXT: u32 = 34753;
+pub const GL_INVARIANT_EXT: u32 = 34754;
+pub const GL_LOCAL_CONSTANT_EXT: u32 = 34755;
+pub const GL_LOCAL_EXT: u32 = 34756;
+pub const GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT: u32 = 34757;
+pub const GL_MAX_VERTEX_SHADER_VARIANTS_EXT: u32 = 34758;
+pub const GL_MAX_VERTEX_SHADER_INVARIANTS_EXT: u32 = 34759;
+pub const GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT: u32 = 34760;
+pub const GL_MAX_VERTEX_SHADER_LOCALS_EXT: u32 = 34761;
+pub const GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT: u32 = 34762;
+pub const GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT: u32 = 34763;
+pub const GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT: u32 = 34764;
+pub const GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT: u32 = 34765;
+pub const GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT: u32 = 34766;
+pub const GL_VERTEX_SHADER_INSTRUCTIONS_EXT: u32 = 34767;
+pub const GL_VERTEX_SHADER_VARIANTS_EXT: u32 = 34768;
+pub const GL_VERTEX_SHADER_INVARIANTS_EXT: u32 = 34769;
+pub const GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT: u32 = 34770;
+pub const GL_VERTEX_SHADER_LOCALS_EXT: u32 = 34771;
+pub const GL_VERTEX_SHADER_OPTIMIZED_EXT: u32 = 34772;
+pub const GL_X_EXT: u32 = 34773;
+pub const GL_Y_EXT: u32 = 34774;
+pub const GL_Z_EXT: u32 = 34775;
+pub const GL_W_EXT: u32 = 34776;
+pub const GL_NEGATIVE_X_EXT: u32 = 34777;
+pub const GL_NEGATIVE_Y_EXT: u32 = 34778;
+pub const GL_NEGATIVE_Z_EXT: u32 = 34779;
+pub const GL_NEGATIVE_W_EXT: u32 = 34780;
+pub const GL_ZERO_EXT: u32 = 34781;
+pub const GL_ONE_EXT: u32 = 34782;
+pub const GL_NEGATIVE_ONE_EXT: u32 = 34783;
+pub const GL_NORMALIZED_RANGE_EXT: u32 = 34784;
+pub const GL_FULL_RANGE_EXT: u32 = 34785;
+pub const GL_CURRENT_VERTEX_EXT: u32 = 34786;
+pub const GL_MVP_MATRIX_EXT: u32 = 34787;
+pub const GL_VARIANT_VALUE_EXT: u32 = 34788;
+pub const GL_VARIANT_DATATYPE_EXT: u32 = 34789;
+pub const GL_VARIANT_ARRAY_STRIDE_EXT: u32 = 34790;
+pub const GL_VARIANT_ARRAY_TYPE_EXT: u32 = 34791;
+pub const GL_VARIANT_ARRAY_EXT: u32 = 34792;
+pub const GL_VARIANT_ARRAY_POINTER_EXT: u32 = 34793;
+pub const GL_INVARIANT_VALUE_EXT: u32 = 34794;
+pub const GL_INVARIANT_DATATYPE_EXT: u32 = 34795;
+pub const GL_LOCAL_CONSTANT_VALUE_EXT: u32 = 34796;
+pub const GL_LOCAL_CONSTANT_DATATYPE_EXT: u32 = 34797;
+pub const GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: u32 = 34798;
+pub const GL_PN_TRIANGLES_ATI: u32 = 34800;
+pub const GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI: u32 = 34801;
+pub const GL_PN_TRIANGLES_POINT_MODE_ATI: u32 = 34802;
+pub const GL_PN_TRIANGLES_NORMAL_MODE_ATI: u32 = 34803;
+pub const GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI: u32 = 34804;
+pub const GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI: u32 = 34805;
+pub const GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI: u32 = 34806;
+pub const GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI: u32 = 34807;
+pub const GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI: u32 = 34808;
+pub const GL_3DC_X_AMD: u32 = 34809;
+pub const GL_3DC_XY_AMD: u32 = 34810;
+pub const GL_VBO_FREE_MEMORY_ATI: u32 = 34811;
+pub const GL_TEXTURE_FREE_MEMORY_ATI: u32 = 34812;
+pub const GL_RENDERBUFFER_FREE_MEMORY_ATI: u32 = 34813;
+pub const GL_NUM_PROGRAM_BINARY_FORMATS: u32 = 34814;
+pub const GL_NUM_PROGRAM_BINARY_FORMATS_OES: u32 = 34814;
+pub const GL_PROGRAM_BINARY_FORMATS: u32 = 34815;
+pub const GL_PROGRAM_BINARY_FORMATS_OES: u32 = 34815;
+pub const GL_STENCIL_BACK_FUNC: u32 = 34816;
+pub const GL_STENCIL_BACK_FUNC_ATI: u32 = 34816;
+pub const GL_STENCIL_BACK_FAIL: u32 = 34817;
+pub const GL_STENCIL_BACK_FAIL_ATI: u32 = 34817;
+pub const GL_STENCIL_BACK_PASS_DEPTH_FAIL: u32 = 34818;
+pub const GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI: u32 = 34818;
+pub const GL_STENCIL_BACK_PASS_DEPTH_PASS: u32 = 34819;
+pub const GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI: u32 = 34819;
+pub const GL_FRAGMENT_PROGRAM_ARB: u32 = 34820;
+pub const GL_PROGRAM_ALU_INSTRUCTIONS_ARB: u32 = 34821;
+pub const GL_PROGRAM_TEX_INSTRUCTIONS_ARB: u32 = 34822;
+pub const GL_PROGRAM_TEX_INDIRECTIONS_ARB: u32 = 34823;
+pub const GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB: u32 = 34824;
+pub const GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB: u32 = 34825;
+pub const GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB: u32 = 34826;
+pub const GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB: u32 = 34827;
+pub const GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB: u32 = 34828;
+pub const GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB: u32 = 34829;
+pub const GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB: u32 = 34830;
+pub const GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB: u32 = 34831;
+pub const GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB: u32 = 34832;
+pub const GL_RGBA32F: u32 = 34836;
+pub const GL_RGBA32F_ARB: u32 = 34836;
+pub const GL_RGBA32F_EXT: u32 = 34836;
+pub const GL_RGBA_FLOAT32_APPLE: u32 = 34836;
+pub const GL_RGBA_FLOAT32_ATI: u32 = 34836;
+pub const GL_RGB32F: u32 = 34837;
+pub const GL_RGB32F_ARB: u32 = 34837;
+pub const GL_RGB32F_EXT: u32 = 34837;
+pub const GL_RGB_FLOAT32_APPLE: u32 = 34837;
+pub const GL_RGB_FLOAT32_ATI: u32 = 34837;
+pub const GL_ALPHA32F_ARB: u32 = 34838;
+pub const GL_ALPHA32F_EXT: u32 = 34838;
+pub const GL_ALPHA_FLOAT32_APPLE: u32 = 34838;
+pub const GL_ALPHA_FLOAT32_ATI: u32 = 34838;
+pub const GL_INTENSITY32F_ARB: u32 = 34839;
+pub const GL_INTENSITY_FLOAT32_APPLE: u32 = 34839;
+pub const GL_INTENSITY_FLOAT32_ATI: u32 = 34839;
+pub const GL_LUMINANCE32F_ARB: u32 = 34840;
+pub const GL_LUMINANCE32F_EXT: u32 = 34840;
+pub const GL_LUMINANCE_FLOAT32_APPLE: u32 = 34840;
+pub const GL_LUMINANCE_FLOAT32_ATI: u32 = 34840;
+pub const GL_LUMINANCE_ALPHA32F_ARB: u32 = 34841;
+pub const GL_LUMINANCE_ALPHA32F_EXT: u32 = 34841;
+pub const GL_LUMINANCE_ALPHA_FLOAT32_APPLE: u32 = 34841;
+pub const GL_LUMINANCE_ALPHA_FLOAT32_ATI: u32 = 34841;
+pub const GL_RGBA16F: u32 = 34842;
+pub const GL_RGBA16F_ARB: u32 = 34842;
+pub const GL_RGBA16F_EXT: u32 = 34842;
+pub const GL_RGBA_FLOAT16_APPLE: u32 = 34842;
+pub const GL_RGBA_FLOAT16_ATI: u32 = 34842;
+pub const GL_RGB16F: u32 = 34843;
+pub const GL_RGB16F_ARB: u32 = 34843;
+pub const GL_RGB16F_EXT: u32 = 34843;
+pub const GL_RGB_FLOAT16_APPLE: u32 = 34843;
+pub const GL_RGB_FLOAT16_ATI: u32 = 34843;
+pub const GL_ALPHA16F_ARB: u32 = 34844;
+pub const GL_ALPHA16F_EXT: u32 = 34844;
+pub const GL_ALPHA_FLOAT16_APPLE: u32 = 34844;
+pub const GL_ALPHA_FLOAT16_ATI: u32 = 34844;
+pub const GL_INTENSITY16F_ARB: u32 = 34845;
+pub const GL_INTENSITY_FLOAT16_APPLE: u32 = 34845;
+pub const GL_INTENSITY_FLOAT16_ATI: u32 = 34845;
+pub const GL_LUMINANCE16F_ARB: u32 = 34846;
+pub const GL_LUMINANCE16F_EXT: u32 = 34846;
+pub const GL_LUMINANCE_FLOAT16_APPLE: u32 = 34846;
+pub const GL_LUMINANCE_FLOAT16_ATI: u32 = 34846;
+pub const GL_LUMINANCE_ALPHA16F_ARB: u32 = 34847;
+pub const GL_LUMINANCE_ALPHA16F_EXT: u32 = 34847;
+pub const GL_LUMINANCE_ALPHA_FLOAT16_APPLE: u32 = 34847;
+pub const GL_LUMINANCE_ALPHA_FLOAT16_ATI: u32 = 34847;
+pub const GL_RGBA_FLOAT_MODE_ARB: u32 = 34848;
+pub const GL_RGBA_FLOAT_MODE_ATI: u32 = 34848;
+pub const GL_WRITEONLY_RENDERING_QCOM: u32 = 34851;
+pub const GL_MAX_DRAW_BUFFERS: u32 = 34852;
+pub const GL_MAX_DRAW_BUFFERS_ARB: u32 = 34852;
+pub const GL_MAX_DRAW_BUFFERS_ATI: u32 = 34852;
+pub const GL_MAX_DRAW_BUFFERS_EXT: u32 = 34852;
+pub const GL_MAX_DRAW_BUFFERS_NV: u32 = 34852;
+pub const GL_DRAW_BUFFER0: u32 = 34853;
+pub const GL_DRAW_BUFFER0_ARB: u32 = 34853;
+pub const GL_DRAW_BUFFER0_ATI: u32 = 34853;
+pub const GL_DRAW_BUFFER0_EXT: u32 = 34853;
+pub const GL_DRAW_BUFFER0_NV: u32 = 34853;
+pub const GL_DRAW_BUFFER1: u32 = 34854;
+pub const GL_DRAW_BUFFER1_ARB: u32 = 34854;
+pub const GL_DRAW_BUFFER1_ATI: u32 = 34854;
+pub const GL_DRAW_BUFFER1_EXT: u32 = 34854;
+pub const GL_DRAW_BUFFER1_NV: u32 = 34854;
+pub const GL_DRAW_BUFFER2: u32 = 34855;
+pub const GL_DRAW_BUFFER2_ARB: u32 = 34855;
+pub const GL_DRAW_BUFFER2_ATI: u32 = 34855;
+pub const GL_DRAW_BUFFER2_EXT: u32 = 34855;
+pub const GL_DRAW_BUFFER2_NV: u32 = 34855;
+pub const GL_DRAW_BUFFER3: u32 = 34856;
+pub const GL_DRAW_BUFFER3_ARB: u32 = 34856;
+pub const GL_DRAW_BUFFER3_ATI: u32 = 34856;
+pub const GL_DRAW_BUFFER3_EXT: u32 = 34856;
+pub const GL_DRAW_BUFFER3_NV: u32 = 34856;
+pub const GL_DRAW_BUFFER4: u32 = 34857;
+pub const GL_DRAW_BUFFER4_ARB: u32 = 34857;
+pub const GL_DRAW_BUFFER4_ATI: u32 = 34857;
+pub const GL_DRAW_BUFFER4_EXT: u32 = 34857;
+pub const GL_DRAW_BUFFER4_NV: u32 = 34857;
+pub const GL_DRAW_BUFFER5: u32 = 34858;
+pub const GL_DRAW_BUFFER5_ARB: u32 = 34858;
+pub const GL_DRAW_BUFFER5_ATI: u32 = 34858;
+pub const GL_DRAW_BUFFER5_EXT: u32 = 34858;
+pub const GL_DRAW_BUFFER5_NV: u32 = 34858;
+pub const GL_DRAW_BUFFER6: u32 = 34859;
+pub const GL_DRAW_BUFFER6_ARB: u32 = 34859;
+pub const GL_DRAW_BUFFER6_ATI: u32 = 34859;
+pub const GL_DRAW_BUFFER6_EXT: u32 = 34859;
+pub const GL_DRAW_BUFFER6_NV: u32 = 34859;
+pub const GL_DRAW_BUFFER7: u32 = 34860;
+pub const GL_DRAW_BUFFER7_ARB: u32 = 34860;
+pub const GL_DRAW_BUFFER7_ATI: u32 = 34860;
+pub const GL_DRAW_BUFFER7_EXT: u32 = 34860;
+pub const GL_DRAW_BUFFER7_NV: u32 = 34860;
+pub const GL_DRAW_BUFFER8: u32 = 34861;
+pub const GL_DRAW_BUFFER8_ARB: u32 = 34861;
+pub const GL_DRAW_BUFFER8_ATI: u32 = 34861;
+pub const GL_DRAW_BUFFER8_EXT: u32 = 34861;
+pub const GL_DRAW_BUFFER8_NV: u32 = 34861;
+pub const GL_DRAW_BUFFER9: u32 = 34862;
+pub const GL_DRAW_BUFFER9_ARB: u32 = 34862;
+pub const GL_DRAW_BUFFER9_ATI: u32 = 34862;
+pub const GL_DRAW_BUFFER9_EXT: u32 = 34862;
+pub const GL_DRAW_BUFFER9_NV: u32 = 34862;
+pub const GL_DRAW_BUFFER10: u32 = 34863;
+pub const GL_DRAW_BUFFER10_ARB: u32 = 34863;
+pub const GL_DRAW_BUFFER10_ATI: u32 = 34863;
+pub const GL_DRAW_BUFFER10_EXT: u32 = 34863;
+pub const GL_DRAW_BUFFER10_NV: u32 = 34863;
+pub const GL_DRAW_BUFFER11: u32 = 34864;
+pub const GL_DRAW_BUFFER11_ARB: u32 = 34864;
+pub const GL_DRAW_BUFFER11_ATI: u32 = 34864;
+pub const GL_DRAW_BUFFER11_EXT: u32 = 34864;
+pub const GL_DRAW_BUFFER11_NV: u32 = 34864;
+pub const GL_DRAW_BUFFER12: u32 = 34865;
+pub const GL_DRAW_BUFFER12_ARB: u32 = 34865;
+pub const GL_DRAW_BUFFER12_ATI: u32 = 34865;
+pub const GL_DRAW_BUFFER12_EXT: u32 = 34865;
+pub const GL_DRAW_BUFFER12_NV: u32 = 34865;
+pub const GL_DRAW_BUFFER13: u32 = 34866;
+pub const GL_DRAW_BUFFER13_ARB: u32 = 34866;
+pub const GL_DRAW_BUFFER13_ATI: u32 = 34866;
+pub const GL_DRAW_BUFFER13_EXT: u32 = 34866;
+pub const GL_DRAW_BUFFER13_NV: u32 = 34866;
+pub const GL_DRAW_BUFFER14: u32 = 34867;
+pub const GL_DRAW_BUFFER14_ARB: u32 = 34867;
+pub const GL_DRAW_BUFFER14_ATI: u32 = 34867;
+pub const GL_DRAW_BUFFER14_EXT: u32 = 34867;
+pub const GL_DRAW_BUFFER14_NV: u32 = 34867;
+pub const GL_DRAW_BUFFER15: u32 = 34868;
+pub const GL_DRAW_BUFFER15_ARB: u32 = 34868;
+pub const GL_DRAW_BUFFER15_ATI: u32 = 34868;
+pub const GL_DRAW_BUFFER15_EXT: u32 = 34868;
+pub const GL_DRAW_BUFFER15_NV: u32 = 34868;
+pub const GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI: u32 = 34869;
+pub const GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI: u32 = 34871;
+pub const GL_BLEND_EQUATION_ALPHA: u32 = 34877;
+pub const GL_BLEND_EQUATION_ALPHA_EXT: u32 = 34877;
+pub const GL_BLEND_EQUATION_ALPHA_OES: u32 = 34877;
+pub const GL_SUBSAMPLE_DISTANCE_AMD: u32 = 34879;
+pub const GL_MATRIX_PALETTE_ARB: u32 = 34880;
+pub const GL_MATRIX_PALETTE_OES: u32 = 34880;
+pub const GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB: u32 = 34881;
+pub const GL_MAX_PALETTE_MATRICES_ARB: u32 = 34882;
+pub const GL_MAX_PALETTE_MATRICES_OES: u32 = 34882;
+pub const GL_CURRENT_PALETTE_MATRIX_ARB: u32 = 34883;
+pub const GL_CURRENT_PALETTE_MATRIX_OES: u32 = 34883;
+pub const GL_MATRIX_INDEX_ARRAY_ARB: u32 = 34884;
+pub const GL_MATRIX_INDEX_ARRAY_OES: u32 = 34884;
+pub const GL_CURRENT_MATRIX_INDEX_ARB: u32 = 34885;
+pub const GL_MATRIX_INDEX_ARRAY_SIZE_ARB: u32 = 34886;
+pub const GL_MATRIX_INDEX_ARRAY_SIZE_OES: u32 = 34886;
+pub const GL_MATRIX_INDEX_ARRAY_TYPE_ARB: u32 = 34887;
+pub const GL_MATRIX_INDEX_ARRAY_TYPE_OES: u32 = 34887;
+pub const GL_MATRIX_INDEX_ARRAY_STRIDE_ARB: u32 = 34888;
+pub const GL_MATRIX_INDEX_ARRAY_STRIDE_OES: u32 = 34888;
+pub const GL_MATRIX_INDEX_ARRAY_POINTER_ARB: u32 = 34889;
+pub const GL_MATRIX_INDEX_ARRAY_POINTER_OES: u32 = 34889;
+pub const GL_TEXTURE_DEPTH_SIZE: u32 = 34890;
+pub const GL_TEXTURE_DEPTH_SIZE_ARB: u32 = 34890;
+pub const GL_DEPTH_TEXTURE_MODE: u32 = 34891;
+pub const GL_DEPTH_TEXTURE_MODE_ARB: u32 = 34891;
+pub const GL_TEXTURE_COMPARE_MODE: u32 = 34892;
+pub const GL_TEXTURE_COMPARE_MODE_ARB: u32 = 34892;
+pub const GL_TEXTURE_COMPARE_MODE_EXT: u32 = 34892;
+pub const GL_TEXTURE_COMPARE_FUNC: u32 = 34893;
+pub const GL_TEXTURE_COMPARE_FUNC_ARB: u32 = 34893;
+pub const GL_TEXTURE_COMPARE_FUNC_EXT: u32 = 34893;
+pub const GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT: u32 = 34894;
+pub const GL_COMPARE_REF_TO_TEXTURE: u32 = 34894;
+pub const GL_COMPARE_REF_TO_TEXTURE_EXT: u32 = 34894;
+pub const GL_COMPARE_R_TO_TEXTURE: u32 = 34894;
+pub const GL_COMPARE_R_TO_TEXTURE_ARB: u32 = 34894;
+pub const GL_TEXTURE_CUBE_MAP_SEAMLESS: u32 = 34895;
+pub const GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV: u32 = 34896;
+pub const GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV: u32 = 34897;
+pub const GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV: u32 = 34898;
+pub const GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV: u32 = 34899;
+pub const GL_OFFSET_HILO_TEXTURE_2D_NV: u32 = 34900;
+pub const GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV: u32 = 34901;
+pub const GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV: u32 = 34902;
+pub const GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV: u32 = 34903;
+pub const GL_DEPENDENT_HILO_TEXTURE_2D_NV: u32 = 34904;
+pub const GL_DEPENDENT_RGB_TEXTURE_3D_NV: u32 = 34905;
+pub const GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV: u32 = 34906;
+pub const GL_DOT_PRODUCT_PASS_THROUGH_NV: u32 = 34907;
+pub const GL_DOT_PRODUCT_TEXTURE_1D_NV: u32 = 34908;
+pub const GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV: u32 = 34909;
+pub const GL_HILO8_NV: u32 = 34910;
+pub const GL_SIGNED_HILO8_NV: u32 = 34911;
+pub const GL_FORCE_BLUE_TO_ONE_NV: u32 = 34912;
+pub const GL_POINT_SPRITE: u32 = 34913;
+pub const GL_POINT_SPRITE_ARB: u32 = 34913;
+pub const GL_POINT_SPRITE_NV: u32 = 34913;
+pub const GL_POINT_SPRITE_OES: u32 = 34913;
+pub const GL_COORD_REPLACE: u32 = 34914;
+pub const GL_COORD_REPLACE_ARB: u32 = 34914;
+pub const GL_COORD_REPLACE_NV: u32 = 34914;
+pub const GL_COORD_REPLACE_OES: u32 = 34914;
+pub const GL_POINT_SPRITE_R_MODE_NV: u32 = 34915;
+pub const GL_PIXEL_COUNTER_BITS_NV: u32 = 34916;
+pub const GL_QUERY_COUNTER_BITS: u32 = 34916;
+pub const GL_QUERY_COUNTER_BITS_ARB: u32 = 34916;
+pub const GL_QUERY_COUNTER_BITS_EXT: u32 = 34916;
+pub const GL_CURRENT_OCCLUSION_QUERY_ID_NV: u32 = 34917;
+pub const GL_CURRENT_QUERY: u32 = 34917;
+pub const GL_CURRENT_QUERY_ARB: u32 = 34917;
+pub const GL_CURRENT_QUERY_EXT: u32 = 34917;
+pub const GL_PIXEL_COUNT_NV: u32 = 34918;
+pub const GL_QUERY_RESULT: u32 = 34918;
+pub const GL_QUERY_RESULT_ARB: u32 = 34918;
+pub const GL_QUERY_RESULT_EXT: u32 = 34918;
+pub const GL_PIXEL_COUNT_AVAILABLE_NV: u32 = 34919;
+pub const GL_QUERY_RESULT_AVAILABLE: u32 = 34919;
+pub const GL_QUERY_RESULT_AVAILABLE_ARB: u32 = 34919;
+pub const GL_QUERY_RESULT_AVAILABLE_EXT: u32 = 34919;
+pub const GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV: u32 = 34920;
+pub const GL_MAX_VERTEX_ATTRIBS: u32 = 34921;
+pub const GL_MAX_VERTEX_ATTRIBS_ARB: u32 = 34921;
+pub const GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: u32 = 34922;
+pub const GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB: u32 = 34922;
+pub const GL_MAX_TESS_CONTROL_INPUT_COMPONENTS: u32 = 34924;
+pub const GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT: u32 = 34924;
+pub const GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_OES: u32 = 34924;
+pub const GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS: u32 = 34925;
+pub const GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT: u32 = 34925;
+pub const GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_OES: u32 = 34925;
+pub const GL_DEPTH_STENCIL_TO_RGBA_NV: u32 = 34926;
+pub const GL_DEPTH_STENCIL_TO_BGRA_NV: u32 = 34927;
+pub const GL_FRAGMENT_PROGRAM_NV: u32 = 34928;
+pub const GL_MAX_TEXTURE_COORDS: u32 = 34929;
+pub const GL_MAX_TEXTURE_COORDS_ARB: u32 = 34929;
+pub const GL_MAX_TEXTURE_COORDS_NV: u32 = 34929;
+pub const GL_MAX_TEXTURE_IMAGE_UNITS: u32 = 34930;
+pub const GL_MAX_TEXTURE_IMAGE_UNITS_ARB: u32 = 34930;
+pub const GL_MAX_TEXTURE_IMAGE_UNITS_NV: u32 = 34930;
+pub const GL_FRAGMENT_PROGRAM_BINDING_NV: u32 = 34931;
+pub const GL_PROGRAM_ERROR_STRING_ARB: u32 = 34932;
+pub const GL_PROGRAM_ERROR_STRING_NV: u32 = 34932;
+pub const GL_PROGRAM_FORMAT_ASCII_ARB: u32 = 34933;
+pub const GL_PROGRAM_FORMAT_ARB: u32 = 34934;
+pub const GL_WRITE_PIXEL_DATA_RANGE_NV: u32 = 34936;
+pub const GL_READ_PIXEL_DATA_RANGE_NV: u32 = 34937;
+pub const GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV: u32 = 34938;
+pub const GL_READ_PIXEL_DATA_RANGE_LENGTH_NV: u32 = 34939;
+pub const GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV: u32 = 34940;
+pub const GL_READ_PIXEL_DATA_RANGE_POINTER_NV: u32 = 34941;
+pub const GL_GEOMETRY_SHADER_INVOCATIONS: u32 = 34943;
+pub const GL_GEOMETRY_SHADER_INVOCATIONS_EXT: u32 = 34943;
+pub const GL_GEOMETRY_SHADER_INVOCATIONS_OES: u32 = 34943;
+pub const GL_FLOAT_R_NV: u32 = 34944;
+pub const GL_FLOAT_RG_NV: u32 = 34945;
+pub const GL_FLOAT_RGB_NV: u32 = 34946;
+pub const GL_FLOAT_RGBA_NV: u32 = 34947;
+pub const GL_FLOAT_R16_NV: u32 = 34948;
+pub const GL_FLOAT_R32_NV: u32 = 34949;
+pub const GL_FLOAT_RG16_NV: u32 = 34950;
+pub const GL_FLOAT_RG32_NV: u32 = 34951;
+pub const GL_FLOAT_RGB16_NV: u32 = 34952;
+pub const GL_FLOAT_RGB32_NV: u32 = 34953;
+pub const GL_FLOAT_RGBA16_NV: u32 = 34954;
+pub const GL_FLOAT_RGBA32_NV: u32 = 34955;
+pub const GL_TEXTURE_FLOAT_COMPONENTS_NV: u32 = 34956;
+pub const GL_FLOAT_CLEAR_COLOR_VALUE_NV: u32 = 34957;
+pub const GL_FLOAT_RGBA_MODE_NV: u32 = 34958;
+pub const GL_TEXTURE_UNSIGNED_REMAP_MODE_NV: u32 = 34959;
+pub const GL_DEPTH_BOUNDS_TEST_EXT: u32 = 34960;
+pub const GL_DEPTH_BOUNDS_EXT: u32 = 34961;
+pub const GL_ARRAY_BUFFER: u32 = 34962;
+pub const GL_ARRAY_BUFFER_ARB: u32 = 34962;
+pub const GL_ELEMENT_ARRAY_BUFFER: u32 = 34963;
+pub const GL_ELEMENT_ARRAY_BUFFER_ARB: u32 = 34963;
+pub const GL_ARRAY_BUFFER_BINDING: u32 = 34964;
+pub const GL_ARRAY_BUFFER_BINDING_ARB: u32 = 34964;
+pub const GL_ELEMENT_ARRAY_BUFFER_BINDING: u32 = 34965;
+pub const GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB: u32 = 34965;
+pub const GL_VERTEX_ARRAY_BUFFER_BINDING: u32 = 34966;
+pub const GL_VERTEX_ARRAY_BUFFER_BINDING_ARB: u32 = 34966;
+pub const GL_NORMAL_ARRAY_BUFFER_BINDING: u32 = 34967;
+pub const GL_NORMAL_ARRAY_BUFFER_BINDING_ARB: u32 = 34967;
+pub const GL_COLOR_ARRAY_BUFFER_BINDING: u32 = 34968;
+pub const GL_COLOR_ARRAY_BUFFER_BINDING_ARB: u32 = 34968;
+pub const GL_INDEX_ARRAY_BUFFER_BINDING: u32 = 34969;
+pub const GL_INDEX_ARRAY_BUFFER_BINDING_ARB: u32 = 34969;
+pub const GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: u32 = 34970;
+pub const GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB: u32 = 34970;
+pub const GL_EDGE_FLAG_ARRAY_BUFFER_BINDING: u32 = 34971;
+pub const GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB: u32 = 34971;
+pub const GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING: u32 = 34972;
+pub const GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB: u32 = 34972;
+pub const GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING: u32 = 34973;
+pub const GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB: u32 = 34973;
+pub const GL_FOG_COORD_ARRAY_BUFFER_BINDING: u32 = 34973;
+pub const GL_WEIGHT_ARRAY_BUFFER_BINDING: u32 = 34974;
+pub const GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB: u32 = 34974;
+pub const GL_WEIGHT_ARRAY_BUFFER_BINDING_OES: u32 = 34974;
+pub const GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: u32 = 34975;
+pub const GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB: u32 = 34975;
+pub const GL_PROGRAM_INSTRUCTIONS_ARB: u32 = 34976;
+pub const GL_MAX_PROGRAM_INSTRUCTIONS_ARB: u32 = 34977;
+pub const GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB: u32 = 34978;
+pub const GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB: u32 = 34979;
+pub const GL_PROGRAM_TEMPORARIES_ARB: u32 = 34980;
+pub const GL_MAX_PROGRAM_TEMPORARIES_ARB: u32 = 34981;
+pub const GL_PROGRAM_NATIVE_TEMPORARIES_ARB: u32 = 34982;
+pub const GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB: u32 = 34983;
+pub const GL_PROGRAM_PARAMETERS_ARB: u32 = 34984;
+pub const GL_MAX_PROGRAM_PARAMETERS_ARB: u32 = 34985;
+pub const GL_PROGRAM_NATIVE_PARAMETERS_ARB: u32 = 34986;
+pub const GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB: u32 = 34987;
+pub const GL_PROGRAM_ATTRIBS_ARB: u32 = 34988;
+pub const GL_MAX_PROGRAM_ATTRIBS_ARB: u32 = 34989;
+pub const GL_PROGRAM_NATIVE_ATTRIBS_ARB: u32 = 34990;
+pub const GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB: u32 = 34991;
+pub const GL_PROGRAM_ADDRESS_REGISTERS_ARB: u32 = 34992;
+pub const GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB: u32 = 34993;
+pub const GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB: u32 = 34994;
+pub const GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB: u32 = 34995;
+pub const GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB: u32 = 34996;
+pub const GL_MAX_PROGRAM_ENV_PARAMETERS_ARB: u32 = 34997;
+pub const GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB: u32 = 34998;
+pub const GL_TRANSPOSE_CURRENT_MATRIX_ARB: u32 = 34999;
+pub const GL_READ_ONLY: u32 = 35000;
+pub const GL_READ_ONLY_ARB: u32 = 35000;
+pub const GL_WRITE_ONLY: u32 = 35001;
+pub const GL_WRITE_ONLY_ARB: u32 = 35001;
+pub const GL_WRITE_ONLY_OES: u32 = 35001;
+pub const GL_READ_WRITE: u32 = 35002;
+pub const GL_READ_WRITE_ARB: u32 = 35002;
+pub const GL_BUFFER_ACCESS: u32 = 35003;
+pub const GL_BUFFER_ACCESS_ARB: u32 = 35003;
+pub const GL_BUFFER_ACCESS_OES: u32 = 35003;
+pub const GL_BUFFER_MAPPED: u32 = 35004;
+pub const GL_BUFFER_MAPPED_ARB: u32 = 35004;
+pub const GL_BUFFER_MAPPED_OES: u32 = 35004;
+pub const GL_BUFFER_MAP_POINTER: u32 = 35005;
+pub const GL_BUFFER_MAP_POINTER_ARB: u32 = 35005;
+pub const GL_BUFFER_MAP_POINTER_OES: u32 = 35005;
+pub const GL_WRITE_DISCARD_NV: u32 = 35006;
+pub const GL_TIME_ELAPSED: u32 = 35007;
+pub const GL_TIME_ELAPSED_EXT: u32 = 35007;
+pub const GL_MATRIX0_ARB: u32 = 35008;
+pub const GL_MATRIX1_ARB: u32 = 35009;
+pub const GL_MATRIX2_ARB: u32 = 35010;
+pub const GL_MATRIX3_ARB: u32 = 35011;
+pub const GL_MATRIX4_ARB: u32 = 35012;
+pub const GL_MATRIX5_ARB: u32 = 35013;
+pub const GL_MATRIX6_ARB: u32 = 35014;
+pub const GL_MATRIX7_ARB: u32 = 35015;
+pub const GL_MATRIX8_ARB: u32 = 35016;
+pub const GL_MATRIX9_ARB: u32 = 35017;
+pub const GL_MATRIX10_ARB: u32 = 35018;
+pub const GL_MATRIX11_ARB: u32 = 35019;
+pub const GL_MATRIX12_ARB: u32 = 35020;
+pub const GL_MATRIX13_ARB: u32 = 35021;
+pub const GL_MATRIX14_ARB: u32 = 35022;
+pub const GL_MATRIX15_ARB: u32 = 35023;
+pub const GL_MATRIX16_ARB: u32 = 35024;
+pub const GL_MATRIX17_ARB: u32 = 35025;
+pub const GL_MATRIX18_ARB: u32 = 35026;
+pub const GL_MATRIX19_ARB: u32 = 35027;
+pub const GL_MATRIX20_ARB: u32 = 35028;
+pub const GL_MATRIX21_ARB: u32 = 35029;
+pub const GL_MATRIX22_ARB: u32 = 35030;
+pub const GL_MATRIX23_ARB: u32 = 35031;
+pub const GL_MATRIX24_ARB: u32 = 35032;
+pub const GL_MATRIX25_ARB: u32 = 35033;
+pub const GL_MATRIX26_ARB: u32 = 35034;
+pub const GL_MATRIX27_ARB: u32 = 35035;
+pub const GL_MATRIX28_ARB: u32 = 35036;
+pub const GL_MATRIX29_ARB: u32 = 35037;
+pub const GL_MATRIX30_ARB: u32 = 35038;
+pub const GL_MATRIX31_ARB: u32 = 35039;
+pub const GL_STREAM_DRAW: u32 = 35040;
+pub const GL_STREAM_DRAW_ARB: u32 = 35040;
+pub const GL_STREAM_READ: u32 = 35041;
+pub const GL_STREAM_READ_ARB: u32 = 35041;
+pub const GL_STREAM_COPY: u32 = 35042;
+pub const GL_STREAM_COPY_ARB: u32 = 35042;
+pub const GL_STATIC_DRAW: u32 = 35044;
+pub const GL_STATIC_DRAW_ARB: u32 = 35044;
+pub const GL_STATIC_READ: u32 = 35045;
+pub const GL_STATIC_READ_ARB: u32 = 35045;
+pub const GL_STATIC_COPY: u32 = 35046;
+pub const GL_STATIC_COPY_ARB: u32 = 35046;
+pub const GL_DYNAMIC_DRAW: u32 = 35048;
+pub const GL_DYNAMIC_DRAW_ARB: u32 = 35048;
+pub const GL_DYNAMIC_READ: u32 = 35049;
+pub const GL_DYNAMIC_READ_ARB: u32 = 35049;
+pub const GL_DYNAMIC_COPY: u32 = 35050;
+pub const GL_DYNAMIC_COPY_ARB: u32 = 35050;
+pub const GL_PIXEL_PACK_BUFFER: u32 = 35051;
+pub const GL_PIXEL_PACK_BUFFER_ARB: u32 = 35051;
+pub const GL_PIXEL_PACK_BUFFER_EXT: u32 = 35051;
+pub const GL_PIXEL_UNPACK_BUFFER: u32 = 35052;
+pub const GL_PIXEL_UNPACK_BUFFER_ARB: u32 = 35052;
+pub const GL_PIXEL_UNPACK_BUFFER_EXT: u32 = 35052;
+pub const GL_PIXEL_PACK_BUFFER_BINDING: u32 = 35053;
+pub const GL_PIXEL_PACK_BUFFER_BINDING_ARB: u32 = 35053;
+pub const GL_PIXEL_PACK_BUFFER_BINDING_EXT: u32 = 35053;
+pub const GL_ETC1_SRGB8_NV: u32 = 35054;
+pub const GL_PIXEL_UNPACK_BUFFER_BINDING: u32 = 35055;
+pub const GL_PIXEL_UNPACK_BUFFER_BINDING_ARB: u32 = 35055;
+pub const GL_PIXEL_UNPACK_BUFFER_BINDING_EXT: u32 = 35055;
+pub const GL_DEPTH24_STENCIL8: u32 = 35056;
+pub const GL_DEPTH24_STENCIL8_EXT: u32 = 35056;
+pub const GL_DEPTH24_STENCIL8_OES: u32 = 35056;
+pub const GL_TEXTURE_STENCIL_SIZE: u32 = 35057;
+pub const GL_TEXTURE_STENCIL_SIZE_EXT: u32 = 35057;
+pub const GL_STENCIL_TAG_BITS_EXT: u32 = 35058;
+pub const GL_STENCIL_CLEAR_TAG_VALUE_EXT: u32 = 35059;
+pub const GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV: u32 = 35060;
+pub const GL_MAX_PROGRAM_CALL_DEPTH_NV: u32 = 35061;
+pub const GL_MAX_PROGRAM_IF_DEPTH_NV: u32 = 35062;
+pub const GL_MAX_PROGRAM_LOOP_DEPTH_NV: u32 = 35063;
+pub const GL_MAX_PROGRAM_LOOP_COUNT_NV: u32 = 35064;
+pub const GL_SRC1_COLOR: u32 = 35065;
+pub const GL_SRC1_COLOR_EXT: u32 = 35065;
+pub const GL_ONE_MINUS_SRC1_COLOR: u32 = 35066;
+pub const GL_ONE_MINUS_SRC1_COLOR_EXT: u32 = 35066;
+pub const GL_ONE_MINUS_SRC1_ALPHA: u32 = 35067;
+pub const GL_ONE_MINUS_SRC1_ALPHA_EXT: u32 = 35067;
+pub const GL_MAX_DUAL_SOURCE_DRAW_BUFFERS: u32 = 35068;
+pub const GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT: u32 = 35068;
+pub const GL_VERTEX_ATTRIB_ARRAY_INTEGER: u32 = 35069;
+pub const GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT: u32 = 35069;
+pub const GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV: u32 = 35069;
+pub const GL_VERTEX_ATTRIB_ARRAY_DIVISOR: u32 = 35070;
+pub const GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE: u32 = 35070;
+pub const GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB: u32 = 35070;
+pub const GL_VERTEX_ATTRIB_ARRAY_DIVISOR_EXT: u32 = 35070;
+pub const GL_VERTEX_ATTRIB_ARRAY_DIVISOR_NV: u32 = 35070;
+pub const GL_MAX_ARRAY_TEXTURE_LAYERS: u32 = 35071;
+pub const GL_MAX_ARRAY_TEXTURE_LAYERS_EXT: u32 = 35071;
+pub const GL_MIN_PROGRAM_TEXEL_OFFSET: u32 = 35076;
+pub const GL_MIN_PROGRAM_TEXEL_OFFSET_EXT: u32 = 35076;
+pub const GL_MIN_PROGRAM_TEXEL_OFFSET_NV: u32 = 35076;
+pub const GL_MAX_PROGRAM_TEXEL_OFFSET: u32 = 35077;
+pub const GL_MAX_PROGRAM_TEXEL_OFFSET_EXT: u32 = 35077;
+pub const GL_MAX_PROGRAM_TEXEL_OFFSET_NV: u32 = 35077;
+pub const GL_PROGRAM_ATTRIB_COMPONENTS_NV: u32 = 35078;
+pub const GL_PROGRAM_RESULT_COMPONENTS_NV: u32 = 35079;
+pub const GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV: u32 = 35080;
+pub const GL_MAX_PROGRAM_RESULT_COMPONENTS_NV: u32 = 35081;
+pub const GL_STENCIL_TEST_TWO_SIDE_EXT: u32 = 35088;
+pub const GL_ACTIVE_STENCIL_FACE_EXT: u32 = 35089;
+pub const GL_MIRROR_CLAMP_TO_BORDER_EXT: u32 = 35090;
+pub const GL_SAMPLES_PASSED: u32 = 35092;
+pub const GL_SAMPLES_PASSED_ARB: u32 = 35092;
+pub const GL_GEOMETRY_LINKED_VERTICES_OUT_EXT: u32 = 35094;
+pub const GL_GEOMETRY_LINKED_VERTICES_OUT_OES: u32 = 35094;
+pub const GL_GEOMETRY_VERTICES_OUT: u32 = 35094;
+pub const GL_GEOMETRY_INPUT_TYPE: u32 = 35095;
+pub const GL_GEOMETRY_LINKED_INPUT_TYPE_EXT: u32 = 35095;
+pub const GL_GEOMETRY_LINKED_INPUT_TYPE_OES: u32 = 35095;
+pub const GL_GEOMETRY_LINKED_OUTPUT_TYPE_EXT: u32 = 35096;
+pub const GL_GEOMETRY_LINKED_OUTPUT_TYPE_OES: u32 = 35096;
+pub const GL_GEOMETRY_OUTPUT_TYPE: u32 = 35096;
+pub const GL_SAMPLER_BINDING: u32 = 35097;
+pub const GL_CLAMP_VERTEX_COLOR: u32 = 35098;
+pub const GL_CLAMP_VERTEX_COLOR_ARB: u32 = 35098;
+pub const GL_CLAMP_FRAGMENT_COLOR: u32 = 35099;
+pub const GL_CLAMP_FRAGMENT_COLOR_ARB: u32 = 35099;
+pub const GL_CLAMP_READ_COLOR: u32 = 35100;
+pub const GL_CLAMP_READ_COLOR_ARB: u32 = 35100;
+pub const GL_FIXED_ONLY: u32 = 35101;
+pub const GL_FIXED_ONLY_ARB: u32 = 35101;
+pub const GL_TESS_CONTROL_PROGRAM_NV: u32 = 35102;
+pub const GL_TESS_EVALUATION_PROGRAM_NV: u32 = 35103;
+pub const GL_FRAGMENT_SHADER_ATI: u32 = 35104;
+pub const GL_REG_0_ATI: u32 = 35105;
+pub const GL_REG_1_ATI: u32 = 35106;
+pub const GL_REG_2_ATI: u32 = 35107;
+pub const GL_REG_3_ATI: u32 = 35108;
+pub const GL_REG_4_ATI: u32 = 35109;
+pub const GL_REG_5_ATI: u32 = 35110;
+pub const GL_REG_6_ATI: u32 = 35111;
+pub const GL_REG_7_ATI: u32 = 35112;
+pub const GL_REG_8_ATI: u32 = 35113;
+pub const GL_REG_9_ATI: u32 = 35114;
+pub const GL_REG_10_ATI: u32 = 35115;
+pub const GL_REG_11_ATI: u32 = 35116;
+pub const GL_REG_12_ATI: u32 = 35117;
+pub const GL_REG_13_ATI: u32 = 35118;
+pub const GL_REG_14_ATI: u32 = 35119;
+pub const GL_REG_15_ATI: u32 = 35120;
+pub const GL_REG_16_ATI: u32 = 35121;
+pub const GL_REG_17_ATI: u32 = 35122;
+pub const GL_REG_18_ATI: u32 = 35123;
+pub const GL_REG_19_ATI: u32 = 35124;
+pub const GL_REG_20_ATI: u32 = 35125;
+pub const GL_REG_21_ATI: u32 = 35126;
+pub const GL_REG_22_ATI: u32 = 35127;
+pub const GL_REG_23_ATI: u32 = 35128;
+pub const GL_REG_24_ATI: u32 = 35129;
+pub const GL_REG_25_ATI: u32 = 35130;
+pub const GL_REG_26_ATI: u32 = 35131;
+pub const GL_REG_27_ATI: u32 = 35132;
+pub const GL_REG_28_ATI: u32 = 35133;
+pub const GL_REG_29_ATI: u32 = 35134;
+pub const GL_REG_30_ATI: u32 = 35135;
+pub const GL_REG_31_ATI: u32 = 35136;
+pub const GL_CON_0_ATI: u32 = 35137;
+pub const GL_CON_1_ATI: u32 = 35138;
+pub const GL_CON_2_ATI: u32 = 35139;
+pub const GL_CON_3_ATI: u32 = 35140;
+pub const GL_CON_4_ATI: u32 = 35141;
+pub const GL_CON_5_ATI: u32 = 35142;
+pub const GL_CON_6_ATI: u32 = 35143;
+pub const GL_CON_7_ATI: u32 = 35144;
+pub const GL_CON_8_ATI: u32 = 35145;
+pub const GL_CON_9_ATI: u32 = 35146;
+pub const GL_CON_10_ATI: u32 = 35147;
+pub const GL_CON_11_ATI: u32 = 35148;
+pub const GL_CON_12_ATI: u32 = 35149;
+pub const GL_CON_13_ATI: u32 = 35150;
+pub const GL_CON_14_ATI: u32 = 35151;
+pub const GL_CON_15_ATI: u32 = 35152;
+pub const GL_CON_16_ATI: u32 = 35153;
+pub const GL_CON_17_ATI: u32 = 35154;
+pub const GL_CON_18_ATI: u32 = 35155;
+pub const GL_CON_19_ATI: u32 = 35156;
+pub const GL_CON_20_ATI: u32 = 35157;
+pub const GL_CON_21_ATI: u32 = 35158;
+pub const GL_CON_22_ATI: u32 = 35159;
+pub const GL_CON_23_ATI: u32 = 35160;
+pub const GL_CON_24_ATI: u32 = 35161;
+pub const GL_CON_25_ATI: u32 = 35162;
+pub const GL_CON_26_ATI: u32 = 35163;
+pub const GL_CON_27_ATI: u32 = 35164;
+pub const GL_CON_28_ATI: u32 = 35165;
+pub const GL_CON_29_ATI: u32 = 35166;
+pub const GL_CON_30_ATI: u32 = 35167;
+pub const GL_CON_31_ATI: u32 = 35168;
+pub const GL_MOV_ATI: u32 = 35169;
+pub const GL_ADD_ATI: u32 = 35171;
+pub const GL_MUL_ATI: u32 = 35172;
+pub const GL_SUB_ATI: u32 = 35173;
+pub const GL_DOT3_ATI: u32 = 35174;
+pub const GL_DOT4_ATI: u32 = 35175;
+pub const GL_MAD_ATI: u32 = 35176;
+pub const GL_LERP_ATI: u32 = 35177;
+pub const GL_CND_ATI: u32 = 35178;
+pub const GL_CND0_ATI: u32 = 35179;
+pub const GL_DOT2_ADD_ATI: u32 = 35180;
+pub const GL_SECONDARY_INTERPOLATOR_ATI: u32 = 35181;
+pub const GL_NUM_FRAGMENT_REGISTERS_ATI: u32 = 35182;
+pub const GL_NUM_FRAGMENT_CONSTANTS_ATI: u32 = 35183;
+pub const GL_NUM_PASSES_ATI: u32 = 35184;
+pub const GL_NUM_INSTRUCTIONS_PER_PASS_ATI: u32 = 35185;
+pub const GL_NUM_INSTRUCTIONS_TOTAL_ATI: u32 = 35186;
+pub const GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI: u32 = 35187;
+pub const GL_NUM_LOOPBACK_COMPONENTS_ATI: u32 = 35188;
+pub const GL_COLOR_ALPHA_PAIRING_ATI: u32 = 35189;
+pub const GL_SWIZZLE_STR_ATI: u32 = 35190;
+pub const GL_SWIZZLE_STQ_ATI: u32 = 35191;
+pub const GL_SWIZZLE_STR_DR_ATI: u32 = 35192;
+pub const GL_SWIZZLE_STQ_DQ_ATI: u32 = 35193;
+pub const GL_SWIZZLE_STRQ_ATI: u32 = 35194;
+pub const GL_SWIZZLE_STRQ_DQ_ATI: u32 = 35195;
+pub const GL_INTERLACE_OML: u32 = 35200;
+pub const GL_INTERLACE_READ_OML: u32 = 35201;
+pub const GL_FORMAT_SUBSAMPLE_24_24_OML: u32 = 35202;
+pub const GL_FORMAT_SUBSAMPLE_244_244_OML: u32 = 35203;
+pub const GL_PACK_RESAMPLE_OML: u32 = 35204;
+pub const GL_UNPACK_RESAMPLE_OML: u32 = 35205;
+pub const GL_RESAMPLE_REPLICATE_OML: u32 = 35206;
+pub const GL_RESAMPLE_ZERO_FILL_OML: u32 = 35207;
+pub const GL_RESAMPLE_AVERAGE_OML: u32 = 35208;
+pub const GL_RESAMPLE_DECIMATE_OML: u32 = 35209;
+pub const GL_POINT_SIZE_ARRAY_TYPE_OES: u32 = 35210;
+pub const GL_POINT_SIZE_ARRAY_STRIDE_OES: u32 = 35211;
+pub const GL_POINT_SIZE_ARRAY_POINTER_OES: u32 = 35212;
+pub const GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES: u32 = 35213;
+pub const GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES: u32 = 35214;
+pub const GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES: u32 = 35215;
+pub const GL_VERTEX_ATTRIB_MAP1_APPLE: u32 = 35328;
+pub const GL_VERTEX_ATTRIB_MAP2_APPLE: u32 = 35329;
+pub const GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE: u32 = 35330;
+pub const GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE: u32 = 35331;
+pub const GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE: u32 = 35332;
+pub const GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE: u32 = 35333;
+pub const GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE: u32 = 35334;
+pub const GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE: u32 = 35335;
+pub const GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE: u32 = 35336;
+pub const GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE: u32 = 35337;
+pub const GL_DRAW_PIXELS_APPLE: u32 = 35338;
+pub const GL_FENCE_APPLE: u32 = 35339;
+pub const GL_ELEMENT_ARRAY_APPLE: u32 = 35340;
+pub const GL_ELEMENT_ARRAY_TYPE_APPLE: u32 = 35341;
+pub const GL_ELEMENT_ARRAY_POINTER_APPLE: u32 = 35342;
+pub const GL_COLOR_FLOAT_APPLE: u32 = 35343;
+pub const GL_UNIFORM_BUFFER: u32 = 35345;
+pub const GL_BUFFER_SERIALIZED_MODIFY_APPLE: u32 = 35346;
+pub const GL_BUFFER_FLUSHING_UNMAP_APPLE: u32 = 35347;
+pub const GL_AUX_DEPTH_STENCIL_APPLE: u32 = 35348;
+pub const GL_PACK_ROW_BYTES_APPLE: u32 = 35349;
+pub const GL_UNPACK_ROW_BYTES_APPLE: u32 = 35350;
+pub const GL_RELEASED_APPLE: u32 = 35353;
+pub const GL_VOLATILE_APPLE: u32 = 35354;
+pub const GL_RETAINED_APPLE: u32 = 35355;
+pub const GL_UNDEFINED_APPLE: u32 = 35356;
+pub const GL_PURGEABLE_APPLE: u32 = 35357;
+pub const GL_RGB_422_APPLE: u32 = 35359;
+pub const GL_UNIFORM_BUFFER_BINDING: u32 = 35368;
+pub const GL_UNIFORM_BUFFER_START: u32 = 35369;
+pub const GL_UNIFORM_BUFFER_SIZE: u32 = 35370;
+pub const GL_MAX_VERTEX_UNIFORM_BLOCKS: u32 = 35371;
+pub const GL_MAX_GEOMETRY_UNIFORM_BLOCKS: u32 = 35372;
+pub const GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT: u32 = 35372;
+pub const GL_MAX_GEOMETRY_UNIFORM_BLOCKS_OES: u32 = 35372;
+pub const GL_MAX_FRAGMENT_UNIFORM_BLOCKS: u32 = 35373;
+pub const GL_MAX_COMBINED_UNIFORM_BLOCKS: u32 = 35374;
+pub const GL_MAX_UNIFORM_BUFFER_BINDINGS: u32 = 35375;
+pub const GL_MAX_UNIFORM_BLOCK_SIZE: u32 = 35376;
+pub const GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: u32 = 35377;
+pub const GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS: u32 = 35378;
+pub const GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT: u32 = 35378;
+pub const GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_OES: u32 = 35378;
+pub const GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: u32 = 35379;
+pub const GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: u32 = 35380;
+pub const GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: u32 = 35381;
+pub const GL_ACTIVE_UNIFORM_BLOCKS: u32 = 35382;
+pub const GL_UNIFORM_TYPE: u32 = 35383;
+pub const GL_UNIFORM_SIZE: u32 = 35384;
+pub const GL_UNIFORM_NAME_LENGTH: u32 = 35385;
+pub const GL_UNIFORM_BLOCK_INDEX: u32 = 35386;
+pub const GL_UNIFORM_OFFSET: u32 = 35387;
+pub const GL_UNIFORM_ARRAY_STRIDE: u32 = 35388;
+pub const GL_UNIFORM_MATRIX_STRIDE: u32 = 35389;
+pub const GL_UNIFORM_IS_ROW_MAJOR: u32 = 35390;
+pub const GL_UNIFORM_BLOCK_BINDING: u32 = 35391;
+pub const GL_UNIFORM_BLOCK_DATA_SIZE: u32 = 35392;
+pub const GL_UNIFORM_BLOCK_NAME_LENGTH: u32 = 35393;
+pub const GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: u32 = 35394;
+pub const GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: u32 = 35395;
+pub const GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: u32 = 35396;
+pub const GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER: u32 = 35397;
+pub const GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: u32 = 35398;
+pub const GL_TEXTURE_SRGB_DECODE_EXT: u32 = 35400;
+pub const GL_DECODE_EXT: u32 = 35401;
+pub const GL_SKIP_DECODE_EXT: u32 = 35402;
+pub const GL_PROGRAM_PIPELINE_OBJECT_EXT: u32 = 35407;
+pub const GL_RGB_RAW_422_APPLE: u32 = 35409;
+pub const GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT: u32 = 35410;
+pub const GL_SYNC_OBJECT_APPLE: u32 = 35411;
+pub const GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: u32 = 35412;
+pub const GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: u32 = 35413;
+pub const GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: u32 = 35414;
+pub const GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: u32 = 35415;
+pub const GL_FRAGMENT_SHADER: u32 = 35632;
+pub const GL_FRAGMENT_SHADER_ARB: u32 = 35632;
+pub const GL_VERTEX_SHADER: u32 = 35633;
+pub const GL_VERTEX_SHADER_ARB: u32 = 35633;
+pub const GL_PROGRAM_OBJECT_ARB: u32 = 35648;
+pub const GL_PROGRAM_OBJECT_EXT: u32 = 35648;
+pub const GL_SHADER_OBJECT_ARB: u32 = 35656;
+pub const GL_SHADER_OBJECT_EXT: u32 = 35656;
+pub const GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: u32 = 35657;
+pub const GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB: u32 = 35657;
+pub const GL_MAX_VERTEX_UNIFORM_COMPONENTS: u32 = 35658;
+pub const GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB: u32 = 35658;
+pub const GL_MAX_VARYING_COMPONENTS: u32 = 35659;
+pub const GL_MAX_VARYING_COMPONENTS_EXT: u32 = 35659;
+pub const GL_MAX_VARYING_FLOATS: u32 = 35659;
+pub const GL_MAX_VARYING_FLOATS_ARB: u32 = 35659;
+pub const GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: u32 = 35660;
+pub const GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB: u32 = 35660;
+pub const GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: u32 = 35661;
+pub const GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB: u32 = 35661;
+pub const GL_OBJECT_TYPE_ARB: u32 = 35662;
+pub const GL_OBJECT_SUBTYPE_ARB: u32 = 35663;
+pub const GL_SHADER_TYPE: u32 = 35663;
+pub const GL_FLOAT_VEC2: u32 = 35664;
+pub const GL_FLOAT_VEC2_ARB: u32 = 35664;
+pub const GL_FLOAT_VEC3: u32 = 35665;
+pub const GL_FLOAT_VEC3_ARB: u32 = 35665;
+pub const GL_FLOAT_VEC4: u32 = 35666;
+pub const GL_FLOAT_VEC4_ARB: u32 = 35666;
+pub const GL_INT_VEC2: u32 = 35667;
+pub const GL_INT_VEC2_ARB: u32 = 35667;
+pub const GL_INT_VEC3: u32 = 35668;
+pub const GL_INT_VEC3_ARB: u32 = 35668;
+pub const GL_INT_VEC4: u32 = 35669;
+pub const GL_INT_VEC4_ARB: u32 = 35669;
+pub const GL_BOOL: u32 = 35670;
+pub const GL_BOOL_ARB: u32 = 35670;
+pub const GL_BOOL_VEC2: u32 = 35671;
+pub const GL_BOOL_VEC2_ARB: u32 = 35671;
+pub const GL_BOOL_VEC3: u32 = 35672;
+pub const GL_BOOL_VEC3_ARB: u32 = 35672;
+pub const GL_BOOL_VEC4: u32 = 35673;
+pub const GL_BOOL_VEC4_ARB: u32 = 35673;
+pub const GL_FLOAT_MAT2: u32 = 35674;
+pub const GL_FLOAT_MAT2_ARB: u32 = 35674;
+pub const GL_FLOAT_MAT3: u32 = 35675;
+pub const GL_FLOAT_MAT3_ARB: u32 = 35675;
+pub const GL_FLOAT_MAT4: u32 = 35676;
+pub const GL_FLOAT_MAT4_ARB: u32 = 35676;
+pub const GL_SAMPLER_1D: u32 = 35677;
+pub const GL_SAMPLER_1D_ARB: u32 = 35677;
+pub const GL_SAMPLER_2D: u32 = 35678;
+pub const GL_SAMPLER_2D_ARB: u32 = 35678;
+pub const GL_SAMPLER_3D: u32 = 35679;
+pub const GL_SAMPLER_3D_ARB: u32 = 35679;
+pub const GL_SAMPLER_3D_OES: u32 = 35679;
+pub const GL_SAMPLER_CUBE: u32 = 35680;
+pub const GL_SAMPLER_CUBE_ARB: u32 = 35680;
+pub const GL_SAMPLER_1D_SHADOW: u32 = 35681;
+pub const GL_SAMPLER_1D_SHADOW_ARB: u32 = 35681;
+pub const GL_SAMPLER_2D_SHADOW: u32 = 35682;
+pub const GL_SAMPLER_2D_SHADOW_ARB: u32 = 35682;
+pub const GL_SAMPLER_2D_SHADOW_EXT: u32 = 35682;
+pub const GL_SAMPLER_2D_RECT: u32 = 35683;
+pub const GL_SAMPLER_2D_RECT_ARB: u32 = 35683;
+pub const GL_SAMPLER_2D_RECT_SHADOW: u32 = 35684;
+pub const GL_SAMPLER_2D_RECT_SHADOW_ARB: u32 = 35684;
+pub const GL_FLOAT_MAT2x3: u32 = 35685;
+pub const GL_FLOAT_MAT2x3_NV: u32 = 35685;
+pub const GL_FLOAT_MAT2x4: u32 = 35686;
+pub const GL_FLOAT_MAT2x4_NV: u32 = 35686;
+pub const GL_FLOAT_MAT3x2: u32 = 35687;
+pub const GL_FLOAT_MAT3x2_NV: u32 = 35687;
+pub const GL_FLOAT_MAT3x4: u32 = 35688;
+pub const GL_FLOAT_MAT3x4_NV: u32 = 35688;
+pub const GL_FLOAT_MAT4x2: u32 = 35689;
+pub const GL_FLOAT_MAT4x2_NV: u32 = 35689;
+pub const GL_FLOAT_MAT4x3: u32 = 35690;
+pub const GL_FLOAT_MAT4x3_NV: u32 = 35690;
+pub const GL_DELETE_STATUS: u32 = 35712;
+pub const GL_OBJECT_DELETE_STATUS_ARB: u32 = 35712;
+pub const GL_COMPILE_STATUS: u32 = 35713;
+pub const GL_OBJECT_COMPILE_STATUS_ARB: u32 = 35713;
+pub const GL_LINK_STATUS: u32 = 35714;
+pub const GL_OBJECT_LINK_STATUS_ARB: u32 = 35714;
+pub const GL_OBJECT_VALIDATE_STATUS_ARB: u32 = 35715;
+pub const GL_VALIDATE_STATUS: u32 = 35715;
+pub const GL_INFO_LOG_LENGTH: u32 = 35716;
+pub const GL_OBJECT_INFO_LOG_LENGTH_ARB: u32 = 35716;
+pub const GL_ATTACHED_SHADERS: u32 = 35717;
+pub const GL_OBJECT_ATTACHED_OBJECTS_ARB: u32 = 35717;
+pub const GL_ACTIVE_UNIFORMS: u32 = 35718;
+pub const GL_OBJECT_ACTIVE_UNIFORMS_ARB: u32 = 35718;
+pub const GL_ACTIVE_UNIFORM_MAX_LENGTH: u32 = 35719;
+pub const GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB: u32 = 35719;
+pub const GL_OBJECT_SHADER_SOURCE_LENGTH_ARB: u32 = 35720;
+pub const GL_SHADER_SOURCE_LENGTH: u32 = 35720;
+pub const GL_ACTIVE_ATTRIBUTES: u32 = 35721;
+pub const GL_OBJECT_ACTIVE_ATTRIBUTES_ARB: u32 = 35721;
+pub const GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: u32 = 35722;
+pub const GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB: u32 = 35722;
+pub const GL_FRAGMENT_SHADER_DERIVATIVE_HINT: u32 = 35723;
+pub const GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB: u32 = 35723;
+pub const GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES: u32 = 35723;
+pub const GL_SHADING_LANGUAGE_VERSION: u32 = 35724;
+pub const GL_SHADING_LANGUAGE_VERSION_ARB: u32 = 35724;
+pub const GL_ACTIVE_PROGRAM_EXT: u32 = 35725;
+pub const GL_CURRENT_PROGRAM: u32 = 35725;
+pub const GL_PALETTE4_RGB8_OES: u32 = 35728;
+pub const GL_PALETTE4_RGBA8_OES: u32 = 35729;
+pub const GL_PALETTE4_R5_G6_B5_OES: u32 = 35730;
+pub const GL_PALETTE4_RGBA4_OES: u32 = 35731;
+pub const GL_PALETTE4_RGB5_A1_OES: u32 = 35732;
+pub const GL_PALETTE8_RGB8_OES: u32 = 35733;
+pub const GL_PALETTE8_RGBA8_OES: u32 = 35734;
+pub const GL_PALETTE8_R5_G6_B5_OES: u32 = 35735;
+pub const GL_PALETTE8_RGBA4_OES: u32 = 35736;
+pub const GL_PALETTE8_RGB5_A1_OES: u32 = 35737;
+pub const GL_IMPLEMENTATION_COLOR_READ_TYPE: u32 = 35738;
+pub const GL_IMPLEMENTATION_COLOR_READ_TYPE_OES: u32 = 35738;
+pub const GL_IMPLEMENTATION_COLOR_READ_FORMAT: u32 = 35739;
+pub const GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES: u32 = 35739;
+pub const GL_POINT_SIZE_ARRAY_OES: u32 = 35740;
+pub const GL_TEXTURE_CROP_RECT_OES: u32 = 35741;
+pub const GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES: u32 = 35742;
+pub const GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES: u32 = 35743;
+pub const GL_FRAGMENT_PROGRAM_POSITION_MESA: u32 = 35760;
+pub const GL_FRAGMENT_PROGRAM_CALLBACK_MESA: u32 = 35761;
+pub const GL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA: u32 = 35762;
+pub const GL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA: u32 = 35763;
+pub const GL_VERTEX_PROGRAM_POSITION_MESA: u32 = 35764;
+pub const GL_VERTEX_PROGRAM_CALLBACK_MESA: u32 = 35765;
+pub const GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA: u32 = 35766;
+pub const GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA: u32 = 35767;
+pub const GL_COUNTER_TYPE_AMD: u32 = 35776;
+pub const GL_COUNTER_RANGE_AMD: u32 = 35777;
+pub const GL_UNSIGNED_INT64_AMD: u32 = 35778;
+pub const GL_PERCENTAGE_AMD: u32 = 35779;
+pub const GL_PERFMON_RESULT_AVAILABLE_AMD: u32 = 35780;
+pub const GL_PERFMON_RESULT_SIZE_AMD: u32 = 35781;
+pub const GL_PERFMON_RESULT_AMD: u32 = 35782;
+pub const GL_TEXTURE_WIDTH_QCOM: u32 = 35794;
+pub const GL_TEXTURE_HEIGHT_QCOM: u32 = 35795;
+pub const GL_TEXTURE_DEPTH_QCOM: u32 = 35796;
+pub const GL_TEXTURE_INTERNAL_FORMAT_QCOM: u32 = 35797;
+pub const GL_TEXTURE_FORMAT_QCOM: u32 = 35798;
+pub const GL_TEXTURE_TYPE_QCOM: u32 = 35799;
+pub const GL_TEXTURE_IMAGE_VALID_QCOM: u32 = 35800;
+pub const GL_TEXTURE_NUM_LEVELS_QCOM: u32 = 35801;
+pub const GL_TEXTURE_TARGET_QCOM: u32 = 35802;
+pub const GL_TEXTURE_OBJECT_VALID_QCOM: u32 = 35803;
+pub const GL_STATE_RESTORE: u32 = 35804;
+pub const GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT: u32 = 35815;
+pub const GL_TEXTURE_PROTECTED_EXT: u32 = 35834;
+pub const GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: u32 = 35840;
+pub const GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: u32 = 35841;
+pub const GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: u32 = 35842;
+pub const GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: u32 = 35843;
+pub const GL_MODULATE_COLOR_IMG: u32 = 35844;
+pub const GL_RECIP_ADD_SIGNED_ALPHA_IMG: u32 = 35845;
+pub const GL_TEXTURE_ALPHA_MODULATE_IMG: u32 = 35846;
+pub const GL_FACTOR_ALPHA_MODULATE_IMG: u32 = 35847;
+pub const GL_FRAGMENT_ALPHA_MODULATE_IMG: u32 = 35848;
+pub const GL_ADD_BLEND_IMG: u32 = 35849;
+pub const GL_SGX_BINARY_IMG: u32 = 35850;
+pub const GL_TEXTURE_RED_TYPE: u32 = 35856;
+pub const GL_TEXTURE_RED_TYPE_ARB: u32 = 35856;
+pub const GL_TEXTURE_GREEN_TYPE: u32 = 35857;
+pub const GL_TEXTURE_GREEN_TYPE_ARB: u32 = 35857;
+pub const GL_TEXTURE_BLUE_TYPE: u32 = 35858;
+pub const GL_TEXTURE_BLUE_TYPE_ARB: u32 = 35858;
+pub const GL_TEXTURE_ALPHA_TYPE: u32 = 35859;
+pub const GL_TEXTURE_ALPHA_TYPE_ARB: u32 = 35859;
+pub const GL_TEXTURE_LUMINANCE_TYPE: u32 = 35860;
+pub const GL_TEXTURE_LUMINANCE_TYPE_ARB: u32 = 35860;
+pub const GL_TEXTURE_INTENSITY_TYPE: u32 = 35861;
+pub const GL_TEXTURE_INTENSITY_TYPE_ARB: u32 = 35861;
+pub const GL_TEXTURE_DEPTH_TYPE: u32 = 35862;
+pub const GL_TEXTURE_DEPTH_TYPE_ARB: u32 = 35862;
+pub const GL_UNSIGNED_NORMALIZED: u32 = 35863;
+pub const GL_UNSIGNED_NORMALIZED_ARB: u32 = 35863;
+pub const GL_UNSIGNED_NORMALIZED_EXT: u32 = 35863;
+pub const GL_TEXTURE_1D_ARRAY: u32 = 35864;
+pub const GL_TEXTURE_1D_ARRAY_EXT: u32 = 35864;
+pub const GL_PROXY_TEXTURE_1D_ARRAY: u32 = 35865;
+pub const GL_PROXY_TEXTURE_1D_ARRAY_EXT: u32 = 35865;
+pub const GL_TEXTURE_2D_ARRAY: u32 = 35866;
+pub const GL_TEXTURE_2D_ARRAY_EXT: u32 = 35866;
+pub const GL_PROXY_TEXTURE_2D_ARRAY: u32 = 35867;
+pub const GL_PROXY_TEXTURE_2D_ARRAY_EXT: u32 = 35867;
+pub const GL_TEXTURE_BINDING_1D_ARRAY: u32 = 35868;
+pub const GL_TEXTURE_BINDING_1D_ARRAY_EXT: u32 = 35868;
+pub const GL_TEXTURE_BINDING_2D_ARRAY: u32 = 35869;
+pub const GL_TEXTURE_BINDING_2D_ARRAY_EXT: u32 = 35869;
+pub const GL_GEOMETRY_PROGRAM_NV: u32 = 35878;
+pub const GL_MAX_PROGRAM_OUTPUT_VERTICES_NV: u32 = 35879;
+pub const GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV: u32 = 35880;
+pub const GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: u32 = 35881;
+pub const GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB: u32 = 35881;
+pub const GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT: u32 = 35881;
+pub const GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_OES: u32 = 35881;
+pub const GL_TEXTURE_BUFFER: u32 = 35882;
+pub const GL_TEXTURE_BUFFER_ARB: u32 = 35882;
+pub const GL_TEXTURE_BUFFER_BINDING: u32 = 35882;
+pub const GL_TEXTURE_BUFFER_BINDING_EXT: u32 = 35882;
+pub const GL_TEXTURE_BUFFER_BINDING_OES: u32 = 35882;
+pub const GL_TEXTURE_BUFFER_EXT: u32 = 35882;
+pub const GL_TEXTURE_BUFFER_OES: u32 = 35882;
+pub const GL_MAX_TEXTURE_BUFFER_SIZE: u32 = 35883;
+pub const GL_MAX_TEXTURE_BUFFER_SIZE_ARB: u32 = 35883;
+pub const GL_MAX_TEXTURE_BUFFER_SIZE_EXT: u32 = 35883;
+pub const GL_MAX_TEXTURE_BUFFER_SIZE_OES: u32 = 35883;
+pub const GL_TEXTURE_BINDING_BUFFER: u32 = 35884;
+pub const GL_TEXTURE_BINDING_BUFFER_ARB: u32 = 35884;
+pub const GL_TEXTURE_BINDING_BUFFER_EXT: u32 = 35884;
+pub const GL_TEXTURE_BINDING_BUFFER_OES: u32 = 35884;
+pub const GL_TEXTURE_BUFFER_DATA_STORE_BINDING: u32 = 35885;
+pub const GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB: u32 = 35885;
+pub const GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT: u32 = 35885;
+pub const GL_TEXTURE_BUFFER_DATA_STORE_BINDING_OES: u32 = 35885;
+pub const GL_TEXTURE_BUFFER_FORMAT_ARB: u32 = 35886;
+pub const GL_TEXTURE_BUFFER_FORMAT_EXT: u32 = 35886;
+pub const GL_ANY_SAMPLES_PASSED: u32 = 35887;
+pub const GL_ANY_SAMPLES_PASSED_EXT: u32 = 35887;
+pub const GL_SAMPLE_SHADING: u32 = 35894;
+pub const GL_SAMPLE_SHADING_ARB: u32 = 35894;
+pub const GL_SAMPLE_SHADING_OES: u32 = 35894;
+pub const GL_MIN_SAMPLE_SHADING_VALUE: u32 = 35895;
+pub const GL_MIN_SAMPLE_SHADING_VALUE_ARB: u32 = 35895;
+pub const GL_MIN_SAMPLE_SHADING_VALUE_OES: u32 = 35895;
+pub const GL_R11F_G11F_B10F: u32 = 35898;
+pub const GL_R11F_G11F_B10F_APPLE: u32 = 35898;
+pub const GL_R11F_G11F_B10F_EXT: u32 = 35898;
+pub const GL_UNSIGNED_INT_10F_11F_11F_REV: u32 = 35899;
+pub const GL_UNSIGNED_INT_10F_11F_11F_REV_APPLE: u32 = 35899;
+pub const GL_UNSIGNED_INT_10F_11F_11F_REV_EXT: u32 = 35899;
+pub const GL_RGBA_SIGNED_COMPONENTS_EXT: u32 = 35900;
+pub const GL_RGB9_E5: u32 = 35901;
+pub const GL_RGB9_E5_APPLE: u32 = 35901;
+pub const GL_RGB9_E5_EXT: u32 = 35901;
+pub const GL_UNSIGNED_INT_5_9_9_9_REV: u32 = 35902;
+pub const GL_UNSIGNED_INT_5_9_9_9_REV_APPLE: u32 = 35902;
+pub const GL_UNSIGNED_INT_5_9_9_9_REV_EXT: u32 = 35902;
+pub const GL_TEXTURE_SHARED_SIZE: u32 = 35903;
+pub const GL_TEXTURE_SHARED_SIZE_EXT: u32 = 35903;
+pub const GL_SRGB: u32 = 35904;
+pub const GL_SRGB_EXT: u32 = 35904;
+pub const GL_SRGB8: u32 = 35905;
+pub const GL_SRGB8_EXT: u32 = 35905;
+pub const GL_SRGB8_NV: u32 = 35905;
+pub const GL_SRGB_ALPHA: u32 = 35906;
+pub const GL_SRGB_ALPHA_EXT: u32 = 35906;
+pub const GL_SRGB8_ALPHA8: u32 = 35907;
+pub const GL_SRGB8_ALPHA8_EXT: u32 = 35907;
+pub const GL_SLUMINANCE_ALPHA: u32 = 35908;
+pub const GL_SLUMINANCE_ALPHA_EXT: u32 = 35908;
+pub const GL_SLUMINANCE_ALPHA_NV: u32 = 35908;
+pub const GL_SLUMINANCE8_ALPHA8: u32 = 35909;
+pub const GL_SLUMINANCE8_ALPHA8_EXT: u32 = 35909;
+pub const GL_SLUMINANCE8_ALPHA8_NV: u32 = 35909;
+pub const GL_SLUMINANCE: u32 = 35910;
+pub const GL_SLUMINANCE_EXT: u32 = 35910;
+pub const GL_SLUMINANCE_NV: u32 = 35910;
+pub const GL_SLUMINANCE8: u32 = 35911;
+pub const GL_SLUMINANCE8_EXT: u32 = 35911;
+pub const GL_SLUMINANCE8_NV: u32 = 35911;
+pub const GL_COMPRESSED_SRGB: u32 = 35912;
+pub const GL_COMPRESSED_SRGB_EXT: u32 = 35912;
+pub const GL_COMPRESSED_SRGB_ALPHA: u32 = 35913;
+pub const GL_COMPRESSED_SRGB_ALPHA_EXT: u32 = 35913;
+pub const GL_COMPRESSED_SLUMINANCE: u32 = 35914;
+pub const GL_COMPRESSED_SLUMINANCE_EXT: u32 = 35914;
+pub const GL_COMPRESSED_SLUMINANCE_ALPHA: u32 = 35915;
+pub const GL_COMPRESSED_SLUMINANCE_ALPHA_EXT: u32 = 35915;
+pub const GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: u32 = 35916;
+pub const GL_COMPRESSED_SRGB_S3TC_DXT1_NV: u32 = 35916;
+pub const GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: u32 = 35917;
+pub const GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV: u32 = 35917;
+pub const GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: u32 = 35918;
+pub const GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV: u32 = 35918;
+pub const GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: u32 = 35919;
+pub const GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV: u32 = 35919;
+pub const GL_COMPRESSED_LUMINANCE_LATC1_EXT: u32 = 35952;
+pub const GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: u32 = 35953;
+pub const GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: u32 = 35954;
+pub const GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: u32 = 35955;
+pub const GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV: u32 = 35956;
+pub const GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV: u32 = 35957;
+pub const GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: u32 = 35958;
+pub const GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT: u32 = 35958;
+pub const GL_BACK_PRIMARY_COLOR_NV: u32 = 35959;
+pub const GL_BACK_SECONDARY_COLOR_NV: u32 = 35960;
+pub const GL_TEXTURE_COORD_NV: u32 = 35961;
+pub const GL_CLIP_DISTANCE_NV: u32 = 35962;
+pub const GL_VERTEX_ID_NV: u32 = 35963;
+pub const GL_PRIMITIVE_ID_NV: u32 = 35964;
+pub const GL_GENERIC_ATTRIB_NV: u32 = 35965;
+pub const GL_TRANSFORM_FEEDBACK_ATTRIBS_NV: u32 = 35966;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_MODE: u32 = 35967;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT: u32 = 35967;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV: u32 = 35967;
+pub const GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: u32 = 35968;
+pub const GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT: u32 = 35968;
+pub const GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV: u32 = 35968;
+pub const GL_ACTIVE_VARYINGS_NV: u32 = 35969;
+pub const GL_ACTIVE_VARYING_MAX_LENGTH_NV: u32 = 35970;
+pub const GL_TRANSFORM_FEEDBACK_VARYINGS: u32 = 35971;
+pub const GL_TRANSFORM_FEEDBACK_VARYINGS_EXT: u32 = 35971;
+pub const GL_TRANSFORM_FEEDBACK_VARYINGS_NV: u32 = 35971;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_START: u32 = 35972;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT: u32 = 35972;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_START_NV: u32 = 35972;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: u32 = 35973;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT: u32 = 35973;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV: u32 = 35973;
+pub const GL_TRANSFORM_FEEDBACK_RECORD_NV: u32 = 35974;
+pub const GL_PRIMITIVES_GENERATED: u32 = 35975;
+pub const GL_PRIMITIVES_GENERATED_EXT: u32 = 35975;
+pub const GL_PRIMITIVES_GENERATED_NV: u32 = 35975;
+pub const GL_PRIMITIVES_GENERATED_OES: u32 = 35975;
+pub const GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: u32 = 35976;
+pub const GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT: u32 = 35976;
+pub const GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV: u32 = 35976;
+pub const GL_RASTERIZER_DISCARD: u32 = 35977;
+pub const GL_RASTERIZER_DISCARD_EXT: u32 = 35977;
+pub const GL_RASTERIZER_DISCARD_NV: u32 = 35977;
+pub const GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: u32 = 35978;
+pub const GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT: u32 = 35978;
+pub const GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV: u32 = 35978;
+pub const GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: u32 = 35979;
+pub const GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT: u32 = 35979;
+pub const GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV: u32 = 35979;
+pub const GL_INTERLEAVED_ATTRIBS: u32 = 35980;
+pub const GL_INTERLEAVED_ATTRIBS_EXT: u32 = 35980;
+pub const GL_INTERLEAVED_ATTRIBS_NV: u32 = 35980;
+pub const GL_SEPARATE_ATTRIBS: u32 = 35981;
+pub const GL_SEPARATE_ATTRIBS_EXT: u32 = 35981;
+pub const GL_SEPARATE_ATTRIBS_NV: u32 = 35981;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER: u32 = 35982;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_EXT: u32 = 35982;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_NV: u32 = 35982;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: u32 = 35983;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT: u32 = 35983;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV: u32 = 35983;
+pub const GL_ATC_RGB_AMD: u32 = 35986;
+pub const GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: u32 = 35987;
+pub const GL_POINT_SPRITE_COORD_ORIGIN: u32 = 36000;
+pub const GL_LOWER_LEFT: u32 = 36001;
+pub const GL_UPPER_LEFT: u32 = 36002;
+pub const GL_STENCIL_BACK_REF: u32 = 36003;
+pub const GL_STENCIL_BACK_VALUE_MASK: u32 = 36004;
+pub const GL_STENCIL_BACK_WRITEMASK: u32 = 36005;
+pub const GL_DRAW_FRAMEBUFFER_BINDING: u32 = 36006;
+pub const GL_DRAW_FRAMEBUFFER_BINDING_ANGLE: u32 = 36006;
+pub const GL_DRAW_FRAMEBUFFER_BINDING_APPLE: u32 = 36006;
+pub const GL_DRAW_FRAMEBUFFER_BINDING_EXT: u32 = 36006;
+pub const GL_DRAW_FRAMEBUFFER_BINDING_NV: u32 = 36006;
+pub const GL_FRAMEBUFFER_BINDING: u32 = 36006;
+pub const GL_FRAMEBUFFER_BINDING_ANGLE: u32 = 36006;
+pub const GL_FRAMEBUFFER_BINDING_EXT: u32 = 36006;
+pub const GL_FRAMEBUFFER_BINDING_OES: u32 = 36006;
+pub const GL_RENDERBUFFER_BINDING: u32 = 36007;
+pub const GL_RENDERBUFFER_BINDING_ANGLE: u32 = 36007;
+pub const GL_RENDERBUFFER_BINDING_EXT: u32 = 36007;
+pub const GL_RENDERBUFFER_BINDING_OES: u32 = 36007;
+pub const GL_READ_FRAMEBUFFER: u32 = 36008;
+pub const GL_READ_FRAMEBUFFER_ANGLE: u32 = 36008;
+pub const GL_READ_FRAMEBUFFER_APPLE: u32 = 36008;
+pub const GL_READ_FRAMEBUFFER_EXT: u32 = 36008;
+pub const GL_READ_FRAMEBUFFER_NV: u32 = 36008;
+pub const GL_DRAW_FRAMEBUFFER: u32 = 36009;
+pub const GL_DRAW_FRAMEBUFFER_ANGLE: u32 = 36009;
+pub const GL_DRAW_FRAMEBUFFER_APPLE: u32 = 36009;
+pub const GL_DRAW_FRAMEBUFFER_EXT: u32 = 36009;
+pub const GL_DRAW_FRAMEBUFFER_NV: u32 = 36009;
+pub const GL_READ_FRAMEBUFFER_BINDING: u32 = 36010;
+pub const GL_READ_FRAMEBUFFER_BINDING_ANGLE: u32 = 36010;
+pub const GL_READ_FRAMEBUFFER_BINDING_APPLE: u32 = 36010;
+pub const GL_READ_FRAMEBUFFER_BINDING_EXT: u32 = 36010;
+pub const GL_READ_FRAMEBUFFER_BINDING_NV: u32 = 36010;
+pub const GL_RENDERBUFFER_COVERAGE_SAMPLES_NV: u32 = 36011;
+pub const GL_RENDERBUFFER_SAMPLES: u32 = 36011;
+pub const GL_RENDERBUFFER_SAMPLES_ANGLE: u32 = 36011;
+pub const GL_RENDERBUFFER_SAMPLES_APPLE: u32 = 36011;
+pub const GL_RENDERBUFFER_SAMPLES_EXT: u32 = 36011;
+pub const GL_RENDERBUFFER_SAMPLES_NV: u32 = 36011;
+pub const GL_DEPTH_COMPONENT32F: u32 = 36012;
+pub const GL_DEPTH32F_STENCIL8: u32 = 36013;
+pub const GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: u32 = 36048;
+pub const GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: u32 = 36048;
+pub const GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES: u32 = 36048;
+pub const GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: u32 = 36049;
+pub const GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT: u32 = 36049;
+pub const GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES: u32 = 36049;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: u32 = 36050;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT: u32 = 36050;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES: u32 = 36050;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: u32 = 36051;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT: u32 = 36051;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES: u32 = 36051;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT: u32 = 36052;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES: u32 = 36052;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: u32 = 36052;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT: u32 = 36052;
+pub const GL_FRAMEBUFFER_COMPLETE: u32 = 36053;
+pub const GL_FRAMEBUFFER_COMPLETE_EXT: u32 = 36053;
+pub const GL_FRAMEBUFFER_COMPLETE_OES: u32 = 36053;
+pub const GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: u32 = 36054;
+pub const GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: u32 = 36054;
+pub const GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES: u32 = 36054;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: u32 = 36055;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: u32 = 36055;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES: u32 = 36055;
+pub const GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: u32 = 36057;
+pub const GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: u32 = 36057;
+pub const GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES: u32 = 36057;
+pub const GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: u32 = 36058;
+pub const GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES: u32 = 36058;
+pub const GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: u32 = 36059;
+pub const GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: u32 = 36059;
+pub const GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES: u32 = 36059;
+pub const GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: u32 = 36060;
+pub const GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: u32 = 36060;
+pub const GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES: u32 = 36060;
+pub const GL_FRAMEBUFFER_UNSUPPORTED: u32 = 36061;
+pub const GL_FRAMEBUFFER_UNSUPPORTED_EXT: u32 = 36061;
+pub const GL_FRAMEBUFFER_UNSUPPORTED_OES: u32 = 36061;
+pub const GL_MAX_COLOR_ATTACHMENTS: u32 = 36063;
+pub const GL_MAX_COLOR_ATTACHMENTS_EXT: u32 = 36063;
+pub const GL_MAX_COLOR_ATTACHMENTS_NV: u32 = 36063;
+pub const GL_COLOR_ATTACHMENT0: u32 = 36064;
+pub const GL_COLOR_ATTACHMENT0_EXT: u32 = 36064;
+pub const GL_COLOR_ATTACHMENT0_NV: u32 = 36064;
+pub const GL_COLOR_ATTACHMENT0_OES: u32 = 36064;
+pub const GL_COLOR_ATTACHMENT1: u32 = 36065;
+pub const GL_COLOR_ATTACHMENT1_EXT: u32 = 36065;
+pub const GL_COLOR_ATTACHMENT1_NV: u32 = 36065;
+pub const GL_COLOR_ATTACHMENT2: u32 = 36066;
+pub const GL_COLOR_ATTACHMENT2_EXT: u32 = 36066;
+pub const GL_COLOR_ATTACHMENT2_NV: u32 = 36066;
+pub const GL_COLOR_ATTACHMENT3: u32 = 36067;
+pub const GL_COLOR_ATTACHMENT3_EXT: u32 = 36067;
+pub const GL_COLOR_ATTACHMENT3_NV: u32 = 36067;
+pub const GL_COLOR_ATTACHMENT4: u32 = 36068;
+pub const GL_COLOR_ATTACHMENT4_EXT: u32 = 36068;
+pub const GL_COLOR_ATTACHMENT4_NV: u32 = 36068;
+pub const GL_COLOR_ATTACHMENT5: u32 = 36069;
+pub const GL_COLOR_ATTACHMENT5_EXT: u32 = 36069;
+pub const GL_COLOR_ATTACHMENT5_NV: u32 = 36069;
+pub const GL_COLOR_ATTACHMENT6: u32 = 36070;
+pub const GL_COLOR_ATTACHMENT6_EXT: u32 = 36070;
+pub const GL_COLOR_ATTACHMENT6_NV: u32 = 36070;
+pub const GL_COLOR_ATTACHMENT7: u32 = 36071;
+pub const GL_COLOR_ATTACHMENT7_EXT: u32 = 36071;
+pub const GL_COLOR_ATTACHMENT7_NV: u32 = 36071;
+pub const GL_COLOR_ATTACHMENT8: u32 = 36072;
+pub const GL_COLOR_ATTACHMENT8_EXT: u32 = 36072;
+pub const GL_COLOR_ATTACHMENT8_NV: u32 = 36072;
+pub const GL_COLOR_ATTACHMENT9: u32 = 36073;
+pub const GL_COLOR_ATTACHMENT9_EXT: u32 = 36073;
+pub const GL_COLOR_ATTACHMENT9_NV: u32 = 36073;
+pub const GL_COLOR_ATTACHMENT10: u32 = 36074;
+pub const GL_COLOR_ATTACHMENT10_EXT: u32 = 36074;
+pub const GL_COLOR_ATTACHMENT10_NV: u32 = 36074;
+pub const GL_COLOR_ATTACHMENT11: u32 = 36075;
+pub const GL_COLOR_ATTACHMENT11_EXT: u32 = 36075;
+pub const GL_COLOR_ATTACHMENT11_NV: u32 = 36075;
+pub const GL_COLOR_ATTACHMENT12: u32 = 36076;
+pub const GL_COLOR_ATTACHMENT12_EXT: u32 = 36076;
+pub const GL_COLOR_ATTACHMENT12_NV: u32 = 36076;
+pub const GL_COLOR_ATTACHMENT13: u32 = 36077;
+pub const GL_COLOR_ATTACHMENT13_EXT: u32 = 36077;
+pub const GL_COLOR_ATTACHMENT13_NV: u32 = 36077;
+pub const GL_COLOR_ATTACHMENT14: u32 = 36078;
+pub const GL_COLOR_ATTACHMENT14_EXT: u32 = 36078;
+pub const GL_COLOR_ATTACHMENT14_NV: u32 = 36078;
+pub const GL_COLOR_ATTACHMENT15: u32 = 36079;
+pub const GL_COLOR_ATTACHMENT15_EXT: u32 = 36079;
+pub const GL_COLOR_ATTACHMENT15_NV: u32 = 36079;
+pub const GL_COLOR_ATTACHMENT16: u32 = 36080;
+pub const GL_COLOR_ATTACHMENT17: u32 = 36081;
+pub const GL_COLOR_ATTACHMENT18: u32 = 36082;
+pub const GL_COLOR_ATTACHMENT19: u32 = 36083;
+pub const GL_COLOR_ATTACHMENT20: u32 = 36084;
+pub const GL_COLOR_ATTACHMENT21: u32 = 36085;
+pub const GL_COLOR_ATTACHMENT22: u32 = 36086;
+pub const GL_COLOR_ATTACHMENT23: u32 = 36087;
+pub const GL_COLOR_ATTACHMENT24: u32 = 36088;
+pub const GL_COLOR_ATTACHMENT25: u32 = 36089;
+pub const GL_COLOR_ATTACHMENT26: u32 = 36090;
+pub const GL_COLOR_ATTACHMENT27: u32 = 36091;
+pub const GL_COLOR_ATTACHMENT28: u32 = 36092;
+pub const GL_COLOR_ATTACHMENT29: u32 = 36093;
+pub const GL_COLOR_ATTACHMENT30: u32 = 36094;
+pub const GL_COLOR_ATTACHMENT31: u32 = 36095;
+pub const GL_DEPTH_ATTACHMENT: u32 = 36096;
+pub const GL_DEPTH_ATTACHMENT_EXT: u32 = 36096;
+pub const GL_DEPTH_ATTACHMENT_OES: u32 = 36096;
+pub const GL_STENCIL_ATTACHMENT: u32 = 36128;
+pub const GL_STENCIL_ATTACHMENT_EXT: u32 = 36128;
+pub const GL_STENCIL_ATTACHMENT_OES: u32 = 36128;
+pub const GL_FRAMEBUFFER: u32 = 36160;
+pub const GL_FRAMEBUFFER_EXT: u32 = 36160;
+pub const GL_FRAMEBUFFER_OES: u32 = 36160;
+pub const GL_RENDERBUFFER: u32 = 36161;
+pub const GL_RENDERBUFFER_EXT: u32 = 36161;
+pub const GL_RENDERBUFFER_OES: u32 = 36161;
+pub const GL_RENDERBUFFER_WIDTH: u32 = 36162;
+pub const GL_RENDERBUFFER_WIDTH_EXT: u32 = 36162;
+pub const GL_RENDERBUFFER_WIDTH_OES: u32 = 36162;
+pub const GL_RENDERBUFFER_HEIGHT: u32 = 36163;
+pub const GL_RENDERBUFFER_HEIGHT_EXT: u32 = 36163;
+pub const GL_RENDERBUFFER_HEIGHT_OES: u32 = 36163;
+pub const GL_RENDERBUFFER_INTERNAL_FORMAT: u32 = 36164;
+pub const GL_RENDERBUFFER_INTERNAL_FORMAT_EXT: u32 = 36164;
+pub const GL_RENDERBUFFER_INTERNAL_FORMAT_OES: u32 = 36164;
+pub const GL_STENCIL_INDEX1: u32 = 36166;
+pub const GL_STENCIL_INDEX1_EXT: u32 = 36166;
+pub const GL_STENCIL_INDEX1_OES: u32 = 36166;
+pub const GL_STENCIL_INDEX4: u32 = 36167;
+pub const GL_STENCIL_INDEX4_EXT: u32 = 36167;
+pub const GL_STENCIL_INDEX4_OES: u32 = 36167;
+pub const GL_STENCIL_INDEX8: u32 = 36168;
+pub const GL_STENCIL_INDEX8_EXT: u32 = 36168;
+pub const GL_STENCIL_INDEX8_OES: u32 = 36168;
+pub const GL_STENCIL_INDEX16: u32 = 36169;
+pub const GL_STENCIL_INDEX16_EXT: u32 = 36169;
+pub const GL_RENDERBUFFER_RED_SIZE: u32 = 36176;
+pub const GL_RENDERBUFFER_RED_SIZE_EXT: u32 = 36176;
+pub const GL_RENDERBUFFER_RED_SIZE_OES: u32 = 36176;
+pub const GL_RENDERBUFFER_GREEN_SIZE: u32 = 36177;
+pub const GL_RENDERBUFFER_GREEN_SIZE_EXT: u32 = 36177;
+pub const GL_RENDERBUFFER_GREEN_SIZE_OES: u32 = 36177;
+pub const GL_RENDERBUFFER_BLUE_SIZE: u32 = 36178;
+pub const GL_RENDERBUFFER_BLUE_SIZE_EXT: u32 = 36178;
+pub const GL_RENDERBUFFER_BLUE_SIZE_OES: u32 = 36178;
+pub const GL_RENDERBUFFER_ALPHA_SIZE: u32 = 36179;
+pub const GL_RENDERBUFFER_ALPHA_SIZE_EXT: u32 = 36179;
+pub const GL_RENDERBUFFER_ALPHA_SIZE_OES: u32 = 36179;
+pub const GL_RENDERBUFFER_DEPTH_SIZE: u32 = 36180;
+pub const GL_RENDERBUFFER_DEPTH_SIZE_EXT: u32 = 36180;
+pub const GL_RENDERBUFFER_DEPTH_SIZE_OES: u32 = 36180;
+pub const GL_RENDERBUFFER_STENCIL_SIZE: u32 = 36181;
+pub const GL_RENDERBUFFER_STENCIL_SIZE_EXT: u32 = 36181;
+pub const GL_RENDERBUFFER_STENCIL_SIZE_OES: u32 = 36181;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: u32 = 36182;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE: u32 = 36182;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_APPLE: u32 = 36182;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: u32 = 36182;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_NV: u32 = 36182;
+pub const GL_MAX_SAMPLES: u32 = 36183;
+pub const GL_MAX_SAMPLES_ANGLE: u32 = 36183;
+pub const GL_MAX_SAMPLES_APPLE: u32 = 36183;
+pub const GL_MAX_SAMPLES_EXT: u32 = 36183;
+pub const GL_MAX_SAMPLES_NV: u32 = 36183;
+pub const GL_TEXTURE_GEN_STR_OES: u32 = 36192;
+pub const GL_HALF_FLOAT_OES: u32 = 36193;
+pub const GL_RGB565: u32 = 36194;
+pub const GL_RGB565_OES: u32 = 36194;
+pub const GL_ETC1_RGB8_OES: u32 = 36196;
+pub const GL_TEXTURE_EXTERNAL_OES: u32 = 36197;
+pub const GL_SAMPLER_EXTERNAL_OES: u32 = 36198;
+pub const GL_TEXTURE_BINDING_EXTERNAL_OES: u32 = 36199;
+pub const GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES: u32 = 36200;
+pub const GL_PRIMITIVE_RESTART_FIXED_INDEX: u32 = 36201;
+pub const GL_ANY_SAMPLES_PASSED_CONSERVATIVE: u32 = 36202;
+pub const GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT: u32 = 36202;
+pub const GL_MAX_ELEMENT_INDEX: u32 = 36203;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT: u32 = 36204;
+pub const GL_RGBA32UI: u32 = 36208;
+pub const GL_RGBA32UI_EXT: u32 = 36208;
+pub const GL_RGB32UI: u32 = 36209;
+pub const GL_RGB32UI_EXT: u32 = 36209;
+pub const GL_ALPHA32UI_EXT: u32 = 36210;
+pub const GL_INTENSITY32UI_EXT: u32 = 36211;
+pub const GL_LUMINANCE32UI_EXT: u32 = 36212;
+pub const GL_LUMINANCE_ALPHA32UI_EXT: u32 = 36213;
+pub const GL_RGBA16UI: u32 = 36214;
+pub const GL_RGBA16UI_EXT: u32 = 36214;
+pub const GL_RGB16UI: u32 = 36215;
+pub const GL_RGB16UI_EXT: u32 = 36215;
+pub const GL_ALPHA16UI_EXT: u32 = 36216;
+pub const GL_INTENSITY16UI_EXT: u32 = 36217;
+pub const GL_LUMINANCE16UI_EXT: u32 = 36218;
+pub const GL_LUMINANCE_ALPHA16UI_EXT: u32 = 36219;
+pub const GL_RGBA8UI: u32 = 36220;
+pub const GL_RGBA8UI_EXT: u32 = 36220;
+pub const GL_RGB8UI: u32 = 36221;
+pub const GL_RGB8UI_EXT: u32 = 36221;
+pub const GL_ALPHA8UI_EXT: u32 = 36222;
+pub const GL_INTENSITY8UI_EXT: u32 = 36223;
+pub const GL_LUMINANCE8UI_EXT: u32 = 36224;
+pub const GL_LUMINANCE_ALPHA8UI_EXT: u32 = 36225;
+pub const GL_RGBA32I: u32 = 36226;
+pub const GL_RGBA32I_EXT: u32 = 36226;
+pub const GL_RGB32I: u32 = 36227;
+pub const GL_RGB32I_EXT: u32 = 36227;
+pub const GL_ALPHA32I_EXT: u32 = 36228;
+pub const GL_INTENSITY32I_EXT: u32 = 36229;
+pub const GL_LUMINANCE32I_EXT: u32 = 36230;
+pub const GL_LUMINANCE_ALPHA32I_EXT: u32 = 36231;
+pub const GL_RGBA16I: u32 = 36232;
+pub const GL_RGBA16I_EXT: u32 = 36232;
+pub const GL_RGB16I: u32 = 36233;
+pub const GL_RGB16I_EXT: u32 = 36233;
+pub const GL_ALPHA16I_EXT: u32 = 36234;
+pub const GL_INTENSITY16I_EXT: u32 = 36235;
+pub const GL_LUMINANCE16I_EXT: u32 = 36236;
+pub const GL_LUMINANCE_ALPHA16I_EXT: u32 = 36237;
+pub const GL_RGBA8I: u32 = 36238;
+pub const GL_RGBA8I_EXT: u32 = 36238;
+pub const GL_RGB8I: u32 = 36239;
+pub const GL_RGB8I_EXT: u32 = 36239;
+pub const GL_ALPHA8I_EXT: u32 = 36240;
+pub const GL_INTENSITY8I_EXT: u32 = 36241;
+pub const GL_LUMINANCE8I_EXT: u32 = 36242;
+pub const GL_LUMINANCE_ALPHA8I_EXT: u32 = 36243;
+pub const GL_RED_INTEGER: u32 = 36244;
+pub const GL_RED_INTEGER_EXT: u32 = 36244;
+pub const GL_GREEN_INTEGER: u32 = 36245;
+pub const GL_GREEN_INTEGER_EXT: u32 = 36245;
+pub const GL_BLUE_INTEGER: u32 = 36246;
+pub const GL_BLUE_INTEGER_EXT: u32 = 36246;
+pub const GL_ALPHA_INTEGER: u32 = 36247;
+pub const GL_ALPHA_INTEGER_EXT: u32 = 36247;
+pub const GL_RGB_INTEGER: u32 = 36248;
+pub const GL_RGB_INTEGER_EXT: u32 = 36248;
+pub const GL_RGBA_INTEGER: u32 = 36249;
+pub const GL_RGBA_INTEGER_EXT: u32 = 36249;
+pub const GL_BGR_INTEGER: u32 = 36250;
+pub const GL_BGR_INTEGER_EXT: u32 = 36250;
+pub const GL_BGRA_INTEGER: u32 = 36251;
+pub const GL_BGRA_INTEGER_EXT: u32 = 36251;
+pub const GL_LUMINANCE_INTEGER_EXT: u32 = 36252;
+pub const GL_LUMINANCE_ALPHA_INTEGER_EXT: u32 = 36253;
+pub const GL_RGBA_INTEGER_MODE_EXT: u32 = 36254;
+pub const GL_INT_2_10_10_10_REV: u32 = 36255;
+pub const GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV: u32 = 36256;
+pub const GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV: u32 = 36257;
+pub const GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV: u32 = 36258;
+pub const GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV: u32 = 36259;
+pub const GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV: u32 = 36260;
+pub const GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV: u32 = 36261;
+pub const GL_MAX_PROGRAM_GENERIC_RESULTS_NV: u32 = 36262;
+pub const GL_FRAMEBUFFER_ATTACHMENT_LAYERED: u32 = 36263;
+pub const GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB: u32 = 36263;
+pub const GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT: u32 = 36263;
+pub const GL_FRAMEBUFFER_ATTACHMENT_LAYERED_OES: u32 = 36263;
+pub const GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: u32 = 36264;
+pub const GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB: u32 = 36264;
+pub const GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT: u32 = 36264;
+pub const GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_OES: u32 = 36264;
+pub const GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB: u32 = 36265;
+pub const GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT: u32 = 36265;
+pub const GL_LAYER_NV: u32 = 36266;
+pub const GL_DEPTH_COMPONENT32F_NV: u32 = 36267;
+pub const GL_DEPTH32F_STENCIL8_NV: u32 = 36268;
+pub const GL_FLOAT_32_UNSIGNED_INT_24_8_REV: u32 = 36269;
+pub const GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV: u32 = 36269;
+pub const GL_SHADER_INCLUDE_ARB: u32 = 36270;
+pub const GL_DEPTH_BUFFER_FLOAT_MODE_NV: u32 = 36271;
+pub const GL_FRAMEBUFFER_SRGB: u32 = 36281;
+pub const GL_FRAMEBUFFER_SRGB_EXT: u32 = 36281;
+pub const GL_FRAMEBUFFER_SRGB_CAPABLE_EXT: u32 = 36282;
+pub const GL_COMPRESSED_RED_RGTC1: u32 = 36283;
+pub const GL_COMPRESSED_RED_RGTC1_EXT: u32 = 36283;
+pub const GL_COMPRESSED_SIGNED_RED_RGTC1: u32 = 36284;
+pub const GL_COMPRESSED_SIGNED_RED_RGTC1_EXT: u32 = 36284;
+pub const GL_COMPRESSED_RED_GREEN_RGTC2_EXT: u32 = 36285;
+pub const GL_COMPRESSED_RG_RGTC2: u32 = 36285;
+pub const GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: u32 = 36286;
+pub const GL_COMPRESSED_SIGNED_RG_RGTC2: u32 = 36286;
+pub const GL_SAMPLER_1D_ARRAY: u32 = 36288;
+pub const GL_SAMPLER_1D_ARRAY_EXT: u32 = 36288;
+pub const GL_SAMPLER_2D_ARRAY: u32 = 36289;
+pub const GL_SAMPLER_2D_ARRAY_EXT: u32 = 36289;
+pub const GL_SAMPLER_BUFFER: u32 = 36290;
+pub const GL_SAMPLER_BUFFER_EXT: u32 = 36290;
+pub const GL_SAMPLER_BUFFER_OES: u32 = 36290;
+pub const GL_SAMPLER_1D_ARRAY_SHADOW: u32 = 36291;
+pub const GL_SAMPLER_1D_ARRAY_SHADOW_EXT: u32 = 36291;
+pub const GL_SAMPLER_2D_ARRAY_SHADOW: u32 = 36292;
+pub const GL_SAMPLER_2D_ARRAY_SHADOW_EXT: u32 = 36292;
+pub const GL_SAMPLER_2D_ARRAY_SHADOW_NV: u32 = 36292;
+pub const GL_SAMPLER_CUBE_SHADOW: u32 = 36293;
+pub const GL_SAMPLER_CUBE_SHADOW_EXT: u32 = 36293;
+pub const GL_SAMPLER_CUBE_SHADOW_NV: u32 = 36293;
+pub const GL_UNSIGNED_INT_VEC2: u32 = 36294;
+pub const GL_UNSIGNED_INT_VEC2_EXT: u32 = 36294;
+pub const GL_UNSIGNED_INT_VEC3: u32 = 36295;
+pub const GL_UNSIGNED_INT_VEC3_EXT: u32 = 36295;
+pub const GL_UNSIGNED_INT_VEC4: u32 = 36296;
+pub const GL_UNSIGNED_INT_VEC4_EXT: u32 = 36296;
+pub const GL_INT_SAMPLER_1D: u32 = 36297;
+pub const GL_INT_SAMPLER_1D_EXT: u32 = 36297;
+pub const GL_INT_SAMPLER_2D: u32 = 36298;
+pub const GL_INT_SAMPLER_2D_EXT: u32 = 36298;
+pub const GL_INT_SAMPLER_3D: u32 = 36299;
+pub const GL_INT_SAMPLER_3D_EXT: u32 = 36299;
+pub const GL_INT_SAMPLER_CUBE: u32 = 36300;
+pub const GL_INT_SAMPLER_CUBE_EXT: u32 = 36300;
+pub const GL_INT_SAMPLER_2D_RECT: u32 = 36301;
+pub const GL_INT_SAMPLER_2D_RECT_EXT: u32 = 36301;
+pub const GL_INT_SAMPLER_1D_ARRAY: u32 = 36302;
+pub const GL_INT_SAMPLER_1D_ARRAY_EXT: u32 = 36302;
+pub const GL_INT_SAMPLER_2D_ARRAY: u32 = 36303;
+pub const GL_INT_SAMPLER_2D_ARRAY_EXT: u32 = 36303;
+pub const GL_INT_SAMPLER_BUFFER: u32 = 36304;
+pub const GL_INT_SAMPLER_BUFFER_EXT: u32 = 36304;
+pub const GL_INT_SAMPLER_BUFFER_OES: u32 = 36304;
+pub const GL_UNSIGNED_INT_SAMPLER_1D: u32 = 36305;
+pub const GL_UNSIGNED_INT_SAMPLER_1D_EXT: u32 = 36305;
+pub const GL_UNSIGNED_INT_SAMPLER_2D: u32 = 36306;
+pub const GL_UNSIGNED_INT_SAMPLER_2D_EXT: u32 = 36306;
+pub const GL_UNSIGNED_INT_SAMPLER_3D: u32 = 36307;
+pub const GL_UNSIGNED_INT_SAMPLER_3D_EXT: u32 = 36307;
+pub const GL_UNSIGNED_INT_SAMPLER_CUBE: u32 = 36308;
+pub const GL_UNSIGNED_INT_SAMPLER_CUBE_EXT: u32 = 36308;
+pub const GL_UNSIGNED_INT_SAMPLER_2D_RECT: u32 = 36309;
+pub const GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT: u32 = 36309;
+pub const GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: u32 = 36310;
+pub const GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT: u32 = 36310;
+pub const GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: u32 = 36311;
+pub const GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT: u32 = 36311;
+pub const GL_UNSIGNED_INT_SAMPLER_BUFFER: u32 = 36312;
+pub const GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT: u32 = 36312;
+pub const GL_UNSIGNED_INT_SAMPLER_BUFFER_OES: u32 = 36312;
+pub const GL_GEOMETRY_SHADER: u32 = 36313;
+pub const GL_GEOMETRY_SHADER_ARB: u32 = 36313;
+pub const GL_GEOMETRY_SHADER_EXT: u32 = 36313;
+pub const GL_GEOMETRY_SHADER_OES: u32 = 36313;
+pub const GL_GEOMETRY_VERTICES_OUT_ARB: u32 = 36314;
+pub const GL_GEOMETRY_VERTICES_OUT_EXT: u32 = 36314;
+pub const GL_GEOMETRY_INPUT_TYPE_ARB: u32 = 36315;
+pub const GL_GEOMETRY_INPUT_TYPE_EXT: u32 = 36315;
+pub const GL_GEOMETRY_OUTPUT_TYPE_ARB: u32 = 36316;
+pub const GL_GEOMETRY_OUTPUT_TYPE_EXT: u32 = 36316;
+pub const GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB: u32 = 36317;
+pub const GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT: u32 = 36317;
+pub const GL_MAX_VERTEX_VARYING_COMPONENTS_ARB: u32 = 36318;
+pub const GL_MAX_VERTEX_VARYING_COMPONENTS_EXT: u32 = 36318;
+pub const GL_MAX_GEOMETRY_UNIFORM_COMPONENTS: u32 = 36319;
+pub const GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB: u32 = 36319;
+pub const GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT: u32 = 36319;
+pub const GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_OES: u32 = 36319;
+pub const GL_MAX_GEOMETRY_OUTPUT_VERTICES: u32 = 36320;
+pub const GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB: u32 = 36320;
+pub const GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT: u32 = 36320;
+pub const GL_MAX_GEOMETRY_OUTPUT_VERTICES_OES: u32 = 36320;
+pub const GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS: u32 = 36321;
+pub const GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB: u32 = 36321;
+pub const GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT: u32 = 36321;
+pub const GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_OES: u32 = 36321;
+pub const GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT: u32 = 36322;
+pub const GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT: u32 = 36323;
+pub const GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT: u32 = 36324;
+pub const GL_ACTIVE_SUBROUTINES: u32 = 36325;
+pub const GL_ACTIVE_SUBROUTINE_UNIFORMS: u32 = 36326;
+pub const GL_MAX_SUBROUTINES: u32 = 36327;
+pub const GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS: u32 = 36328;
+pub const GL_NAMED_STRING_LENGTH_ARB: u32 = 36329;
+pub const GL_NAMED_STRING_TYPE_ARB: u32 = 36330;
+pub const GL_MAX_BINDABLE_UNIFORM_SIZE_EXT: u32 = 36333;
+pub const GL_UNIFORM_BUFFER_EXT: u32 = 36334;
+pub const GL_UNIFORM_BUFFER_BINDING_EXT: u32 = 36335;
+pub const GL_LOW_FLOAT: u32 = 36336;
+pub const GL_MEDIUM_FLOAT: u32 = 36337;
+pub const GL_HIGH_FLOAT: u32 = 36338;
+pub const GL_LOW_INT: u32 = 36339;
+pub const GL_MEDIUM_INT: u32 = 36340;
+pub const GL_HIGH_INT: u32 = 36341;
+pub const GL_UNSIGNED_INT_10_10_10_2_OES: u32 = 36342;
+pub const GL_INT_10_10_10_2_OES: u32 = 36343;
+pub const GL_SHADER_BINARY_FORMATS: u32 = 36344;
+pub const GL_NUM_SHADER_BINARY_FORMATS: u32 = 36345;
+pub const GL_SHADER_COMPILER: u32 = 36346;
+pub const GL_MAX_VERTEX_UNIFORM_VECTORS: u32 = 36347;
+pub const GL_MAX_VARYING_VECTORS: u32 = 36348;
+pub const GL_MAX_FRAGMENT_UNIFORM_VECTORS: u32 = 36349;
+pub const GL_RENDERBUFFER_COLOR_SAMPLES_NV: u32 = 36368;
+pub const GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV: u32 = 36369;
+pub const GL_MULTISAMPLE_COVERAGE_MODES_NV: u32 = 36370;
+pub const GL_QUERY_WAIT: u32 = 36371;
+pub const GL_QUERY_WAIT_NV: u32 = 36371;
+pub const GL_QUERY_NO_WAIT: u32 = 36372;
+pub const GL_QUERY_NO_WAIT_NV: u32 = 36372;
+pub const GL_QUERY_BY_REGION_WAIT: u32 = 36373;
+pub const GL_QUERY_BY_REGION_WAIT_NV: u32 = 36373;
+pub const GL_QUERY_BY_REGION_NO_WAIT: u32 = 36374;
+pub const GL_QUERY_BY_REGION_NO_WAIT_NV: u32 = 36374;
+pub const GL_QUERY_WAIT_INVERTED: u32 = 36375;
+pub const GL_QUERY_NO_WAIT_INVERTED: u32 = 36376;
+pub const GL_QUERY_BY_REGION_WAIT_INVERTED: u32 = 36377;
+pub const GL_QUERY_BY_REGION_NO_WAIT_INVERTED: u32 = 36378;
+pub const GL_POLYGON_OFFSET_CLAMP_EXT: u32 = 36379;
+pub const GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS: u32 = 36382;
+pub const GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT: u32 = 36382;
+pub const GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_OES: u32 = 36382;
+pub const GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS: u32 = 36383;
+pub const GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT: u32 = 36383;
+pub const GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_OES: u32 = 36383;
+pub const GL_COLOR_SAMPLES_NV: u32 = 36384;
+pub const GL_TRANSFORM_FEEDBACK: u32 = 36386;
+pub const GL_TRANSFORM_FEEDBACK_NV: u32 = 36386;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED: u32 = 36387;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV: u32 = 36387;
+pub const GL_TRANSFORM_FEEDBACK_PAUSED: u32 = 36387;
+pub const GL_TRANSFORM_FEEDBACK_ACTIVE: u32 = 36388;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE: u32 = 36388;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV: u32 = 36388;
+pub const GL_TRANSFORM_FEEDBACK_BINDING: u32 = 36389;
+pub const GL_TRANSFORM_FEEDBACK_BINDING_NV: u32 = 36389;
+pub const GL_FRAME_NV: u32 = 36390;
+pub const GL_FIELDS_NV: u32 = 36391;
+pub const GL_CURRENT_TIME_NV: u32 = 36392;
+pub const GL_TIMESTAMP: u32 = 36392;
+pub const GL_TIMESTAMP_EXT: u32 = 36392;
+pub const GL_NUM_FILL_STREAMS_NV: u32 = 36393;
+pub const GL_PRESENT_TIME_NV: u32 = 36394;
+pub const GL_PRESENT_DURATION_NV: u32 = 36395;
+pub const GL_DEPTH_COMPONENT16_NONLINEAR_NV: u32 = 36396;
+pub const GL_PROGRAM_MATRIX_EXT: u32 = 36397;
+pub const GL_TRANSPOSE_PROGRAM_MATRIX_EXT: u32 = 36398;
+pub const GL_PROGRAM_MATRIX_STACK_DEPTH_EXT: u32 = 36399;
+pub const GL_TEXTURE_SWIZZLE_R: u32 = 36418;
+pub const GL_TEXTURE_SWIZZLE_R_EXT: u32 = 36418;
+pub const GL_TEXTURE_SWIZZLE_G: u32 = 36419;
+pub const GL_TEXTURE_SWIZZLE_G_EXT: u32 = 36419;
+pub const GL_TEXTURE_SWIZZLE_B: u32 = 36420;
+pub const GL_TEXTURE_SWIZZLE_B_EXT: u32 = 36420;
+pub const GL_TEXTURE_SWIZZLE_A: u32 = 36421;
+pub const GL_TEXTURE_SWIZZLE_A_EXT: u32 = 36421;
+pub const GL_TEXTURE_SWIZZLE_RGBA: u32 = 36422;
+pub const GL_TEXTURE_SWIZZLE_RGBA_EXT: u32 = 36422;
+pub const GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS: u32 = 36423;
+pub const GL_ACTIVE_SUBROUTINE_MAX_LENGTH: u32 = 36424;
+pub const GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH: u32 = 36425;
+pub const GL_NUM_COMPATIBLE_SUBROUTINES: u32 = 36426;
+pub const GL_COMPATIBLE_SUBROUTINES: u32 = 36427;
+pub const GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION: u32 = 36428;
+pub const GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT: u32 = 36428;
+pub const GL_FIRST_VERTEX_CONVENTION: u32 = 36429;
+pub const GL_FIRST_VERTEX_CONVENTION_EXT: u32 = 36429;
+pub const GL_FIRST_VERTEX_CONVENTION_OES: u32 = 36429;
+pub const GL_LAST_VERTEX_CONVENTION: u32 = 36430;
+pub const GL_LAST_VERTEX_CONVENTION_EXT: u32 = 36430;
+pub const GL_LAST_VERTEX_CONVENTION_OES: u32 = 36430;
+pub const GL_PROVOKING_VERTEX: u32 = 36431;
+pub const GL_PROVOKING_VERTEX_EXT: u32 = 36431;
+pub const GL_SAMPLE_LOCATION_ARB: u32 = 36432;
+pub const GL_SAMPLE_LOCATION_NV: u32 = 36432;
+pub const GL_SAMPLE_POSITION: u32 = 36432;
+pub const GL_SAMPLE_POSITION_NV: u32 = 36432;
+pub const GL_SAMPLE_MASK: u32 = 36433;
+pub const GL_SAMPLE_MASK_NV: u32 = 36433;
+pub const GL_SAMPLE_MASK_VALUE: u32 = 36434;
+pub const GL_SAMPLE_MASK_VALUE_NV: u32 = 36434;
+pub const GL_TEXTURE_BINDING_RENDERBUFFER_NV: u32 = 36435;
+pub const GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV: u32 = 36436;
+pub const GL_TEXTURE_RENDERBUFFER_NV: u32 = 36437;
+pub const GL_SAMPLER_RENDERBUFFER_NV: u32 = 36438;
+pub const GL_INT_SAMPLER_RENDERBUFFER_NV: u32 = 36439;
+pub const GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV: u32 = 36440;
+pub const GL_MAX_SAMPLE_MASK_WORDS: u32 = 36441;
+pub const GL_MAX_SAMPLE_MASK_WORDS_NV: u32 = 36441;
+pub const GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV: u32 = 36442;
+pub const GL_MAX_GEOMETRY_SHADER_INVOCATIONS: u32 = 36442;
+pub const GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT: u32 = 36442;
+pub const GL_MAX_GEOMETRY_SHADER_INVOCATIONS_OES: u32 = 36442;
+pub const GL_MIN_FRAGMENT_INTERPOLATION_OFFSET: u32 = 36443;
+pub const GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV: u32 = 36443;
+pub const GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES: u32 = 36443;
+pub const GL_MAX_FRAGMENT_INTERPOLATION_OFFSET: u32 = 36444;
+pub const GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV: u32 = 36444;
+pub const GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_OES: u32 = 36444;
+pub const GL_FRAGMENT_INTERPOLATION_OFFSET_BITS: u32 = 36445;
+pub const GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES: u32 = 36445;
+pub const GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV: u32 = 36445;
+pub const GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET: u32 = 36446;
+pub const GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB: u32 = 36446;
+pub const GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV: u32 = 36446;
+pub const GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET: u32 = 36447;
+pub const GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB: u32 = 36447;
+pub const GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV: u32 = 36447;
+pub const GL_MAX_TRANSFORM_FEEDBACK_BUFFERS: u32 = 36464;
+pub const GL_MAX_VERTEX_STREAMS: u32 = 36465;
+pub const GL_PATCH_VERTICES: u32 = 36466;
+pub const GL_PATCH_VERTICES_EXT: u32 = 36466;
+pub const GL_PATCH_VERTICES_OES: u32 = 36466;
+pub const GL_PATCH_DEFAULT_INNER_LEVEL: u32 = 36467;
+pub const GL_PATCH_DEFAULT_INNER_LEVEL_EXT: u32 = 36467;
+pub const GL_PATCH_DEFAULT_OUTER_LEVEL: u32 = 36468;
+pub const GL_PATCH_DEFAULT_OUTER_LEVEL_EXT: u32 = 36468;
+pub const GL_TESS_CONTROL_OUTPUT_VERTICES: u32 = 36469;
+pub const GL_TESS_CONTROL_OUTPUT_VERTICES_EXT: u32 = 36469;
+pub const GL_TESS_CONTROL_OUTPUT_VERTICES_OES: u32 = 36469;
+pub const GL_TESS_GEN_MODE: u32 = 36470;
+pub const GL_TESS_GEN_MODE_EXT: u32 = 36470;
+pub const GL_TESS_GEN_MODE_OES: u32 = 36470;
+pub const GL_TESS_GEN_SPACING: u32 = 36471;
+pub const GL_TESS_GEN_SPACING_EXT: u32 = 36471;
+pub const GL_TESS_GEN_SPACING_OES: u32 = 36471;
+pub const GL_TESS_GEN_VERTEX_ORDER: u32 = 36472;
+pub const GL_TESS_GEN_VERTEX_ORDER_EXT: u32 = 36472;
+pub const GL_TESS_GEN_VERTEX_ORDER_OES: u32 = 36472;
+pub const GL_TESS_GEN_POINT_MODE: u32 = 36473;
+pub const GL_TESS_GEN_POINT_MODE_EXT: u32 = 36473;
+pub const GL_TESS_GEN_POINT_MODE_OES: u32 = 36473;
+pub const GL_ISOLINES: u32 = 36474;
+pub const GL_ISOLINES_EXT: u32 = 36474;
+pub const GL_ISOLINES_OES: u32 = 36474;
+pub const GL_FRACTIONAL_ODD: u32 = 36475;
+pub const GL_FRACTIONAL_ODD_EXT: u32 = 36475;
+pub const GL_FRACTIONAL_ODD_OES: u32 = 36475;
+pub const GL_FRACTIONAL_EVEN: u32 = 36476;
+pub const GL_FRACTIONAL_EVEN_EXT: u32 = 36476;
+pub const GL_FRACTIONAL_EVEN_OES: u32 = 36476;
+pub const GL_MAX_PATCH_VERTICES: u32 = 36477;
+pub const GL_MAX_PATCH_VERTICES_EXT: u32 = 36477;
+pub const GL_MAX_PATCH_VERTICES_OES: u32 = 36477;
+pub const GL_MAX_TESS_GEN_LEVEL: u32 = 36478;
+pub const GL_MAX_TESS_GEN_LEVEL_EXT: u32 = 36478;
+pub const GL_MAX_TESS_GEN_LEVEL_OES: u32 = 36478;
+pub const GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS: u32 = 36479;
+pub const GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_EXT: u32 = 36479;
+pub const GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_OES: u32 = 36479;
+pub const GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS: u32 = 36480;
+pub const GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT: u32 = 36480;
+pub const GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_OES: u32 = 36480;
+pub const GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS: u32 = 36481;
+pub const GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_EXT: u32 = 36481;
+pub const GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_OES: u32 = 36481;
+pub const GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS: u32 = 36482;
+pub const GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_EXT: u32 = 36482;
+pub const GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_OES: u32 = 36482;
+pub const GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS: u32 = 36483;
+pub const GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT: u32 = 36483;
+pub const GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_OES: u32 = 36483;
+pub const GL_MAX_TESS_PATCH_COMPONENTS: u32 = 36484;
+pub const GL_MAX_TESS_PATCH_COMPONENTS_EXT: u32 = 36484;
+pub const GL_MAX_TESS_PATCH_COMPONENTS_OES: u32 = 36484;
+pub const GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS: u32 = 36485;
+pub const GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_EXT: u32 = 36485;
+pub const GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_OES: u32 = 36485;
+pub const GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS: u32 = 36486;
+pub const GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT: u32 = 36486;
+pub const GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_OES: u32 = 36486;
+pub const GL_TESS_EVALUATION_SHADER: u32 = 36487;
+pub const GL_TESS_EVALUATION_SHADER_EXT: u32 = 36487;
+pub const GL_TESS_EVALUATION_SHADER_OES: u32 = 36487;
+pub const GL_TESS_CONTROL_SHADER: u32 = 36488;
+pub const GL_TESS_CONTROL_SHADER_EXT: u32 = 36488;
+pub const GL_TESS_CONTROL_SHADER_OES: u32 = 36488;
+pub const GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS: u32 = 36489;
+pub const GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_EXT: u32 = 36489;
+pub const GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_OES: u32 = 36489;
+pub const GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS: u32 = 36490;
+pub const GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_EXT: u32 = 36490;
+pub const GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_OES: u32 = 36490;
+pub const GL_COMPRESSED_RGBA_BPTC_UNORM: u32 = 36492;
+pub const GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: u32 = 36492;
+pub const GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: u32 = 36493;
+pub const GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: u32 = 36493;
+pub const GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: u32 = 36494;
+pub const GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: u32 = 36494;
+pub const GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: u32 = 36495;
+pub const GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: u32 = 36495;
+pub const GL_COVERAGE_COMPONENT_NV: u32 = 36560;
+pub const GL_COVERAGE_COMPONENT4_NV: u32 = 36561;
+pub const GL_COVERAGE_ATTACHMENT_NV: u32 = 36562;
+pub const GL_COVERAGE_BUFFERS_NV: u32 = 36563;
+pub const GL_COVERAGE_SAMPLES_NV: u32 = 36564;
+pub const GL_COVERAGE_ALL_FRAGMENTS_NV: u32 = 36565;
+pub const GL_COVERAGE_EDGE_FRAGMENTS_NV: u32 = 36566;
+pub const GL_COVERAGE_AUTOMATIC_NV: u32 = 36567;
+pub const GL_INCLUSIVE_EXT: u32 = 36624;
+pub const GL_EXCLUSIVE_EXT: u32 = 36625;
+pub const GL_WINDOW_RECTANGLE_EXT: u32 = 36626;
+pub const GL_WINDOW_RECTANGLE_MODE_EXT: u32 = 36627;
+pub const GL_MAX_WINDOW_RECTANGLES_EXT: u32 = 36628;
+pub const GL_NUM_WINDOW_RECTANGLES_EXT: u32 = 36629;
+pub const GL_BUFFER_GPU_ADDRESS_NV: u32 = 36637;
+pub const GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV: u32 = 36638;
+pub const GL_ELEMENT_ARRAY_UNIFIED_NV: u32 = 36639;
+pub const GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV: u32 = 36640;
+pub const GL_VERTEX_ARRAY_ADDRESS_NV: u32 = 36641;
+pub const GL_NORMAL_ARRAY_ADDRESS_NV: u32 = 36642;
+pub const GL_COLOR_ARRAY_ADDRESS_NV: u32 = 36643;
+pub const GL_INDEX_ARRAY_ADDRESS_NV: u32 = 36644;
+pub const GL_TEXTURE_COORD_ARRAY_ADDRESS_NV: u32 = 36645;
+pub const GL_EDGE_FLAG_ARRAY_ADDRESS_NV: u32 = 36646;
+pub const GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV: u32 = 36647;
+pub const GL_FOG_COORD_ARRAY_ADDRESS_NV: u32 = 36648;
+pub const GL_ELEMENT_ARRAY_ADDRESS_NV: u32 = 36649;
+pub const GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV: u32 = 36650;
+pub const GL_VERTEX_ARRAY_LENGTH_NV: u32 = 36651;
+pub const GL_NORMAL_ARRAY_LENGTH_NV: u32 = 36652;
+pub const GL_COLOR_ARRAY_LENGTH_NV: u32 = 36653;
+pub const GL_INDEX_ARRAY_LENGTH_NV: u32 = 36654;
+pub const GL_TEXTURE_COORD_ARRAY_LENGTH_NV: u32 = 36655;
+pub const GL_EDGE_FLAG_ARRAY_LENGTH_NV: u32 = 36656;
+pub const GL_SECONDARY_COLOR_ARRAY_LENGTH_NV: u32 = 36657;
+pub const GL_FOG_COORD_ARRAY_LENGTH_NV: u32 = 36658;
+pub const GL_ELEMENT_ARRAY_LENGTH_NV: u32 = 36659;
+pub const GL_GPU_ADDRESS_NV: u32 = 36660;
+pub const GL_MAX_SHADER_BUFFER_ADDRESS_NV: u32 = 36661;
+pub const GL_COPY_READ_BUFFER: u32 = 36662;
+pub const GL_COPY_READ_BUFFER_BINDING: u32 = 36662;
+pub const GL_COPY_READ_BUFFER_NV: u32 = 36662;
+pub const GL_COPY_WRITE_BUFFER: u32 = 36663;
+pub const GL_COPY_WRITE_BUFFER_BINDING: u32 = 36663;
+pub const GL_COPY_WRITE_BUFFER_NV: u32 = 36663;
+pub const GL_MAX_IMAGE_UNITS: u32 = 36664;
+pub const GL_MAX_IMAGE_UNITS_EXT: u32 = 36664;
+pub const GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS: u32 = 36665;
+pub const GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT: u32 = 36665;
+pub const GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES: u32 = 36665;
+pub const GL_IMAGE_BINDING_NAME: u32 = 36666;
+pub const GL_IMAGE_BINDING_NAME_EXT: u32 = 36666;
+pub const GL_IMAGE_BINDING_LEVEL: u32 = 36667;
+pub const GL_IMAGE_BINDING_LEVEL_EXT: u32 = 36667;
+pub const GL_IMAGE_BINDING_LAYERED: u32 = 36668;
+pub const GL_IMAGE_BINDING_LAYERED_EXT: u32 = 36668;
+pub const GL_IMAGE_BINDING_LAYER: u32 = 36669;
+pub const GL_IMAGE_BINDING_LAYER_EXT: u32 = 36669;
+pub const GL_IMAGE_BINDING_ACCESS: u32 = 36670;
+pub const GL_IMAGE_BINDING_ACCESS_EXT: u32 = 36670;
+pub const GL_DRAW_INDIRECT_BUFFER: u32 = 36671;
+pub const GL_DRAW_INDIRECT_UNIFIED_NV: u32 = 36672;
+pub const GL_DRAW_INDIRECT_ADDRESS_NV: u32 = 36673;
+pub const GL_DRAW_INDIRECT_LENGTH_NV: u32 = 36674;
+pub const GL_DRAW_INDIRECT_BUFFER_BINDING: u32 = 36675;
+pub const GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV: u32 = 36676;
+pub const GL_MAX_PROGRAM_SUBROUTINE_NUM_NV: u32 = 36677;
+pub const GL_DOUBLE_MAT2: u32 = 36678;
+pub const GL_DOUBLE_MAT2_EXT: u32 = 36678;
+pub const GL_DOUBLE_MAT3: u32 = 36679;
+pub const GL_DOUBLE_MAT3_EXT: u32 = 36679;
+pub const GL_DOUBLE_MAT4: u32 = 36680;
+pub const GL_DOUBLE_MAT4_EXT: u32 = 36680;
+pub const GL_DOUBLE_MAT2x3: u32 = 36681;
+pub const GL_DOUBLE_MAT2x3_EXT: u32 = 36681;
+pub const GL_DOUBLE_MAT2x4: u32 = 36682;
+pub const GL_DOUBLE_MAT2x4_EXT: u32 = 36682;
+pub const GL_DOUBLE_MAT3x2: u32 = 36683;
+pub const GL_DOUBLE_MAT3x2_EXT: u32 = 36683;
+pub const GL_DOUBLE_MAT3x4: u32 = 36684;
+pub const GL_DOUBLE_MAT3x4_EXT: u32 = 36684;
+pub const GL_DOUBLE_MAT4x2: u32 = 36685;
+pub const GL_DOUBLE_MAT4x2_EXT: u32 = 36685;
+pub const GL_DOUBLE_MAT4x3: u32 = 36686;
+pub const GL_DOUBLE_MAT4x3_EXT: u32 = 36686;
+pub const GL_VERTEX_BINDING_BUFFER: u32 = 36687;
+pub const GL_MALI_SHADER_BINARY_ARM: u32 = 36704;
+pub const GL_MALI_PROGRAM_BINARY_ARM: u32 = 36705;
+pub const GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_FAST_SIZE_EXT: u32 = 36707;
+pub const GL_SHADER_PIXEL_LOCAL_STORAGE_EXT: u32 = 36708;
+pub const GL_FETCH_PER_SAMPLE_ARM: u32 = 36709;
+pub const GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM: u32 = 36710;
+pub const GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT: u32 = 36711;
+pub const GL_RED_SNORM: u32 = 36752;
+pub const GL_RG_SNORM: u32 = 36753;
+pub const GL_RGB_SNORM: u32 = 36754;
+pub const GL_RGBA_SNORM: u32 = 36755;
+pub const GL_R8_SNORM: u32 = 36756;
+pub const GL_RG8_SNORM: u32 = 36757;
+pub const GL_RGB8_SNORM: u32 = 36758;
+pub const GL_RGBA8_SNORM: u32 = 36759;
+pub const GL_R16_SNORM: u32 = 36760;
+pub const GL_R16_SNORM_EXT: u32 = 36760;
+pub const GL_RG16_SNORM: u32 = 36761;
+pub const GL_RG16_SNORM_EXT: u32 = 36761;
+pub const GL_RGB16_SNORM: u32 = 36762;
+pub const GL_RGB16_SNORM_EXT: u32 = 36762;
+pub const GL_RGBA16_SNORM: u32 = 36763;
+pub const GL_RGBA16_SNORM_EXT: u32 = 36763;
+pub const GL_SIGNED_NORMALIZED: u32 = 36764;
+pub const GL_PRIMITIVE_RESTART: u32 = 36765;
+pub const GL_PRIMITIVE_RESTART_INDEX: u32 = 36766;
+pub const GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB: u32 = 36767;
+pub const GL_PERFMON_GLOBAL_MODE_QCOM: u32 = 36768;
+pub const GL_BINNING_CONTROL_HINT_QCOM: u32 = 36784;
+pub const GL_CPU_OPTIMIZED_QCOM: u32 = 36785;
+pub const GL_GPU_OPTIMIZED_QCOM: u32 = 36786;
+pub const GL_RENDER_DIRECT_TO_FRAMEBUFFER_QCOM: u32 = 36787;
+pub const GL_GPU_DISJOINT_EXT: u32 = 36795;
+pub const GL_SR8_EXT: u32 = 36797;
+pub const GL_SRG8_EXT: u32 = 36798;
+pub const GL_SHADER_BINARY_VIV: u32 = 36804;
+pub const GL_INT8_NV: u32 = 36832;
+pub const GL_INT8_VEC2_NV: u32 = 36833;
+pub const GL_INT8_VEC3_NV: u32 = 36834;
+pub const GL_INT8_VEC4_NV: u32 = 36835;
+pub const GL_INT16_NV: u32 = 36836;
+pub const GL_INT16_VEC2_NV: u32 = 36837;
+pub const GL_INT16_VEC3_NV: u32 = 36838;
+pub const GL_INT16_VEC4_NV: u32 = 36839;
+pub const GL_INT64_VEC2_ARB: u32 = 36841;
+pub const GL_INT64_VEC2_NV: u32 = 36841;
+pub const GL_INT64_VEC3_ARB: u32 = 36842;
+pub const GL_INT64_VEC3_NV: u32 = 36842;
+pub const GL_INT64_VEC4_ARB: u32 = 36843;
+pub const GL_INT64_VEC4_NV: u32 = 36843;
+pub const GL_UNSIGNED_INT8_NV: u32 = 36844;
+pub const GL_UNSIGNED_INT8_VEC2_NV: u32 = 36845;
+pub const GL_UNSIGNED_INT8_VEC3_NV: u32 = 36846;
+pub const GL_UNSIGNED_INT8_VEC4_NV: u32 = 36847;
+pub const GL_UNSIGNED_INT16_NV: u32 = 36848;
+pub const GL_UNSIGNED_INT16_VEC2_NV: u32 = 36849;
+pub const GL_UNSIGNED_INT16_VEC3_NV: u32 = 36850;
+pub const GL_UNSIGNED_INT16_VEC4_NV: u32 = 36851;
+pub const GL_UNSIGNED_INT64_VEC2_ARB: u32 = 36853;
+pub const GL_UNSIGNED_INT64_VEC2_NV: u32 = 36853;
+pub const GL_UNSIGNED_INT64_VEC3_ARB: u32 = 36854;
+pub const GL_UNSIGNED_INT64_VEC3_NV: u32 = 36854;
+pub const GL_UNSIGNED_INT64_VEC4_ARB: u32 = 36855;
+pub const GL_UNSIGNED_INT64_VEC4_NV: u32 = 36855;
+pub const GL_FLOAT16_NV: u32 = 36856;
+pub const GL_FLOAT16_VEC2_NV: u32 = 36857;
+pub const GL_FLOAT16_VEC3_NV: u32 = 36858;
+pub const GL_FLOAT16_VEC4_NV: u32 = 36859;
+pub const GL_DOUBLE_VEC2: u32 = 36860;
+pub const GL_DOUBLE_VEC2_EXT: u32 = 36860;
+pub const GL_DOUBLE_VEC3: u32 = 36861;
+pub const GL_DOUBLE_VEC3_EXT: u32 = 36861;
+pub const GL_DOUBLE_VEC4: u32 = 36862;
+pub const GL_DOUBLE_VEC4_EXT: u32 = 36862;
+pub const GL_SAMPLER_BUFFER_AMD: u32 = 36865;
+pub const GL_INT_SAMPLER_BUFFER_AMD: u32 = 36866;
+pub const GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD: u32 = 36867;
+pub const GL_TESSELLATION_MODE_AMD: u32 = 36868;
+pub const GL_TESSELLATION_FACTOR_AMD: u32 = 36869;
+pub const GL_DISCRETE_AMD: u32 = 36870;
+pub const GL_CONTINUOUS_AMD: u32 = 36871;
+pub const GL_TEXTURE_CUBE_MAP_ARRAY: u32 = 36873;
+pub const GL_TEXTURE_CUBE_MAP_ARRAY_ARB: u32 = 36873;
+pub const GL_TEXTURE_CUBE_MAP_ARRAY_EXT: u32 = 36873;
+pub const GL_TEXTURE_CUBE_MAP_ARRAY_OES: u32 = 36873;
+pub const GL_TEXTURE_BINDING_CUBE_MAP_ARRAY: u32 = 36874;
+pub const GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB: u32 = 36874;
+pub const GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_EXT: u32 = 36874;
+pub const GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_OES: u32 = 36874;
+pub const GL_PROXY_TEXTURE_CUBE_MAP_ARRAY: u32 = 36875;
+pub const GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB: u32 = 36875;
+pub const GL_SAMPLER_CUBE_MAP_ARRAY: u32 = 36876;
+pub const GL_SAMPLER_CUBE_MAP_ARRAY_ARB: u32 = 36876;
+pub const GL_SAMPLER_CUBE_MAP_ARRAY_EXT: u32 = 36876;
+pub const GL_SAMPLER_CUBE_MAP_ARRAY_OES: u32 = 36876;
+pub const GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: u32 = 36877;
+pub const GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB: u32 = 36877;
+pub const GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_EXT: u32 = 36877;
+pub const GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_OES: u32 = 36877;
+pub const GL_INT_SAMPLER_CUBE_MAP_ARRAY: u32 = 36878;
+pub const GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB: u32 = 36878;
+pub const GL_INT_SAMPLER_CUBE_MAP_ARRAY_EXT: u32 = 36878;
+pub const GL_INT_SAMPLER_CUBE_MAP_ARRAY_OES: u32 = 36878;
+pub const GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: u32 = 36879;
+pub const GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB: u32 = 36879;
+pub const GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_EXT: u32 = 36879;
+pub const GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_OES: u32 = 36879;
+pub const GL_ALPHA_SNORM: u32 = 36880;
+pub const GL_LUMINANCE_SNORM: u32 = 36881;
+pub const GL_LUMINANCE_ALPHA_SNORM: u32 = 36882;
+pub const GL_INTENSITY_SNORM: u32 = 36883;
+pub const GL_ALPHA8_SNORM: u32 = 36884;
+pub const GL_LUMINANCE8_SNORM: u32 = 36885;
+pub const GL_LUMINANCE8_ALPHA8_SNORM: u32 = 36886;
+pub const GL_INTENSITY8_SNORM: u32 = 36887;
+pub const GL_ALPHA16_SNORM: u32 = 36888;
+pub const GL_LUMINANCE16_SNORM: u32 = 36889;
+pub const GL_LUMINANCE16_ALPHA16_SNORM: u32 = 36890;
+pub const GL_INTENSITY16_SNORM: u32 = 36891;
+pub const GL_FACTOR_MIN_AMD: u32 = 36892;
+pub const GL_FACTOR_MAX_AMD: u32 = 36893;
+pub const GL_DEPTH_CLAMP_NEAR_AMD: u32 = 36894;
+pub const GL_DEPTH_CLAMP_FAR_AMD: u32 = 36895;
+pub const GL_VIDEO_BUFFER_NV: u32 = 36896;
+pub const GL_VIDEO_BUFFER_BINDING_NV: u32 = 36897;
+pub const GL_FIELD_UPPER_NV: u32 = 36898;
+pub const GL_FIELD_LOWER_NV: u32 = 36899;
+pub const GL_NUM_VIDEO_CAPTURE_STREAMS_NV: u32 = 36900;
+pub const GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV: u32 = 36901;
+pub const GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV: u32 = 36902;
+pub const GL_LAST_VIDEO_CAPTURE_STATUS_NV: u32 = 36903;
+pub const GL_VIDEO_BUFFER_PITCH_NV: u32 = 36904;
+pub const GL_VIDEO_COLOR_CONVERSION_MATRIX_NV: u32 = 36905;
+pub const GL_VIDEO_COLOR_CONVERSION_MAX_NV: u32 = 36906;
+pub const GL_VIDEO_COLOR_CONVERSION_MIN_NV: u32 = 36907;
+pub const GL_VIDEO_COLOR_CONVERSION_OFFSET_NV: u32 = 36908;
+pub const GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV: u32 = 36909;
+pub const GL_PARTIAL_SUCCESS_NV: u32 = 36910;
+pub const GL_SUCCESS_NV: u32 = 36911;
+pub const GL_FAILURE_NV: u32 = 36912;
+pub const GL_YCBYCR8_422_NV: u32 = 36913;
+pub const GL_YCBAYCR8A_4224_NV: u32 = 36914;
+pub const GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV: u32 = 36915;
+pub const GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV: u32 = 36916;
+pub const GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV: u32 = 36917;
+pub const GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV: u32 = 36918;
+pub const GL_Z4Y12Z4CB12Z4CR12_444_NV: u32 = 36919;
+pub const GL_VIDEO_CAPTURE_FRAME_WIDTH_NV: u32 = 36920;
+pub const GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV: u32 = 36921;
+pub const GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV: u32 = 36922;
+pub const GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV: u32 = 36923;
+pub const GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV: u32 = 36924;
+pub const GL_TEXTURE_COVERAGE_SAMPLES_NV: u32 = 36933;
+pub const GL_TEXTURE_COLOR_SAMPLES_NV: u32 = 36934;
+pub const GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: u32 = 36935;
+pub const GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: u32 = 36936;
+pub const GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX: u32 = 36937;
+pub const GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX: u32 = 36938;
+pub const GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX: u32 = 36939;
+pub const GL_IMAGE_1D: u32 = 36940;
+pub const GL_IMAGE_1D_EXT: u32 = 36940;
+pub const GL_IMAGE_2D: u32 = 36941;
+pub const GL_IMAGE_2D_EXT: u32 = 36941;
+pub const GL_IMAGE_3D: u32 = 36942;
+pub const GL_IMAGE_3D_EXT: u32 = 36942;
+pub const GL_IMAGE_2D_RECT: u32 = 36943;
+pub const GL_IMAGE_2D_RECT_EXT: u32 = 36943;
+pub const GL_IMAGE_CUBE: u32 = 36944;
+pub const GL_IMAGE_CUBE_EXT: u32 = 36944;
+pub const GL_IMAGE_BUFFER: u32 = 36945;
+pub const GL_IMAGE_BUFFER_EXT: u32 = 36945;
+pub const GL_IMAGE_BUFFER_OES: u32 = 36945;
+pub const GL_IMAGE_1D_ARRAY: u32 = 36946;
+pub const GL_IMAGE_1D_ARRAY_EXT: u32 = 36946;
+pub const GL_IMAGE_2D_ARRAY: u32 = 36947;
+pub const GL_IMAGE_2D_ARRAY_EXT: u32 = 36947;
+pub const GL_IMAGE_CUBE_MAP_ARRAY: u32 = 36948;
+pub const GL_IMAGE_CUBE_MAP_ARRAY_EXT: u32 = 36948;
+pub const GL_IMAGE_CUBE_MAP_ARRAY_OES: u32 = 36948;
+pub const GL_IMAGE_2D_MULTISAMPLE: u32 = 36949;
+pub const GL_IMAGE_2D_MULTISAMPLE_EXT: u32 = 36949;
+pub const GL_IMAGE_2D_MULTISAMPLE_ARRAY: u32 = 36950;
+pub const GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT: u32 = 36950;
+pub const GL_INT_IMAGE_1D: u32 = 36951;
+pub const GL_INT_IMAGE_1D_EXT: u32 = 36951;
+pub const GL_INT_IMAGE_2D: u32 = 36952;
+pub const GL_INT_IMAGE_2D_EXT: u32 = 36952;
+pub const GL_INT_IMAGE_3D: u32 = 36953;
+pub const GL_INT_IMAGE_3D_EXT: u32 = 36953;
+pub const GL_INT_IMAGE_2D_RECT: u32 = 36954;
+pub const GL_INT_IMAGE_2D_RECT_EXT: u32 = 36954;
+pub const GL_INT_IMAGE_CUBE: u32 = 36955;
+pub const GL_INT_IMAGE_CUBE_EXT: u32 = 36955;
+pub const GL_INT_IMAGE_BUFFER: u32 = 36956;
+pub const GL_INT_IMAGE_BUFFER_EXT: u32 = 36956;
+pub const GL_INT_IMAGE_BUFFER_OES: u32 = 36956;
+pub const GL_INT_IMAGE_1D_ARRAY: u32 = 36957;
+pub const GL_INT_IMAGE_1D_ARRAY_EXT: u32 = 36957;
+pub const GL_INT_IMAGE_2D_ARRAY: u32 = 36958;
+pub const GL_INT_IMAGE_2D_ARRAY_EXT: u32 = 36958;
+pub const GL_INT_IMAGE_CUBE_MAP_ARRAY: u32 = 36959;
+pub const GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT: u32 = 36959;
+pub const GL_INT_IMAGE_CUBE_MAP_ARRAY_OES: u32 = 36959;
+pub const GL_INT_IMAGE_2D_MULTISAMPLE: u32 = 36960;
+pub const GL_INT_IMAGE_2D_MULTISAMPLE_EXT: u32 = 36960;
+pub const GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: u32 = 36961;
+pub const GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT: u32 = 36961;
+pub const GL_UNSIGNED_INT_IMAGE_1D: u32 = 36962;
+pub const GL_UNSIGNED_INT_IMAGE_1D_EXT: u32 = 36962;
+pub const GL_UNSIGNED_INT_IMAGE_2D: u32 = 36963;
+pub const GL_UNSIGNED_INT_IMAGE_2D_EXT: u32 = 36963;
+pub const GL_UNSIGNED_INT_IMAGE_3D: u32 = 36964;
+pub const GL_UNSIGNED_INT_IMAGE_3D_EXT: u32 = 36964;
+pub const GL_UNSIGNED_INT_IMAGE_2D_RECT: u32 = 36965;
+pub const GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT: u32 = 36965;
+pub const GL_UNSIGNED_INT_IMAGE_CUBE: u32 = 36966;
+pub const GL_UNSIGNED_INT_IMAGE_CUBE_EXT: u32 = 36966;
+pub const GL_UNSIGNED_INT_IMAGE_BUFFER: u32 = 36967;
+pub const GL_UNSIGNED_INT_IMAGE_BUFFER_EXT: u32 = 36967;
+pub const GL_UNSIGNED_INT_IMAGE_BUFFER_OES: u32 = 36967;
+pub const GL_UNSIGNED_INT_IMAGE_1D_ARRAY: u32 = 36968;
+pub const GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT: u32 = 36968;
+pub const GL_UNSIGNED_INT_IMAGE_2D_ARRAY: u32 = 36969;
+pub const GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT: u32 = 36969;
+pub const GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: u32 = 36970;
+pub const GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT: u32 = 36970;
+pub const GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_OES: u32 = 36970;
+pub const GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: u32 = 36971;
+pub const GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT: u32 = 36971;
+pub const GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: u32 = 36972;
+pub const GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT: u32 = 36972;
+pub const GL_MAX_IMAGE_SAMPLES: u32 = 36973;
+pub const GL_MAX_IMAGE_SAMPLES_EXT: u32 = 36973;
+pub const GL_IMAGE_BINDING_FORMAT: u32 = 36974;
+pub const GL_IMAGE_BINDING_FORMAT_EXT: u32 = 36974;
+pub const GL_RGB10_A2UI: u32 = 36975;
+pub const GL_PATH_FORMAT_SVG_NV: u32 = 36976;
+pub const GL_PATH_FORMAT_PS_NV: u32 = 36977;
+pub const GL_STANDARD_FONT_NAME_NV: u32 = 36978;
+pub const GL_SYSTEM_FONT_NAME_NV: u32 = 36979;
+pub const GL_FILE_NAME_NV: u32 = 36980;
+pub const GL_PATH_STROKE_WIDTH_NV: u32 = 36981;
+pub const GL_PATH_END_CAPS_NV: u32 = 36982;
+pub const GL_PATH_INITIAL_END_CAP_NV: u32 = 36983;
+pub const GL_PATH_TERMINAL_END_CAP_NV: u32 = 36984;
+pub const GL_PATH_JOIN_STYLE_NV: u32 = 36985;
+pub const GL_PATH_MITER_LIMIT_NV: u32 = 36986;
+pub const GL_PATH_DASH_CAPS_NV: u32 = 36987;
+pub const GL_PATH_INITIAL_DASH_CAP_NV: u32 = 36988;
+pub const GL_PATH_TERMINAL_DASH_CAP_NV: u32 = 36989;
+pub const GL_PATH_DASH_OFFSET_NV: u32 = 36990;
+pub const GL_PATH_CLIENT_LENGTH_NV: u32 = 36991;
+pub const GL_PATH_FILL_MODE_NV: u32 = 36992;
+pub const GL_PATH_FILL_MASK_NV: u32 = 36993;
+pub const GL_PATH_FILL_COVER_MODE_NV: u32 = 36994;
+pub const GL_PATH_STROKE_COVER_MODE_NV: u32 = 36995;
+pub const GL_PATH_STROKE_MASK_NV: u32 = 36996;
+pub const GL_COUNT_UP_NV: u32 = 37000;
+pub const GL_COUNT_DOWN_NV: u32 = 37001;
+pub const GL_PATH_OBJECT_BOUNDING_BOX_NV: u32 = 37002;
+pub const GL_CONVEX_HULL_NV: u32 = 37003;
+pub const GL_BOUNDING_BOX_NV: u32 = 37005;
+pub const GL_TRANSLATE_X_NV: u32 = 37006;
+pub const GL_TRANSLATE_Y_NV: u32 = 37007;
+pub const GL_TRANSLATE_2D_NV: u32 = 37008;
+pub const GL_TRANSLATE_3D_NV: u32 = 37009;
+pub const GL_AFFINE_2D_NV: u32 = 37010;
+pub const GL_AFFINE_3D_NV: u32 = 37012;
+pub const GL_TRANSPOSE_AFFINE_2D_NV: u32 = 37014;
+pub const GL_TRANSPOSE_AFFINE_3D_NV: u32 = 37016;
+pub const GL_UTF8_NV: u32 = 37018;
+pub const GL_UTF16_NV: u32 = 37019;
+pub const GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV: u32 = 37020;
+pub const GL_PATH_COMMAND_COUNT_NV: u32 = 37021;
+pub const GL_PATH_COORD_COUNT_NV: u32 = 37022;
+pub const GL_PATH_DASH_ARRAY_COUNT_NV: u32 = 37023;
+pub const GL_PATH_COMPUTED_LENGTH_NV: u32 = 37024;
+pub const GL_PATH_FILL_BOUNDING_BOX_NV: u32 = 37025;
+pub const GL_PATH_STROKE_BOUNDING_BOX_NV: u32 = 37026;
+pub const GL_SQUARE_NV: u32 = 37027;
+pub const GL_ROUND_NV: u32 = 37028;
+pub const GL_TRIANGULAR_NV: u32 = 37029;
+pub const GL_BEVEL_NV: u32 = 37030;
+pub const GL_MITER_REVERT_NV: u32 = 37031;
+pub const GL_MITER_TRUNCATE_NV: u32 = 37032;
+pub const GL_SKIP_MISSING_GLYPH_NV: u32 = 37033;
+pub const GL_USE_MISSING_GLYPH_NV: u32 = 37034;
+pub const GL_PATH_ERROR_POSITION_NV: u32 = 37035;
+pub const GL_PATH_FOG_GEN_MODE_NV: u32 = 37036;
+pub const GL_ACCUM_ADJACENT_PAIRS_NV: u32 = 37037;
+pub const GL_ADJACENT_PAIRS_NV: u32 = 37038;
+pub const GL_FIRST_TO_REST_NV: u32 = 37039;
+pub const GL_PATH_GEN_MODE_NV: u32 = 37040;
+pub const GL_PATH_GEN_COEFF_NV: u32 = 37041;
+pub const GL_PATH_GEN_COLOR_FORMAT_NV: u32 = 37042;
+pub const GL_PATH_GEN_COMPONENTS_NV: u32 = 37043;
+pub const GL_PATH_DASH_OFFSET_RESET_NV: u32 = 37044;
+pub const GL_MOVE_TO_RESETS_NV: u32 = 37045;
+pub const GL_MOVE_TO_CONTINUES_NV: u32 = 37046;
+pub const GL_PATH_STENCIL_FUNC_NV: u32 = 37047;
+pub const GL_PATH_STENCIL_REF_NV: u32 = 37048;
+pub const GL_PATH_STENCIL_VALUE_MASK_NV: u32 = 37049;
+pub const GL_SCALED_RESOLVE_FASTEST_EXT: u32 = 37050;
+pub const GL_SCALED_RESOLVE_NICEST_EXT: u32 = 37051;
+pub const GL_MIN_MAP_BUFFER_ALIGNMENT: u32 = 37052;
+pub const GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV: u32 = 37053;
+pub const GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV: u32 = 37054;
+pub const GL_PATH_COVER_DEPTH_FUNC_NV: u32 = 37055;
+pub const GL_IMAGE_FORMAT_COMPATIBILITY_TYPE: u32 = 37063;
+pub const GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE: u32 = 37064;
+pub const GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS: u32 = 37065;
+pub const GL_MAX_VERTEX_IMAGE_UNIFORMS: u32 = 37066;
+pub const GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS: u32 = 37067;
+pub const GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_EXT: u32 = 37067;
+pub const GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_OES: u32 = 37067;
+pub const GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS: u32 = 37068;
+pub const GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_EXT: u32 = 37068;
+pub const GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_OES: u32 = 37068;
+pub const GL_MAX_GEOMETRY_IMAGE_UNIFORMS: u32 = 37069;
+pub const GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT: u32 = 37069;
+pub const GL_MAX_GEOMETRY_IMAGE_UNIFORMS_OES: u32 = 37069;
+pub const GL_MAX_FRAGMENT_IMAGE_UNIFORMS: u32 = 37070;
+pub const GL_MAX_COMBINED_IMAGE_UNIFORMS: u32 = 37071;
+pub const GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV: u32 = 37072;
+pub const GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV: u32 = 37073;
+pub const GL_SHADER_STORAGE_BUFFER: u32 = 37074;
+pub const GL_SHADER_STORAGE_BUFFER_BINDING: u32 = 37075;
+pub const GL_SHADER_STORAGE_BUFFER_START: u32 = 37076;
+pub const GL_SHADER_STORAGE_BUFFER_SIZE: u32 = 37077;
+pub const GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: u32 = 37078;
+pub const GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS: u32 = 37079;
+pub const GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT: u32 = 37079;
+pub const GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_OES: u32 = 37079;
+pub const GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS: u32 = 37080;
+pub const GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT: u32 = 37080;
+pub const GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_OES: u32 = 37080;
+pub const GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS: u32 = 37081;
+pub const GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT: u32 = 37081;
+pub const GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_OES: u32 = 37081;
+pub const GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS: u32 = 37082;
+pub const GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: u32 = 37083;
+pub const GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: u32 = 37084;
+pub const GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: u32 = 37085;
+pub const GL_MAX_SHADER_STORAGE_BLOCK_SIZE: u32 = 37086;
+pub const GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT: u32 = 37087;
+pub const GL_SYNC_X11_FENCE_EXT: u32 = 37089;
+pub const GL_DEPTH_STENCIL_TEXTURE_MODE: u32 = 37098;
+pub const GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB: u32 = 37099;
+pub const GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: u32 = 37099;
+pub const GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER: u32 = 37100;
+pub const GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER: u32 = 37101;
+pub const GL_DISPATCH_INDIRECT_BUFFER: u32 = 37102;
+pub const GL_DISPATCH_INDIRECT_BUFFER_BINDING: u32 = 37103;
+pub const GL_COLOR_ATTACHMENT_EXT: u32 = 37104;
+pub const GL_MULTIVIEW_EXT: u32 = 37105;
+pub const GL_MAX_MULTIVIEW_BUFFERS_EXT: u32 = 37106;
+pub const GL_CONTEXT_ROBUST_ACCESS: u32 = 37107;
+pub const GL_CONTEXT_ROBUST_ACCESS_EXT: u32 = 37107;
+pub const GL_CONTEXT_ROBUST_ACCESS_KHR: u32 = 37107;
+pub const GL_COMPUTE_PROGRAM_NV: u32 = 37115;
+pub const GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV: u32 = 37116;
+pub const GL_TEXTURE_2D_MULTISAMPLE: u32 = 37120;
+pub const GL_PROXY_TEXTURE_2D_MULTISAMPLE: u32 = 37121;
+pub const GL_TEXTURE_2D_MULTISAMPLE_ARRAY: u32 = 37122;
+pub const GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES: u32 = 37122;
+pub const GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY: u32 = 37123;
+pub const GL_TEXTURE_BINDING_2D_MULTISAMPLE: u32 = 37124;
+pub const GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY: u32 = 37125;
+pub const GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY_OES: u32 = 37125;
+pub const GL_TEXTURE_SAMPLES: u32 = 37126;
+pub const GL_TEXTURE_FIXED_SAMPLE_LOCATIONS: u32 = 37127;
+pub const GL_SAMPLER_2D_MULTISAMPLE: u32 = 37128;
+pub const GL_INT_SAMPLER_2D_MULTISAMPLE: u32 = 37129;
+pub const GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: u32 = 37130;
+pub const GL_SAMPLER_2D_MULTISAMPLE_ARRAY: u32 = 37131;
+pub const GL_SAMPLER_2D_MULTISAMPLE_ARRAY_OES: u32 = 37131;
+pub const GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: u32 = 37132;
+pub const GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES: u32 = 37132;
+pub const GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: u32 = 37133;
+pub const GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES: u32 = 37133;
+pub const GL_MAX_COLOR_TEXTURE_SAMPLES: u32 = 37134;
+pub const GL_MAX_DEPTH_TEXTURE_SAMPLES: u32 = 37135;
+pub const GL_MAX_INTEGER_SAMPLES: u32 = 37136;
+pub const GL_MAX_SERVER_WAIT_TIMEOUT: u32 = 37137;
+pub const GL_MAX_SERVER_WAIT_TIMEOUT_APPLE: u32 = 37137;
+pub const GL_OBJECT_TYPE: u32 = 37138;
+pub const GL_OBJECT_TYPE_APPLE: u32 = 37138;
+pub const GL_SYNC_CONDITION: u32 = 37139;
+pub const GL_SYNC_CONDITION_APPLE: u32 = 37139;
+pub const GL_SYNC_STATUS: u32 = 37140;
+pub const GL_SYNC_STATUS_APPLE: u32 = 37140;
+pub const GL_SYNC_FLAGS: u32 = 37141;
+pub const GL_SYNC_FLAGS_APPLE: u32 = 37141;
+pub const GL_SYNC_FENCE: u32 = 37142;
+pub const GL_SYNC_FENCE_APPLE: u32 = 37142;
+pub const GL_SYNC_GPU_COMMANDS_COMPLETE: u32 = 37143;
+pub const GL_SYNC_GPU_COMMANDS_COMPLETE_APPLE: u32 = 37143;
+pub const GL_UNSIGNALED: u32 = 37144;
+pub const GL_UNSIGNALED_APPLE: u32 = 37144;
+pub const GL_SIGNALED: u32 = 37145;
+pub const GL_SIGNALED_APPLE: u32 = 37145;
+pub const GL_ALREADY_SIGNALED: u32 = 37146;
+pub const GL_ALREADY_SIGNALED_APPLE: u32 = 37146;
+pub const GL_TIMEOUT_EXPIRED: u32 = 37147;
+pub const GL_TIMEOUT_EXPIRED_APPLE: u32 = 37147;
+pub const GL_CONDITION_SATISFIED: u32 = 37148;
+pub const GL_CONDITION_SATISFIED_APPLE: u32 = 37148;
+pub const GL_WAIT_FAILED: u32 = 37149;
+pub const GL_WAIT_FAILED_APPLE: u32 = 37149;
+pub const GL_BUFFER_ACCESS_FLAGS: u32 = 37151;
+pub const GL_BUFFER_MAP_LENGTH: u32 = 37152;
+pub const GL_BUFFER_MAP_OFFSET: u32 = 37153;
+pub const GL_MAX_VERTEX_OUTPUT_COMPONENTS: u32 = 37154;
+pub const GL_MAX_GEOMETRY_INPUT_COMPONENTS: u32 = 37155;
+pub const GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT: u32 = 37155;
+pub const GL_MAX_GEOMETRY_INPUT_COMPONENTS_OES: u32 = 37155;
+pub const GL_MAX_GEOMETRY_OUTPUT_COMPONENTS: u32 = 37156;
+pub const GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT: u32 = 37156;
+pub const GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_OES: u32 = 37156;
+pub const GL_MAX_FRAGMENT_INPUT_COMPONENTS: u32 = 37157;
+pub const GL_CONTEXT_PROFILE_MASK: u32 = 37158;
+pub const GL_UNPACK_COMPRESSED_BLOCK_WIDTH: u32 = 37159;
+pub const GL_UNPACK_COMPRESSED_BLOCK_HEIGHT: u32 = 37160;
+pub const GL_UNPACK_COMPRESSED_BLOCK_DEPTH: u32 = 37161;
+pub const GL_UNPACK_COMPRESSED_BLOCK_SIZE: u32 = 37162;
+pub const GL_PACK_COMPRESSED_BLOCK_WIDTH: u32 = 37163;
+pub const GL_PACK_COMPRESSED_BLOCK_HEIGHT: u32 = 37164;
+pub const GL_PACK_COMPRESSED_BLOCK_DEPTH: u32 = 37165;
+pub const GL_PACK_COMPRESSED_BLOCK_SIZE: u32 = 37166;
+pub const GL_TEXTURE_IMMUTABLE_FORMAT: u32 = 37167;
+pub const GL_TEXTURE_IMMUTABLE_FORMAT_EXT: u32 = 37167;
+pub const GL_SGX_PROGRAM_BINARY_IMG: u32 = 37168;
+pub const GL_RENDERBUFFER_SAMPLES_IMG: u32 = 37171;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG: u32 = 37172;
+pub const GL_MAX_SAMPLES_IMG: u32 = 37173;
+pub const GL_TEXTURE_SAMPLES_IMG: u32 = 37174;
+pub const GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: u32 = 37175;
+pub const GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: u32 = 37176;
+pub const GL_CUBIC_IMG: u32 = 37177;
+pub const GL_CUBIC_MIPMAP_NEAREST_IMG: u32 = 37178;
+pub const GL_CUBIC_MIPMAP_LINEAR_IMG: u32 = 37179;
+pub const GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_AND_DOWNSAMPLE_IMG: u32 = 37180;
+pub const GL_NUM_DOWNSAMPLE_SCALES_IMG: u32 = 37181;
+pub const GL_DOWNSAMPLE_SCALES_IMG: u32 = 37182;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG: u32 = 37183;
+pub const GL_MAX_DEBUG_MESSAGE_LENGTH: u32 = 37187;
+pub const GL_MAX_DEBUG_MESSAGE_LENGTH_AMD: u32 = 37187;
+pub const GL_MAX_DEBUG_MESSAGE_LENGTH_ARB: u32 = 37187;
+pub const GL_MAX_DEBUG_MESSAGE_LENGTH_KHR: u32 = 37187;
+pub const GL_MAX_DEBUG_LOGGED_MESSAGES: u32 = 37188;
+pub const GL_MAX_DEBUG_LOGGED_MESSAGES_AMD: u32 = 37188;
+pub const GL_MAX_DEBUG_LOGGED_MESSAGES_ARB: u32 = 37188;
+pub const GL_MAX_DEBUG_LOGGED_MESSAGES_KHR: u32 = 37188;
+pub const GL_DEBUG_LOGGED_MESSAGES: u32 = 37189;
+pub const GL_DEBUG_LOGGED_MESSAGES_AMD: u32 = 37189;
+pub const GL_DEBUG_LOGGED_MESSAGES_ARB: u32 = 37189;
+pub const GL_DEBUG_LOGGED_MESSAGES_KHR: u32 = 37189;
+pub const GL_DEBUG_SEVERITY_HIGH: u32 = 37190;
+pub const GL_DEBUG_SEVERITY_HIGH_AMD: u32 = 37190;
+pub const GL_DEBUG_SEVERITY_HIGH_ARB: u32 = 37190;
+pub const GL_DEBUG_SEVERITY_HIGH_KHR: u32 = 37190;
+pub const GL_DEBUG_SEVERITY_MEDIUM: u32 = 37191;
+pub const GL_DEBUG_SEVERITY_MEDIUM_AMD: u32 = 37191;
+pub const GL_DEBUG_SEVERITY_MEDIUM_ARB: u32 = 37191;
+pub const GL_DEBUG_SEVERITY_MEDIUM_KHR: u32 = 37191;
+pub const GL_DEBUG_SEVERITY_LOW: u32 = 37192;
+pub const GL_DEBUG_SEVERITY_LOW_AMD: u32 = 37192;
+pub const GL_DEBUG_SEVERITY_LOW_ARB: u32 = 37192;
+pub const GL_DEBUG_SEVERITY_LOW_KHR: u32 = 37192;
+pub const GL_DEBUG_CATEGORY_API_ERROR_AMD: u32 = 37193;
+pub const GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: u32 = 37194;
+pub const GL_DEBUG_CATEGORY_DEPRECATION_AMD: u32 = 37195;
+pub const GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: u32 = 37196;
+pub const GL_DEBUG_CATEGORY_PERFORMANCE_AMD: u32 = 37197;
+pub const GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: u32 = 37198;
+pub const GL_DEBUG_CATEGORY_APPLICATION_AMD: u32 = 37199;
+pub const GL_DEBUG_CATEGORY_OTHER_AMD: u32 = 37200;
+pub const GL_BUFFER_OBJECT_EXT: u32 = 37201;
+pub const GL_DATA_BUFFER_AMD: u32 = 37201;
+pub const GL_PERFORMANCE_MONITOR_AMD: u32 = 37202;
+pub const GL_QUERY_OBJECT_AMD: u32 = 37203;
+pub const GL_QUERY_OBJECT_EXT: u32 = 37203;
+pub const GL_VERTEX_ARRAY_OBJECT_AMD: u32 = 37204;
+pub const GL_VERTEX_ARRAY_OBJECT_EXT: u32 = 37204;
+pub const GL_SAMPLER_OBJECT_AMD: u32 = 37205;
+pub const GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD: u32 = 37216;
+pub const GL_QUERY_BUFFER: u32 = 37266;
+pub const GL_QUERY_BUFFER_AMD: u32 = 37266;
+pub const GL_QUERY_BUFFER_BINDING: u32 = 37267;
+pub const GL_QUERY_BUFFER_BINDING_AMD: u32 = 37267;
+pub const GL_QUERY_RESULT_NO_WAIT: u32 = 37268;
+pub const GL_QUERY_RESULT_NO_WAIT_AMD: u32 = 37268;
+pub const GL_VIRTUAL_PAGE_SIZE_X_AMD: u32 = 37269;
+pub const GL_VIRTUAL_PAGE_SIZE_X_ARB: u32 = 37269;
+pub const GL_VIRTUAL_PAGE_SIZE_X_EXT: u32 = 37269;
+pub const GL_VIRTUAL_PAGE_SIZE_Y_AMD: u32 = 37270;
+pub const GL_VIRTUAL_PAGE_SIZE_Y_ARB: u32 = 37270;
+pub const GL_VIRTUAL_PAGE_SIZE_Y_EXT: u32 = 37270;
+pub const GL_VIRTUAL_PAGE_SIZE_Z_AMD: u32 = 37271;
+pub const GL_VIRTUAL_PAGE_SIZE_Z_ARB: u32 = 37271;
+pub const GL_VIRTUAL_PAGE_SIZE_Z_EXT: u32 = 37271;
+pub const GL_MAX_SPARSE_TEXTURE_SIZE_AMD: u32 = 37272;
+pub const GL_MAX_SPARSE_TEXTURE_SIZE_ARB: u32 = 37272;
+pub const GL_MAX_SPARSE_TEXTURE_SIZE_EXT: u32 = 37272;
+pub const GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD: u32 = 37273;
+pub const GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB: u32 = 37273;
+pub const GL_MAX_SPARSE_3D_TEXTURE_SIZE_EXT: u32 = 37273;
+pub const GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS: u32 = 37274;
+pub const GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB: u32 = 37274;
+pub const GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_EXT: u32 = 37274;
+pub const GL_MIN_SPARSE_LEVEL_AMD: u32 = 37275;
+pub const GL_MIN_LOD_WARNING_AMD: u32 = 37276;
+pub const GL_TEXTURE_BUFFER_OFFSET: u32 = 37277;
+pub const GL_TEXTURE_BUFFER_OFFSET_EXT: u32 = 37277;
+pub const GL_TEXTURE_BUFFER_OFFSET_OES: u32 = 37277;
+pub const GL_TEXTURE_BUFFER_SIZE: u32 = 37278;
+pub const GL_TEXTURE_BUFFER_SIZE_EXT: u32 = 37278;
+pub const GL_TEXTURE_BUFFER_SIZE_OES: u32 = 37278;
+pub const GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT: u32 = 37279;
+pub const GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_EXT: u32 = 37279;
+pub const GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_OES: u32 = 37279;
+pub const GL_STREAM_RASTERIZATION_AMD: u32 = 37280;
+pub const GL_VERTEX_ELEMENT_SWIZZLE_AMD: u32 = 37284;
+pub const GL_VERTEX_ID_SWIZZLE_AMD: u32 = 37285;
+pub const GL_TEXTURE_SPARSE_ARB: u32 = 37286;
+pub const GL_TEXTURE_SPARSE_EXT: u32 = 37286;
+pub const GL_VIRTUAL_PAGE_SIZE_INDEX_ARB: u32 = 37287;
+pub const GL_VIRTUAL_PAGE_SIZE_INDEX_EXT: u32 = 37287;
+pub const GL_NUM_VIRTUAL_PAGE_SIZES_ARB: u32 = 37288;
+pub const GL_NUM_VIRTUAL_PAGE_SIZES_EXT: u32 = 37288;
+pub const GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB: u32 = 37289;
+pub const GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_EXT: u32 = 37289;
+pub const GL_NUM_SPARSE_LEVELS_ARB: u32 = 37290;
+pub const GL_NUM_SPARSE_LEVELS_EXT: u32 = 37290;
+pub const GL_PIXELS_PER_SAMPLE_PATTERN_X_AMD: u32 = 37294;
+pub const GL_PIXELS_PER_SAMPLE_PATTERN_Y_AMD: u32 = 37295;
+pub const GL_MAX_SHADER_COMPILER_THREADS_ARB: u32 = 37296;
+pub const GL_COMPLETION_STATUS_ARB: u32 = 37297;
+pub const GL_COMPUTE_SHADER: u32 = 37305;
+pub const GL_MAX_COMPUTE_UNIFORM_BLOCKS: u32 = 37307;
+pub const GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS: u32 = 37308;
+pub const GL_MAX_COMPUTE_IMAGE_UNIFORMS: u32 = 37309;
+pub const GL_MAX_COMPUTE_WORK_GROUP_COUNT: u32 = 37310;
+pub const GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB: u32 = 37311;
+pub const GL_MAX_COMPUTE_WORK_GROUP_SIZE: u32 = 37311;
+pub const GL_FLOAT16_MAT2_AMD: u32 = 37317;
+pub const GL_FLOAT16_MAT3_AMD: u32 = 37318;
+pub const GL_FLOAT16_MAT4_AMD: u32 = 37319;
+pub const GL_FLOAT16_MAT2x3_AMD: u32 = 37320;
+pub const GL_FLOAT16_MAT2x4_AMD: u32 = 37321;
+pub const GL_FLOAT16_MAT3x2_AMD: u32 = 37322;
+pub const GL_FLOAT16_MAT3x4_AMD: u32 = 37323;
+pub const GL_FLOAT16_MAT4x2_AMD: u32 = 37324;
+pub const GL_FLOAT16_MAT4x3_AMD: u32 = 37325;
+pub const GL_UNPACK_FLIP_Y_WEBGL: u32 = 37440;
+pub const GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL: u32 = 37441;
+pub const GL_CONTEXT_LOST_WEBGL: u32 = 37442;
+pub const GL_UNPACK_COLORSPACE_CONVERSION_WEBGL: u32 = 37443;
+pub const GL_BROWSER_DEFAULT_WEBGL: u32 = 37444;
+pub const GL_SHADER_BINARY_DMP: u32 = 37456;
+pub const GL_SMAPHS30_PROGRAM_BINARY_DMP: u32 = 37457;
+pub const GL_SMAPHS_PROGRAM_BINARY_DMP: u32 = 37458;
+pub const GL_DMP_PROGRAM_BINARY_DMP: u32 = 37459;
+pub const GL_GCCSO_SHADER_BINARY_FJ: u32 = 37472;
+pub const GL_COMPRESSED_R11_EAC: u32 = 37488;
+pub const GL_COMPRESSED_R11_EAC_OES: u32 = 37488;
+pub const GL_COMPRESSED_SIGNED_R11_EAC: u32 = 37489;
+pub const GL_COMPRESSED_SIGNED_R11_EAC_OES: u32 = 37489;
+pub const GL_COMPRESSED_RG11_EAC: u32 = 37490;
+pub const GL_COMPRESSED_RG11_EAC_OES: u32 = 37490;
+pub const GL_COMPRESSED_SIGNED_RG11_EAC: u32 = 37491;
+pub const GL_COMPRESSED_SIGNED_RG11_EAC_OES: u32 = 37491;
+pub const GL_COMPRESSED_RGB8_ETC2: u32 = 37492;
+pub const GL_COMPRESSED_RGB8_ETC2_OES: u32 = 37492;
+pub const GL_COMPRESSED_SRGB8_ETC2: u32 = 37493;
+pub const GL_COMPRESSED_SRGB8_ETC2_OES: u32 = 37493;
+pub const GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: u32 = 37494;
+pub const GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2_OES: u32 = 37494;
+pub const GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: u32 = 37495;
+pub const GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2_OES: u32 = 37495;
+pub const GL_COMPRESSED_RGBA8_ETC2_EAC: u32 = 37496;
+pub const GL_COMPRESSED_RGBA8_ETC2_EAC_OES: u32 = 37496;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: u32 = 37497;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC_OES: u32 = 37497;
+pub const GL_BLEND_PREMULTIPLIED_SRC_NV: u32 = 37504;
+pub const GL_BLEND_OVERLAP_NV: u32 = 37505;
+pub const GL_UNCORRELATED_NV: u32 = 37506;
+pub const GL_DISJOINT_NV: u32 = 37507;
+pub const GL_CONJOINT_NV: u32 = 37508;
+pub const GL_BLEND_ADVANCED_COHERENT_KHR: u32 = 37509;
+pub const GL_BLEND_ADVANCED_COHERENT_NV: u32 = 37509;
+pub const GL_SRC_NV: u32 = 37510;
+pub const GL_DST_NV: u32 = 37511;
+pub const GL_SRC_OVER_NV: u32 = 37512;
+pub const GL_DST_OVER_NV: u32 = 37513;
+pub const GL_SRC_IN_NV: u32 = 37514;
+pub const GL_DST_IN_NV: u32 = 37515;
+pub const GL_SRC_OUT_NV: u32 = 37516;
+pub const GL_DST_OUT_NV: u32 = 37517;
+pub const GL_SRC_ATOP_NV: u32 = 37518;
+pub const GL_DST_ATOP_NV: u32 = 37519;
+pub const GL_PLUS_NV: u32 = 37521;
+pub const GL_PLUS_DARKER_NV: u32 = 37522;
+pub const GL_MULTIPLY: u32 = 37524;
+pub const GL_MULTIPLY_KHR: u32 = 37524;
+pub const GL_MULTIPLY_NV: u32 = 37524;
+pub const GL_SCREEN: u32 = 37525;
+pub const GL_SCREEN_KHR: u32 = 37525;
+pub const GL_SCREEN_NV: u32 = 37525;
+pub const GL_OVERLAY: u32 = 37526;
+pub const GL_OVERLAY_KHR: u32 = 37526;
+pub const GL_OVERLAY_NV: u32 = 37526;
+pub const GL_DARKEN: u32 = 37527;
+pub const GL_DARKEN_KHR: u32 = 37527;
+pub const GL_DARKEN_NV: u32 = 37527;
+pub const GL_LIGHTEN: u32 = 37528;
+pub const GL_LIGHTEN_KHR: u32 = 37528;
+pub const GL_LIGHTEN_NV: u32 = 37528;
+pub const GL_COLORDODGE: u32 = 37529;
+pub const GL_COLORDODGE_KHR: u32 = 37529;
+pub const GL_COLORDODGE_NV: u32 = 37529;
+pub const GL_COLORBURN: u32 = 37530;
+pub const GL_COLORBURN_KHR: u32 = 37530;
+pub const GL_COLORBURN_NV: u32 = 37530;
+pub const GL_HARDLIGHT: u32 = 37531;
+pub const GL_HARDLIGHT_KHR: u32 = 37531;
+pub const GL_HARDLIGHT_NV: u32 = 37531;
+pub const GL_SOFTLIGHT: u32 = 37532;
+pub const GL_SOFTLIGHT_KHR: u32 = 37532;
+pub const GL_SOFTLIGHT_NV: u32 = 37532;
+pub const GL_DIFFERENCE: u32 = 37534;
+pub const GL_DIFFERENCE_KHR: u32 = 37534;
+pub const GL_DIFFERENCE_NV: u32 = 37534;
+pub const GL_MINUS_NV: u32 = 37535;
+pub const GL_EXCLUSION: u32 = 37536;
+pub const GL_EXCLUSION_KHR: u32 = 37536;
+pub const GL_EXCLUSION_NV: u32 = 37536;
+pub const GL_CONTRAST_NV: u32 = 37537;
+pub const GL_INVERT_RGB_NV: u32 = 37539;
+pub const GL_LINEARDODGE_NV: u32 = 37540;
+pub const GL_LINEARBURN_NV: u32 = 37541;
+pub const GL_VIVIDLIGHT_NV: u32 = 37542;
+pub const GL_LINEARLIGHT_NV: u32 = 37543;
+pub const GL_PINLIGHT_NV: u32 = 37544;
+pub const GL_HARDMIX_NV: u32 = 37545;
+pub const GL_HSL_HUE: u32 = 37549;
+pub const GL_HSL_HUE_KHR: u32 = 37549;
+pub const GL_HSL_HUE_NV: u32 = 37549;
+pub const GL_HSL_SATURATION: u32 = 37550;
+pub const GL_HSL_SATURATION_KHR: u32 = 37550;
+pub const GL_HSL_SATURATION_NV: u32 = 37550;
+pub const GL_HSL_COLOR: u32 = 37551;
+pub const GL_HSL_COLOR_KHR: u32 = 37551;
+pub const GL_HSL_COLOR_NV: u32 = 37551;
+pub const GL_HSL_LUMINOSITY: u32 = 37552;
+pub const GL_HSL_LUMINOSITY_KHR: u32 = 37552;
+pub const GL_HSL_LUMINOSITY_NV: u32 = 37552;
+pub const GL_PLUS_CLAMPED_NV: u32 = 37553;
+pub const GL_PLUS_CLAMPED_ALPHA_NV: u32 = 37554;
+pub const GL_MINUS_CLAMPED_NV: u32 = 37555;
+pub const GL_INVERT_OVG_NV: u32 = 37556;
+pub const GL_PURGED_CONTEXT_RESET_NV: u32 = 37563;
+pub const GL_PRIMITIVE_BOUNDING_BOX: u32 = 37566;
+pub const GL_PRIMITIVE_BOUNDING_BOX_ARB: u32 = 37566;
+pub const GL_PRIMITIVE_BOUNDING_BOX_EXT: u32 = 37566;
+pub const GL_PRIMITIVE_BOUNDING_BOX_OES: u32 = 37566;
+pub const GL_ATOMIC_COUNTER_BUFFER: u32 = 37568;
+pub const GL_ATOMIC_COUNTER_BUFFER_BINDING: u32 = 37569;
+pub const GL_ATOMIC_COUNTER_BUFFER_START: u32 = 37570;
+pub const GL_ATOMIC_COUNTER_BUFFER_SIZE: u32 = 37571;
+pub const GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE: u32 = 37572;
+pub const GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS: u32 = 37573;
+pub const GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES: u32 = 37574;
+pub const GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER: u32 = 37575;
+pub const GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER: u32 = 37576;
+pub const GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER: u32 = 37577;
+pub const GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER: u32 = 37578;
+pub const GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER: u32 = 37579;
+pub const GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS: u32 = 37580;
+pub const GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS: u32 = 37581;
+pub const GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT: u32 = 37581;
+pub const GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_OES: u32 = 37581;
+pub const GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS: u32 = 37582;
+pub const GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT: u32 = 37582;
+pub const GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_OES: u32 = 37582;
+pub const GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS: u32 = 37583;
+pub const GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT: u32 = 37583;
+pub const GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_OES: u32 = 37583;
+pub const GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS: u32 = 37584;
+pub const GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS: u32 = 37585;
+pub const GL_MAX_VERTEX_ATOMIC_COUNTERS: u32 = 37586;
+pub const GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS: u32 = 37587;
+pub const GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT: u32 = 37587;
+pub const GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_OES: u32 = 37587;
+pub const GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS: u32 = 37588;
+pub const GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT: u32 = 37588;
+pub const GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_OES: u32 = 37588;
+pub const GL_MAX_GEOMETRY_ATOMIC_COUNTERS: u32 = 37589;
+pub const GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT: u32 = 37589;
+pub const GL_MAX_GEOMETRY_ATOMIC_COUNTERS_OES: u32 = 37589;
+pub const GL_MAX_FRAGMENT_ATOMIC_COUNTERS: u32 = 37590;
+pub const GL_MAX_COMBINED_ATOMIC_COUNTERS: u32 = 37591;
+pub const GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE: u32 = 37592;
+pub const GL_ACTIVE_ATOMIC_COUNTER_BUFFERS: u32 = 37593;
+pub const GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX: u32 = 37594;
+pub const GL_UNSIGNED_INT_ATOMIC_COUNTER: u32 = 37595;
+pub const GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS: u32 = 37596;
+pub const GL_FRAGMENT_COVERAGE_TO_COLOR_NV: u32 = 37597;
+pub const GL_FRAGMENT_COVERAGE_COLOR_NV: u32 = 37598;
+pub const GL_DEBUG_OUTPUT: u32 = 37600;
+pub const GL_DEBUG_OUTPUT_KHR: u32 = 37600;
+pub const GL_UNIFORM: u32 = 37601;
+pub const GL_UNIFORM_BLOCK: u32 = 37602;
+pub const GL_PROGRAM_INPUT: u32 = 37603;
+pub const GL_PROGRAM_OUTPUT: u32 = 37604;
+pub const GL_BUFFER_VARIABLE: u32 = 37605;
+pub const GL_SHADER_STORAGE_BLOCK: u32 = 37606;
+pub const GL_IS_PER_PATCH: u32 = 37607;
+pub const GL_IS_PER_PATCH_EXT: u32 = 37607;
+pub const GL_IS_PER_PATCH_OES: u32 = 37607;
+pub const GL_VERTEX_SUBROUTINE: u32 = 37608;
+pub const GL_TESS_CONTROL_SUBROUTINE: u32 = 37609;
+pub const GL_TESS_EVALUATION_SUBROUTINE: u32 = 37610;
+pub const GL_GEOMETRY_SUBROUTINE: u32 = 37611;
+pub const GL_FRAGMENT_SUBROUTINE: u32 = 37612;
+pub const GL_COMPUTE_SUBROUTINE: u32 = 37613;
+pub const GL_VERTEX_SUBROUTINE_UNIFORM: u32 = 37614;
+pub const GL_TESS_CONTROL_SUBROUTINE_UNIFORM: u32 = 37615;
+pub const GL_TESS_EVALUATION_SUBROUTINE_UNIFORM: u32 = 37616;
+pub const GL_GEOMETRY_SUBROUTINE_UNIFORM: u32 = 37617;
+pub const GL_FRAGMENT_SUBROUTINE_UNIFORM: u32 = 37618;
+pub const GL_COMPUTE_SUBROUTINE_UNIFORM: u32 = 37619;
+pub const GL_TRANSFORM_FEEDBACK_VARYING: u32 = 37620;
+pub const GL_ACTIVE_RESOURCES: u32 = 37621;
+pub const GL_MAX_NAME_LENGTH: u32 = 37622;
+pub const GL_MAX_NUM_ACTIVE_VARIABLES: u32 = 37623;
+pub const GL_MAX_NUM_COMPATIBLE_SUBROUTINES: u32 = 37624;
+pub const GL_NAME_LENGTH: u32 = 37625;
+pub const GL_TYPE: u32 = 37626;
+pub const GL_ARRAY_SIZE: u32 = 37627;
+pub const GL_OFFSET: u32 = 37628;
+pub const GL_BLOCK_INDEX: u32 = 37629;
+pub const GL_ARRAY_STRIDE: u32 = 37630;
+pub const GL_MATRIX_STRIDE: u32 = 37631;
+pub const GL_IS_ROW_MAJOR: u32 = 37632;
+pub const GL_ATOMIC_COUNTER_BUFFER_INDEX: u32 = 37633;
+pub const GL_BUFFER_BINDING: u32 = 37634;
+pub const GL_BUFFER_DATA_SIZE: u32 = 37635;
+pub const GL_NUM_ACTIVE_VARIABLES: u32 = 37636;
+pub const GL_ACTIVE_VARIABLES: u32 = 37637;
+pub const GL_REFERENCED_BY_VERTEX_SHADER: u32 = 37638;
+pub const GL_REFERENCED_BY_TESS_CONTROL_SHADER: u32 = 37639;
+pub const GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT: u32 = 37639;
+pub const GL_REFERENCED_BY_TESS_CONTROL_SHADER_OES: u32 = 37639;
+pub const GL_REFERENCED_BY_TESS_EVALUATION_SHADER: u32 = 37640;
+pub const GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT: u32 = 37640;
+pub const GL_REFERENCED_BY_TESS_EVALUATION_SHADER_OES: u32 = 37640;
+pub const GL_REFERENCED_BY_GEOMETRY_SHADER: u32 = 37641;
+pub const GL_REFERENCED_BY_GEOMETRY_SHADER_EXT: u32 = 37641;
+pub const GL_REFERENCED_BY_GEOMETRY_SHADER_OES: u32 = 37641;
+pub const GL_REFERENCED_BY_FRAGMENT_SHADER: u32 = 37642;
+pub const GL_REFERENCED_BY_COMPUTE_SHADER: u32 = 37643;
+pub const GL_TOP_LEVEL_ARRAY_SIZE: u32 = 37644;
+pub const GL_TOP_LEVEL_ARRAY_STRIDE: u32 = 37645;
+pub const GL_LOCATION: u32 = 37646;
+pub const GL_LOCATION_INDEX: u32 = 37647;
+pub const GL_LOCATION_INDEX_EXT: u32 = 37647;
+pub const GL_FRAMEBUFFER_DEFAULT_WIDTH: u32 = 37648;
+pub const GL_FRAMEBUFFER_DEFAULT_HEIGHT: u32 = 37649;
+pub const GL_FRAMEBUFFER_DEFAULT_LAYERS: u32 = 37650;
+pub const GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT: u32 = 37650;
+pub const GL_FRAMEBUFFER_DEFAULT_LAYERS_OES: u32 = 37650;
+pub const GL_FRAMEBUFFER_DEFAULT_SAMPLES: u32 = 37651;
+pub const GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS: u32 = 37652;
+pub const GL_MAX_FRAMEBUFFER_WIDTH: u32 = 37653;
+pub const GL_MAX_FRAMEBUFFER_HEIGHT: u32 = 37654;
+pub const GL_MAX_FRAMEBUFFER_LAYERS: u32 = 37655;
+pub const GL_MAX_FRAMEBUFFER_LAYERS_EXT: u32 = 37655;
+pub const GL_MAX_FRAMEBUFFER_LAYERS_OES: u32 = 37655;
+pub const GL_MAX_FRAMEBUFFER_SAMPLES: u32 = 37656;
+pub const GL_RASTER_MULTISAMPLE_EXT: u32 = 37671;
+pub const GL_RASTER_SAMPLES_EXT: u32 = 37672;
+pub const GL_MAX_RASTER_SAMPLES_EXT: u32 = 37673;
+pub const GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT: u32 = 37674;
+pub const GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT: u32 = 37675;
+pub const GL_EFFECTIVE_RASTER_SAMPLES_EXT: u32 = 37676;
+pub const GL_DEPTH_SAMPLES_NV: u32 = 37677;
+pub const GL_STENCIL_SAMPLES_NV: u32 = 37678;
+pub const GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV: u32 = 37679;
+pub const GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV: u32 = 37680;
+pub const GL_COVERAGE_MODULATION_TABLE_NV: u32 = 37681;
+pub const GL_COVERAGE_MODULATION_NV: u32 = 37682;
+pub const GL_COVERAGE_MODULATION_TABLE_SIZE_NV: u32 = 37683;
+pub const GL_WARP_SIZE_NV: u32 = 37689;
+pub const GL_WARPS_PER_SM_NV: u32 = 37690;
+pub const GL_SM_COUNT_NV: u32 = 37691;
+pub const GL_FILL_RECTANGLE_NV: u32 = 37692;
+pub const GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB: u32 = 37693;
+pub const GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV: u32 = 37693;
+pub const GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB: u32 = 37694;
+pub const GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV: u32 = 37694;
+pub const GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB: u32 = 37695;
+pub const GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV: u32 = 37695;
+pub const GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB: u32 = 37696;
+pub const GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV: u32 = 37696;
+pub const GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB: u32 = 37697;
+pub const GL_PROGRAMMABLE_SAMPLE_LOCATION_NV: u32 = 37697;
+pub const GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB: u32 = 37698;
+pub const GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV: u32 = 37698;
+pub const GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB: u32 = 37699;
+pub const GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV: u32 = 37699;
+pub const GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB: u32 = 37700;
+pub const GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB: u32 = 37701;
+pub const GL_CONSERVATIVE_RASTERIZATION_NV: u32 = 37702;
+pub const GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV: u32 = 37703;
+pub const GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV: u32 = 37704;
+pub const GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV: u32 = 37705;
+pub const GL_LOCATION_COMPONENT: u32 = 37706;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_INDEX: u32 = 37707;
+pub const GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE: u32 = 37708;
+pub const GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV: u32 = 37712;
+pub const GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV: u32 = 37713;
+pub const GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV: u32 = 37714;
+pub const GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV: u32 = 37715;
+pub const GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV: u32 = 37716;
+pub const GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV: u32 = 37717;
+pub const GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV: u32 = 37718;
+pub const GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV: u32 = 37719;
+pub const GL_VIEWPORT_SWIZZLE_X_NV: u32 = 37720;
+pub const GL_VIEWPORT_SWIZZLE_Y_NV: u32 = 37721;
+pub const GL_VIEWPORT_SWIZZLE_Z_NV: u32 = 37722;
+pub const GL_VIEWPORT_SWIZZLE_W_NV: u32 = 37723;
+pub const GL_CLIP_ORIGIN: u32 = 37724;
+pub const GL_CLIP_DEPTH_MODE: u32 = 37725;
+pub const GL_NEGATIVE_ONE_TO_ONE: u32 = 37726;
+pub const GL_ZERO_TO_ONE: u32 = 37727;
+pub const GL_CLEAR_TEXTURE: u32 = 37733;
+pub const GL_TEXTURE_REDUCTION_MODE_ARB: u32 = 37734;
+pub const GL_WEIGHTED_AVERAGE_ARB: u32 = 37735;
+pub const GL_FONT_GLYPHS_AVAILABLE_NV: u32 = 37736;
+pub const GL_FONT_TARGET_UNAVAILABLE_NV: u32 = 37737;
+pub const GL_FONT_UNAVAILABLE_NV: u32 = 37738;
+pub const GL_FONT_UNINTELLIGIBLE_NV: u32 = 37739;
+pub const GL_STANDARD_FONT_FORMAT_NV: u32 = 37740;
+pub const GL_FRAGMENT_INPUT_NV: u32 = 37741;
+pub const GL_UNIFORM_BUFFER_UNIFIED_NV: u32 = 37742;
+pub const GL_UNIFORM_BUFFER_ADDRESS_NV: u32 = 37743;
+pub const GL_UNIFORM_BUFFER_LENGTH_NV: u32 = 37744;
+pub const GL_MULTISAMPLES_NV: u32 = 37745;
+pub const GL_SUPERSAMPLE_SCALE_X_NV: u32 = 37746;
+pub const GL_SUPERSAMPLE_SCALE_Y_NV: u32 = 37747;
+pub const GL_CONFORMANT_NV: u32 = 37748;
+pub const GL_CONSERVATIVE_RASTER_DILATE_NV: u32 = 37753;
+pub const GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV: u32 = 37754;
+pub const GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV: u32 = 37755;
+pub const GL_VIEWPORT_POSITION_W_SCALE_NV: u32 = 37756;
+pub const GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV: u32 = 37757;
+pub const GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV: u32 = 37758;
+pub const GL_NUM_SAMPLE_COUNTS: u32 = 37760;
+pub const GL_MULTISAMPLE_LINE_WIDTH_RANGE: u32 = 37761;
+pub const GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB: u32 = 37761;
+pub const GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY: u32 = 37762;
+pub const GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB: u32 = 37762;
+pub const GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE: u32 = 37792;
+pub const GL_BGRA8_EXT: u32 = 37793;
+pub const GL_TEXTURE_USAGE_ANGLE: u32 = 37794;
+pub const GL_FRAMEBUFFER_ATTACHMENT_ANGLE: u32 = 37795;
+pub const GL_PACK_REVERSE_ROW_ORDER_ANGLE: u32 = 37796;
+pub const GL_PROGRAM_BINARY_ANGLE: u32 = 37798;
+pub const GL_COMPRESSED_RGBA_ASTC_4x4: u32 = 37808;
+pub const GL_COMPRESSED_RGBA_ASTC_4x4_KHR: u32 = 37808;
+pub const GL_COMPRESSED_RGBA_ASTC_5x4: u32 = 37809;
+pub const GL_COMPRESSED_RGBA_ASTC_5x4_KHR: u32 = 37809;
+pub const GL_COMPRESSED_RGBA_ASTC_5x5: u32 = 37810;
+pub const GL_COMPRESSED_RGBA_ASTC_5x5_KHR: u32 = 37810;
+pub const GL_COMPRESSED_RGBA_ASTC_6x5: u32 = 37811;
+pub const GL_COMPRESSED_RGBA_ASTC_6x5_KHR: u32 = 37811;
+pub const GL_COMPRESSED_RGBA_ASTC_6x6: u32 = 37812;
+pub const GL_COMPRESSED_RGBA_ASTC_6x6_KHR: u32 = 37812;
+pub const GL_COMPRESSED_RGBA_ASTC_8x5: u32 = 37813;
+pub const GL_COMPRESSED_RGBA_ASTC_8x5_KHR: u32 = 37813;
+pub const GL_COMPRESSED_RGBA_ASTC_8x6: u32 = 37814;
+pub const GL_COMPRESSED_RGBA_ASTC_8x6_KHR: u32 = 37814;
+pub const GL_COMPRESSED_RGBA_ASTC_8x8: u32 = 37815;
+pub const GL_COMPRESSED_RGBA_ASTC_8x8_KHR: u32 = 37815;
+pub const GL_COMPRESSED_RGBA_ASTC_10x5: u32 = 37816;
+pub const GL_COMPRESSED_RGBA_ASTC_10x5_KHR: u32 = 37816;
+pub const GL_COMPRESSED_RGBA_ASTC_10x6: u32 = 37817;
+pub const GL_COMPRESSED_RGBA_ASTC_10x6_KHR: u32 = 37817;
+pub const GL_COMPRESSED_RGBA_ASTC_10x8: u32 = 37818;
+pub const GL_COMPRESSED_RGBA_ASTC_10x8_KHR: u32 = 37818;
+pub const GL_COMPRESSED_RGBA_ASTC_10x10: u32 = 37819;
+pub const GL_COMPRESSED_RGBA_ASTC_10x10_KHR: u32 = 37819;
+pub const GL_COMPRESSED_RGBA_ASTC_12x10: u32 = 37820;
+pub const GL_COMPRESSED_RGBA_ASTC_12x10_KHR: u32 = 37820;
+pub const GL_COMPRESSED_RGBA_ASTC_12x12: u32 = 37821;
+pub const GL_COMPRESSED_RGBA_ASTC_12x12_KHR: u32 = 37821;
+pub const GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: u32 = 37824;
+pub const GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: u32 = 37825;
+pub const GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: u32 = 37826;
+pub const GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: u32 = 37827;
+pub const GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: u32 = 37828;
+pub const GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: u32 = 37829;
+pub const GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: u32 = 37830;
+pub const GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: u32 = 37831;
+pub const GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: u32 = 37832;
+pub const GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: u32 = 37833;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: u32 = 37840;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: u32 = 37840;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: u32 = 37841;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: u32 = 37841;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: u32 = 37842;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: u32 = 37842;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: u32 = 37843;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: u32 = 37843;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: u32 = 37844;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: u32 = 37844;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: u32 = 37845;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: u32 = 37845;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: u32 = 37846;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: u32 = 37846;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: u32 = 37847;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: u32 = 37847;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: u32 = 37848;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: u32 = 37848;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: u32 = 37849;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: u32 = 37849;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: u32 = 37850;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: u32 = 37850;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: u32 = 37851;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: u32 = 37851;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: u32 = 37852;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: u32 = 37852;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: u32 = 37853;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: u32 = 37853;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: u32 = 37856;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: u32 = 37857;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: u32 = 37858;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: u32 = 37859;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: u32 = 37860;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: u32 = 37861;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: u32 = 37862;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: u32 = 37863;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: u32 = 37864;
+pub const GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: u32 = 37865;
+pub const GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: u32 = 37872;
+pub const GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: u32 = 37873;
+pub const GL_PERFQUERY_COUNTER_EVENT_INTEL: u32 = 38128;
+pub const GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL: u32 = 38129;
+pub const GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL: u32 = 38130;
+pub const GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL: u32 = 38131;
+pub const GL_PERFQUERY_COUNTER_RAW_INTEL: u32 = 38132;
+pub const GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL: u32 = 38133;
+pub const GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL: u32 = 38136;
+pub const GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL: u32 = 38137;
+pub const GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL: u32 = 38138;
+pub const GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL: u32 = 38139;
+pub const GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL: u32 = 38140;
+pub const GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL: u32 = 38141;
+pub const GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL: u32 = 38142;
+pub const GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL: u32 = 38143;
+pub const GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL: u32 = 38144;
+pub const GL_CONSERVATIVE_RASTER_MODE_NV: u32 = 38221;
+pub const GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV: u32 = 38222;
+pub const GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV: u32 = 38223;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR: u32 = 38448;
+pub const GL_MAX_VIEWS_OVR: u32 = 38449;
+pub const GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR: u32 = 38450;
+pub const GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR: u32 = 38451;
+pub const GL_GS_SHADER_BINARY_MTK: u32 = 38464;
+pub const GL_GS_PROGRAM_BINARY_MTK: u32 = 38465;
+pub const GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_FAST_SIZE_EXT: u32 = 38480;
+pub const GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_SIZE_EXT: u32 = 38481;
+pub const GL_FRAMEBUFFER_INCOMPLETE_INSUFFICIENT_SHADER_COMBINED_LOCAL_STORAGE_EXT: u32 = 38482;
+pub const GL_SHARED_EDGE_NV: u32 = 192;
+pub const GL_ROUNDED_RECT_NV: u32 = 232;
+pub const GL_RELATIVE_ROUNDED_RECT_NV: u32 = 233;
+pub const GL_ROUNDED_RECT2_NV: u32 = 234;
+pub const GL_RELATIVE_ROUNDED_RECT2_NV: u32 = 235;
+pub const GL_ROUNDED_RECT4_NV: u32 = 236;
+pub const GL_RELATIVE_ROUNDED_RECT4_NV: u32 = 237;
+pub const GL_ROUNDED_RECT8_NV: u32 = 238;
+pub const GL_RELATIVE_ROUNDED_RECT8_NV: u32 = 239;
+pub const GL_RESTART_PATH_NV: u32 = 240;
+pub const GL_DUP_FIRST_CUBIC_CURVE_TO_NV: u32 = 242;
+pub const GL_DUP_LAST_CUBIC_CURVE_TO_NV: u32 = 244;
+pub const GL_RECT_NV: u32 = 246;
+pub const GL_RELATIVE_RECT_NV: u32 = 247;
+pub const GL_CIRCULAR_CCW_ARC_TO_NV: u32 = 248;
+pub const GL_CIRCULAR_CW_ARC_TO_NV: u32 = 250;
+pub const GL_CIRCULAR_TANGENT_ARC_TO_NV: u32 = 252;
+pub const GL_ARC_TO_NV: u32 = 254;
+pub const GL_RELATIVE_ARC_TO_NV: u32 = 255;
+pub const GL_TRACE_ALL_BITS_MESA: u32 = 65535;
+pub const GL_ALL_BARRIER_BITS: u32 = 4294967295;
+pub const GL_ALL_BARRIER_BITS_EXT: u32 = 4294967295;
+pub const GL_ALL_PIXELS_AMD: u32 = 4294967295;
+pub const GL_ALL_SHADER_BITS: u32 = 4294967295;
+pub const GL_ALL_SHADER_BITS_EXT: u32 = 4294967295;
+pub const GL_CLIENT_ALL_ATTRIB_BITS: u32 = 4294967295;
+pub const GL_INVALID_INDEX: u32 = 4294967295;
+pub const GL_QUERY_ALL_EVENT_BITS_AMD: u32 = 4294967295;
+pub const GL_TIMEOUT_IGNORED: i32 = -1;
+pub const GL_TIMEOUT_IGNORED_APPLE: i32 = -1;
+pub const GL_LAYOUT_LINEAR_INTEL: u32 = 1;
+pub const GL_ONE: u32 = 1;
+pub const GL_TRUE: u32 = 1;
+pub const GL_VERSION_ES_CL_1_0: u32 = 1;
+pub const GL_VERSION_ES_CL_1_1: u32 = 1;
+pub const GL_VERSION_ES_CM_1_1: u32 = 1;
+pub const GL_CULL_VERTEX_IBM: u32 = 103050;
+pub const GL_ALL_STATIC_DATA_IBM: u32 = 103060;
+pub const GL_STATIC_VERTEX_ARRAY_IBM: u32 = 103061;
+pub const GL_VERTEX_ARRAY_LIST_IBM: u32 = 103070;
+pub const GL_NORMAL_ARRAY_LIST_IBM: u32 = 103071;
+pub const GL_COLOR_ARRAY_LIST_IBM: u32 = 103072;
+pub const GL_INDEX_ARRAY_LIST_IBM: u32 = 103073;
+pub const GL_TEXTURE_COORD_ARRAY_LIST_IBM: u32 = 103074;
+pub const GL_EDGE_FLAG_ARRAY_LIST_IBM: u32 = 103075;
+pub const GL_FOG_COORDINATE_ARRAY_LIST_IBM: u32 = 103076;
+pub const GL_SECONDARY_COLOR_ARRAY_LIST_IBM: u32 = 103077;
+pub const GL_VERTEX_ARRAY_LIST_STRIDE_IBM: u32 = 103080;
+pub const GL_NORMAL_ARRAY_LIST_STRIDE_IBM: u32 = 103081;
+pub const GL_COLOR_ARRAY_LIST_STRIDE_IBM: u32 = 103082;
+pub const GL_INDEX_ARRAY_LIST_STRIDE_IBM: u32 = 103083;
+pub const GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM: u32 = 103084;
+pub const GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM: u32 = 103085;
+pub const GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM: u32 = 103086;
+pub const GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM: u32 = 103087;
+pub const GL_LAYOUT_LINEAR_CPU_CACHED_INTEL: u32 = 2;
+pub const EGL_VERSION_1_0: u32 = 1;
+pub const EGL_VERSION_1_1: u32 = 1;
+pub const EGL_VERSION_1_2: u32 = 1;
+pub const EGL_VERSION_1_3: u32 = 1;
+pub const EGL_VERSION_1_4: u32 = 1;
+pub const EGL_VERSION_1_5: u32 = 1;
+pub const EGL_ANDROID_blob_cache: u32 = 1;
+pub const EGL_ANDROID_create_native_client_buffer: u32 = 1;
+pub const EGL_ANDROID_framebuffer_target: u32 = 1;
+pub const EGL_ANDROID_front_buffer_auto_refresh: u32 = 1;
+pub const EGL_ANDROID_image_native_buffer: u32 = 1;
+pub const EGL_ANDROID_native_fence_sync: u32 = 1;
+pub const EGL_ANDROID_presentation_time: u32 = 1;
+pub const EGL_ANDROID_recordable: u32 = 1;
+pub const EGL_ANGLE_d3d_share_handle_client_buffer: u32 = 1;
+pub const EGL_ANGLE_device_d3d: u32 = 1;
+pub const EGL_ANGLE_query_surface_pointer: u32 = 1;
+pub const EGL_ANGLE_surface_d3d_texture_2d_share_handle: u32 = 1;
+pub const EGL_ANGLE_window_fixed_size: u32 = 1;
+pub const EGL_ARM_implicit_external_sync: u32 = 1;
+pub const EGL_ARM_pixmap_multisample_discard: u32 = 1;
+pub const EGL_EXT_buffer_age: u32 = 1;
+pub const EGL_EXT_client_extensions: u32 = 1;
+pub const EGL_EXT_create_context_robustness: u32 = 1;
+pub const EGL_EXT_device_base: u32 = 1;
+pub const EGL_EXT_device_drm: u32 = 1;
+pub const EGL_EXT_device_enumeration: u32 = 1;
+pub const EGL_EXT_device_openwf: u32 = 1;
+pub const EGL_EXT_device_query: u32 = 1;
+pub const EGL_EXT_gl_colorspace_bt2020_linear: u32 = 1;
+pub const EGL_EXT_gl_colorspace_bt2020_pq: u32 = 1;
+pub const EGL_EXT_gl_colorspace_scrgb_linear: u32 = 1;
+pub const EGL_EXT_image_dma_buf_import: u32 = 1;
+pub const EGL_EXT_image_dma_buf_import_modifiers: u32 = 1;
+pub const EGL_EXT_multiview_window: u32 = 1;
+pub const EGL_EXT_output_base: u32 = 1;
+pub const EGL_EXT_output_drm: u32 = 1;
+pub const EGL_EXT_output_openwf: u32 = 1;
+pub const EGL_EXT_pixel_format_float: u32 = 1;
+pub const EGL_EXT_platform_base: u32 = 1;
+pub const EGL_EXT_platform_device: u32 = 1;
+pub const EGL_EXT_platform_wayland: u32 = 1;
+pub const EGL_EXT_platform_x11: u32 = 1;
+pub const EGL_EXT_protected_content: u32 = 1;
+pub const EGL_EXT_protected_surface: u32 = 1;
+pub const EGL_EXT_stream_consumer_egloutput: u32 = 1;
+pub const EGL_EXT_surface_SMPTE2086_metadata: u32 = 1;
+pub const EGL_EXT_swap_buffers_with_damage: u32 = 1;
+pub const EGL_EXT_yuv_surface: u32 = 1;
+pub const EGL_HI_clientpixmap: u32 = 1;
+pub const EGL_HI_colorformats: u32 = 1;
+pub const EGL_IMG_context_priority: u32 = 1;
+pub const EGL_IMG_image_plane_attribs: u32 = 1;
+pub const EGL_KHR_cl_event: u32 = 1;
+pub const EGL_KHR_cl_event2: u32 = 1;
+pub const EGL_KHR_client_get_all_proc_addresses: u32 = 1;
+pub const EGL_KHR_config_attribs: u32 = 1;
+pub const EGL_KHR_context_flush_control: u32 = 1;
+pub const EGL_KHR_create_context: u32 = 1;
+pub const EGL_KHR_create_context_no_error: u32 = 1;
+pub const EGL_KHR_debug: u32 = 1;
+pub const EGL_KHR_fence_sync: u32 = 1;
+pub const EGL_KHR_get_all_proc_addresses: u32 = 1;
+pub const EGL_KHR_gl_colorspace: u32 = 1;
+pub const EGL_KHR_gl_renderbuffer_image: u32 = 1;
+pub const EGL_KHR_gl_texture_2D_image: u32 = 1;
+pub const EGL_KHR_gl_texture_3D_image: u32 = 1;
+pub const EGL_KHR_gl_texture_cubemap_image: u32 = 1;
+pub const EGL_KHR_image: u32 = 1;
+pub const EGL_KHR_image_base: u32 = 1;
+pub const EGL_KHR_image_pixmap: u32 = 1;
+pub const EGL_KHR_lock_surface: u32 = 1;
+pub const EGL_KHR_lock_surface2: u32 = 1;
+pub const EGL_KHR_lock_surface3: u32 = 1;
+pub const EGL_KHR_mutable_render_buffer: u32 = 1;
+pub const EGL_KHR_no_config_context: u32 = 1;
+pub const EGL_KHR_partial_update: u32 = 1;
+pub const EGL_KHR_platform_android: u32 = 1;
+pub const EGL_KHR_platform_gbm: u32 = 1;
+pub const EGL_KHR_platform_wayland: u32 = 1;
+pub const EGL_KHR_platform_x11: u32 = 1;
+pub const EGL_KHR_reusable_sync: u32 = 1;
+pub const EGL_KHR_stream: u32 = 1;
+pub const EGL_KHR_stream_attrib: u32 = 1;
+pub const EGL_KHR_stream_consumer_gltexture: u32 = 1;
+pub const EGL_KHR_stream_cross_process_fd: u32 = 1;
+pub const EGL_KHR_stream_fifo: u32 = 1;
+pub const EGL_KHR_stream_producer_aldatalocator: u32 = 1;
+pub const EGL_KHR_stream_producer_eglsurface: u32 = 1;
+pub const EGL_KHR_surfaceless_context: u32 = 1;
+pub const EGL_KHR_swap_buffers_with_damage: u32 = 1;
+pub const EGL_KHR_vg_parent_image: u32 = 1;
+pub const EGL_KHR_wait_sync: u32 = 1;
+pub const EGL_MESA_drm_image: u32 = 1;
+pub const EGL_MESA_image_dma_buf_export: u32 = 1;
+pub const EGL_MESA_platform_gbm: u32 = 1;
+pub const EGL_MESA_platform_surfaceless: u32 = 1;
+pub const EGL_NOK_swap_region: u32 = 1;
+pub const EGL_NOK_swap_region2: u32 = 1;
+pub const EGL_NOK_texture_from_pixmap: u32 = 1;
+pub const EGL_NV_3dvision_surface: u32 = 1;
+pub const EGL_NV_coverage_sample: u32 = 1;
+pub const EGL_NV_coverage_sample_resolve: u32 = 1;
+pub const EGL_NV_cuda_event: u32 = 1;
+pub const EGL_NV_depth_nonlinear: u32 = 1;
+pub const EGL_NV_device_cuda: u32 = 1;
+pub const EGL_NV_native_query: u32 = 1;
+pub const EGL_NV_post_convert_rounding: u32 = 1;
+pub const EGL_NV_post_sub_buffer: u32 = 1;
+pub const EGL_NV_robustness_video_memory_purge: u32 = 1;
+pub const EGL_NV_stream_consumer_gltexture_yuv: u32 = 1;
+pub const EGL_NV_stream_cross_display: u32 = 1;
+pub const EGL_NV_stream_cross_object: u32 = 1;
+pub const EGL_NV_stream_cross_partition: u32 = 1;
+pub const EGL_NV_stream_cross_process: u32 = 1;
+pub const EGL_NV_stream_cross_system: u32 = 1;
+pub const EGL_NV_stream_fifo_next: u32 = 1;
+pub const EGL_NV_stream_fifo_synchronous: u32 = 1;
+pub const EGL_NV_stream_frame_limits: u32 = 1;
+pub const EGL_NV_stream_metadata: u32 = 1;
+pub const EGL_NV_stream_remote: u32 = 1;
+pub const EGL_NV_stream_reset: u32 = 1;
+pub const EGL_NV_stream_socket: u32 = 1;
+pub const EGL_NV_stream_socket_inet: u32 = 1;
+pub const EGL_NV_stream_socket_unix: u32 = 1;
+pub const EGL_NV_stream_sync: u32 = 1;
+pub const EGL_NV_sync: u32 = 1;
+pub const EGL_NV_system_time: u32 = 1;
+pub const EGL_TIZEN_image_native_buffer: u32 = 1;
+pub const EGL_TIZEN_image_native_surface: u32 = 1;
+pub const EGL_NO_NATIVE_FENCE_FD_ANDROID: i32 = -1;
+pub const EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR: u32 = 0;
+pub const EGL_DEPTH_ENCODING_NONE_NV: u32 = 0;
+pub const EGL_FALSE: u32 = 0;
+pub const EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT: u32 = 1;
+pub const EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR: u32 = 1;
+pub const EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR: u32 = 1;
+pub const EGL_DRM_BUFFER_USE_SCANOUT_MESA: u32 = 1;
+pub const EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID: u32 = 1;
+pub const EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT: u32 = 2;
+pub const EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR: u32 = 2;
+pub const EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR: u32 = 2;
+pub const EGL_DRM_BUFFER_USE_SHARE_MESA: u32 = 2;
+pub const EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID: u32 = 2;
+pub const EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR: u32 = 4;
+pub const EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID: u32 = 4;
+pub const EGL_OPENGL_ES3_BIT: u32 = 64;
+pub const EGL_OPENGL_ES3_BIT_KHR: u32 = 64;
+pub const EGL_OPENGL_ES_BIT: u32 = 1;
+pub const EGL_PBUFFER_BIT: u32 = 1;
+pub const EGL_READ_SURFACE_BIT_KHR: u32 = 1;
+pub const EGL_SYNC_FLUSH_COMMANDS_BIT: u32 = 1;
+pub const EGL_SYNC_FLUSH_COMMANDS_BIT_KHR: u32 = 1;
+pub const EGL_SYNC_FLUSH_COMMANDS_BIT_NV: u32 = 1;
+pub const EGL_OPENVG_BIT: u32 = 2;
+pub const EGL_PIXMAP_BIT: u32 = 2;
+pub const EGL_WRITE_SURFACE_BIT_KHR: u32 = 2;
+pub const EGL_OPENGL_ES2_BIT: u32 = 4;
+pub const EGL_WINDOW_BIT: u32 = 4;
+pub const EGL_OPENGL_BIT: u32 = 8;
+pub const EGL_PBUFFER_IMAGE_BIT_TAO: u32 = 8;
+pub const EGL_INTEROP_BIT_KHR: u32 = 16;
+pub const EGL_PBUFFER_PALETTE_IMAGE_BIT_TAO: u32 = 16;
+pub const EGL_OPENMAX_IL_BIT_KHR: u32 = 32;
+pub const EGL_VG_COLORSPACE_LINEAR_BIT: u32 = 32;
+pub const EGL_VG_COLORSPACE_LINEAR_BIT_KHR: u32 = 32;
+pub const EGL_VG_ALPHA_FORMAT_PRE_BIT: u32 = 64;
+pub const EGL_VG_ALPHA_FORMAT_PRE_BIT_KHR: u32 = 64;
+pub const EGL_LOCK_SURFACE_BIT_KHR: u32 = 128;
+pub const EGL_OPTIMAL_FORMAT_BIT_KHR: u32 = 256;
+pub const EGL_MULTISAMPLE_RESOLVE_BOX_BIT: u32 = 512;
+pub const EGL_SWAP_BEHAVIOR_PRESERVED_BIT: u32 = 1024;
+pub const EGL_STREAM_BIT_KHR: u32 = 2048;
+pub const EGL_MUTABLE_RENDER_BUFFER_BIT_KHR: u32 = 4096;
+pub const EGL_CONTEXT_RELEASE_BEHAVIOR_KHR: u32 = 8343;
+pub const EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR: u32 = 8344;
+pub const EGL_SUCCESS: u32 = 12288;
+pub const EGL_NOT_INITIALIZED: u32 = 12289;
+pub const EGL_BAD_ACCESS: u32 = 12290;
+pub const EGL_BAD_ALLOC: u32 = 12291;
+pub const EGL_BAD_ATTRIBUTE: u32 = 12292;
+pub const EGL_BAD_CONFIG: u32 = 12293;
+pub const EGL_BAD_CONTEXT: u32 = 12294;
+pub const EGL_BAD_CURRENT_SURFACE: u32 = 12295;
+pub const EGL_BAD_DISPLAY: u32 = 12296;
+pub const EGL_BAD_MATCH: u32 = 12297;
+pub const EGL_BAD_NATIVE_PIXMAP: u32 = 12298;
+pub const EGL_BAD_NATIVE_WINDOW: u32 = 12299;
+pub const EGL_BAD_PARAMETER: u32 = 12300;
+pub const EGL_BAD_SURFACE: u32 = 12301;
+pub const EGL_CONTEXT_LOST: u32 = 12302;
+pub const EGL_BUFFER_SIZE: u32 = 12320;
+pub const EGL_ALPHA_SIZE: u32 = 12321;
+pub const EGL_BLUE_SIZE: u32 = 12322;
+pub const EGL_GREEN_SIZE: u32 = 12323;
+pub const EGL_RED_SIZE: u32 = 12324;
+pub const EGL_DEPTH_SIZE: u32 = 12325;
+pub const EGL_STENCIL_SIZE: u32 = 12326;
+pub const EGL_CONFIG_CAVEAT: u32 = 12327;
+pub const EGL_CONFIG_ID: u32 = 12328;
+pub const EGL_LEVEL: u32 = 12329;
+pub const EGL_MAX_PBUFFER_HEIGHT: u32 = 12330;
+pub const EGL_MAX_PBUFFER_PIXELS: u32 = 12331;
+pub const EGL_MAX_PBUFFER_WIDTH: u32 = 12332;
+pub const EGL_NATIVE_RENDERABLE: u32 = 12333;
+pub const EGL_NATIVE_VISUAL_ID: u32 = 12334;
+pub const EGL_NATIVE_VISUAL_TYPE: u32 = 12335;
+pub const EGL_SAMPLES: u32 = 12337;
+pub const EGL_SAMPLE_BUFFERS: u32 = 12338;
+pub const EGL_SURFACE_TYPE: u32 = 12339;
+pub const EGL_TRANSPARENT_TYPE: u32 = 12340;
+pub const EGL_TRANSPARENT_BLUE_VALUE: u32 = 12341;
+pub const EGL_TRANSPARENT_GREEN_VALUE: u32 = 12342;
+pub const EGL_TRANSPARENT_RED_VALUE: u32 = 12343;
+pub const EGL_NONE: u32 = 12344;
+pub const EGL_BIND_TO_TEXTURE_RGB: u32 = 12345;
+pub const EGL_BIND_TO_TEXTURE_RGBA: u32 = 12346;
+pub const EGL_MIN_SWAP_INTERVAL: u32 = 12347;
+pub const EGL_MAX_SWAP_INTERVAL: u32 = 12348;
+pub const EGL_LUMINANCE_SIZE: u32 = 12349;
+pub const EGL_ALPHA_MASK_SIZE: u32 = 12350;
+pub const EGL_COLOR_BUFFER_TYPE: u32 = 12351;
+pub const EGL_RENDERABLE_TYPE: u32 = 12352;
+pub const EGL_MATCH_NATIVE_PIXMAP: u32 = 12353;
+pub const EGL_CONFORMANT: u32 = 12354;
+pub const EGL_CONFORMANT_KHR: u32 = 12354;
+pub const EGL_MATCH_FORMAT_KHR: u32 = 12355;
+pub const EGL_SLOW_CONFIG: u32 = 12368;
+pub const EGL_NON_CONFORMANT_CONFIG: u32 = 12369;
+pub const EGL_TRANSPARENT_RGB: u32 = 12370;
+pub const EGL_VENDOR: u32 = 12371;
+pub const EGL_VERSION: u32 = 12372;
+pub const EGL_EXTENSIONS: u32 = 12373;
+pub const EGL_HEIGHT: u32 = 12374;
+pub const EGL_WIDTH: u32 = 12375;
+pub const EGL_LARGEST_PBUFFER: u32 = 12376;
+pub const EGL_DRAW: u32 = 12377;
+pub const EGL_READ: u32 = 12378;
+pub const EGL_CORE_NATIVE_ENGINE: u32 = 12379;
+pub const EGL_NO_TEXTURE: u32 = 12380;
+pub const EGL_TEXTURE_RGB: u32 = 12381;
+pub const EGL_TEXTURE_RGBA: u32 = 12382;
+pub const EGL_TEXTURE_2D: u32 = 12383;
+pub const EGL_Y_INVERTED_NOK: u32 = 12415;
+pub const EGL_TEXTURE_FORMAT: u32 = 12416;
+pub const EGL_TEXTURE_TARGET: u32 = 12417;
+pub const EGL_MIPMAP_TEXTURE: u32 = 12418;
+pub const EGL_MIPMAP_LEVEL: u32 = 12419;
+pub const EGL_BACK_BUFFER: u32 = 12420;
+pub const EGL_SINGLE_BUFFER: u32 = 12421;
+pub const EGL_RENDER_BUFFER: u32 = 12422;
+pub const EGL_COLORSPACE: u32 = 12423;
+pub const EGL_VG_COLORSPACE: u32 = 12423;
+pub const EGL_ALPHA_FORMAT: u32 = 12424;
+pub const EGL_VG_ALPHA_FORMAT: u32 = 12424;
+pub const EGL_COLORSPACE_sRGB: u32 = 12425;
+pub const EGL_GL_COLORSPACE_SRGB: u32 = 12425;
+pub const EGL_GL_COLORSPACE_SRGB_KHR: u32 = 12425;
+pub const EGL_VG_COLORSPACE_sRGB: u32 = 12425;
+pub const EGL_COLORSPACE_LINEAR: u32 = 12426;
+pub const EGL_GL_COLORSPACE_LINEAR: u32 = 12426;
+pub const EGL_GL_COLORSPACE_LINEAR_KHR: u32 = 12426;
+pub const EGL_VG_COLORSPACE_LINEAR: u32 = 12426;
+pub const EGL_ALPHA_FORMAT_NONPRE: u32 = 12427;
+pub const EGL_VG_ALPHA_FORMAT_NONPRE: u32 = 12427;
+pub const EGL_ALPHA_FORMAT_PRE: u32 = 12428;
+pub const EGL_VG_ALPHA_FORMAT_PRE: u32 = 12428;
+pub const EGL_CLIENT_APIS: u32 = 12429;
+pub const EGL_RGB_BUFFER: u32 = 12430;
+pub const EGL_LUMINANCE_BUFFER: u32 = 12431;
+pub const EGL_HORIZONTAL_RESOLUTION: u32 = 12432;
+pub const EGL_VERTICAL_RESOLUTION: u32 = 12433;
+pub const EGL_PIXEL_ASPECT_RATIO: u32 = 12434;
+pub const EGL_SWAP_BEHAVIOR: u32 = 12435;
+pub const EGL_BUFFER_PRESERVED: u32 = 12436;
+pub const EGL_BUFFER_DESTROYED: u32 = 12437;
+pub const EGL_OPENVG_IMAGE: u32 = 12438;
+pub const EGL_CONTEXT_CLIENT_TYPE: u32 = 12439;
+pub const EGL_CONTEXT_CLIENT_VERSION: u32 = 12440;
+pub const EGL_CONTEXT_MAJOR_VERSION: u32 = 12440;
+pub const EGL_CONTEXT_MAJOR_VERSION_KHR: u32 = 12440;
+pub const EGL_MULTISAMPLE_RESOLVE: u32 = 12441;
+pub const EGL_MULTISAMPLE_RESOLVE_DEFAULT: u32 = 12442;
+pub const EGL_MULTISAMPLE_RESOLVE_BOX: u32 = 12443;
+pub const EGL_CL_EVENT_HANDLE: u32 = 12444;
+pub const EGL_CL_EVENT_HANDLE_KHR: u32 = 12444;
+pub const EGL_GL_COLORSPACE: u32 = 12445;
+pub const EGL_GL_COLORSPACE_KHR: u32 = 12445;
+pub const EGL_OPENGL_ES_API: u32 = 12448;
+pub const EGL_OPENVG_API: u32 = 12449;
+pub const EGL_OPENGL_API: u32 = 12450;
+pub const EGL_NATIVE_PIXMAP_KHR: u32 = 12464;
+pub const EGL_GL_TEXTURE_2D: u32 = 12465;
+pub const EGL_GL_TEXTURE_2D_KHR: u32 = 12465;
+pub const EGL_GL_TEXTURE_3D: u32 = 12466;
+pub const EGL_GL_TEXTURE_3D_KHR: u32 = 12466;
+pub const EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: u32 = 12467;
+pub const EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR: u32 = 12467;
+pub const EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: u32 = 12468;
+pub const EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR: u32 = 12468;
+pub const EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: u32 = 12469;
+pub const EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR: u32 = 12469;
+pub const EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: u32 = 12470;
+pub const EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR: u32 = 12470;
+pub const EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: u32 = 12471;
+pub const EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR: u32 = 12471;
+pub const EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: u32 = 12472;
+pub const EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR: u32 = 12472;
+pub const EGL_GL_RENDERBUFFER: u32 = 12473;
+pub const EGL_GL_RENDERBUFFER_KHR: u32 = 12473;
+pub const EGL_VG_PARENT_IMAGE_KHR: u32 = 12474;
+pub const EGL_GL_TEXTURE_LEVEL: u32 = 12476;
+pub const EGL_GL_TEXTURE_LEVEL_KHR: u32 = 12476;
+pub const EGL_GL_TEXTURE_ZOFFSET: u32 = 12477;
+pub const EGL_GL_TEXTURE_ZOFFSET_KHR: u32 = 12477;
+pub const EGL_POST_SUB_BUFFER_SUPPORTED_NV: u32 = 12478;
+pub const EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT: u32 = 12479;
+pub const EGL_FORMAT_RGB_565_EXACT_KHR: u32 = 12480;
+pub const EGL_FORMAT_RGB_565_KHR: u32 = 12481;
+pub const EGL_FORMAT_RGBA_8888_EXACT_KHR: u32 = 12482;
+pub const EGL_FORMAT_RGBA_8888_KHR: u32 = 12483;
+pub const EGL_MAP_PRESERVE_PIXELS_KHR: u32 = 12484;
+pub const EGL_LOCK_USAGE_HINT_KHR: u32 = 12485;
+pub const EGL_BITMAP_POINTER_KHR: u32 = 12486;
+pub const EGL_BITMAP_PITCH_KHR: u32 = 12487;
+pub const EGL_BITMAP_ORIGIN_KHR: u32 = 12488;
+pub const EGL_BITMAP_PIXEL_RED_OFFSET_KHR: u32 = 12489;
+pub const EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR: u32 = 12490;
+pub const EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR: u32 = 12491;
+pub const EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR: u32 = 12492;
+pub const EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR: u32 = 12493;
+pub const EGL_LOWER_LEFT_KHR: u32 = 12494;
+pub const EGL_UPPER_LEFT_KHR: u32 = 12495;
+pub const EGL_IMAGE_PRESERVED: u32 = 12498;
+pub const EGL_IMAGE_PRESERVED_KHR: u32 = 12498;
+pub const EGL_SHARED_IMAGE_NOK: u32 = 12506;
+pub const EGL_COVERAGE_BUFFERS_NV: u32 = 12512;
+pub const EGL_COVERAGE_SAMPLES_NV: u32 = 12513;
+pub const EGL_DEPTH_ENCODING_NV: u32 = 12514;
+pub const EGL_DEPTH_ENCODING_NONLINEAR_NV: u32 = 12515;
+pub const EGL_SYNC_PRIOR_COMMANDS_COMPLETE_NV: u32 = 12518;
+pub const EGL_SYNC_STATUS_NV: u32 = 12519;
+pub const EGL_SIGNALED_NV: u32 = 12520;
+pub const EGL_UNSIGNALED_NV: u32 = 12521;
+pub const EGL_ALREADY_SIGNALED_NV: u32 = 12522;
+pub const EGL_TIMEOUT_EXPIRED_NV: u32 = 12523;
+pub const EGL_CONDITION_SATISFIED_NV: u32 = 12524;
+pub const EGL_SYNC_TYPE_NV: u32 = 12525;
+pub const EGL_SYNC_CONDITION_NV: u32 = 12526;
+pub const EGL_SYNC_FENCE_NV: u32 = 12527;
+pub const EGL_SYNC_PRIOR_COMMANDS_COMPLETE: u32 = 12528;
+pub const EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR: u32 = 12528;
+pub const EGL_SYNC_STATUS: u32 = 12529;
+pub const EGL_SYNC_STATUS_KHR: u32 = 12529;
+pub const EGL_SIGNALED: u32 = 12530;
+pub const EGL_SIGNALED_KHR: u32 = 12530;
+pub const EGL_UNSIGNALED: u32 = 12531;
+pub const EGL_UNSIGNALED_KHR: u32 = 12531;
+pub const EGL_TIMEOUT_EXPIRED: u32 = 12533;
+pub const EGL_TIMEOUT_EXPIRED_KHR: u32 = 12533;
+pub const EGL_CONDITION_SATISFIED: u32 = 12534;
+pub const EGL_CONDITION_SATISFIED_KHR: u32 = 12534;
+pub const EGL_SYNC_TYPE: u32 = 12535;
+pub const EGL_SYNC_TYPE_KHR: u32 = 12535;
+pub const EGL_SYNC_CONDITION: u32 = 12536;
+pub const EGL_SYNC_CONDITION_KHR: u32 = 12536;
+pub const EGL_SYNC_FENCE: u32 = 12537;
+pub const EGL_SYNC_FENCE_KHR: u32 = 12537;
+pub const EGL_SYNC_REUSABLE_KHR: u32 = 12538;
+pub const EGL_CONTEXT_MINOR_VERSION: u32 = 12539;
+pub const EGL_CONTEXT_MINOR_VERSION_KHR: u32 = 12539;
+pub const EGL_CONTEXT_FLAGS_KHR: u32 = 12540;
+pub const EGL_CONTEXT_OPENGL_PROFILE_MASK: u32 = 12541;
+pub const EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR: u32 = 12541;
+pub const EGL_SYNC_CL_EVENT: u32 = 12542;
+pub const EGL_SYNC_CL_EVENT_KHR: u32 = 12542;
+pub const EGL_SYNC_CL_EVENT_COMPLETE: u32 = 12543;
+pub const EGL_SYNC_CL_EVENT_COMPLETE_KHR: u32 = 12543;
+pub const EGL_CONTEXT_PRIORITY_LEVEL_IMG: u32 = 12544;
+pub const EGL_CONTEXT_PRIORITY_HIGH_IMG: u32 = 12545;
+pub const EGL_CONTEXT_PRIORITY_MEDIUM_IMG: u32 = 12546;
+pub const EGL_CONTEXT_PRIORITY_LOW_IMG: u32 = 12547;
+pub const EGL_NATIVE_BUFFER_MULTIPLANE_SEPARATE_IMG: u32 = 12549;
+pub const EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG: u32 = 12550;
+pub const EGL_BITMAP_PIXEL_SIZE_KHR: u32 = 12560;
+pub const EGL_COVERAGE_SAMPLE_RESOLVE_NV: u32 = 12593;
+pub const EGL_COVERAGE_SAMPLE_RESOLVE_DEFAULT_NV: u32 = 12594;
+pub const EGL_COVERAGE_SAMPLE_RESOLVE_NONE_NV: u32 = 12595;
+pub const EGL_MULTIVIEW_VIEW_COUNT_EXT: u32 = 12596;
+pub const EGL_AUTO_STEREO_NV: u32 = 12598;
+pub const EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT: u32 = 12600;
+pub const EGL_BUFFER_AGE_EXT: u32 = 12605;
+pub const EGL_BUFFER_AGE_KHR: u32 = 12605;
+pub const EGL_PLATFORM_DEVICE_EXT: u32 = 12607;
+pub const EGL_NATIVE_BUFFER_ANDROID: u32 = 12608;
+pub const EGL_PLATFORM_ANDROID_KHR: u32 = 12609;
+pub const EGL_RECORDABLE_ANDROID: u32 = 12610;
+pub const EGL_NATIVE_BUFFER_USAGE_ANDROID: u32 = 12611;
+pub const EGL_SYNC_NATIVE_FENCE_ANDROID: u32 = 12612;
+pub const EGL_SYNC_NATIVE_FENCE_FD_ANDROID: u32 = 12613;
+pub const EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID: u32 = 12614;
+pub const EGL_FRAMEBUFFER_TARGET_ANDROID: u32 = 12615;
+pub const EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID: u32 = 12620;
+pub const EGL_CONTEXT_OPENGL_DEBUG: u32 = 12720;
+pub const EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE: u32 = 12721;
+pub const EGL_CONTEXT_OPENGL_ROBUST_ACCESS: u32 = 12722;
+pub const EGL_CONTEXT_OPENGL_NO_ERROR_KHR: u32 = 12723;
+pub const EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY: u32 = 12733;
+pub const EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR: u32 = 12733;
+pub const EGL_NO_RESET_NOTIFICATION: u32 = 12734;
+pub const EGL_NO_RESET_NOTIFICATION_EXT: u32 = 12734;
+pub const EGL_NO_RESET_NOTIFICATION_KHR: u32 = 12734;
+pub const EGL_LOSE_CONTEXT_ON_RESET: u32 = 12735;
+pub const EGL_LOSE_CONTEXT_ON_RESET_EXT: u32 = 12735;
+pub const EGL_LOSE_CONTEXT_ON_RESET_KHR: u32 = 12735;
+pub const EGL_DRM_BUFFER_FORMAT_MESA: u32 = 12752;
+pub const EGL_DRM_BUFFER_USE_MESA: u32 = 12753;
+pub const EGL_DRM_BUFFER_FORMAT_ARGB32_MESA: u32 = 12754;
+pub const EGL_DRM_BUFFER_MESA: u32 = 12755;
+pub const EGL_DRM_BUFFER_STRIDE_MESA: u32 = 12756;
+pub const EGL_PLATFORM_X11_EXT: u32 = 12757;
+pub const EGL_PLATFORM_X11_KHR: u32 = 12757;
+pub const EGL_PLATFORM_X11_SCREEN_EXT: u32 = 12758;
+pub const EGL_PLATFORM_X11_SCREEN_KHR: u32 = 12758;
+pub const EGL_PLATFORM_GBM_KHR: u32 = 12759;
+pub const EGL_PLATFORM_GBM_MESA: u32 = 12759;
+pub const EGL_PLATFORM_WAYLAND_EXT: u32 = 12760;
+pub const EGL_PLATFORM_WAYLAND_KHR: u32 = 12760;
+pub const EGL_PLATFORM_SURFACELESS_MESA: u32 = 12765;
+pub const EGL_STREAM_FIFO_LENGTH_KHR: u32 = 12796;
+pub const EGL_STREAM_TIME_NOW_KHR: u32 = 12797;
+pub const EGL_STREAM_TIME_CONSUMER_KHR: u32 = 12798;
+pub const EGL_STREAM_TIME_PRODUCER_KHR: u32 = 12799;
+pub const EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE: u32 = 12800;
+pub const EGL_FIXED_SIZE_ANGLE: u32 = 12801;
+pub const EGL_CONSUMER_LATENCY_USEC_KHR: u32 = 12816;
+pub const EGL_PRODUCER_FRAME_KHR: u32 = 12818;
+pub const EGL_CONSUMER_FRAME_KHR: u32 = 12819;
+pub const EGL_STREAM_STATE_KHR: u32 = 12820;
+pub const EGL_STREAM_STATE_CREATED_KHR: u32 = 12821;
+pub const EGL_STREAM_STATE_CONNECTING_KHR: u32 = 12822;
+pub const EGL_STREAM_STATE_EMPTY_KHR: u32 = 12823;
+pub const EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR: u32 = 12824;
+pub const EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR: u32 = 12825;
+pub const EGL_STREAM_STATE_DISCONNECTED_KHR: u32 = 12826;
+pub const EGL_BAD_STREAM_KHR: u32 = 12827;
+pub const EGL_BAD_STATE_KHR: u32 = 12828;
+pub const EGL_BUFFER_COUNT_NV: u32 = 12829;
+pub const EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR: u32 = 12830;
+pub const EGL_SYNC_NEW_FRAME_NV: u32 = 12831;
+pub const EGL_BAD_DEVICE_EXT: u32 = 12843;
+pub const EGL_DEVICE_EXT: u32 = 12844;
+pub const EGL_BAD_OUTPUT_LAYER_EXT: u32 = 12845;
+pub const EGL_BAD_OUTPUT_PORT_EXT: u32 = 12846;
+pub const EGL_SWAP_INTERVAL_EXT: u32 = 12847;
+pub const EGL_DRM_DEVICE_FILE_EXT: u32 = 12851;
+pub const EGL_DRM_CRTC_EXT: u32 = 12852;
+pub const EGL_DRM_PLANE_EXT: u32 = 12853;
+pub const EGL_DRM_CONNECTOR_EXT: u32 = 12854;
+pub const EGL_OPENWF_DEVICE_ID_EXT: u32 = 12855;
+pub const EGL_OPENWF_PIPELINE_ID_EXT: u32 = 12856;
+pub const EGL_OPENWF_PORT_ID_EXT: u32 = 12857;
+pub const EGL_CUDA_DEVICE_NV: u32 = 12858;
+pub const EGL_CUDA_EVENT_HANDLE_NV: u32 = 12859;
+pub const EGL_SYNC_CUDA_EVENT_NV: u32 = 12860;
+pub const EGL_SYNC_CUDA_EVENT_COMPLETE_NV: u32 = 12861;
+pub const EGL_STREAM_CROSS_PARTITION_NV: u32 = 12863;
+pub const EGL_STREAM_STATE_INITIALIZING_NV: u32 = 12864;
+pub const EGL_STREAM_TYPE_NV: u32 = 12865;
+pub const EGL_STREAM_PROTOCOL_NV: u32 = 12866;
+pub const EGL_STREAM_ENDPOINT_NV: u32 = 12867;
+pub const EGL_STREAM_LOCAL_NV: u32 = 12868;
+pub const EGL_STREAM_CROSS_PROCESS_NV: u32 = 12869;
+pub const EGL_STREAM_PROTOCOL_FD_NV: u32 = 12870;
+pub const EGL_STREAM_PRODUCER_NV: u32 = 12871;
+pub const EGL_STREAM_CONSUMER_NV: u32 = 12872;
+pub const EGL_STREAM_PROTOCOL_SOCKET_NV: u32 = 12875;
+pub const EGL_SOCKET_HANDLE_NV: u32 = 12876;
+pub const EGL_SOCKET_TYPE_NV: u32 = 12877;
+pub const EGL_SOCKET_TYPE_UNIX_NV: u32 = 12878;
+pub const EGL_SOCKET_TYPE_INET_NV: u32 = 12879;
+pub const EGL_MAX_STREAM_METADATA_BLOCKS_NV: u32 = 12880;
+pub const EGL_MAX_STREAM_METADATA_BLOCK_SIZE_NV: u32 = 12881;
+pub const EGL_MAX_STREAM_METADATA_TOTAL_SIZE_NV: u32 = 12882;
+pub const EGL_PRODUCER_METADATA_NV: u32 = 12883;
+pub const EGL_CONSUMER_METADATA_NV: u32 = 12884;
+pub const EGL_METADATA0_SIZE_NV: u32 = 12885;
+pub const EGL_METADATA1_SIZE_NV: u32 = 12886;
+pub const EGL_METADATA2_SIZE_NV: u32 = 12887;
+pub const EGL_METADATA3_SIZE_NV: u32 = 12888;
+pub const EGL_METADATA0_TYPE_NV: u32 = 12889;
+pub const EGL_METADATA1_TYPE_NV: u32 = 12890;
+pub const EGL_METADATA2_TYPE_NV: u32 = 12891;
+pub const EGL_METADATA3_TYPE_NV: u32 = 12892;
+pub const EGL_LINUX_DMA_BUF_EXT: u32 = 12912;
+pub const EGL_LINUX_DRM_FOURCC_EXT: u32 = 12913;
+pub const EGL_DMA_BUF_PLANE0_FD_EXT: u32 = 12914;
+pub const EGL_DMA_BUF_PLANE0_OFFSET_EXT: u32 = 12915;
+pub const EGL_DMA_BUF_PLANE0_PITCH_EXT: u32 = 12916;
+pub const EGL_DMA_BUF_PLANE1_FD_EXT: u32 = 12917;
+pub const EGL_DMA_BUF_PLANE1_OFFSET_EXT: u32 = 12918;
+pub const EGL_DMA_BUF_PLANE1_PITCH_EXT: u32 = 12919;
+pub const EGL_DMA_BUF_PLANE2_FD_EXT: u32 = 12920;
+pub const EGL_DMA_BUF_PLANE2_OFFSET_EXT: u32 = 12921;
+pub const EGL_DMA_BUF_PLANE2_PITCH_EXT: u32 = 12922;
+pub const EGL_YUV_COLOR_SPACE_HINT_EXT: u32 = 12923;
+pub const EGL_SAMPLE_RANGE_HINT_EXT: u32 = 12924;
+pub const EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT: u32 = 12925;
+pub const EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT: u32 = 12926;
+pub const EGL_ITU_REC601_EXT: u32 = 12927;
+pub const EGL_ITU_REC709_EXT: u32 = 12928;
+pub const EGL_ITU_REC2020_EXT: u32 = 12929;
+pub const EGL_YUV_FULL_RANGE_EXT: u32 = 12930;
+pub const EGL_YUV_NARROW_RANGE_EXT: u32 = 12931;
+pub const EGL_YUV_CHROMA_SITING_0_EXT: u32 = 12932;
+pub const EGL_YUV_CHROMA_SITING_0_5_EXT: u32 = 12933;
+pub const EGL_DISCARD_SAMPLES_ARM: u32 = 12934;
+pub const EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM: u32 = 12938;
+pub const EGL_NATIVE_BUFFER_TIZEN: u32 = 12960;
+pub const EGL_NATIVE_SURFACE_TIZEN: u32 = 12961;
+pub const EGL_PROTECTED_CONTENT_EXT: u32 = 12992;
+pub const EGL_YUV_BUFFER_EXT: u32 = 13056;
+pub const EGL_YUV_ORDER_EXT: u32 = 13057;
+pub const EGL_YUV_ORDER_YUV_EXT: u32 = 13058;
+pub const EGL_YUV_ORDER_YVU_EXT: u32 = 13059;
+pub const EGL_YUV_ORDER_YUYV_EXT: u32 = 13060;
+pub const EGL_YUV_ORDER_UYVY_EXT: u32 = 13061;
+pub const EGL_YUV_ORDER_YVYU_EXT: u32 = 13062;
+pub const EGL_YUV_ORDER_VYUY_EXT: u32 = 13063;
+pub const EGL_YUV_ORDER_AYUV_EXT: u32 = 13064;
+pub const EGL_YUV_CSC_STANDARD_EXT: u32 = 13066;
+pub const EGL_YUV_CSC_STANDARD_601_EXT: u32 = 13067;
+pub const EGL_YUV_CSC_STANDARD_709_EXT: u32 = 13068;
+pub const EGL_YUV_CSC_STANDARD_2020_EXT: u32 = 13069;
+pub const EGL_YUV_NUMBER_OF_PLANES_EXT: u32 = 13073;
+pub const EGL_YUV_SUBSAMPLE_EXT: u32 = 13074;
+pub const EGL_YUV_SUBSAMPLE_4_2_0_EXT: u32 = 13075;
+pub const EGL_YUV_SUBSAMPLE_4_2_2_EXT: u32 = 13076;
+pub const EGL_YUV_SUBSAMPLE_4_4_4_EXT: u32 = 13077;
+pub const EGL_YUV_DEPTH_RANGE_EXT: u32 = 13079;
+pub const EGL_YUV_DEPTH_RANGE_LIMITED_EXT: u32 = 13080;
+pub const EGL_YUV_DEPTH_RANGE_FULL_EXT: u32 = 13081;
+pub const EGL_YUV_PLANE_BPP_EXT: u32 = 13082;
+pub const EGL_YUV_PLANE_BPP_0_EXT: u32 = 13083;
+pub const EGL_YUV_PLANE_BPP_8_EXT: u32 = 13084;
+pub const EGL_YUV_PLANE_BPP_10_EXT: u32 = 13085;
+pub const EGL_PENDING_METADATA_NV: u32 = 13096;
+pub const EGL_PENDING_FRAME_NV: u32 = 13097;
+pub const EGL_STREAM_TIME_PENDING_NV: u32 = 13098;
+pub const EGL_YUV_PLANE0_TEXTURE_UNIT_NV: u32 = 13100;
+pub const EGL_YUV_PLANE1_TEXTURE_UNIT_NV: u32 = 13101;
+pub const EGL_YUV_PLANE2_TEXTURE_UNIT_NV: u32 = 13102;
+pub const EGL_SUPPORT_RESET_NV: u32 = 13108;
+pub const EGL_SUPPORT_REUSE_NV: u32 = 13109;
+pub const EGL_STREAM_FIFO_SYNCHRONOUS_NV: u32 = 13110;
+pub const EGL_PRODUCER_MAX_FRAME_HINT_NV: u32 = 13111;
+pub const EGL_CONSUMER_MAX_FRAME_HINT_NV: u32 = 13112;
+pub const EGL_COLOR_COMPONENT_TYPE_EXT: u32 = 13113;
+pub const EGL_COLOR_COMPONENT_TYPE_FIXED_EXT: u32 = 13114;
+pub const EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT: u32 = 13115;
+pub const EGL_GL_COLORSPACE_BT2020_LINEAR_EXT: u32 = 13119;
+pub const EGL_GL_COLORSPACE_BT2020_PQ_EXT: u32 = 13120;
+pub const EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT: u32 = 13121;
+pub const EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT: u32 = 13122;
+pub const EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT: u32 = 13123;
+pub const EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT: u32 = 13124;
+pub const EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT: u32 = 13125;
+pub const EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT: u32 = 13126;
+pub const EGL_SMPTE2086_WHITE_POINT_X_EXT: u32 = 13127;
+pub const EGL_SMPTE2086_WHITE_POINT_Y_EXT: u32 = 13128;
+pub const EGL_SMPTE2086_MAX_LUMINANCE_EXT: u32 = 13129;
+pub const EGL_SMPTE2086_MIN_LUMINANCE_EXT: u32 = 13130;
+pub const EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV: u32 = 13132;
+pub const EGL_STREAM_CROSS_OBJECT_NV: u32 = 13133;
+pub const EGL_STREAM_CROSS_DISPLAY_NV: u32 = 13134;
+pub const EGL_STREAM_CROSS_SYSTEM_NV: u32 = 13135;
+pub const EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: u32 = 13136;
+pub const EGL_D3D9_DEVICE_ANGLE: u32 = 13216;
+pub const EGL_D3D11_DEVICE_ANGLE: u32 = 13217;
+pub const EGL_OBJECT_THREAD_KHR: u32 = 13232;
+pub const EGL_OBJECT_DISPLAY_KHR: u32 = 13233;
+pub const EGL_OBJECT_CONTEXT_KHR: u32 = 13234;
+pub const EGL_OBJECT_SURFACE_KHR: u32 = 13235;
+pub const EGL_OBJECT_IMAGE_KHR: u32 = 13236;
+pub const EGL_OBJECT_SYNC_KHR: u32 = 13237;
+pub const EGL_OBJECT_STREAM_KHR: u32 = 13238;
+pub const EGL_DEBUG_CALLBACK_KHR: u32 = 13240;
+pub const EGL_DEBUG_MSG_CRITICAL_KHR: u32 = 13241;
+pub const EGL_DEBUG_MSG_ERROR_KHR: u32 = 13242;
+pub const EGL_DEBUG_MSG_WARN_KHR: u32 = 13243;
+pub const EGL_DEBUG_MSG_INFO_KHR: u32 = 13244;
+pub const EGL_DMA_BUF_PLANE3_FD_EXT: u32 = 13376;
+pub const EGL_DMA_BUF_PLANE3_OFFSET_EXT: u32 = 13377;
+pub const EGL_DMA_BUF_PLANE3_PITCH_EXT: u32 = 13378;
+pub const EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT: u32 = 13379;
+pub const EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT: u32 = 13380;
+pub const EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT: u32 = 13381;
+pub const EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT: u32 = 13382;
+pub const EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT: u32 = 13383;
+pub const EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT: u32 = 13384;
+pub const EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT: u32 = 13385;
+pub const EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT: u32 = 13386;
+pub const EGL_COLOR_FORMAT_HI: u32 = 36720;
+pub const EGL_COLOR_RGB_HI: u32 = 36721;
+pub const EGL_COLOR_RGBA_HI: u32 = 36722;
+pub const EGL_COLOR_ARGB_HI: u32 = 36723;
+pub const EGL_CLIENT_PIXMAP_POINTER_HI: u32 = 36724;
+pub const EGL_FOREVER: i32 = -1;
+pub const EGL_FOREVER_KHR: i32 = -1;
+pub const EGL_FOREVER_NV: i32 = -1;
+pub const EGL_TRUE: u32 = 1;
+pub const EGL_DISPLAY_SCALING: u32 = 10000;
+pub type __int32_t = ::std::os::raw::c_int;
+pub type __int64_t = ::std::os::raw::c_long;
+pub type __uint64_t = ::std::os::raw::c_ulong;
+pub type khronos_int32_t = i32;
+pub type khronos_uint64_t = u64;
+pub type khronos_ssize_t = ::std::os::raw::c_long;
+pub type khronos_utime_nanoseconds_t = u64;
+pub type khronos_stime_nanoseconds_t = i64;
+pub type GLenum = ::std::os::raw::c_uint;
+pub type GLboolean = ::std::os::raw::c_uchar;
+pub type GLbitfield = ::std::os::raw::c_uint;
+pub type GLvoid = ::std::os::raw::c_void;
+pub type GLbyte = ::std::os::raw::c_schar;
+pub type GLshort = ::std::os::raw::c_short;
+pub type GLint = ::std::os::raw::c_int;
+pub type GLclampx = ::std::os::raw::c_int;
+pub type GLubyte = ::std::os::raw::c_uchar;
+pub type GLushort = ::std::os::raw::c_ushort;
+pub type GLuint = ::std::os::raw::c_uint;
+pub type GLsizei = ::std::os::raw::c_int;
+pub type GLfloat = f32;
+pub type GLclampf = f32;
+pub type GLdouble = f64;
+pub type GLclampd = f64;
+pub type GLeglImageOES = *mut ::std::os::raw::c_void;
+pub type GLchar = ::std::os::raw::c_char;
+pub type GLcharARB = ::std::os::raw::c_char;
+pub type GLhandleARB = ::std::os::raw::c_uint;
+pub type GLhalfARB = ::std::os::raw::c_ushort;
+pub type GLhalf = ::std::os::raw::c_ushort;
+pub type GLfixed = GLint;
+pub type GLintptr = isize;
+pub type GLsizeiptr = isize;
+pub type GLint64 = i64;
+pub type GLuint64 = u64;
+pub type GLintptrARB = isize;
+pub type GLsizeiptrARB = isize;
+pub type GLint64EXT = i64;
+pub type GLuint64EXT = u64;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __GLsync {
+ _unused: [u8; 0],
+}
+pub type GLsync = *mut __GLsync;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct _cl_context {
+ _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct _cl_event {
+ _unused: [u8; 0],
+}
+pub type GLDEBUGPROC = ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ id: GLuint,
+ severity: GLenum,
+ length: GLsizei,
+ message: *const GLchar,
+ userParam: *const ::std::os::raw::c_void,
+ ),
+>;
+pub type GLDEBUGPROCARB = ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ id: GLuint,
+ severity: GLenum,
+ length: GLsizei,
+ message: *const GLchar,
+ userParam: *const ::std::os::raw::c_void,
+ ),
+>;
+pub type GLDEBUGPROCKHR = ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ id: GLuint,
+ severity: GLenum,
+ length: GLsizei,
+ message: *const GLchar,
+ userParam: *const ::std::os::raw::c_void,
+ ),
+>;
+pub type GLDEBUGPROCAMD = ::std::option::Option<
+ unsafe extern "C" fn(
+ id: GLuint,
+ category: GLenum,
+ severity: GLenum,
+ length: GLsizei,
+ message: *const GLchar,
+ userParam: *mut ::std::os::raw::c_void,
+ ),
+>;
+pub type GLhalfNV = ::std::os::raw::c_ushort;
+pub type GLvdpauSurfaceNV = GLintptr;
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAccum"]
+ pub static mut epoxy_glAccum:
+ ::std::option::Option<unsafe extern "C" fn(op: GLenum, value: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAccumxOES"]
+ pub static mut epoxy_glAccumxOES:
+ ::std::option::Option<unsafe extern "C" fn(op: GLenum, value: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glActiveProgramEXT"]
+ pub static mut epoxy_glActiveProgramEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glActiveShaderProgram"]
+ pub static mut epoxy_glActiveShaderProgram:
+ ::std::option::Option<unsafe extern "C" fn(pipeline: GLuint, program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glActiveShaderProgramEXT"]
+ pub static mut epoxy_glActiveShaderProgramEXT:
+ ::std::option::Option<unsafe extern "C" fn(pipeline: GLuint, program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glActiveStencilFaceEXT"]
+ pub static mut epoxy_glActiveStencilFaceEXT:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glActiveTexture"]
+ pub static mut epoxy_glActiveTexture:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glActiveTextureARB"]
+ pub static mut epoxy_glActiveTextureARB:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glActiveVaryingNV"]
+ pub static mut epoxy_glActiveVaryingNV:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, name: *const GLchar)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAlphaFragmentOp1ATI"]
+ pub static mut epoxy_glAlphaFragmentOp1ATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ op: GLenum,
+ dst: GLuint,
+ dstMod: GLuint,
+ arg1: GLuint,
+ arg1Rep: GLuint,
+ arg1Mod: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAlphaFragmentOp2ATI"]
+ pub static mut epoxy_glAlphaFragmentOp2ATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ op: GLenum,
+ dst: GLuint,
+ dstMod: GLuint,
+ arg1: GLuint,
+ arg1Rep: GLuint,
+ arg1Mod: GLuint,
+ arg2: GLuint,
+ arg2Rep: GLuint,
+ arg2Mod: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAlphaFragmentOp3ATI"]
+ pub static mut epoxy_glAlphaFragmentOp3ATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ op: GLenum,
+ dst: GLuint,
+ dstMod: GLuint,
+ arg1: GLuint,
+ arg1Rep: GLuint,
+ arg1Mod: GLuint,
+ arg2: GLuint,
+ arg2Rep: GLuint,
+ arg2Mod: GLuint,
+ arg3: GLuint,
+ arg3Rep: GLuint,
+ arg3Mod: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAlphaFunc"]
+ pub static mut epoxy_glAlphaFunc:
+ ::std::option::Option<unsafe extern "C" fn(func: GLenum, ref_: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAlphaFuncQCOM"]
+ pub static mut epoxy_glAlphaFuncQCOM:
+ ::std::option::Option<unsafe extern "C" fn(func: GLenum, ref_: GLclampf)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAlphaFuncx"]
+ pub static mut epoxy_glAlphaFuncx:
+ ::std::option::Option<unsafe extern "C" fn(func: GLenum, ref_: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAlphaFuncxOES"]
+ pub static mut epoxy_glAlphaFuncxOES:
+ ::std::option::Option<unsafe extern "C" fn(func: GLenum, ref_: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glApplyFramebufferAttachmentCMAAINTEL"]
+ pub static mut epoxy_glApplyFramebufferAttachmentCMAAINTEL:
+ ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glApplyTextureEXT"]
+ pub static mut epoxy_glApplyTextureEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAreProgramsResidentNV"]
+ pub static mut epoxy_glAreProgramsResidentNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ n: GLsizei,
+ programs: *const GLuint,
+ residences: *mut GLboolean,
+ ) -> GLboolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAreTexturesResident"]
+ pub static mut epoxy_glAreTexturesResident: ::std::option::Option<
+ unsafe extern "C" fn(
+ n: GLsizei,
+ textures: *const GLuint,
+ residences: *mut GLboolean,
+ ) -> GLboolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAreTexturesResidentEXT"]
+ pub static mut epoxy_glAreTexturesResidentEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ n: GLsizei,
+ textures: *const GLuint,
+ residences: *mut GLboolean,
+ ) -> GLboolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glArrayElement"]
+ pub static mut epoxy_glArrayElement: ::std::option::Option<unsafe extern "C" fn(i: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glArrayElementEXT"]
+ pub static mut epoxy_glArrayElementEXT: ::std::option::Option<unsafe extern "C" fn(i: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glArrayObjectATI"]
+ pub static mut epoxy_glArrayObjectATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ array: GLenum,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ buffer: GLuint,
+ offset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAsyncMarkerSGIX"]
+ pub static mut epoxy_glAsyncMarkerSGIX:
+ ::std::option::Option<unsafe extern "C" fn(marker: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAttachObjectARB"]
+ pub static mut epoxy_glAttachObjectARB:
+ ::std::option::Option<unsafe extern "C" fn(containerObj: GLhandleARB, obj: GLhandleARB)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glAttachShader"]
+ pub static mut epoxy_glAttachShader:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, shader: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBegin"]
+ pub static mut epoxy_glBegin: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginConditionalRender"]
+ pub static mut epoxy_glBeginConditionalRender:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginConditionalRenderNV"]
+ pub static mut epoxy_glBeginConditionalRenderNV:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginConditionalRenderNVX"]
+ pub static mut epoxy_glBeginConditionalRenderNVX:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginFragmentShaderATI"]
+ pub static mut epoxy_glBeginFragmentShaderATI: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginOcclusionQueryNV"]
+ pub static mut epoxy_glBeginOcclusionQueryNV:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginPerfMonitorAMD"]
+ pub static mut epoxy_glBeginPerfMonitorAMD:
+ ::std::option::Option<unsafe extern "C" fn(monitor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginPerfQueryINTEL"]
+ pub static mut epoxy_glBeginPerfQueryINTEL:
+ ::std::option::Option<unsafe extern "C" fn(queryHandle: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginQuery"]
+ pub static mut epoxy_glBeginQuery:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginQueryARB"]
+ pub static mut epoxy_glBeginQueryARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginQueryEXT"]
+ pub static mut epoxy_glBeginQueryEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginQueryIndexed"]
+ pub static mut epoxy_glBeginQueryIndexed:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginTransformFeedback"]
+ pub static mut epoxy_glBeginTransformFeedback:
+ ::std::option::Option<unsafe extern "C" fn(primitiveMode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginTransformFeedbackEXT"]
+ pub static mut epoxy_glBeginTransformFeedbackEXT:
+ ::std::option::Option<unsafe extern "C" fn(primitiveMode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginTransformFeedbackNV"]
+ pub static mut epoxy_glBeginTransformFeedbackNV:
+ ::std::option::Option<unsafe extern "C" fn(primitiveMode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginVertexShaderEXT"]
+ pub static mut epoxy_glBeginVertexShaderEXT: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBeginVideoCaptureNV"]
+ pub static mut epoxy_glBeginVideoCaptureNV:
+ ::std::option::Option<unsafe extern "C" fn(video_capture_slot: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindAttribLocation"]
+ pub static mut epoxy_glBindAttribLocation: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, index: GLuint, name: *const GLchar),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindAttribLocationARB"]
+ pub static mut epoxy_glBindAttribLocationARB: ::std::option::Option<
+ unsafe extern "C" fn(programObj: GLhandleARB, index: GLuint, name: *const GLcharARB),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBuffer"]
+ pub static mut epoxy_glBindBuffer:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferARB"]
+ pub static mut epoxy_glBindBufferARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferBase"]
+ pub static mut epoxy_glBindBufferBase:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint, buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferBaseEXT"]
+ pub static mut epoxy_glBindBufferBaseEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint, buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferBaseNV"]
+ pub static mut epoxy_glBindBufferBaseNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint, buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferOffsetEXT"]
+ pub static mut epoxy_glBindBufferOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, buffer: GLuint, offset: GLintptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferOffsetNV"]
+ pub static mut epoxy_glBindBufferOffsetNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, buffer: GLuint, offset: GLintptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferRange"]
+ pub static mut epoxy_glBindBufferRange: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferRangeEXT"]
+ pub static mut epoxy_glBindBufferRangeEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBufferRangeNV"]
+ pub static mut epoxy_glBindBufferRangeNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBuffersBase"]
+ pub static mut epoxy_glBindBuffersBase: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, first: GLuint, count: GLsizei, buffers: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindBuffersRange"]
+ pub static mut epoxy_glBindBuffersRange: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ first: GLuint,
+ count: GLsizei,
+ buffers: *const GLuint,
+ offsets: *const GLintptr,
+ sizes: *const GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindFragDataLocation"]
+ pub static mut epoxy_glBindFragDataLocation: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, color: GLuint, name: *const GLchar),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindFragDataLocationEXT"]
+ pub static mut epoxy_glBindFragDataLocationEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, color: GLuint, name: *const GLchar),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindFragDataLocationIndexed"]
+ pub static mut epoxy_glBindFragDataLocationIndexed: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ colorNumber: GLuint,
+ index: GLuint,
+ name: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindFragDataLocationIndexedEXT"]
+ pub static mut epoxy_glBindFragDataLocationIndexedEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ colorNumber: GLuint,
+ index: GLuint,
+ name: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindFragmentShaderATI"]
+ pub static mut epoxy_glBindFragmentShaderATI:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindFramebuffer"]
+ pub static mut epoxy_glBindFramebuffer:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, framebuffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindFramebufferEXT"]
+ pub static mut epoxy_glBindFramebufferEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, framebuffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindFramebufferOES"]
+ pub static mut epoxy_glBindFramebufferOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, framebuffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindImageTexture"]
+ pub static mut epoxy_glBindImageTexture: ::std::option::Option<
+ unsafe extern "C" fn(
+ unit: GLuint,
+ texture: GLuint,
+ level: GLint,
+ layered: GLboolean,
+ layer: GLint,
+ access: GLenum,
+ format: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindImageTextureEXT"]
+ pub static mut epoxy_glBindImageTextureEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ texture: GLuint,
+ level: GLint,
+ layered: GLboolean,
+ layer: GLint,
+ access: GLenum,
+ format: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindImageTextures"]
+ pub static mut epoxy_glBindImageTextures: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, textures: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindLightParameterEXT"]
+ pub static mut epoxy_glBindLightParameterEXT:
+ ::std::option::Option<unsafe extern "C" fn(light: GLenum, value: GLenum) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindMaterialParameterEXT"]
+ pub static mut epoxy_glBindMaterialParameterEXT:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, value: GLenum) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindMultiTextureEXT"]
+ pub static mut epoxy_glBindMultiTextureEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, texture: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindParameterEXT"]
+ pub static mut epoxy_glBindParameterEXT:
+ ::std::option::Option<unsafe extern "C" fn(value: GLenum) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindProgramARB"]
+ pub static mut epoxy_glBindProgramARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindProgramNV"]
+ pub static mut epoxy_glBindProgramNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindProgramPipeline"]
+ pub static mut epoxy_glBindProgramPipeline:
+ ::std::option::Option<unsafe extern "C" fn(pipeline: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindProgramPipelineEXT"]
+ pub static mut epoxy_glBindProgramPipelineEXT:
+ ::std::option::Option<unsafe extern "C" fn(pipeline: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindRenderbuffer"]
+ pub static mut epoxy_glBindRenderbuffer:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, renderbuffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindRenderbufferEXT"]
+ pub static mut epoxy_glBindRenderbufferEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, renderbuffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindRenderbufferOES"]
+ pub static mut epoxy_glBindRenderbufferOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, renderbuffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindSampler"]
+ pub static mut epoxy_glBindSampler:
+ ::std::option::Option<unsafe extern "C" fn(unit: GLuint, sampler: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindSamplers"]
+ pub static mut epoxy_glBindSamplers: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, samplers: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindTexGenParameterEXT"]
+ pub static mut epoxy_glBindTexGenParameterEXT: ::std::option::Option<
+ unsafe extern "C" fn(unit: GLenum, coord: GLenum, value: GLenum) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindTexture"]
+ pub static mut epoxy_glBindTexture:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, texture: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindTextureEXT"]
+ pub static mut epoxy_glBindTextureEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, texture: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindTextureUnit"]
+ pub static mut epoxy_glBindTextureUnit:
+ ::std::option::Option<unsafe extern "C" fn(unit: GLuint, texture: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindTextureUnitParameterEXT"]
+ pub static mut epoxy_glBindTextureUnitParameterEXT:
+ ::std::option::Option<unsafe extern "C" fn(unit: GLenum, value: GLenum) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindTextures"]
+ pub static mut epoxy_glBindTextures: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, textures: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindTransformFeedback"]
+ pub static mut epoxy_glBindTransformFeedback:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindTransformFeedbackNV"]
+ pub static mut epoxy_glBindTransformFeedbackNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindVertexArray"]
+ pub static mut epoxy_glBindVertexArray:
+ ::std::option::Option<unsafe extern "C" fn(array: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindVertexArrayAPPLE"]
+ pub static mut epoxy_glBindVertexArrayAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(array: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindVertexArrayOES"]
+ pub static mut epoxy_glBindVertexArrayOES:
+ ::std::option::Option<unsafe extern "C" fn(array: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindVertexBuffer"]
+ pub static mut epoxy_glBindVertexBuffer: ::std::option::Option<
+ unsafe extern "C" fn(
+ bindingindex: GLuint,
+ buffer: GLuint,
+ offset: GLintptr,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindVertexBuffers"]
+ pub static mut epoxy_glBindVertexBuffers: ::std::option::Option<
+ unsafe extern "C" fn(
+ first: GLuint,
+ count: GLsizei,
+ buffers: *const GLuint,
+ offsets: *const GLintptr,
+ strides: *const GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindVertexShaderEXT"]
+ pub static mut epoxy_glBindVertexShaderEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindVideoCaptureStreamBufferNV"]
+ pub static mut epoxy_glBindVideoCaptureStreamBufferNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ stream: GLuint,
+ frame_region: GLenum,
+ offset: GLintptrARB,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBindVideoCaptureStreamTextureNV"]
+ pub static mut epoxy_glBindVideoCaptureStreamTextureNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ stream: GLuint,
+ frame_region: GLenum,
+ target: GLenum,
+ texture: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3bEXT"]
+ pub static mut epoxy_glBinormal3bEXT:
+ ::std::option::Option<unsafe extern "C" fn(bx: GLbyte, by: GLbyte, bz: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3bvEXT"]
+ pub static mut epoxy_glBinormal3bvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3dEXT"]
+ pub static mut epoxy_glBinormal3dEXT:
+ ::std::option::Option<unsafe extern "C" fn(bx: GLdouble, by: GLdouble, bz: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3dvEXT"]
+ pub static mut epoxy_glBinormal3dvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3fEXT"]
+ pub static mut epoxy_glBinormal3fEXT:
+ ::std::option::Option<unsafe extern "C" fn(bx: GLfloat, by: GLfloat, bz: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3fvEXT"]
+ pub static mut epoxy_glBinormal3fvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3iEXT"]
+ pub static mut epoxy_glBinormal3iEXT:
+ ::std::option::Option<unsafe extern "C" fn(bx: GLint, by: GLint, bz: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3ivEXT"]
+ pub static mut epoxy_glBinormal3ivEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3sEXT"]
+ pub static mut epoxy_glBinormal3sEXT:
+ ::std::option::Option<unsafe extern "C" fn(bx: GLshort, by: GLshort, bz: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormal3svEXT"]
+ pub static mut epoxy_glBinormal3svEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBinormalPointerEXT"]
+ pub static mut epoxy_glBinormalPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBitmap"]
+ pub static mut epoxy_glBitmap: ::std::option::Option<
+ unsafe extern "C" fn(
+ width: GLsizei,
+ height: GLsizei,
+ xorig: GLfloat,
+ yorig: GLfloat,
+ xmove: GLfloat,
+ ymove: GLfloat,
+ bitmap: *const GLubyte,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBitmapxOES"]
+ pub static mut epoxy_glBitmapxOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ width: GLsizei,
+ height: GLsizei,
+ xorig: GLfixed,
+ yorig: GLfixed,
+ xmove: GLfixed,
+ ymove: GLfixed,
+ bitmap: *const GLubyte,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendBarrier"]
+ pub static mut epoxy_glBlendBarrier: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendBarrierKHR"]
+ pub static mut epoxy_glBlendBarrierKHR: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendBarrierNV"]
+ pub static mut epoxy_glBlendBarrierNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendColor"]
+ pub static mut epoxy_glBlendColor: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendColorEXT"]
+ pub static mut epoxy_glBlendColorEXT: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendColorxOES"]
+ pub static mut epoxy_glBlendColorxOES: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquation"]
+ pub static mut epoxy_glBlendEquation: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationEXT"]
+ pub static mut epoxy_glBlendEquationEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationIndexedAMD"]
+ pub static mut epoxy_glBlendEquationIndexedAMD:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationOES"]
+ pub static mut epoxy_glBlendEquationOES:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationSeparate"]
+ pub static mut epoxy_glBlendEquationSeparate:
+ ::std::option::Option<unsafe extern "C" fn(modeRGB: GLenum, modeAlpha: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationSeparateEXT"]
+ pub static mut epoxy_glBlendEquationSeparateEXT:
+ ::std::option::Option<unsafe extern "C" fn(modeRGB: GLenum, modeAlpha: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationSeparateIndexedAMD"]
+ pub static mut epoxy_glBlendEquationSeparateIndexedAMD: ::std::option::Option<
+ unsafe extern "C" fn(buf: GLuint, modeRGB: GLenum, modeAlpha: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationSeparateOES"]
+ pub static mut epoxy_glBlendEquationSeparateOES:
+ ::std::option::Option<unsafe extern "C" fn(modeRGB: GLenum, modeAlpha: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationSeparatei"]
+ pub static mut epoxy_glBlendEquationSeparatei: ::std::option::Option<
+ unsafe extern "C" fn(buf: GLuint, modeRGB: GLenum, modeAlpha: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationSeparateiARB"]
+ pub static mut epoxy_glBlendEquationSeparateiARB: ::std::option::Option<
+ unsafe extern "C" fn(buf: GLuint, modeRGB: GLenum, modeAlpha: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationSeparateiEXT"]
+ pub static mut epoxy_glBlendEquationSeparateiEXT: ::std::option::Option<
+ unsafe extern "C" fn(buf: GLuint, modeRGB: GLenum, modeAlpha: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationSeparateiOES"]
+ pub static mut epoxy_glBlendEquationSeparateiOES: ::std::option::Option<
+ unsafe extern "C" fn(buf: GLuint, modeRGB: GLenum, modeAlpha: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationi"]
+ pub static mut epoxy_glBlendEquationi:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationiARB"]
+ pub static mut epoxy_glBlendEquationiARB:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationiEXT"]
+ pub static mut epoxy_glBlendEquationiEXT:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendEquationiOES"]
+ pub static mut epoxy_glBlendEquationiOES:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFunc"]
+ pub static mut epoxy_glBlendFunc:
+ ::std::option::Option<unsafe extern "C" fn(sfactor: GLenum, dfactor: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncIndexedAMD"]
+ pub static mut epoxy_glBlendFuncIndexedAMD:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, src: GLenum, dst: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparate"]
+ pub static mut epoxy_glBlendFuncSeparate: ::std::option::Option<
+ unsafe extern "C" fn(
+ sfactorRGB: GLenum,
+ dfactorRGB: GLenum,
+ sfactorAlpha: GLenum,
+ dfactorAlpha: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparateEXT"]
+ pub static mut epoxy_glBlendFuncSeparateEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ sfactorRGB: GLenum,
+ dfactorRGB: GLenum,
+ sfactorAlpha: GLenum,
+ dfactorAlpha: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparateINGR"]
+ pub static mut epoxy_glBlendFuncSeparateINGR: ::std::option::Option<
+ unsafe extern "C" fn(
+ sfactorRGB: GLenum,
+ dfactorRGB: GLenum,
+ sfactorAlpha: GLenum,
+ dfactorAlpha: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparateIndexedAMD"]
+ pub static mut epoxy_glBlendFuncSeparateIndexedAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ buf: GLuint,
+ srcRGB: GLenum,
+ dstRGB: GLenum,
+ srcAlpha: GLenum,
+ dstAlpha: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparateOES"]
+ pub static mut epoxy_glBlendFuncSeparateOES: ::std::option::Option<
+ unsafe extern "C" fn(srcRGB: GLenum, dstRGB: GLenum, srcAlpha: GLenum, dstAlpha: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparatei"]
+ pub static mut epoxy_glBlendFuncSeparatei: ::std::option::Option<
+ unsafe extern "C" fn(
+ buf: GLuint,
+ srcRGB: GLenum,
+ dstRGB: GLenum,
+ srcAlpha: GLenum,
+ dstAlpha: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparateiARB"]
+ pub static mut epoxy_glBlendFuncSeparateiARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ buf: GLuint,
+ srcRGB: GLenum,
+ dstRGB: GLenum,
+ srcAlpha: GLenum,
+ dstAlpha: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparateiEXT"]
+ pub static mut epoxy_glBlendFuncSeparateiEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buf: GLuint,
+ srcRGB: GLenum,
+ dstRGB: GLenum,
+ srcAlpha: GLenum,
+ dstAlpha: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFuncSeparateiOES"]
+ pub static mut epoxy_glBlendFuncSeparateiOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ buf: GLuint,
+ srcRGB: GLenum,
+ dstRGB: GLenum,
+ srcAlpha: GLenum,
+ dstAlpha: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFunci"]
+ pub static mut epoxy_glBlendFunci:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, src: GLenum, dst: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFunciARB"]
+ pub static mut epoxy_glBlendFunciARB:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, src: GLenum, dst: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFunciEXT"]
+ pub static mut epoxy_glBlendFunciEXT:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, src: GLenum, dst: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendFunciOES"]
+ pub static mut epoxy_glBlendFunciOES:
+ ::std::option::Option<unsafe extern "C" fn(buf: GLuint, src: GLenum, dst: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlendParameteriNV"]
+ pub static mut epoxy_glBlendParameteriNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, value: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlitFramebuffer"]
+ pub static mut epoxy_glBlitFramebuffer: ::std::option::Option<
+ unsafe extern "C" fn(
+ srcX0: GLint,
+ srcY0: GLint,
+ srcX1: GLint,
+ srcY1: GLint,
+ dstX0: GLint,
+ dstY0: GLint,
+ dstX1: GLint,
+ dstY1: GLint,
+ mask: GLbitfield,
+ filter: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlitFramebufferANGLE"]
+ pub static mut epoxy_glBlitFramebufferANGLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ srcX0: GLint,
+ srcY0: GLint,
+ srcX1: GLint,
+ srcY1: GLint,
+ dstX0: GLint,
+ dstY0: GLint,
+ dstX1: GLint,
+ dstY1: GLint,
+ mask: GLbitfield,
+ filter: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlitFramebufferEXT"]
+ pub static mut epoxy_glBlitFramebufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ srcX0: GLint,
+ srcY0: GLint,
+ srcX1: GLint,
+ srcY1: GLint,
+ dstX0: GLint,
+ dstY0: GLint,
+ dstX1: GLint,
+ dstY1: GLint,
+ mask: GLbitfield,
+ filter: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlitFramebufferNV"]
+ pub static mut epoxy_glBlitFramebufferNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ srcX0: GLint,
+ srcY0: GLint,
+ srcX1: GLint,
+ srcY1: GLint,
+ dstX0: GLint,
+ dstY0: GLint,
+ dstX1: GLint,
+ dstY1: GLint,
+ mask: GLbitfield,
+ filter: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBlitNamedFramebuffer"]
+ pub static mut epoxy_glBlitNamedFramebuffer: ::std::option::Option<
+ unsafe extern "C" fn(
+ readFramebuffer: GLuint,
+ drawFramebuffer: GLuint,
+ srcX0: GLint,
+ srcY0: GLint,
+ srcX1: GLint,
+ srcY1: GLint,
+ dstX0: GLint,
+ dstY0: GLint,
+ dstX1: GLint,
+ dstY1: GLint,
+ mask: GLbitfield,
+ filter: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferAddressRangeNV"]
+ pub static mut epoxy_glBufferAddressRangeNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ pname: GLenum,
+ index: GLuint,
+ address: GLuint64EXT,
+ length: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferData"]
+ pub static mut epoxy_glBufferData: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ usage: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferDataARB"]
+ pub static mut epoxy_glBufferDataARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ size: GLsizeiptrARB,
+ data: *const ::std::os::raw::c_void,
+ usage: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferPageCommitmentARB"]
+ pub static mut epoxy_glBufferPageCommitmentARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, offset: GLintptr, size: GLsizeiptr, commit: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferParameteriAPPLE"]
+ pub static mut epoxy_glBufferParameteriAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferStorage"]
+ pub static mut epoxy_glBufferStorage: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ flags: GLbitfield,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferStorageEXT"]
+ pub static mut epoxy_glBufferStorageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ flags: GLbitfield,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferSubData"]
+ pub static mut epoxy_glBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glBufferSubDataARB"]
+ pub static mut epoxy_glBufferSubDataARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ offset: GLintptrARB,
+ size: GLsizeiptrARB,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCallCommandListNV"]
+ pub static mut epoxy_glCallCommandListNV:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCallList"]
+ pub static mut epoxy_glCallList: ::std::option::Option<unsafe extern "C" fn(list: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCallLists"]
+ pub static mut epoxy_glCallLists: ::std::option::Option<
+ unsafe extern "C" fn(n: GLsizei, type_: GLenum, lists: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCheckFramebufferStatus"]
+ pub static mut epoxy_glCheckFramebufferStatus:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum) -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCheckFramebufferStatusEXT"]
+ pub static mut epoxy_glCheckFramebufferStatusEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum) -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCheckFramebufferStatusOES"]
+ pub static mut epoxy_glCheckFramebufferStatusOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum) -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCheckNamedFramebufferStatus"]
+ pub static mut epoxy_glCheckNamedFramebufferStatus:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint, target: GLenum) -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCheckNamedFramebufferStatusEXT"]
+ pub static mut epoxy_glCheckNamedFramebufferStatusEXT:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint, target: GLenum) -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClampColor"]
+ pub static mut epoxy_glClampColor:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, clamp: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClampColorARB"]
+ pub static mut epoxy_glClampColorARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, clamp: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClear"]
+ pub static mut epoxy_glClear: ::std::option::Option<unsafe extern "C" fn(mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearAccum"]
+ pub static mut epoxy_glClearAccum: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearAccumxOES"]
+ pub static mut epoxy_glClearAccumxOES: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearBufferData"]
+ pub static mut epoxy_glClearBufferData: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearBufferSubData"]
+ pub static mut epoxy_glClearBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearBufferfi"]
+ pub static mut epoxy_glClearBufferfi: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLenum, drawbuffer: GLint, depth: GLfloat, stencil: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearBufferfv"]
+ pub static mut epoxy_glClearBufferfv: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLenum, drawbuffer: GLint, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearBufferiv"]
+ pub static mut epoxy_glClearBufferiv: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLenum, drawbuffer: GLint, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearBufferuiv"]
+ pub static mut epoxy_glClearBufferuiv: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLenum, drawbuffer: GLint, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearColor"]
+ pub static mut epoxy_glClearColor: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearColorIiEXT"]
+ pub static mut epoxy_glClearColorIiEXT: ::std::option::Option<
+ unsafe extern "C" fn(red: GLint, green: GLint, blue: GLint, alpha: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearColorIuiEXT"]
+ pub static mut epoxy_glClearColorIuiEXT: ::std::option::Option<
+ unsafe extern "C" fn(red: GLuint, green: GLuint, blue: GLuint, alpha: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearColorx"]
+ pub static mut epoxy_glClearColorx: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearColorxOES"]
+ pub static mut epoxy_glClearColorxOES: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearDepth"]
+ pub static mut epoxy_glClearDepth: ::std::option::Option<unsafe extern "C" fn(depth: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearDepthdNV"]
+ pub static mut epoxy_glClearDepthdNV:
+ ::std::option::Option<unsafe extern "C" fn(depth: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearDepthf"]
+ pub static mut epoxy_glClearDepthf: ::std::option::Option<unsafe extern "C" fn(d: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearDepthfOES"]
+ pub static mut epoxy_glClearDepthfOES:
+ ::std::option::Option<unsafe extern "C" fn(depth: GLclampf)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearDepthx"]
+ pub static mut epoxy_glClearDepthx: ::std::option::Option<unsafe extern "C" fn(depth: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearDepthxOES"]
+ pub static mut epoxy_glClearDepthxOES:
+ ::std::option::Option<unsafe extern "C" fn(depth: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearIndex"]
+ pub static mut epoxy_glClearIndex: ::std::option::Option<unsafe extern "C" fn(c: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearNamedBufferData"]
+ pub static mut epoxy_glClearNamedBufferData: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ internalformat: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearNamedBufferDataEXT"]
+ pub static mut epoxy_glClearNamedBufferDataEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ internalformat: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearNamedBufferSubData"]
+ pub static mut epoxy_glClearNamedBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ internalformat: GLenum,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearNamedBufferSubDataEXT"]
+ pub static mut epoxy_glClearNamedBufferSubDataEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ internalformat: GLenum,
+ offset: GLsizeiptr,
+ size: GLsizeiptr,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearNamedFramebufferfi"]
+ pub static mut epoxy_glClearNamedFramebufferfi: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ buffer: GLenum,
+ drawbuffer: GLint,
+ depth: GLfloat,
+ stencil: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearNamedFramebufferfv"]
+ pub static mut epoxy_glClearNamedFramebufferfv: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ buffer: GLenum,
+ drawbuffer: GLint,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearNamedFramebufferiv"]
+ pub static mut epoxy_glClearNamedFramebufferiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ buffer: GLenum,
+ drawbuffer: GLint,
+ value: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearNamedFramebufferuiv"]
+ pub static mut epoxy_glClearNamedFramebufferuiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ buffer: GLenum,
+ drawbuffer: GLint,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearPixelLocalStorageuiEXT"]
+ pub static mut epoxy_glClearPixelLocalStorageuiEXT: ::std::option::Option<
+ unsafe extern "C" fn(offset: GLsizei, n: GLsizei, values: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearStencil"]
+ pub static mut epoxy_glClearStencil: ::std::option::Option<unsafe extern "C" fn(s: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearTexImage"]
+ pub static mut epoxy_glClearTexImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearTexImageEXT"]
+ pub static mut epoxy_glClearTexImageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearTexSubImage"]
+ pub static mut epoxy_glClearTexSubImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClearTexSubImageEXT"]
+ pub static mut epoxy_glClearTexSubImageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClientActiveTexture"]
+ pub static mut epoxy_glClientActiveTexture:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClientActiveTextureARB"]
+ pub static mut epoxy_glClientActiveTextureARB:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClientActiveVertexStreamATI"]
+ pub static mut epoxy_glClientActiveVertexStreamATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClientAttribDefaultEXT"]
+ pub static mut epoxy_glClientAttribDefaultEXT:
+ ::std::option::Option<unsafe extern "C" fn(mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClientWaitSync"]
+ pub static mut epoxy_glClientWaitSync: ::std::option::Option<
+ unsafe extern "C" fn(sync: GLsync, flags: GLbitfield, timeout: GLuint64) -> GLenum,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClientWaitSyncAPPLE"]
+ pub static mut epoxy_glClientWaitSyncAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(sync: GLsync, flags: GLbitfield, timeout: GLuint64) -> GLenum,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClipControl"]
+ pub static mut epoxy_glClipControl:
+ ::std::option::Option<unsafe extern "C" fn(origin: GLenum, depth: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClipPlane"]
+ pub static mut epoxy_glClipPlane:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClipPlanef"]
+ pub static mut epoxy_glClipPlanef:
+ ::std::option::Option<unsafe extern "C" fn(p: GLenum, eqn: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClipPlanefIMG"]
+ pub static mut epoxy_glClipPlanefIMG:
+ ::std::option::Option<unsafe extern "C" fn(p: GLenum, eqn: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClipPlanefOES"]
+ pub static mut epoxy_glClipPlanefOES:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClipPlanex"]
+ pub static mut epoxy_glClipPlanex:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClipPlanexIMG"]
+ pub static mut epoxy_glClipPlanexIMG:
+ ::std::option::Option<unsafe extern "C" fn(p: GLenum, eqn: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glClipPlanexOES"]
+ pub static mut epoxy_glClipPlanexOES:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3b"]
+ pub static mut epoxy_glColor3b:
+ ::std::option::Option<unsafe extern "C" fn(red: GLbyte, green: GLbyte, blue: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3bv"]
+ pub static mut epoxy_glColor3bv: ::std::option::Option<unsafe extern "C" fn(v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3d"]
+ pub static mut epoxy_glColor3d:
+ ::std::option::Option<unsafe extern "C" fn(red: GLdouble, green: GLdouble, blue: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3dv"]
+ pub static mut epoxy_glColor3dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3f"]
+ pub static mut epoxy_glColor3f:
+ ::std::option::Option<unsafe extern "C" fn(red: GLfloat, green: GLfloat, blue: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3fVertex3fSUN"]
+ pub static mut epoxy_glColor3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ r: GLfloat,
+ g: GLfloat,
+ b: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3fVertex3fvSUN"]
+ pub static mut epoxy_glColor3fVertex3fvSUN:
+ ::std::option::Option<unsafe extern "C" fn(c: *const GLfloat, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3fv"]
+ pub static mut epoxy_glColor3fv: ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3hNV"]
+ pub static mut epoxy_glColor3hNV:
+ ::std::option::Option<unsafe extern "C" fn(red: GLhalfNV, green: GLhalfNV, blue: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3hvNV"]
+ pub static mut epoxy_glColor3hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3i"]
+ pub static mut epoxy_glColor3i:
+ ::std::option::Option<unsafe extern "C" fn(red: GLint, green: GLint, blue: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3iv"]
+ pub static mut epoxy_glColor3iv: ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3s"]
+ pub static mut epoxy_glColor3s:
+ ::std::option::Option<unsafe extern "C" fn(red: GLshort, green: GLshort, blue: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3sv"]
+ pub static mut epoxy_glColor3sv: ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3ub"]
+ pub static mut epoxy_glColor3ub:
+ ::std::option::Option<unsafe extern "C" fn(red: GLubyte, green: GLubyte, blue: GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3ubv"]
+ pub static mut epoxy_glColor3ubv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3ui"]
+ pub static mut epoxy_glColor3ui:
+ ::std::option::Option<unsafe extern "C" fn(red: GLuint, green: GLuint, blue: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3uiv"]
+ pub static mut epoxy_glColor3uiv: ::std::option::Option<unsafe extern "C" fn(v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3us"]
+ pub static mut epoxy_glColor3us:
+ ::std::option::Option<unsafe extern "C" fn(red: GLushort, green: GLushort, blue: GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3usv"]
+ pub static mut epoxy_glColor3usv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3xOES"]
+ pub static mut epoxy_glColor3xOES:
+ ::std::option::Option<unsafe extern "C" fn(red: GLfixed, green: GLfixed, blue: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor3xvOES"]
+ pub static mut epoxy_glColor3xvOES:
+ ::std::option::Option<unsafe extern "C" fn(components: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4b"]
+ pub static mut epoxy_glColor4b: ::std::option::Option<
+ unsafe extern "C" fn(red: GLbyte, green: GLbyte, blue: GLbyte, alpha: GLbyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4bv"]
+ pub static mut epoxy_glColor4bv: ::std::option::Option<unsafe extern "C" fn(v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4d"]
+ pub static mut epoxy_glColor4d: ::std::option::Option<
+ unsafe extern "C" fn(red: GLdouble, green: GLdouble, blue: GLdouble, alpha: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4dv"]
+ pub static mut epoxy_glColor4dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4f"]
+ pub static mut epoxy_glColor4f: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4fNormal3fVertex3fSUN"]
+ pub static mut epoxy_glColor4fNormal3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ r: GLfloat,
+ g: GLfloat,
+ b: GLfloat,
+ a: GLfloat,
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4fNormal3fVertex3fvSUN"]
+ pub static mut epoxy_glColor4fNormal3fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(c: *const GLfloat, n: *const GLfloat, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4fv"]
+ pub static mut epoxy_glColor4fv: ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4hNV"]
+ pub static mut epoxy_glColor4hNV: ::std::option::Option<
+ unsafe extern "C" fn(red: GLhalfNV, green: GLhalfNV, blue: GLhalfNV, alpha: GLhalfNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4hvNV"]
+ pub static mut epoxy_glColor4hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4i"]
+ pub static mut epoxy_glColor4i: ::std::option::Option<
+ unsafe extern "C" fn(red: GLint, green: GLint, blue: GLint, alpha: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4iv"]
+ pub static mut epoxy_glColor4iv: ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4s"]
+ pub static mut epoxy_glColor4s: ::std::option::Option<
+ unsafe extern "C" fn(red: GLshort, green: GLshort, blue: GLshort, alpha: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4sv"]
+ pub static mut epoxy_glColor4sv: ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4ub"]
+ pub static mut epoxy_glColor4ub: ::std::option::Option<
+ unsafe extern "C" fn(red: GLubyte, green: GLubyte, blue: GLubyte, alpha: GLubyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4ubVertex2fSUN"]
+ pub static mut epoxy_glColor4ubVertex2fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ r: GLubyte,
+ g: GLubyte,
+ b: GLubyte,
+ a: GLubyte,
+ x: GLfloat,
+ y: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4ubVertex2fvSUN"]
+ pub static mut epoxy_glColor4ubVertex2fvSUN:
+ ::std::option::Option<unsafe extern "C" fn(c: *const GLubyte, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4ubVertex3fSUN"]
+ pub static mut epoxy_glColor4ubVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ r: GLubyte,
+ g: GLubyte,
+ b: GLubyte,
+ a: GLubyte,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4ubVertex3fvSUN"]
+ pub static mut epoxy_glColor4ubVertex3fvSUN:
+ ::std::option::Option<unsafe extern "C" fn(c: *const GLubyte, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4ubv"]
+ pub static mut epoxy_glColor4ubv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4ui"]
+ pub static mut epoxy_glColor4ui: ::std::option::Option<
+ unsafe extern "C" fn(red: GLuint, green: GLuint, blue: GLuint, alpha: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4uiv"]
+ pub static mut epoxy_glColor4uiv: ::std::option::Option<unsafe extern "C" fn(v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4us"]
+ pub static mut epoxy_glColor4us: ::std::option::Option<
+ unsafe extern "C" fn(red: GLushort, green: GLushort, blue: GLushort, alpha: GLushort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4usv"]
+ pub static mut epoxy_glColor4usv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4x"]
+ pub static mut epoxy_glColor4x: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4xOES"]
+ pub static mut epoxy_glColor4xOES: ::std::option::Option<
+ unsafe extern "C" fn(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColor4xvOES"]
+ pub static mut epoxy_glColor4xvOES:
+ ::std::option::Option<unsafe extern "C" fn(components: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorFormatNV"]
+ pub static mut epoxy_glColorFormatNV:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, type_: GLenum, stride: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorFragmentOp1ATI"]
+ pub static mut epoxy_glColorFragmentOp1ATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ op: GLenum,
+ dst: GLuint,
+ dstMask: GLuint,
+ dstMod: GLuint,
+ arg1: GLuint,
+ arg1Rep: GLuint,
+ arg1Mod: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorFragmentOp2ATI"]
+ pub static mut epoxy_glColorFragmentOp2ATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ op: GLenum,
+ dst: GLuint,
+ dstMask: GLuint,
+ dstMod: GLuint,
+ arg1: GLuint,
+ arg1Rep: GLuint,
+ arg1Mod: GLuint,
+ arg2: GLuint,
+ arg2Rep: GLuint,
+ arg2Mod: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorFragmentOp3ATI"]
+ pub static mut epoxy_glColorFragmentOp3ATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ op: GLenum,
+ dst: GLuint,
+ dstMask: GLuint,
+ dstMod: GLuint,
+ arg1: GLuint,
+ arg1Rep: GLuint,
+ arg1Mod: GLuint,
+ arg2: GLuint,
+ arg2Rep: GLuint,
+ arg2Mod: GLuint,
+ arg3: GLuint,
+ arg3Rep: GLuint,
+ arg3Mod: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorMask"]
+ pub static mut epoxy_glColorMask: ::std::option::Option<
+ unsafe extern "C" fn(red: GLboolean, green: GLboolean, blue: GLboolean, alpha: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorMaskIndexedEXT"]
+ pub static mut epoxy_glColorMaskIndexedEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, r: GLboolean, g: GLboolean, b: GLboolean, a: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorMaski"]
+ pub static mut epoxy_glColorMaski: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, r: GLboolean, g: GLboolean, b: GLboolean, a: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorMaskiEXT"]
+ pub static mut epoxy_glColorMaskiEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, r: GLboolean, g: GLboolean, b: GLboolean, a: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorMaskiOES"]
+ pub static mut epoxy_glColorMaskiOES: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, r: GLboolean, g: GLboolean, b: GLboolean, a: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorMaterial"]
+ pub static mut epoxy_glColorMaterial:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorP3ui"]
+ pub static mut epoxy_glColorP3ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, color: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorP3uiv"]
+ pub static mut epoxy_glColorP3uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, color: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorP4ui"]
+ pub static mut epoxy_glColorP4ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, color: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorP4uiv"]
+ pub static mut epoxy_glColorP4uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, color: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorPointer"]
+ pub static mut epoxy_glColorPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorPointerEXT"]
+ pub static mut epoxy_glColorPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ count: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorPointerListIBM"]
+ pub static mut epoxy_glColorPointerListIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLint,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ptrstride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorPointervINTEL"]
+ pub static mut epoxy_glColorPointervINTEL: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorSubTable"]
+ pub static mut epoxy_glColorSubTable: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ start: GLsizei,
+ count: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorSubTableEXT"]
+ pub static mut epoxy_glColorSubTableEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ start: GLsizei,
+ count: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorTable"]
+ pub static mut epoxy_glColorTable: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ table: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorTableEXT"]
+ pub static mut epoxy_glColorTableEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalFormat: GLenum,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ table: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorTableParameterfv"]
+ pub static mut epoxy_glColorTableParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorTableParameterfvSGI"]
+ pub static mut epoxy_glColorTableParameterfvSGI: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorTableParameteriv"]
+ pub static mut epoxy_glColorTableParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorTableParameterivSGI"]
+ pub static mut epoxy_glColorTableParameterivSGI: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glColorTableSGI"]
+ pub static mut epoxy_glColorTableSGI: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ table: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCombinerInputNV"]
+ pub static mut epoxy_glCombinerInputNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ stage: GLenum,
+ portion: GLenum,
+ variable: GLenum,
+ input: GLenum,
+ mapping: GLenum,
+ componentUsage: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCombinerOutputNV"]
+ pub static mut epoxy_glCombinerOutputNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ stage: GLenum,
+ portion: GLenum,
+ abOutput: GLenum,
+ cdOutput: GLenum,
+ sumOutput: GLenum,
+ scale: GLenum,
+ bias: GLenum,
+ abDotProduct: GLboolean,
+ cdDotProduct: GLboolean,
+ muxSum: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCombinerParameterfNV"]
+ pub static mut epoxy_glCombinerParameterfNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCombinerParameterfvNV"]
+ pub static mut epoxy_glCombinerParameterfvNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCombinerParameteriNV"]
+ pub static mut epoxy_glCombinerParameteriNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCombinerParameterivNV"]
+ pub static mut epoxy_glCombinerParameterivNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCombinerStageParameterfvNV"]
+ pub static mut epoxy_glCombinerStageParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(stage: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCommandListSegmentsNV"]
+ pub static mut epoxy_glCommandListSegmentsNV:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint, segments: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompileCommandListNV"]
+ pub static mut epoxy_glCompileCommandListNV:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompileShader"]
+ pub static mut epoxy_glCompileShader:
+ ::std::option::Option<unsafe extern "C" fn(shader: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompileShaderARB"]
+ pub static mut epoxy_glCompileShaderARB:
+ ::std::option::Option<unsafe extern "C" fn(shaderObj: GLhandleARB)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompileShaderIncludeARB"]
+ pub static mut epoxy_glCompileShaderIncludeARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ shader: GLuint,
+ count: GLsizei,
+ path: *const *const GLchar,
+ length: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedMultiTexImage1DEXT"]
+ pub static mut epoxy_glCompressedMultiTexImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedMultiTexImage2DEXT"]
+ pub static mut epoxy_glCompressedMultiTexImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedMultiTexImage3DEXT"]
+ pub static mut epoxy_glCompressedMultiTexImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedMultiTexSubImage1DEXT"]
+ pub static mut epoxy_glCompressedMultiTexSubImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedMultiTexSubImage2DEXT"]
+ pub static mut epoxy_glCompressedMultiTexSubImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedMultiTexSubImage3DEXT"]
+ pub static mut epoxy_glCompressedMultiTexSubImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexImage1D"]
+ pub static mut epoxy_glCompressedTexImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexImage1DARB"]
+ pub static mut epoxy_glCompressedTexImage1DARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexImage2D"]
+ pub static mut epoxy_glCompressedTexImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexImage2DARB"]
+ pub static mut epoxy_glCompressedTexImage2DARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexImage3D"]
+ pub static mut epoxy_glCompressedTexImage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexImage3DARB"]
+ pub static mut epoxy_glCompressedTexImage3DARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexImage3DOES"]
+ pub static mut epoxy_glCompressedTexImage3DOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexSubImage1D"]
+ pub static mut epoxy_glCompressedTexSubImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexSubImage1DARB"]
+ pub static mut epoxy_glCompressedTexSubImage1DARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexSubImage2D"]
+ pub static mut epoxy_glCompressedTexSubImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexSubImage2DARB"]
+ pub static mut epoxy_glCompressedTexSubImage2DARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexSubImage3D"]
+ pub static mut epoxy_glCompressedTexSubImage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexSubImage3DARB"]
+ pub static mut epoxy_glCompressedTexSubImage3DARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTexSubImage3DOES"]
+ pub static mut epoxy_glCompressedTexSubImage3DOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureImage1DEXT"]
+ pub static mut epoxy_glCompressedTextureImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureImage2DEXT"]
+ pub static mut epoxy_glCompressedTextureImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureImage3DEXT"]
+ pub static mut epoxy_glCompressedTextureImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureSubImage1D"]
+ pub static mut epoxy_glCompressedTextureSubImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureSubImage1DEXT"]
+ pub static mut epoxy_glCompressedTextureSubImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureSubImage2D"]
+ pub static mut epoxy_glCompressedTextureSubImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureSubImage2DEXT"]
+ pub static mut epoxy_glCompressedTextureSubImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureSubImage3D"]
+ pub static mut epoxy_glCompressedTextureSubImage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCompressedTextureSubImage3DEXT"]
+ pub static mut epoxy_glCompressedTextureSubImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ imageSize: GLsizei,
+ bits: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConservativeRasterParameterfNV"]
+ pub static mut epoxy_glConservativeRasterParameterfNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, value: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConservativeRasterParameteriNV"]
+ pub static mut epoxy_glConservativeRasterParameteriNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionFilter1D"]
+ pub static mut epoxy_glConvolutionFilter1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ image: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionFilter1DEXT"]
+ pub static mut epoxy_glConvolutionFilter1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ image: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionFilter2D"]
+ pub static mut epoxy_glConvolutionFilter2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ image: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionFilter2DEXT"]
+ pub static mut epoxy_glConvolutionFilter2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ image: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameterf"]
+ pub static mut epoxy_glConvolutionParameterf:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, params: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameterfEXT"]
+ pub static mut epoxy_glConvolutionParameterfEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, params: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameterfv"]
+ pub static mut epoxy_glConvolutionParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameterfvEXT"]
+ pub static mut epoxy_glConvolutionParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameteri"]
+ pub static mut epoxy_glConvolutionParameteri:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, params: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameteriEXT"]
+ pub static mut epoxy_glConvolutionParameteriEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, params: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameteriv"]
+ pub static mut epoxy_glConvolutionParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameterivEXT"]
+ pub static mut epoxy_glConvolutionParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameterxOES"]
+ pub static mut epoxy_glConvolutionParameterxOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glConvolutionParameterxvOES"]
+ pub static mut epoxy_glConvolutionParameterxvOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyBufferSubData"]
+ pub static mut epoxy_glCopyBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ readTarget: GLenum,
+ writeTarget: GLenum,
+ readOffset: GLintptr,
+ writeOffset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyBufferSubDataNV"]
+ pub static mut epoxy_glCopyBufferSubDataNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ readTarget: GLenum,
+ writeTarget: GLenum,
+ readOffset: GLintptr,
+ writeOffset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyColorSubTable"]
+ pub static mut epoxy_glCopyColorSubTable: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, start: GLsizei, x: GLint, y: GLint, width: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyColorSubTableEXT"]
+ pub static mut epoxy_glCopyColorSubTableEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, start: GLsizei, x: GLint, y: GLint, width: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyColorTable"]
+ pub static mut epoxy_glCopyColorTable: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyColorTableSGI"]
+ pub static mut epoxy_glCopyColorTableSGI: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyConvolutionFilter1D"]
+ pub static mut epoxy_glCopyConvolutionFilter1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyConvolutionFilter1DEXT"]
+ pub static mut epoxy_glCopyConvolutionFilter1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyConvolutionFilter2D"]
+ pub static mut epoxy_glCopyConvolutionFilter2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyConvolutionFilter2DEXT"]
+ pub static mut epoxy_glCopyConvolutionFilter2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyImageSubData"]
+ pub static mut epoxy_glCopyImageSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ srcName: GLuint,
+ srcTarget: GLenum,
+ srcLevel: GLint,
+ srcX: GLint,
+ srcY: GLint,
+ srcZ: GLint,
+ dstName: GLuint,
+ dstTarget: GLenum,
+ dstLevel: GLint,
+ dstX: GLint,
+ dstY: GLint,
+ dstZ: GLint,
+ srcWidth: GLsizei,
+ srcHeight: GLsizei,
+ srcDepth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyImageSubDataEXT"]
+ pub static mut epoxy_glCopyImageSubDataEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ srcName: GLuint,
+ srcTarget: GLenum,
+ srcLevel: GLint,
+ srcX: GLint,
+ srcY: GLint,
+ srcZ: GLint,
+ dstName: GLuint,
+ dstTarget: GLenum,
+ dstLevel: GLint,
+ dstX: GLint,
+ dstY: GLint,
+ dstZ: GLint,
+ srcWidth: GLsizei,
+ srcHeight: GLsizei,
+ srcDepth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyImageSubDataNV"]
+ pub static mut epoxy_glCopyImageSubDataNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ srcName: GLuint,
+ srcTarget: GLenum,
+ srcLevel: GLint,
+ srcX: GLint,
+ srcY: GLint,
+ srcZ: GLint,
+ dstName: GLuint,
+ dstTarget: GLenum,
+ dstLevel: GLint,
+ dstX: GLint,
+ dstY: GLint,
+ dstZ: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyImageSubDataOES"]
+ pub static mut epoxy_glCopyImageSubDataOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ srcName: GLuint,
+ srcTarget: GLenum,
+ srcLevel: GLint,
+ srcX: GLint,
+ srcY: GLint,
+ srcZ: GLint,
+ dstName: GLuint,
+ dstTarget: GLenum,
+ dstLevel: GLint,
+ dstX: GLint,
+ dstY: GLint,
+ dstZ: GLint,
+ srcWidth: GLsizei,
+ srcHeight: GLsizei,
+ srcDepth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyMultiTexImage1DEXT"]
+ pub static mut epoxy_glCopyMultiTexImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ border: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyMultiTexImage2DEXT"]
+ pub static mut epoxy_glCopyMultiTexImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyMultiTexSubImage1DEXT"]
+ pub static mut epoxy_glCopyMultiTexSubImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyMultiTexSubImage2DEXT"]
+ pub static mut epoxy_glCopyMultiTexSubImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyMultiTexSubImage3DEXT"]
+ pub static mut epoxy_glCopyMultiTexSubImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyNamedBufferSubData"]
+ pub static mut epoxy_glCopyNamedBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ readBuffer: GLuint,
+ writeBuffer: GLuint,
+ readOffset: GLintptr,
+ writeOffset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyPathNV"]
+ pub static mut epoxy_glCopyPathNV:
+ ::std::option::Option<unsafe extern "C" fn(resultPath: GLuint, srcPath: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyPixels"]
+ pub static mut epoxy_glCopyPixels: ::std::option::Option<
+ unsafe extern "C" fn(x: GLint, y: GLint, width: GLsizei, height: GLsizei, type_: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexImage1D"]
+ pub static mut epoxy_glCopyTexImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ border: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexImage1DEXT"]
+ pub static mut epoxy_glCopyTexImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ border: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexImage2D"]
+ pub static mut epoxy_glCopyTexImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexImage2DEXT"]
+ pub static mut epoxy_glCopyTexImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexSubImage1D"]
+ pub static mut epoxy_glCopyTexSubImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexSubImage1DEXT"]
+ pub static mut epoxy_glCopyTexSubImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexSubImage2D"]
+ pub static mut epoxy_glCopyTexSubImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexSubImage2DEXT"]
+ pub static mut epoxy_glCopyTexSubImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexSubImage3D"]
+ pub static mut epoxy_glCopyTexSubImage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexSubImage3DEXT"]
+ pub static mut epoxy_glCopyTexSubImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTexSubImage3DOES"]
+ pub static mut epoxy_glCopyTexSubImage3DOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureImage1DEXT"]
+ pub static mut epoxy_glCopyTextureImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ border: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureImage2DEXT"]
+ pub static mut epoxy_glCopyTextureImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureLevelsAPPLE"]
+ pub static mut epoxy_glCopyTextureLevelsAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ destinationTexture: GLuint,
+ sourceTexture: GLuint,
+ sourceBaseLevel: GLint,
+ sourceLevelCount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureSubImage1D"]
+ pub static mut epoxy_glCopyTextureSubImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureSubImage1DEXT"]
+ pub static mut epoxy_glCopyTextureSubImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureSubImage2D"]
+ pub static mut epoxy_glCopyTextureSubImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureSubImage2DEXT"]
+ pub static mut epoxy_glCopyTextureSubImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureSubImage3D"]
+ pub static mut epoxy_glCopyTextureSubImage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCopyTextureSubImage3DEXT"]
+ pub static mut epoxy_glCopyTextureSubImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCoverFillPathInstancedNV"]
+ pub static mut epoxy_glCoverFillPathInstancedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ numPaths: GLsizei,
+ pathNameType: GLenum,
+ paths: *const ::std::os::raw::c_void,
+ pathBase: GLuint,
+ coverMode: GLenum,
+ transformType: GLenum,
+ transformValues: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCoverFillPathNV"]
+ pub static mut epoxy_glCoverFillPathNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, coverMode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCoverStrokePathInstancedNV"]
+ pub static mut epoxy_glCoverStrokePathInstancedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ numPaths: GLsizei,
+ pathNameType: GLenum,
+ paths: *const ::std::os::raw::c_void,
+ pathBase: GLuint,
+ coverMode: GLenum,
+ transformType: GLenum,
+ transformValues: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCoverStrokePathNV"]
+ pub static mut epoxy_glCoverStrokePathNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, coverMode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCoverageMaskNV"]
+ pub static mut epoxy_glCoverageMaskNV:
+ ::std::option::Option<unsafe extern "C" fn(mask: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCoverageModulationNV"]
+ pub static mut epoxy_glCoverageModulationNV:
+ ::std::option::Option<unsafe extern "C" fn(components: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCoverageModulationTableNV"]
+ pub static mut epoxy_glCoverageModulationTableNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCoverageOperationNV"]
+ pub static mut epoxy_glCoverageOperationNV:
+ ::std::option::Option<unsafe extern "C" fn(operation: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateBuffers"]
+ pub static mut epoxy_glCreateBuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, buffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateCommandListsNV"]
+ pub static mut epoxy_glCreateCommandListsNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, lists: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateFramebuffers"]
+ pub static mut epoxy_glCreateFramebuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, framebuffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreatePerfQueryINTEL"]
+ pub static mut epoxy_glCreatePerfQueryINTEL:
+ ::std::option::Option<unsafe extern "C" fn(queryId: GLuint, queryHandle: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateProgram"]
+ pub static mut epoxy_glCreateProgram: ::std::option::Option<unsafe extern "C" fn() -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateProgramObjectARB"]
+ pub static mut epoxy_glCreateProgramObjectARB:
+ ::std::option::Option<unsafe extern "C" fn() -> GLhandleARB>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateProgramPipelines"]
+ pub static mut epoxy_glCreateProgramPipelines:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, pipelines: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateQueries"]
+ pub static mut epoxy_glCreateQueries:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, n: GLsizei, ids: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateRenderbuffers"]
+ pub static mut epoxy_glCreateRenderbuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, renderbuffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateSamplers"]
+ pub static mut epoxy_glCreateSamplers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, samplers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateShader"]
+ pub static mut epoxy_glCreateShader:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateShaderObjectARB"]
+ pub static mut epoxy_glCreateShaderObjectARB:
+ ::std::option::Option<unsafe extern "C" fn(shaderType: GLenum) -> GLhandleARB>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateShaderProgramEXT"]
+ pub static mut epoxy_glCreateShaderProgramEXT:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, string: *const GLchar) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateShaderProgramv"]
+ pub static mut epoxy_glCreateShaderProgramv: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ count: GLsizei,
+ strings: *const *const GLchar,
+ ) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateShaderProgramvEXT"]
+ pub static mut epoxy_glCreateShaderProgramvEXT: ::std::option::Option<
+ unsafe extern "C" fn(type_: GLenum, count: GLsizei, strings: *mut *const GLchar) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateStatesNV"]
+ pub static mut epoxy_glCreateStatesNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, states: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateSyncFromCLeventARB"]
+ pub static mut epoxy_glCreateSyncFromCLeventARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ context: *mut _cl_context,
+ event: *mut _cl_event,
+ flags: GLbitfield,
+ ) -> GLsync,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateTextures"]
+ pub static mut epoxy_glCreateTextures: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, n: GLsizei, textures: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateTransformFeedbacks"]
+ pub static mut epoxy_glCreateTransformFeedbacks:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCreateVertexArrays"]
+ pub static mut epoxy_glCreateVertexArrays:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, arrays: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCullFace"]
+ pub static mut epoxy_glCullFace: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCullParameterdvEXT"]
+ pub static mut epoxy_glCullParameterdvEXT:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *mut GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCullParameterfvEXT"]
+ pub static mut epoxy_glCullParameterfvEXT:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCurrentPaletteMatrixARB"]
+ pub static mut epoxy_glCurrentPaletteMatrixARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glCurrentPaletteMatrixOES"]
+ pub static mut epoxy_glCurrentPaletteMatrixOES:
+ ::std::option::Option<unsafe extern "C" fn(matrixpaletteindex: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageCallback"]
+ pub static mut epoxy_glDebugMessageCallback: ::std::option::Option<
+ unsafe extern "C" fn(callback: GLDEBUGPROC, userParam: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageCallbackAMD"]
+ pub static mut epoxy_glDebugMessageCallbackAMD: ::std::option::Option<
+ unsafe extern "C" fn(callback: GLDEBUGPROCAMD, userParam: *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageCallbackARB"]
+ pub static mut epoxy_glDebugMessageCallbackARB: ::std::option::Option<
+ unsafe extern "C" fn(callback: GLDEBUGPROCARB, userParam: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageCallbackKHR"]
+ pub static mut epoxy_glDebugMessageCallbackKHR: ::std::option::Option<
+ unsafe extern "C" fn(callback: GLDEBUGPROCKHR, userParam: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageControl"]
+ pub static mut epoxy_glDebugMessageControl: ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ severity: GLenum,
+ count: GLsizei,
+ ids: *const GLuint,
+ enabled: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageControlARB"]
+ pub static mut epoxy_glDebugMessageControlARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ severity: GLenum,
+ count: GLsizei,
+ ids: *const GLuint,
+ enabled: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageControlKHR"]
+ pub static mut epoxy_glDebugMessageControlKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ severity: GLenum,
+ count: GLsizei,
+ ids: *const GLuint,
+ enabled: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageEnableAMD"]
+ pub static mut epoxy_glDebugMessageEnableAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ category: GLenum,
+ severity: GLenum,
+ count: GLsizei,
+ ids: *const GLuint,
+ enabled: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageInsert"]
+ pub static mut epoxy_glDebugMessageInsert: ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ id: GLuint,
+ severity: GLenum,
+ length: GLsizei,
+ buf: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageInsertAMD"]
+ pub static mut epoxy_glDebugMessageInsertAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ category: GLenum,
+ severity: GLenum,
+ id: GLuint,
+ length: GLsizei,
+ buf: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageInsertARB"]
+ pub static mut epoxy_glDebugMessageInsertARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ id: GLuint,
+ severity: GLenum,
+ length: GLsizei,
+ buf: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDebugMessageInsertKHR"]
+ pub static mut epoxy_glDebugMessageInsertKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ source: GLenum,
+ type_: GLenum,
+ id: GLuint,
+ severity: GLenum,
+ length: GLsizei,
+ buf: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeformSGIX"]
+ pub static mut epoxy_glDeformSGIX:
+ ::std::option::Option<unsafe extern "C" fn(mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeformationMap3dSGIX"]
+ pub static mut epoxy_glDeformationMap3dSGIX: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ u1: GLdouble,
+ u2: GLdouble,
+ ustride: GLint,
+ uorder: GLint,
+ v1: GLdouble,
+ v2: GLdouble,
+ vstride: GLint,
+ vorder: GLint,
+ w1: GLdouble,
+ w2: GLdouble,
+ wstride: GLint,
+ worder: GLint,
+ points: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeformationMap3fSGIX"]
+ pub static mut epoxy_glDeformationMap3fSGIX: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ u1: GLfloat,
+ u2: GLfloat,
+ ustride: GLint,
+ uorder: GLint,
+ v1: GLfloat,
+ v2: GLfloat,
+ vstride: GLint,
+ vorder: GLint,
+ w1: GLfloat,
+ w2: GLfloat,
+ wstride: GLint,
+ worder: GLint,
+ points: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteAsyncMarkersSGIX"]
+ pub static mut epoxy_glDeleteAsyncMarkersSGIX:
+ ::std::option::Option<unsafe extern "C" fn(marker: GLuint, range: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteBuffers"]
+ pub static mut epoxy_glDeleteBuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, buffers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteBuffersARB"]
+ pub static mut epoxy_glDeleteBuffersARB:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, buffers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteCommandListsNV"]
+ pub static mut epoxy_glDeleteCommandListsNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, lists: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteFencesAPPLE"]
+ pub static mut epoxy_glDeleteFencesAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, fences: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteFencesNV"]
+ pub static mut epoxy_glDeleteFencesNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, fences: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteFragmentShaderATI"]
+ pub static mut epoxy_glDeleteFragmentShaderATI:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteFramebuffers"]
+ pub static mut epoxy_glDeleteFramebuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, framebuffers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteFramebuffersEXT"]
+ pub static mut epoxy_glDeleteFramebuffersEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, framebuffers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteFramebuffersOES"]
+ pub static mut epoxy_glDeleteFramebuffersOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, framebuffers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteLists"]
+ pub static mut epoxy_glDeleteLists:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint, range: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteNamedStringARB"]
+ pub static mut epoxy_glDeleteNamedStringARB:
+ ::std::option::Option<unsafe extern "C" fn(namelen: GLint, name: *const GLchar)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteNamesAMD"]
+ pub static mut epoxy_glDeleteNamesAMD: ::std::option::Option<
+ unsafe extern "C" fn(identifier: GLenum, num: GLuint, names: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteObjectARB"]
+ pub static mut epoxy_glDeleteObjectARB:
+ ::std::option::Option<unsafe extern "C" fn(obj: GLhandleARB)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteOcclusionQueriesNV"]
+ pub static mut epoxy_glDeleteOcclusionQueriesNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeletePathsNV"]
+ pub static mut epoxy_glDeletePathsNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, range: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeletePerfMonitorsAMD"]
+ pub static mut epoxy_glDeletePerfMonitorsAMD:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, monitors: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeletePerfQueryINTEL"]
+ pub static mut epoxy_glDeletePerfQueryINTEL:
+ ::std::option::Option<unsafe extern "C" fn(queryHandle: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteProgram"]
+ pub static mut epoxy_glDeleteProgram:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteProgramPipelines"]
+ pub static mut epoxy_glDeleteProgramPipelines:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, pipelines: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteProgramPipelinesEXT"]
+ pub static mut epoxy_glDeleteProgramPipelinesEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, pipelines: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteProgramsARB"]
+ pub static mut epoxy_glDeleteProgramsARB:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, programs: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteProgramsNV"]
+ pub static mut epoxy_glDeleteProgramsNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, programs: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteQueries"]
+ pub static mut epoxy_glDeleteQueries:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteQueriesARB"]
+ pub static mut epoxy_glDeleteQueriesARB:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteQueriesEXT"]
+ pub static mut epoxy_glDeleteQueriesEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteRenderbuffers"]
+ pub static mut epoxy_glDeleteRenderbuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, renderbuffers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteRenderbuffersEXT"]
+ pub static mut epoxy_glDeleteRenderbuffersEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, renderbuffers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteRenderbuffersOES"]
+ pub static mut epoxy_glDeleteRenderbuffersOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, renderbuffers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteSamplers"]
+ pub static mut epoxy_glDeleteSamplers:
+ ::std::option::Option<unsafe extern "C" fn(count: GLsizei, samplers: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteShader"]
+ pub static mut epoxy_glDeleteShader:
+ ::std::option::Option<unsafe extern "C" fn(shader: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteStatesNV"]
+ pub static mut epoxy_glDeleteStatesNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, states: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteSync"]
+ pub static mut epoxy_glDeleteSync: ::std::option::Option<unsafe extern "C" fn(sync: GLsync)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteSyncAPPLE"]
+ pub static mut epoxy_glDeleteSyncAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(sync: GLsync)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteTextures"]
+ pub static mut epoxy_glDeleteTextures:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, textures: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteTexturesEXT"]
+ pub static mut epoxy_glDeleteTexturesEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, textures: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteTransformFeedbacks"]
+ pub static mut epoxy_glDeleteTransformFeedbacks:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteTransformFeedbacksNV"]
+ pub static mut epoxy_glDeleteTransformFeedbacksNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteVertexArrays"]
+ pub static mut epoxy_glDeleteVertexArrays:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, arrays: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteVertexArraysAPPLE"]
+ pub static mut epoxy_glDeleteVertexArraysAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, arrays: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteVertexArraysOES"]
+ pub static mut epoxy_glDeleteVertexArraysOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, arrays: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDeleteVertexShaderEXT"]
+ pub static mut epoxy_glDeleteVertexShaderEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthBoundsEXT"]
+ pub static mut epoxy_glDepthBoundsEXT:
+ ::std::option::Option<unsafe extern "C" fn(zmin: GLclampd, zmax: GLclampd)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthBoundsdNV"]
+ pub static mut epoxy_glDepthBoundsdNV:
+ ::std::option::Option<unsafe extern "C" fn(zmin: GLdouble, zmax: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthFunc"]
+ pub static mut epoxy_glDepthFunc: ::std::option::Option<unsafe extern "C" fn(func: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthMask"]
+ pub static mut epoxy_glDepthMask: ::std::option::Option<unsafe extern "C" fn(flag: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRange"]
+ pub static mut epoxy_glDepthRange:
+ ::std::option::Option<unsafe extern "C" fn(hither: GLdouble, yon: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangeArrayfvNV"]
+ pub static mut epoxy_glDepthRangeArrayfvNV: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangeArrayfvOES"]
+ pub static mut epoxy_glDepthRangeArrayfvOES: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangeArrayv"]
+ pub static mut epoxy_glDepthRangeArrayv: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangeIndexed"]
+ pub static mut epoxy_glDepthRangeIndexed:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, n: GLdouble, f: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangeIndexedfNV"]
+ pub static mut epoxy_glDepthRangeIndexedfNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, n: GLfloat, f: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangeIndexedfOES"]
+ pub static mut epoxy_glDepthRangeIndexedfOES:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, n: GLfloat, f: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangedNV"]
+ pub static mut epoxy_glDepthRangedNV:
+ ::std::option::Option<unsafe extern "C" fn(zNear: GLdouble, zFar: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangef"]
+ pub static mut epoxy_glDepthRangef:
+ ::std::option::Option<unsafe extern "C" fn(n: GLfloat, f: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangefOES"]
+ pub static mut epoxy_glDepthRangefOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLclampf, f: GLclampf)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangex"]
+ pub static mut epoxy_glDepthRangex:
+ ::std::option::Option<unsafe extern "C" fn(n: GLfixed, f: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDepthRangexOES"]
+ pub static mut epoxy_glDepthRangexOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLfixed, f: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDetachObjectARB"]
+ pub static mut epoxy_glDetachObjectARB: ::std::option::Option<
+ unsafe extern "C" fn(containerObj: GLhandleARB, attachedObj: GLhandleARB),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDetachShader"]
+ pub static mut epoxy_glDetachShader:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, shader: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDetailTexFuncSGIS"]
+ pub static mut epoxy_glDetailTexFuncSGIS: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, n: GLsizei, points: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisable"]
+ pub static mut epoxy_glDisable: ::std::option::Option<unsafe extern "C" fn(cap: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableClientState"]
+ pub static mut epoxy_glDisableClientState:
+ ::std::option::Option<unsafe extern "C" fn(array: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableClientStateIndexedEXT"]
+ pub static mut epoxy_glDisableClientStateIndexedEXT:
+ ::std::option::Option<unsafe extern "C" fn(array: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableClientStateiEXT"]
+ pub static mut epoxy_glDisableClientStateiEXT:
+ ::std::option::Option<unsafe extern "C" fn(array: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableDriverControlQCOM"]
+ pub static mut epoxy_glDisableDriverControlQCOM:
+ ::std::option::Option<unsafe extern "C" fn(driverControl: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableIndexedEXT"]
+ pub static mut epoxy_glDisableIndexedEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableVariantClientStateEXT"]
+ pub static mut epoxy_glDisableVariantClientStateEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableVertexArrayAttrib"]
+ pub static mut epoxy_glDisableVertexArrayAttrib:
+ ::std::option::Option<unsafe extern "C" fn(vaobj: GLuint, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableVertexArrayAttribEXT"]
+ pub static mut epoxy_glDisableVertexArrayAttribEXT:
+ ::std::option::Option<unsafe extern "C" fn(vaobj: GLuint, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableVertexArrayEXT"]
+ pub static mut epoxy_glDisableVertexArrayEXT:
+ ::std::option::Option<unsafe extern "C" fn(vaobj: GLuint, array: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableVertexAttribAPPLE"]
+ pub static mut epoxy_glDisableVertexAttribAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, pname: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableVertexAttribArray"]
+ pub static mut epoxy_glDisableVertexAttribArray:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableVertexAttribArrayARB"]
+ pub static mut epoxy_glDisableVertexAttribArrayARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisablei"]
+ pub static mut epoxy_glDisablei:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableiEXT"]
+ pub static mut epoxy_glDisableiEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableiNV"]
+ pub static mut epoxy_glDisableiNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDisableiOES"]
+ pub static mut epoxy_glDisableiOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDiscardFramebufferEXT"]
+ pub static mut epoxy_glDiscardFramebufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, numAttachments: GLsizei, attachments: *const GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDispatchCompute"]
+ pub static mut epoxy_glDispatchCompute: ::std::option::Option<
+ unsafe extern "C" fn(num_groups_x: GLuint, num_groups_y: GLuint, num_groups_z: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDispatchComputeGroupSizeARB"]
+ pub static mut epoxy_glDispatchComputeGroupSizeARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ num_groups_x: GLuint,
+ num_groups_y: GLuint,
+ num_groups_z: GLuint,
+ group_size_x: GLuint,
+ group_size_y: GLuint,
+ group_size_z: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDispatchComputeIndirect"]
+ pub static mut epoxy_glDispatchComputeIndirect:
+ ::std::option::Option<unsafe extern "C" fn(indirect: GLintptr)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArrays"]
+ pub static mut epoxy_glDrawArrays:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, first: GLint, count: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysEXT"]
+ pub static mut epoxy_glDrawArraysEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, first: GLint, count: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysIndirect"]
+ pub static mut epoxy_glDrawArraysIndirect: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, indirect: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysInstanced"]
+ pub static mut epoxy_glDrawArraysInstanced: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, first: GLint, count: GLsizei, instancecount: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysInstancedANGLE"]
+ pub static mut epoxy_glDrawArraysInstancedANGLE: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, first: GLint, count: GLsizei, primcount: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysInstancedARB"]
+ pub static mut epoxy_glDrawArraysInstancedARB: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, first: GLint, count: GLsizei, primcount: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysInstancedBaseInstance"]
+ pub static mut epoxy_glDrawArraysInstancedBaseInstance: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ first: GLint,
+ count: GLsizei,
+ instancecount: GLsizei,
+ baseinstance: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysInstancedBaseInstanceEXT"]
+ pub static mut epoxy_glDrawArraysInstancedBaseInstanceEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ first: GLint,
+ count: GLsizei,
+ instancecount: GLsizei,
+ baseinstance: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysInstancedEXT"]
+ pub static mut epoxy_glDrawArraysInstancedEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, start: GLint, count: GLsizei, primcount: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawArraysInstancedNV"]
+ pub static mut epoxy_glDrawArraysInstancedNV: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, first: GLint, count: GLsizei, primcount: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawBuffer"]
+ pub static mut epoxy_glDrawBuffer: ::std::option::Option<unsafe extern "C" fn(buf: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawBuffers"]
+ pub static mut epoxy_glDrawBuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, bufs: *const GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawBuffersARB"]
+ pub static mut epoxy_glDrawBuffersARB:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, bufs: *const GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawBuffersATI"]
+ pub static mut epoxy_glDrawBuffersATI:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, bufs: *const GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawBuffersEXT"]
+ pub static mut epoxy_glDrawBuffersEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, bufs: *const GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawBuffersIndexedEXT"]
+ pub static mut epoxy_glDrawBuffersIndexedEXT: ::std::option::Option<
+ unsafe extern "C" fn(n: GLint, location: *const GLenum, indices: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawBuffersNV"]
+ pub static mut epoxy_glDrawBuffersNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, bufs: *const GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawCommandsAddressNV"]
+ pub static mut epoxy_glDrawCommandsAddressNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ primitiveMode: GLenum,
+ indirects: *const GLuint64,
+ sizes: *const GLsizei,
+ count: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawCommandsNV"]
+ pub static mut epoxy_glDrawCommandsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ primitiveMode: GLenum,
+ buffer: GLuint,
+ indirects: *const GLintptr,
+ sizes: *const GLsizei,
+ count: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawCommandsStatesAddressNV"]
+ pub static mut epoxy_glDrawCommandsStatesAddressNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ indirects: *const GLuint64,
+ sizes: *const GLsizei,
+ states: *const GLuint,
+ fbos: *const GLuint,
+ count: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawCommandsStatesNV"]
+ pub static mut epoxy_glDrawCommandsStatesNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ indirects: *const GLintptr,
+ sizes: *const GLsizei,
+ states: *const GLuint,
+ fbos: *const GLuint,
+ count: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementArrayAPPLE"]
+ pub static mut epoxy_glDrawElementArrayAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, first: GLint, count: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementArrayATI"]
+ pub static mut epoxy_glDrawElementArrayATI:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, count: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElements"]
+ pub static mut epoxy_glDrawElements: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsBaseVertex"]
+ pub static mut epoxy_glDrawElementsBaseVertex: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsBaseVertexEXT"]
+ pub static mut epoxy_glDrawElementsBaseVertexEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsBaseVertexOES"]
+ pub static mut epoxy_glDrawElementsBaseVertexOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsIndirect"]
+ pub static mut epoxy_glDrawElementsIndirect: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, type_: GLenum, indirect: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstanced"]
+ pub static mut epoxy_glDrawElementsInstanced: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ instancecount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedANGLE"]
+ pub static mut epoxy_glDrawElementsInstancedANGLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedARB"]
+ pub static mut epoxy_glDrawElementsInstancedARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedBaseInstance"]
+ pub static mut epoxy_glDrawElementsInstancedBaseInstance: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ instancecount: GLsizei,
+ baseinstance: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedBaseInstanceEXT"]
+ pub static mut epoxy_glDrawElementsInstancedBaseInstanceEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ instancecount: GLsizei,
+ baseinstance: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedBaseVertex"]
+ pub static mut epoxy_glDrawElementsInstancedBaseVertex: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ instancecount: GLsizei,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedBaseVertexBaseInstance"]
+ pub static mut epoxy_glDrawElementsInstancedBaseVertexBaseInstance: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ instancecount: GLsizei,
+ basevertex: GLint,
+ baseinstance: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedBaseVertexBaseInstanceEXT"]
+ pub static mut epoxy_glDrawElementsInstancedBaseVertexBaseInstanceEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ instancecount: GLsizei,
+ basevertex: GLint,
+ baseinstance: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedBaseVertexEXT"]
+ pub static mut epoxy_glDrawElementsInstancedBaseVertexEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ instancecount: GLsizei,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedBaseVertexOES"]
+ pub static mut epoxy_glDrawElementsInstancedBaseVertexOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ instancecount: GLsizei,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedEXT"]
+ pub static mut epoxy_glDrawElementsInstancedEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawElementsInstancedNV"]
+ pub static mut epoxy_glDrawElementsInstancedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawMeshArraysSUN"]
+ pub static mut epoxy_glDrawMeshArraysSUN: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, first: GLint, count: GLsizei, width: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawPixels"]
+ pub static mut epoxy_glDrawPixels: ::std::option::Option<
+ unsafe extern "C" fn(
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawRangeElementArrayAPPLE"]
+ pub static mut epoxy_glDrawRangeElementArrayAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ start: GLuint,
+ end: GLuint,
+ first: GLint,
+ count: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawRangeElementArrayATI"]
+ pub static mut epoxy_glDrawRangeElementArrayATI: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, start: GLuint, end: GLuint, count: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawRangeElements"]
+ pub static mut epoxy_glDrawRangeElements: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ start: GLuint,
+ end: GLuint,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawRangeElementsBaseVertex"]
+ pub static mut epoxy_glDrawRangeElementsBaseVertex: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ start: GLuint,
+ end: GLuint,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawRangeElementsBaseVertexEXT"]
+ pub static mut epoxy_glDrawRangeElementsBaseVertexEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ start: GLuint,
+ end: GLuint,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawRangeElementsBaseVertexOES"]
+ pub static mut epoxy_glDrawRangeElementsBaseVertexOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ start: GLuint,
+ end: GLuint,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ basevertex: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawRangeElementsEXT"]
+ pub static mut epoxy_glDrawRangeElementsEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ start: GLuint,
+ end: GLuint,
+ count: GLsizei,
+ type_: GLenum,
+ indices: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTexfOES"]
+ pub static mut epoxy_glDrawTexfOES: ::std::option::Option<
+ unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat, width: GLfloat, height: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTexfvOES"]
+ pub static mut epoxy_glDrawTexfvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTexiOES"]
+ pub static mut epoxy_glDrawTexiOES: ::std::option::Option<
+ unsafe extern "C" fn(x: GLint, y: GLint, z: GLint, width: GLint, height: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTexivOES"]
+ pub static mut epoxy_glDrawTexivOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTexsOES"]
+ pub static mut epoxy_glDrawTexsOES: ::std::option::Option<
+ unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort, width: GLshort, height: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTexsvOES"]
+ pub static mut epoxy_glDrawTexsvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTextureNV"]
+ pub static mut epoxy_glDrawTextureNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ sampler: GLuint,
+ x0: GLfloat,
+ y0: GLfloat,
+ x1: GLfloat,
+ y1: GLfloat,
+ z: GLfloat,
+ s0: GLfloat,
+ t0: GLfloat,
+ s1: GLfloat,
+ t1: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTexxOES"]
+ pub static mut epoxy_glDrawTexxOES: ::std::option::Option<
+ unsafe extern "C" fn(x: GLfixed, y: GLfixed, z: GLfixed, width: GLfixed, height: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTexxvOES"]
+ pub static mut epoxy_glDrawTexxvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTransformFeedback"]
+ pub static mut epoxy_glDrawTransformFeedback:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTransformFeedbackEXT"]
+ pub static mut epoxy_glDrawTransformFeedbackEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTransformFeedbackInstanced"]
+ pub static mut epoxy_glDrawTransformFeedbackInstanced: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, id: GLuint, instancecount: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTransformFeedbackInstancedEXT"]
+ pub static mut epoxy_glDrawTransformFeedbackInstancedEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, id: GLuint, instancecount: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTransformFeedbackNV"]
+ pub static mut epoxy_glDrawTransformFeedbackNV:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTransformFeedbackStream"]
+ pub static mut epoxy_glDrawTransformFeedbackStream:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, id: GLuint, stream: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glDrawTransformFeedbackStreamInstanced"]
+ pub static mut epoxy_glDrawTransformFeedbackStreamInstanced: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, id: GLuint, stream: GLuint, instancecount: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEGLImageTargetRenderbufferStorageOES"]
+ pub static mut epoxy_glEGLImageTargetRenderbufferStorageOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, image: GLeglImageOES)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEGLImageTargetTexture2DOES"]
+ pub static mut epoxy_glEGLImageTargetTexture2DOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, image: GLeglImageOES)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEdgeFlag"]
+ pub static mut epoxy_glEdgeFlag: ::std::option::Option<unsafe extern "C" fn(flag: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEdgeFlagFormatNV"]
+ pub static mut epoxy_glEdgeFlagFormatNV:
+ ::std::option::Option<unsafe extern "C" fn(stride: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEdgeFlagPointer"]
+ pub static mut epoxy_glEdgeFlagPointer: ::std::option::Option<
+ unsafe extern "C" fn(stride: GLsizei, pointer: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEdgeFlagPointerEXT"]
+ pub static mut epoxy_glEdgeFlagPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(stride: GLsizei, count: GLsizei, pointer: *const GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEdgeFlagPointerListIBM"]
+ pub static mut epoxy_glEdgeFlagPointerListIBM: ::std::option::Option<
+ unsafe extern "C" fn(stride: GLint, pointer: *mut *const GLboolean, ptrstride: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEdgeFlagv"]
+ pub static mut epoxy_glEdgeFlagv:
+ ::std::option::Option<unsafe extern "C" fn(flag: *const GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glElementPointerAPPLE"]
+ pub static mut epoxy_glElementPointerAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(type_: GLenum, pointer: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glElementPointerATI"]
+ pub static mut epoxy_glElementPointerATI: ::std::option::Option<
+ unsafe extern "C" fn(type_: GLenum, pointer: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnable"]
+ pub static mut epoxy_glEnable: ::std::option::Option<unsafe extern "C" fn(cap: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableClientState"]
+ pub static mut epoxy_glEnableClientState:
+ ::std::option::Option<unsafe extern "C" fn(array: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableClientStateIndexedEXT"]
+ pub static mut epoxy_glEnableClientStateIndexedEXT:
+ ::std::option::Option<unsafe extern "C" fn(array: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableClientStateiEXT"]
+ pub static mut epoxy_glEnableClientStateiEXT:
+ ::std::option::Option<unsafe extern "C" fn(array: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableDriverControlQCOM"]
+ pub static mut epoxy_glEnableDriverControlQCOM:
+ ::std::option::Option<unsafe extern "C" fn(driverControl: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableIndexedEXT"]
+ pub static mut epoxy_glEnableIndexedEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableVariantClientStateEXT"]
+ pub static mut epoxy_glEnableVariantClientStateEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableVertexArrayAttrib"]
+ pub static mut epoxy_glEnableVertexArrayAttrib:
+ ::std::option::Option<unsafe extern "C" fn(vaobj: GLuint, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableVertexArrayAttribEXT"]
+ pub static mut epoxy_glEnableVertexArrayAttribEXT:
+ ::std::option::Option<unsafe extern "C" fn(vaobj: GLuint, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableVertexArrayEXT"]
+ pub static mut epoxy_glEnableVertexArrayEXT:
+ ::std::option::Option<unsafe extern "C" fn(vaobj: GLuint, array: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableVertexAttribAPPLE"]
+ pub static mut epoxy_glEnableVertexAttribAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, pname: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableVertexAttribArray"]
+ pub static mut epoxy_glEnableVertexAttribArray:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableVertexAttribArrayARB"]
+ pub static mut epoxy_glEnableVertexAttribArrayARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnablei"]
+ pub static mut epoxy_glEnablei:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableiEXT"]
+ pub static mut epoxy_glEnableiEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableiNV"]
+ pub static mut epoxy_glEnableiNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnableiOES"]
+ pub static mut epoxy_glEnableiOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEnd"]
+ pub static mut epoxy_glEnd: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndConditionalRender"]
+ pub static mut epoxy_glEndConditionalRender: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndConditionalRenderNV"]
+ pub static mut epoxy_glEndConditionalRenderNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndConditionalRenderNVX"]
+ pub static mut epoxy_glEndConditionalRenderNVX: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndFragmentShaderATI"]
+ pub static mut epoxy_glEndFragmentShaderATI: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndList"]
+ pub static mut epoxy_glEndList: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndOcclusionQueryNV"]
+ pub static mut epoxy_glEndOcclusionQueryNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndPerfMonitorAMD"]
+ pub static mut epoxy_glEndPerfMonitorAMD:
+ ::std::option::Option<unsafe extern "C" fn(monitor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndPerfQueryINTEL"]
+ pub static mut epoxy_glEndPerfQueryINTEL:
+ ::std::option::Option<unsafe extern "C" fn(queryHandle: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndQuery"]
+ pub static mut epoxy_glEndQuery: ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndQueryARB"]
+ pub static mut epoxy_glEndQueryARB: ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndQueryEXT"]
+ pub static mut epoxy_glEndQueryEXT: ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndQueryIndexed"]
+ pub static mut epoxy_glEndQueryIndexed:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndTilingQCOM"]
+ pub static mut epoxy_glEndTilingQCOM:
+ ::std::option::Option<unsafe extern "C" fn(preserveMask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndTransformFeedback"]
+ pub static mut epoxy_glEndTransformFeedback: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndTransformFeedbackEXT"]
+ pub static mut epoxy_glEndTransformFeedbackEXT: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndTransformFeedbackNV"]
+ pub static mut epoxy_glEndTransformFeedbackNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndVertexShaderEXT"]
+ pub static mut epoxy_glEndVertexShaderEXT: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEndVideoCaptureNV"]
+ pub static mut epoxy_glEndVideoCaptureNV:
+ ::std::option::Option<unsafe extern "C" fn(video_capture_slot: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord1d"]
+ pub static mut epoxy_glEvalCoord1d: ::std::option::Option<unsafe extern "C" fn(u: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord1dv"]
+ pub static mut epoxy_glEvalCoord1dv:
+ ::std::option::Option<unsafe extern "C" fn(u: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord1f"]
+ pub static mut epoxy_glEvalCoord1f: ::std::option::Option<unsafe extern "C" fn(u: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord1fv"]
+ pub static mut epoxy_glEvalCoord1fv:
+ ::std::option::Option<unsafe extern "C" fn(u: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord1xOES"]
+ pub static mut epoxy_glEvalCoord1xOES: ::std::option::Option<unsafe extern "C" fn(u: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord1xvOES"]
+ pub static mut epoxy_glEvalCoord1xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord2d"]
+ pub static mut epoxy_glEvalCoord2d:
+ ::std::option::Option<unsafe extern "C" fn(u: GLdouble, v: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord2dv"]
+ pub static mut epoxy_glEvalCoord2dv:
+ ::std::option::Option<unsafe extern "C" fn(u: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord2f"]
+ pub static mut epoxy_glEvalCoord2f:
+ ::std::option::Option<unsafe extern "C" fn(u: GLfloat, v: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord2fv"]
+ pub static mut epoxy_glEvalCoord2fv:
+ ::std::option::Option<unsafe extern "C" fn(u: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord2xOES"]
+ pub static mut epoxy_glEvalCoord2xOES:
+ ::std::option::Option<unsafe extern "C" fn(u: GLfixed, v: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalCoord2xvOES"]
+ pub static mut epoxy_glEvalCoord2xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalMapsNV"]
+ pub static mut epoxy_glEvalMapsNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalMesh1"]
+ pub static mut epoxy_glEvalMesh1:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, i1: GLint, i2: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalMesh2"]
+ pub static mut epoxy_glEvalMesh2: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, i1: GLint, i2: GLint, j1: GLint, j2: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalPoint1"]
+ pub static mut epoxy_glEvalPoint1: ::std::option::Option<unsafe extern "C" fn(i: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvalPoint2"]
+ pub static mut epoxy_glEvalPoint2:
+ ::std::option::Option<unsafe extern "C" fn(i: GLint, j: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glEvaluateDepthValuesARB"]
+ pub static mut epoxy_glEvaluateDepthValuesARB: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExecuteProgramNV"]
+ pub static mut epoxy_glExecuteProgramNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, id: GLuint, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetBufferPointervQCOM"]
+ pub static mut epoxy_glExtGetBufferPointervQCOM: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, params: *mut *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetBuffersQCOM"]
+ pub static mut epoxy_glExtGetBuffersQCOM: ::std::option::Option<
+ unsafe extern "C" fn(buffers: *mut GLuint, maxBuffers: GLint, numBuffers: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetFramebuffersQCOM"]
+ pub static mut epoxy_glExtGetFramebuffersQCOM: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffers: *mut GLuint,
+ maxFramebuffers: GLint,
+ numFramebuffers: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetProgramBinarySourceQCOM"]
+ pub static mut epoxy_glExtGetProgramBinarySourceQCOM: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ shadertype: GLenum,
+ source: *mut GLchar,
+ length: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetProgramsQCOM"]
+ pub static mut epoxy_glExtGetProgramsQCOM: ::std::option::Option<
+ unsafe extern "C" fn(programs: *mut GLuint, maxPrograms: GLint, numPrograms: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetRenderbuffersQCOM"]
+ pub static mut epoxy_glExtGetRenderbuffersQCOM: ::std::option::Option<
+ unsafe extern "C" fn(
+ renderbuffers: *mut GLuint,
+ maxRenderbuffers: GLint,
+ numRenderbuffers: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetShadersQCOM"]
+ pub static mut epoxy_glExtGetShadersQCOM: ::std::option::Option<
+ unsafe extern "C" fn(shaders: *mut GLuint, maxShaders: GLint, numShaders: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetTexLevelParameterivQCOM"]
+ pub static mut epoxy_glExtGetTexLevelParameterivQCOM: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ face: GLenum,
+ level: GLint,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetTexSubImageQCOM"]
+ pub static mut epoxy_glExtGetTexSubImageQCOM: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ texels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtGetTexturesQCOM"]
+ pub static mut epoxy_glExtGetTexturesQCOM: ::std::option::Option<
+ unsafe extern "C" fn(textures: *mut GLuint, maxTextures: GLint, numTextures: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtIsProgramBinaryQCOM"]
+ pub static mut epoxy_glExtIsProgramBinaryQCOM:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtTexObjectStateOverrideiQCOM"]
+ pub static mut epoxy_glExtTexObjectStateOverrideiQCOM:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glExtractComponentEXT"]
+ pub static mut epoxy_glExtractComponentEXT:
+ ::std::option::Option<unsafe extern "C" fn(res: GLuint, src: GLuint, num: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFeedbackBuffer"]
+ pub static mut epoxy_glFeedbackBuffer: ::std::option::Option<
+ unsafe extern "C" fn(size: GLsizei, type_: GLenum, buffer: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFeedbackBufferxOES"]
+ pub static mut epoxy_glFeedbackBufferxOES: ::std::option::Option<
+ unsafe extern "C" fn(n: GLsizei, type_: GLenum, buffer: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFenceSync"]
+ pub static mut epoxy_glFenceSync:
+ ::std::option::Option<unsafe extern "C" fn(condition: GLenum, flags: GLbitfield) -> GLsync>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFenceSyncAPPLE"]
+ pub static mut epoxy_glFenceSyncAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(condition: GLenum, flags: GLbitfield) -> GLsync>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFinalCombinerInputNV"]
+ pub static mut epoxy_glFinalCombinerInputNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ variable: GLenum,
+ input: GLenum,
+ mapping: GLenum,
+ componentUsage: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFinish"]
+ pub static mut epoxy_glFinish: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFinishAsyncSGIX"]
+ pub static mut epoxy_glFinishAsyncSGIX:
+ ::std::option::Option<unsafe extern "C" fn(markerp: *mut GLuint) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFinishFenceAPPLE"]
+ pub static mut epoxy_glFinishFenceAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(fence: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFinishFenceNV"]
+ pub static mut epoxy_glFinishFenceNV:
+ ::std::option::Option<unsafe extern "C" fn(fence: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFinishObjectAPPLE"]
+ pub static mut epoxy_glFinishObjectAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(object: GLenum, name: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFinishTextureSUNX"]
+ pub static mut epoxy_glFinishTextureSUNX: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlush"]
+ pub static mut epoxy_glFlush: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushMappedBufferRange"]
+ pub static mut epoxy_glFlushMappedBufferRange: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, offset: GLintptr, length: GLsizeiptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushMappedBufferRangeAPPLE"]
+ pub static mut epoxy_glFlushMappedBufferRangeAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, offset: GLintptr, size: GLsizeiptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushMappedBufferRangeEXT"]
+ pub static mut epoxy_glFlushMappedBufferRangeEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, offset: GLintptr, length: GLsizeiptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushMappedNamedBufferRange"]
+ pub static mut epoxy_glFlushMappedNamedBufferRange: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, offset: GLintptr, length: GLsizeiptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushMappedNamedBufferRangeEXT"]
+ pub static mut epoxy_glFlushMappedNamedBufferRangeEXT: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, offset: GLintptr, length: GLsizeiptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushPixelDataRangeNV"]
+ pub static mut epoxy_glFlushPixelDataRangeNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushRasterSGIX"]
+ pub static mut epoxy_glFlushRasterSGIX: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushStaticDataIBM"]
+ pub static mut epoxy_glFlushStaticDataIBM:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushVertexArrayRangeAPPLE"]
+ pub static mut epoxy_glFlushVertexArrayRangeAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(length: GLsizei, pointer: *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFlushVertexArrayRangeNV"]
+ pub static mut epoxy_glFlushVertexArrayRangeNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordFormatNV"]
+ pub static mut epoxy_glFogCoordFormatNV:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, stride: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordPointer"]
+ pub static mut epoxy_glFogCoordPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordPointerEXT"]
+ pub static mut epoxy_glFogCoordPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordPointerListIBM"]
+ pub static mut epoxy_glFogCoordPointerListIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLint,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ptrstride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordd"]
+ pub static mut epoxy_glFogCoordd: ::std::option::Option<unsafe extern "C" fn(coord: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoorddEXT"]
+ pub static mut epoxy_glFogCoorddEXT:
+ ::std::option::Option<unsafe extern "C" fn(coord: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoorddv"]
+ pub static mut epoxy_glFogCoorddv:
+ ::std::option::Option<unsafe extern "C" fn(coord: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoorddvEXT"]
+ pub static mut epoxy_glFogCoorddvEXT:
+ ::std::option::Option<unsafe extern "C" fn(coord: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordf"]
+ pub static mut epoxy_glFogCoordf: ::std::option::Option<unsafe extern "C" fn(coord: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordfEXT"]
+ pub static mut epoxy_glFogCoordfEXT:
+ ::std::option::Option<unsafe extern "C" fn(coord: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordfv"]
+ pub static mut epoxy_glFogCoordfv:
+ ::std::option::Option<unsafe extern "C" fn(coord: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordfvEXT"]
+ pub static mut epoxy_glFogCoordfvEXT:
+ ::std::option::Option<unsafe extern "C" fn(coord: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordhNV"]
+ pub static mut epoxy_glFogCoordhNV: ::std::option::Option<unsafe extern "C" fn(fog: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogCoordhvNV"]
+ pub static mut epoxy_glFogCoordhvNV:
+ ::std::option::Option<unsafe extern "C" fn(fog: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogFuncSGIS"]
+ pub static mut epoxy_glFogFuncSGIS:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, points: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogf"]
+ pub static mut epoxy_glFogf:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogfv"]
+ pub static mut epoxy_glFogfv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogi"]
+ pub static mut epoxy_glFogi:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogiv"]
+ pub static mut epoxy_glFogiv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogx"]
+ pub static mut epoxy_glFogx:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogxOES"]
+ pub static mut epoxy_glFogxOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogxv"]
+ pub static mut epoxy_glFogxv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFogxvOES"]
+ pub static mut epoxy_glFogxvOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentColorMaterialSGIX"]
+ pub static mut epoxy_glFragmentColorMaterialSGIX:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentCoverageColorNV"]
+ pub static mut epoxy_glFragmentCoverageColorNV:
+ ::std::option::Option<unsafe extern "C" fn(color: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentLightModelfSGIX"]
+ pub static mut epoxy_glFragmentLightModelfSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentLightModelfvSGIX"]
+ pub static mut epoxy_glFragmentLightModelfvSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentLightModeliSGIX"]
+ pub static mut epoxy_glFragmentLightModeliSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentLightModelivSGIX"]
+ pub static mut epoxy_glFragmentLightModelivSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentLightfSGIX"]
+ pub static mut epoxy_glFragmentLightfSGIX:
+ ::std::option::Option<unsafe extern "C" fn(light: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentLightfvSGIX"]
+ pub static mut epoxy_glFragmentLightfvSGIX: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentLightiSGIX"]
+ pub static mut epoxy_glFragmentLightiSGIX:
+ ::std::option::Option<unsafe extern "C" fn(light: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentLightivSGIX"]
+ pub static mut epoxy_glFragmentLightivSGIX: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentMaterialfSGIX"]
+ pub static mut epoxy_glFragmentMaterialfSGIX:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentMaterialfvSGIX"]
+ pub static mut epoxy_glFragmentMaterialfvSGIX: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentMaterialiSGIX"]
+ pub static mut epoxy_glFragmentMaterialiSGIX:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFragmentMaterialivSGIX"]
+ pub static mut epoxy_glFragmentMaterialivSGIX: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFrameTerminatorGREMEDY"]
+ pub static mut epoxy_glFrameTerminatorGREMEDY: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFrameZoomSGIX"]
+ pub static mut epoxy_glFrameZoomSGIX:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferDrawBufferEXT"]
+ pub static mut epoxy_glFramebufferDrawBufferEXT:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferDrawBuffersEXT"]
+ pub static mut epoxy_glFramebufferDrawBuffersEXT: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, n: GLsizei, bufs: *const GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferParameteri"]
+ pub static mut epoxy_glFramebufferParameteri:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferPixelLocalStorageSizeEXT"]
+ pub static mut epoxy_glFramebufferPixelLocalStorageSizeEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLuint, size: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferReadBufferEXT"]
+ pub static mut epoxy_glFramebufferReadBufferEXT:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferRenderbuffer"]
+ pub static mut epoxy_glFramebufferRenderbuffer: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ renderbuffertarget: GLenum,
+ renderbuffer: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferRenderbufferEXT"]
+ pub static mut epoxy_glFramebufferRenderbufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ renderbuffertarget: GLenum,
+ renderbuffer: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferRenderbufferOES"]
+ pub static mut epoxy_glFramebufferRenderbufferOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ renderbuffertarget: GLenum,
+ renderbuffer: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferSampleLocationsfvARB"]
+ pub static mut epoxy_glFramebufferSampleLocationsfvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, start: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferSampleLocationsfvNV"]
+ pub static mut epoxy_glFramebufferSampleLocationsfvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, start: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferSamplePositionsfvAMD"]
+ pub static mut epoxy_glFramebufferSamplePositionsfvAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ numsamples: GLuint,
+ pixelindex: GLuint,
+ values: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture"]
+ pub static mut epoxy_glFramebufferTexture: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture1D"]
+ pub static mut epoxy_glFramebufferTexture1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture1DEXT"]
+ pub static mut epoxy_glFramebufferTexture1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture2D"]
+ pub static mut epoxy_glFramebufferTexture2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture2DDownsampleIMG"]
+ pub static mut epoxy_glFramebufferTexture2DDownsampleIMG: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ xscale: GLint,
+ yscale: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture2DEXT"]
+ pub static mut epoxy_glFramebufferTexture2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture2DMultisampleEXT"]
+ pub static mut epoxy_glFramebufferTexture2DMultisampleEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ samples: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture2DMultisampleIMG"]
+ pub static mut epoxy_glFramebufferTexture2DMultisampleIMG: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ samples: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture2DOES"]
+ pub static mut epoxy_glFramebufferTexture2DOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture3D"]
+ pub static mut epoxy_glFramebufferTexture3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ zoffset: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture3DEXT"]
+ pub static mut epoxy_glFramebufferTexture3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ zoffset: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTexture3DOES"]
+ pub static mut epoxy_glFramebufferTexture3DOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ zoffset: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureARB"]
+ pub static mut epoxy_glFramebufferTextureARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureEXT"]
+ pub static mut epoxy_glFramebufferTextureEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureFaceARB"]
+ pub static mut epoxy_glFramebufferTextureFaceARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ face: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureFaceEXT"]
+ pub static mut epoxy_glFramebufferTextureFaceEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ face: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureLayer"]
+ pub static mut epoxy_glFramebufferTextureLayer: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ layer: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureLayerARB"]
+ pub static mut epoxy_glFramebufferTextureLayerARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ layer: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureLayerDownsampleIMG"]
+ pub static mut epoxy_glFramebufferTextureLayerDownsampleIMG: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ layer: GLint,
+ xscale: GLint,
+ yscale: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureLayerEXT"]
+ pub static mut epoxy_glFramebufferTextureLayerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ layer: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureMultisampleMultiviewOVR"]
+ pub static mut epoxy_glFramebufferTextureMultisampleMultiviewOVR: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ samples: GLsizei,
+ baseViewIndex: GLint,
+ numViews: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureMultiviewOVR"]
+ pub static mut epoxy_glFramebufferTextureMultiviewOVR: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ baseViewIndex: GLint,
+ numViews: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFramebufferTextureOES"]
+ pub static mut epoxy_glFramebufferTextureOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFreeObjectBufferATI"]
+ pub static mut epoxy_glFreeObjectBufferATI:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFrontFace"]
+ pub static mut epoxy_glFrontFace: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFrustum"]
+ pub static mut epoxy_glFrustum: ::std::option::Option<
+ unsafe extern "C" fn(
+ left: GLdouble,
+ right: GLdouble,
+ bottom: GLdouble,
+ top: GLdouble,
+ zNear: GLdouble,
+ zFar: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFrustumf"]
+ pub static mut epoxy_glFrustumf: ::std::option::Option<
+ unsafe extern "C" fn(
+ l: GLfloat,
+ r: GLfloat,
+ b: GLfloat,
+ t: GLfloat,
+ n: GLfloat,
+ f: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFrustumfOES"]
+ pub static mut epoxy_glFrustumfOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ l: GLfloat,
+ r: GLfloat,
+ b: GLfloat,
+ t: GLfloat,
+ n: GLfloat,
+ f: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFrustumx"]
+ pub static mut epoxy_glFrustumx: ::std::option::Option<
+ unsafe extern "C" fn(
+ l: GLfixed,
+ r: GLfixed,
+ b: GLfixed,
+ t: GLfixed,
+ n: GLfixed,
+ f: GLfixed,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glFrustumxOES"]
+ pub static mut epoxy_glFrustumxOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ l: GLfixed,
+ r: GLfixed,
+ b: GLfixed,
+ t: GLfixed,
+ n: GLfixed,
+ f: GLfixed,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenAsyncMarkersSGIX"]
+ pub static mut epoxy_glGenAsyncMarkersSGIX:
+ ::std::option::Option<unsafe extern "C" fn(range: GLsizei) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenBuffers"]
+ pub static mut epoxy_glGenBuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, buffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenBuffersARB"]
+ pub static mut epoxy_glGenBuffersARB:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, buffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenFencesAPPLE"]
+ pub static mut epoxy_glGenFencesAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, fences: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenFencesNV"]
+ pub static mut epoxy_glGenFencesNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, fences: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenFragmentShadersATI"]
+ pub static mut epoxy_glGenFragmentShadersATI:
+ ::std::option::Option<unsafe extern "C" fn(range: GLuint) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenFramebuffers"]
+ pub static mut epoxy_glGenFramebuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, framebuffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenFramebuffersEXT"]
+ pub static mut epoxy_glGenFramebuffersEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, framebuffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenFramebuffersOES"]
+ pub static mut epoxy_glGenFramebuffersOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, framebuffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenLists"]
+ pub static mut epoxy_glGenLists:
+ ::std::option::Option<unsafe extern "C" fn(range: GLsizei) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenNamesAMD"]
+ pub static mut epoxy_glGenNamesAMD: ::std::option::Option<
+ unsafe extern "C" fn(identifier: GLenum, num: GLuint, names: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenOcclusionQueriesNV"]
+ pub static mut epoxy_glGenOcclusionQueriesNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenPathsNV"]
+ pub static mut epoxy_glGenPathsNV:
+ ::std::option::Option<unsafe extern "C" fn(range: GLsizei) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenPerfMonitorsAMD"]
+ pub static mut epoxy_glGenPerfMonitorsAMD:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, monitors: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenProgramPipelines"]
+ pub static mut epoxy_glGenProgramPipelines:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, pipelines: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenProgramPipelinesEXT"]
+ pub static mut epoxy_glGenProgramPipelinesEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, pipelines: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenProgramsARB"]
+ pub static mut epoxy_glGenProgramsARB:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, programs: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenProgramsNV"]
+ pub static mut epoxy_glGenProgramsNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, programs: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenQueries"]
+ pub static mut epoxy_glGenQueries:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenQueriesARB"]
+ pub static mut epoxy_glGenQueriesARB:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenQueriesEXT"]
+ pub static mut epoxy_glGenQueriesEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenRenderbuffers"]
+ pub static mut epoxy_glGenRenderbuffers:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, renderbuffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenRenderbuffersEXT"]
+ pub static mut epoxy_glGenRenderbuffersEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, renderbuffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenRenderbuffersOES"]
+ pub static mut epoxy_glGenRenderbuffersOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, renderbuffers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenSamplers"]
+ pub static mut epoxy_glGenSamplers:
+ ::std::option::Option<unsafe extern "C" fn(count: GLsizei, samplers: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenSymbolsEXT"]
+ pub static mut epoxy_glGenSymbolsEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ datatype: GLenum,
+ storagetype: GLenum,
+ range: GLenum,
+ components: GLuint,
+ ) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenTextures"]
+ pub static mut epoxy_glGenTextures:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, textures: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenTexturesEXT"]
+ pub static mut epoxy_glGenTexturesEXT:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, textures: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenTransformFeedbacks"]
+ pub static mut epoxy_glGenTransformFeedbacks:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenTransformFeedbacksNV"]
+ pub static mut epoxy_glGenTransformFeedbacksNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, ids: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenVertexArrays"]
+ pub static mut epoxy_glGenVertexArrays:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, arrays: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenVertexArraysAPPLE"]
+ pub static mut epoxy_glGenVertexArraysAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, arrays: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenVertexArraysOES"]
+ pub static mut epoxy_glGenVertexArraysOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, arrays: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenVertexShadersEXT"]
+ pub static mut epoxy_glGenVertexShadersEXT:
+ ::std::option::Option<unsafe extern "C" fn(range: GLuint) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenerateMipmap"]
+ pub static mut epoxy_glGenerateMipmap:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenerateMipmapEXT"]
+ pub static mut epoxy_glGenerateMipmapEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenerateMipmapOES"]
+ pub static mut epoxy_glGenerateMipmapOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenerateMultiTexMipmapEXT"]
+ pub static mut epoxy_glGenerateMultiTexMipmapEXT:
+ ::std::option::Option<unsafe extern "C" fn(texunit: GLenum, target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenerateTextureMipmap"]
+ pub static mut epoxy_glGenerateTextureMipmap:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGenerateTextureMipmapEXT"]
+ pub static mut epoxy_glGenerateTextureMipmapEXT:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint, target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveAtomicCounterBufferiv"]
+ pub static mut epoxy_glGetActiveAtomicCounterBufferiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ bufferIndex: GLuint,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveAttrib"]
+ pub static mut epoxy_glGetActiveAttrib: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ index: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ size: *mut GLint,
+ type_: *mut GLenum,
+ name: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveAttribARB"]
+ pub static mut epoxy_glGetActiveAttribARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ programObj: GLhandleARB,
+ index: GLuint,
+ maxLength: GLsizei,
+ length: *mut GLsizei,
+ size: *mut GLint,
+ type_: *mut GLenum,
+ name: *mut GLcharARB,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveSubroutineName"]
+ pub static mut epoxy_glGetActiveSubroutineName: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ shadertype: GLenum,
+ index: GLuint,
+ bufsize: GLsizei,
+ length: *mut GLsizei,
+ name: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveSubroutineUniformName"]
+ pub static mut epoxy_glGetActiveSubroutineUniformName: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ shadertype: GLenum,
+ index: GLuint,
+ bufsize: GLsizei,
+ length: *mut GLsizei,
+ name: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveSubroutineUniformiv"]
+ pub static mut epoxy_glGetActiveSubroutineUniformiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ shadertype: GLenum,
+ index: GLuint,
+ pname: GLenum,
+ values: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveUniform"]
+ pub static mut epoxy_glGetActiveUniform: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ index: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ size: *mut GLint,
+ type_: *mut GLenum,
+ name: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveUniformARB"]
+ pub static mut epoxy_glGetActiveUniformARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ programObj: GLhandleARB,
+ index: GLuint,
+ maxLength: GLsizei,
+ length: *mut GLsizei,
+ size: *mut GLint,
+ type_: *mut GLenum,
+ name: *mut GLcharARB,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveUniformBlockName"]
+ pub static mut epoxy_glGetActiveUniformBlockName: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ uniformBlockIndex: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ uniformBlockName: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveUniformBlockiv"]
+ pub static mut epoxy_glGetActiveUniformBlockiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ uniformBlockIndex: GLuint,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveUniformName"]
+ pub static mut epoxy_glGetActiveUniformName: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ uniformIndex: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ uniformName: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveUniformsiv"]
+ pub static mut epoxy_glGetActiveUniformsiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ uniformCount: GLsizei,
+ uniformIndices: *const GLuint,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetActiveVaryingNV"]
+ pub static mut epoxy_glGetActiveVaryingNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ index: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ size: *mut GLsizei,
+ type_: *mut GLenum,
+ name: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetArrayObjectfvATI"]
+ pub static mut epoxy_glGetArrayObjectfvATI: ::std::option::Option<
+ unsafe extern "C" fn(array: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetArrayObjectivATI"]
+ pub static mut epoxy_glGetArrayObjectivATI: ::std::option::Option<
+ unsafe extern "C" fn(array: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetAttachedObjectsARB"]
+ pub static mut epoxy_glGetAttachedObjectsARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ containerObj: GLhandleARB,
+ maxCount: GLsizei,
+ count: *mut GLsizei,
+ obj: *mut GLhandleARB,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetAttachedShaders"]
+ pub static mut epoxy_glGetAttachedShaders: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ maxCount: GLsizei,
+ count: *mut GLsizei,
+ shaders: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetAttribLocation"]
+ pub static mut epoxy_glGetAttribLocation:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, name: *const GLchar) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetAttribLocationARB"]
+ pub static mut epoxy_glGetAttribLocationARB: ::std::option::Option<
+ unsafe extern "C" fn(programObj: GLhandleARB, name: *const GLcharARB) -> GLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBooleanIndexedvEXT"]
+ pub static mut epoxy_glGetBooleanIndexedvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBooleani_v"]
+ pub static mut epoxy_glGetBooleani_v: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBooleanv"]
+ pub static mut epoxy_glGetBooleanv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, data: *mut GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferParameteri64v"]
+ pub static mut epoxy_glGetBufferParameteri64v: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferParameteriv"]
+ pub static mut epoxy_glGetBufferParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferParameterivARB"]
+ pub static mut epoxy_glGetBufferParameterivARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferParameterui64vNV"]
+ pub static mut epoxy_glGetBufferParameterui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferPointerv"]
+ pub static mut epoxy_glGetBufferPointerv: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ pname: GLenum,
+ params: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferPointervARB"]
+ pub static mut epoxy_glGetBufferPointervARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ pname: GLenum,
+ params: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferPointervOES"]
+ pub static mut epoxy_glGetBufferPointervOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ pname: GLenum,
+ params: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferSubData"]
+ pub static mut epoxy_glGetBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetBufferSubDataARB"]
+ pub static mut epoxy_glGetBufferSubDataARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ offset: GLintptrARB,
+ size: GLsizeiptrARB,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetClipPlane"]
+ pub static mut epoxy_glGetClipPlane:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *mut GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetClipPlanef"]
+ pub static mut epoxy_glGetClipPlanef:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetClipPlanefOES"]
+ pub static mut epoxy_glGetClipPlanefOES:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetClipPlanex"]
+ pub static mut epoxy_glGetClipPlanex:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *mut GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetClipPlanexOES"]
+ pub static mut epoxy_glGetClipPlanexOES:
+ ::std::option::Option<unsafe extern "C" fn(plane: GLenum, equation: *mut GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTable"]
+ pub static mut epoxy_glGetColorTable: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ table: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTableEXT"]
+ pub static mut epoxy_glGetColorTableEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTableParameterfv"]
+ pub static mut epoxy_glGetColorTableParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTableParameterfvEXT"]
+ pub static mut epoxy_glGetColorTableParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTableParameterfvSGI"]
+ pub static mut epoxy_glGetColorTableParameterfvSGI: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTableParameteriv"]
+ pub static mut epoxy_glGetColorTableParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTableParameterivEXT"]
+ pub static mut epoxy_glGetColorTableParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTableParameterivSGI"]
+ pub static mut epoxy_glGetColorTableParameterivSGI: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetColorTableSGI"]
+ pub static mut epoxy_glGetColorTableSGI: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ table: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCombinerInputParameterfvNV"]
+ pub static mut epoxy_glGetCombinerInputParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ stage: GLenum,
+ portion: GLenum,
+ variable: GLenum,
+ pname: GLenum,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCombinerInputParameterivNV"]
+ pub static mut epoxy_glGetCombinerInputParameterivNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ stage: GLenum,
+ portion: GLenum,
+ variable: GLenum,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCombinerOutputParameterfvNV"]
+ pub static mut epoxy_glGetCombinerOutputParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(stage: GLenum, portion: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCombinerOutputParameterivNV"]
+ pub static mut epoxy_glGetCombinerOutputParameterivNV: ::std::option::Option<
+ unsafe extern "C" fn(stage: GLenum, portion: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCombinerStageParameterfvNV"]
+ pub static mut epoxy_glGetCombinerStageParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(stage: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCommandHeaderNV"]
+ pub static mut epoxy_glGetCommandHeaderNV:
+ ::std::option::Option<unsafe extern "C" fn(tokenID: GLenum, size: GLuint) -> GLuint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCompressedMultiTexImageEXT"]
+ pub static mut epoxy_glGetCompressedMultiTexImageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ lod: GLint,
+ img: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCompressedTexImage"]
+ pub static mut epoxy_glGetCompressedTexImage: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, level: GLint, img: *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCompressedTexImageARB"]
+ pub static mut epoxy_glGetCompressedTexImageARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, level: GLint, img: *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCompressedTextureImage"]
+ pub static mut epoxy_glGetCompressedTextureImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ bufSize: GLsizei,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCompressedTextureImageEXT"]
+ pub static mut epoxy_glGetCompressedTextureImageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ lod: GLint,
+ img: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCompressedTextureSubImage"]
+ pub static mut epoxy_glGetCompressedTextureSubImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ bufSize: GLsizei,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetConvolutionFilter"]
+ pub static mut epoxy_glGetConvolutionFilter: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ image: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetConvolutionFilterEXT"]
+ pub static mut epoxy_glGetConvolutionFilterEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ image: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetConvolutionParameterfv"]
+ pub static mut epoxy_glGetConvolutionParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetConvolutionParameterfvEXT"]
+ pub static mut epoxy_glGetConvolutionParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetConvolutionParameteriv"]
+ pub static mut epoxy_glGetConvolutionParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetConvolutionParameterivEXT"]
+ pub static mut epoxy_glGetConvolutionParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetConvolutionParameterxvOES"]
+ pub static mut epoxy_glGetConvolutionParameterxvOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetCoverageModulationTableNV"]
+ pub static mut epoxy_glGetCoverageModulationTableNV:
+ ::std::option::Option<unsafe extern "C" fn(bufsize: GLsizei, v: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDebugMessageLog"]
+ pub static mut epoxy_glGetDebugMessageLog: ::std::option::Option<
+ unsafe extern "C" fn(
+ count: GLuint,
+ bufSize: GLsizei,
+ sources: *mut GLenum,
+ types: *mut GLenum,
+ ids: *mut GLuint,
+ severities: *mut GLenum,
+ lengths: *mut GLsizei,
+ messageLog: *mut GLchar,
+ ) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDebugMessageLogAMD"]
+ pub static mut epoxy_glGetDebugMessageLogAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ count: GLuint,
+ bufsize: GLsizei,
+ categories: *mut GLenum,
+ severities: *mut GLuint,
+ ids: *mut GLuint,
+ lengths: *mut GLsizei,
+ message: *mut GLchar,
+ ) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDebugMessageLogARB"]
+ pub static mut epoxy_glGetDebugMessageLogARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ count: GLuint,
+ bufSize: GLsizei,
+ sources: *mut GLenum,
+ types: *mut GLenum,
+ ids: *mut GLuint,
+ severities: *mut GLenum,
+ lengths: *mut GLsizei,
+ messageLog: *mut GLchar,
+ ) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDebugMessageLogKHR"]
+ pub static mut epoxy_glGetDebugMessageLogKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ count: GLuint,
+ bufSize: GLsizei,
+ sources: *mut GLenum,
+ types: *mut GLenum,
+ ids: *mut GLuint,
+ severities: *mut GLenum,
+ lengths: *mut GLsizei,
+ messageLog: *mut GLchar,
+ ) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDetailTexFuncSGIS"]
+ pub static mut epoxy_glGetDetailTexFuncSGIS:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, points: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDoubleIndexedvEXT"]
+ pub static mut epoxy_glGetDoubleIndexedvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDoublei_v"]
+ pub static mut epoxy_glGetDoublei_v: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDoublei_vEXT"]
+ pub static mut epoxy_glGetDoublei_vEXT: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, index: GLuint, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDoublev"]
+ pub static mut epoxy_glGetDoublev:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, data: *mut GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDriverControlStringQCOM"]
+ pub static mut epoxy_glGetDriverControlStringQCOM: ::std::option::Option<
+ unsafe extern "C" fn(
+ driverControl: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ driverControlString: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetDriverControlsQCOM"]
+ pub static mut epoxy_glGetDriverControlsQCOM: ::std::option::Option<
+ unsafe extern "C" fn(num: *mut GLint, size: GLsizei, driverControls: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetError"]
+ pub static mut epoxy_glGetError: ::std::option::Option<unsafe extern "C" fn() -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFenceivNV"]
+ pub static mut epoxy_glGetFenceivNV: ::std::option::Option<
+ unsafe extern "C" fn(fence: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFinalCombinerInputParameterfvNV"]
+ pub static mut epoxy_glGetFinalCombinerInputParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(variable: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFinalCombinerInputParameterivNV"]
+ pub static mut epoxy_glGetFinalCombinerInputParameterivNV: ::std::option::Option<
+ unsafe extern "C" fn(variable: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFirstPerfQueryIdINTEL"]
+ pub static mut epoxy_glGetFirstPerfQueryIdINTEL:
+ ::std::option::Option<unsafe extern "C" fn(queryId: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFixedv"]
+ pub static mut epoxy_glGetFixedv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *mut GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFixedvOES"]
+ pub static mut epoxy_glGetFixedvOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *mut GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFloatIndexedvEXT"]
+ pub static mut epoxy_glGetFloatIndexedvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFloati_v"]
+ pub static mut epoxy_glGetFloati_v: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFloati_vEXT"]
+ pub static mut epoxy_glGetFloati_vEXT: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, index: GLuint, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFloati_vNV"]
+ pub static mut epoxy_glGetFloati_vNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFloati_vOES"]
+ pub static mut epoxy_glGetFloati_vOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFloatv"]
+ pub static mut epoxy_glGetFloatv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, data: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFogFuncSGIS"]
+ pub static mut epoxy_glGetFogFuncSGIS:
+ ::std::option::Option<unsafe extern "C" fn(points: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFragDataIndex"]
+ pub static mut epoxy_glGetFragDataIndex:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, name: *const GLchar) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFragDataIndexEXT"]
+ pub static mut epoxy_glGetFragDataIndexEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, name: *const GLchar) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFragDataLocation"]
+ pub static mut epoxy_glGetFragDataLocation:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, name: *const GLchar) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFragDataLocationEXT"]
+ pub static mut epoxy_glGetFragDataLocationEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, name: *const GLchar) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFragmentLightfvSGIX"]
+ pub static mut epoxy_glGetFragmentLightfvSGIX: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFragmentLightivSGIX"]
+ pub static mut epoxy_glGetFragmentLightivSGIX: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFragmentMaterialfvSGIX"]
+ pub static mut epoxy_glGetFragmentMaterialfvSGIX: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFragmentMaterialivSGIX"]
+ pub static mut epoxy_glGetFragmentMaterialivSGIX: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFramebufferAttachmentParameteriv"]
+ pub static mut epoxy_glGetFramebufferAttachmentParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, attachment: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFramebufferAttachmentParameterivEXT"]
+ pub static mut epoxy_glGetFramebufferAttachmentParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, attachment: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFramebufferAttachmentParameterivOES"]
+ pub static mut epoxy_glGetFramebufferAttachmentParameterivOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, attachment: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFramebufferParameterfvAMD"]
+ pub static mut epoxy_glGetFramebufferParameterfvAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ pname: GLenum,
+ numsamples: GLuint,
+ pixelindex: GLuint,
+ size: GLsizei,
+ values: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFramebufferParameteriv"]
+ pub static mut epoxy_glGetFramebufferParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFramebufferParameterivEXT"]
+ pub static mut epoxy_glGetFramebufferParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetFramebufferPixelLocalStorageSizeEXT"]
+ pub static mut epoxy_glGetFramebufferPixelLocalStorageSizeEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLuint) -> GLsizei>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetGraphicsResetStatus"]
+ pub static mut epoxy_glGetGraphicsResetStatus:
+ ::std::option::Option<unsafe extern "C" fn() -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetGraphicsResetStatusARB"]
+ pub static mut epoxy_glGetGraphicsResetStatusARB:
+ ::std::option::Option<unsafe extern "C" fn() -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetGraphicsResetStatusEXT"]
+ pub static mut epoxy_glGetGraphicsResetStatusEXT:
+ ::std::option::Option<unsafe extern "C" fn() -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetGraphicsResetStatusKHR"]
+ pub static mut epoxy_glGetGraphicsResetStatusKHR:
+ ::std::option::Option<unsafe extern "C" fn() -> GLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetHandleARB"]
+ pub static mut epoxy_glGetHandleARB:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum) -> GLhandleARB>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetHistogram"]
+ pub static mut epoxy_glGetHistogram: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ reset: GLboolean,
+ format: GLenum,
+ type_: GLenum,
+ values: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetHistogramEXT"]
+ pub static mut epoxy_glGetHistogramEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ reset: GLboolean,
+ format: GLenum,
+ type_: GLenum,
+ values: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetHistogramParameterfv"]
+ pub static mut epoxy_glGetHistogramParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetHistogramParameterfvEXT"]
+ pub static mut epoxy_glGetHistogramParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetHistogramParameteriv"]
+ pub static mut epoxy_glGetHistogramParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetHistogramParameterivEXT"]
+ pub static mut epoxy_glGetHistogramParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetHistogramParameterxvOES"]
+ pub static mut epoxy_glGetHistogramParameterxvOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetImageHandleARB"]
+ pub static mut epoxy_glGetImageHandleARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ layered: GLboolean,
+ layer: GLint,
+ format: GLenum,
+ ) -> GLuint64,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetImageHandleNV"]
+ pub static mut epoxy_glGetImageHandleNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ layered: GLboolean,
+ layer: GLint,
+ format: GLenum,
+ ) -> GLuint64,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetImageTransformParameterfvHP"]
+ pub static mut epoxy_glGetImageTransformParameterfvHP: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetImageTransformParameterivHP"]
+ pub static mut epoxy_glGetImageTransformParameterivHP: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInfoLogARB"]
+ pub static mut epoxy_glGetInfoLogARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ obj: GLhandleARB,
+ maxLength: GLsizei,
+ length: *mut GLsizei,
+ infoLog: *mut GLcharARB,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInstrumentsSGIX"]
+ pub static mut epoxy_glGetInstrumentsSGIX:
+ ::std::option::Option<unsafe extern "C" fn() -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInteger64i_v"]
+ pub static mut epoxy_glGetInteger64i_v: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInteger64v"]
+ pub static mut epoxy_glGetInteger64v:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, data: *mut GLint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInteger64vAPPLE"]
+ pub static mut epoxy_glGetInteger64vAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *mut GLint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetIntegerIndexedvEXT"]
+ pub static mut epoxy_glGetIntegerIndexedvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetIntegeri_v"]
+ pub static mut epoxy_glGetIntegeri_v: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetIntegeri_vEXT"]
+ pub static mut epoxy_glGetIntegeri_vEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetIntegerui64i_vNV"]
+ pub static mut epoxy_glGetIntegerui64i_vNV: ::std::option::Option<
+ unsafe extern "C" fn(value: GLenum, index: GLuint, result: *mut GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetIntegerui64vNV"]
+ pub static mut epoxy_glGetIntegerui64vNV:
+ ::std::option::Option<unsafe extern "C" fn(value: GLenum, result: *mut GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetIntegerv"]
+ pub static mut epoxy_glGetIntegerv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, data: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInternalformatSampleivNV"]
+ pub static mut epoxy_glGetInternalformatSampleivNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ samples: GLsizei,
+ pname: GLenum,
+ bufSize: GLsizei,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInternalformati64v"]
+ pub static mut epoxy_glGetInternalformati64v: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ pname: GLenum,
+ bufSize: GLsizei,
+ params: *mut GLint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInternalformativ"]
+ pub static mut epoxy_glGetInternalformativ: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ pname: GLenum,
+ bufSize: GLsizei,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInvariantBooleanvEXT"]
+ pub static mut epoxy_glGetInvariantBooleanvEXT: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInvariantFloatvEXT"]
+ pub static mut epoxy_glGetInvariantFloatvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetInvariantIntegervEXT"]
+ pub static mut epoxy_glGetInvariantIntegervEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetLightfv"]
+ pub static mut epoxy_glGetLightfv: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetLightiv"]
+ pub static mut epoxy_glGetLightiv: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetLightxOES"]
+ pub static mut epoxy_glGetLightxOES: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetLightxv"]
+ pub static mut epoxy_glGetLightxv: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetLightxvOES"]
+ pub static mut epoxy_glGetLightxvOES: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetListParameterfvSGIX"]
+ pub static mut epoxy_glGetListParameterfvSGIX: ::std::option::Option<
+ unsafe extern "C" fn(list: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetListParameterivSGIX"]
+ pub static mut epoxy_glGetListParameterivSGIX: ::std::option::Option<
+ unsafe extern "C" fn(list: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetLocalConstantBooleanvEXT"]
+ pub static mut epoxy_glGetLocalConstantBooleanvEXT: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetLocalConstantFloatvEXT"]
+ pub static mut epoxy_glGetLocalConstantFloatvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetLocalConstantIntegervEXT"]
+ pub static mut epoxy_glGetLocalConstantIntegervEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapAttribParameterfvNV"]
+ pub static mut epoxy_glGetMapAttribParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapAttribParameterivNV"]
+ pub static mut epoxy_glGetMapAttribParameterivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapControlPointsNV"]
+ pub static mut epoxy_glGetMapControlPointsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ type_: GLenum,
+ ustride: GLsizei,
+ vstride: GLsizei,
+ packed: GLboolean,
+ points: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapParameterfvNV"]
+ pub static mut epoxy_glGetMapParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapParameterivNV"]
+ pub static mut epoxy_glGetMapParameterivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapdv"]
+ pub static mut epoxy_glGetMapdv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, query: GLenum, v: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapfv"]
+ pub static mut epoxy_glGetMapfv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, query: GLenum, v: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapiv"]
+ pub static mut epoxy_glGetMapiv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, query: GLenum, v: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMapxvOES"]
+ pub static mut epoxy_glGetMapxvOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, query: GLenum, v: *mut GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMaterialfv"]
+ pub static mut epoxy_glGetMaterialfv: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMaterialiv"]
+ pub static mut epoxy_glGetMaterialiv: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMaterialxOES"]
+ pub static mut epoxy_glGetMaterialxOES:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMaterialxv"]
+ pub static mut epoxy_glGetMaterialxv: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMaterialxvOES"]
+ pub static mut epoxy_glGetMaterialxvOES: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMinmax"]
+ pub static mut epoxy_glGetMinmax: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ reset: GLboolean,
+ format: GLenum,
+ type_: GLenum,
+ values: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMinmaxEXT"]
+ pub static mut epoxy_glGetMinmaxEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ reset: GLboolean,
+ format: GLenum,
+ type_: GLenum,
+ values: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMinmaxParameterfv"]
+ pub static mut epoxy_glGetMinmaxParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMinmaxParameterfvEXT"]
+ pub static mut epoxy_glGetMinmaxParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMinmaxParameteriv"]
+ pub static mut epoxy_glGetMinmaxParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMinmaxParameterivEXT"]
+ pub static mut epoxy_glGetMinmaxParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexEnvfvEXT"]
+ pub static mut epoxy_glGetMultiTexEnvfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexEnvivEXT"]
+ pub static mut epoxy_glGetMultiTexEnvivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexGendvEXT"]
+ pub static mut epoxy_glGetMultiTexGendvEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, coord: GLenum, pname: GLenum, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexGenfvEXT"]
+ pub static mut epoxy_glGetMultiTexGenfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, coord: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexGenivEXT"]
+ pub static mut epoxy_glGetMultiTexGenivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, coord: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexImageEXT"]
+ pub static mut epoxy_glGetMultiTexImageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexLevelParameterfvEXT"]
+ pub static mut epoxy_glGetMultiTexLevelParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ pname: GLenum,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexLevelParameterivEXT"]
+ pub static mut epoxy_glGetMultiTexLevelParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexParameterIivEXT"]
+ pub static mut epoxy_glGetMultiTexParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexParameterIuivEXT"]
+ pub static mut epoxy_glGetMultiTexParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexParameterfvEXT"]
+ pub static mut epoxy_glGetMultiTexParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultiTexParameterivEXT"]
+ pub static mut epoxy_glGetMultiTexParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultisamplefv"]
+ pub static mut epoxy_glGetMultisamplefv: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, index: GLuint, val: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetMultisamplefvNV"]
+ pub static mut epoxy_glGetMultisamplefvNV: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, index: GLuint, val: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedBufferParameteri64v"]
+ pub static mut epoxy_glGetNamedBufferParameteri64v: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, pname: GLenum, params: *mut GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedBufferParameteriv"]
+ pub static mut epoxy_glGetNamedBufferParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedBufferParameterivEXT"]
+ pub static mut epoxy_glGetNamedBufferParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedBufferParameterui64vNV"]
+ pub static mut epoxy_glGetNamedBufferParameterui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, pname: GLenum, params: *mut GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedBufferPointerv"]
+ pub static mut epoxy_glGetNamedBufferPointerv: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ pname: GLenum,
+ params: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedBufferPointervEXT"]
+ pub static mut epoxy_glGetNamedBufferPointervEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ pname: GLenum,
+ params: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedBufferSubData"]
+ pub static mut epoxy_glGetNamedBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedBufferSubDataEXT"]
+ pub static mut epoxy_glGetNamedBufferSubDataEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedFramebufferAttachmentParameteriv"]
+ pub static mut epoxy_glGetNamedFramebufferAttachmentParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedFramebufferAttachmentParameterivEXT"]
+ pub static mut epoxy_glGetNamedFramebufferAttachmentParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedFramebufferParameterfvAMD"]
+ pub static mut epoxy_glGetNamedFramebufferParameterfvAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLenum,
+ pname: GLenum,
+ numsamples: GLuint,
+ pixelindex: GLuint,
+ size: GLsizei,
+ values: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedFramebufferParameteriv"]
+ pub static mut epoxy_glGetNamedFramebufferParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, pname: GLenum, param: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedFramebufferParameterivEXT"]
+ pub static mut epoxy_glGetNamedFramebufferParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedProgramLocalParameterIivEXT"]
+ pub static mut epoxy_glGetNamedProgramLocalParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, target: GLenum, index: GLuint, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedProgramLocalParameterIuivEXT"]
+ pub static mut epoxy_glGetNamedProgramLocalParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, target: GLenum, index: GLuint, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedProgramLocalParameterdvEXT"]
+ pub static mut epoxy_glGetNamedProgramLocalParameterdvEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, target: GLenum, index: GLuint, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedProgramLocalParameterfvEXT"]
+ pub static mut epoxy_glGetNamedProgramLocalParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, target: GLenum, index: GLuint, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedProgramStringEXT"]
+ pub static mut epoxy_glGetNamedProgramStringEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ pname: GLenum,
+ string: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedProgramivEXT"]
+ pub static mut epoxy_glGetNamedProgramivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedRenderbufferParameteriv"]
+ pub static mut epoxy_glGetNamedRenderbufferParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(renderbuffer: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedRenderbufferParameterivEXT"]
+ pub static mut epoxy_glGetNamedRenderbufferParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(renderbuffer: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedStringARB"]
+ pub static mut epoxy_glGetNamedStringARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ namelen: GLint,
+ name: *const GLchar,
+ bufSize: GLsizei,
+ stringlen: *mut GLint,
+ string: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNamedStringivARB"]
+ pub static mut epoxy_glGetNamedStringivARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ namelen: GLint,
+ name: *const GLchar,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetNextPerfQueryIdINTEL"]
+ pub static mut epoxy_glGetNextPerfQueryIdINTEL:
+ ::std::option::Option<unsafe extern "C" fn(queryId: GLuint, nextQueryId: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectBufferfvATI"]
+ pub static mut epoxy_glGetObjectBufferfvATI: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectBufferivATI"]
+ pub static mut epoxy_glGetObjectBufferivATI: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectLabel"]
+ pub static mut epoxy_glGetObjectLabel: ::std::option::Option<
+ unsafe extern "C" fn(
+ identifier: GLenum,
+ name: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ label: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectLabelEXT"]
+ pub static mut epoxy_glGetObjectLabelEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ object: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ label: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectLabelKHR"]
+ pub static mut epoxy_glGetObjectLabelKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ identifier: GLenum,
+ name: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ label: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectParameterfvARB"]
+ pub static mut epoxy_glGetObjectParameterfvARB: ::std::option::Option<
+ unsafe extern "C" fn(obj: GLhandleARB, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectParameterivAPPLE"]
+ pub static mut epoxy_glGetObjectParameterivAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(objectType: GLenum, name: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectParameterivARB"]
+ pub static mut epoxy_glGetObjectParameterivARB: ::std::option::Option<
+ unsafe extern "C" fn(obj: GLhandleARB, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectPtrLabel"]
+ pub static mut epoxy_glGetObjectPtrLabel: ::std::option::Option<
+ unsafe extern "C" fn(
+ ptr: *const ::std::os::raw::c_void,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ label: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetObjectPtrLabelKHR"]
+ pub static mut epoxy_glGetObjectPtrLabelKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ ptr: *const ::std::os::raw::c_void,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ label: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetOcclusionQueryivNV"]
+ pub static mut epoxy_glGetOcclusionQueryivNV:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetOcclusionQueryuivNV"]
+ pub static mut epoxy_glGetOcclusionQueryuivNV:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathColorGenfvNV"]
+ pub static mut epoxy_glGetPathColorGenfvNV: ::std::option::Option<
+ unsafe extern "C" fn(color: GLenum, pname: GLenum, value: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathColorGenivNV"]
+ pub static mut epoxy_glGetPathColorGenivNV: ::std::option::Option<
+ unsafe extern "C" fn(color: GLenum, pname: GLenum, value: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathCommandsNV"]
+ pub static mut epoxy_glGetPathCommandsNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, commands: *mut GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathCoordsNV"]
+ pub static mut epoxy_glGetPathCoordsNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, coords: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathDashArrayNV"]
+ pub static mut epoxy_glGetPathDashArrayNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, dashArray: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathLengthNV"]
+ pub static mut epoxy_glGetPathLengthNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, startSegment: GLsizei, numSegments: GLsizei) -> GLfloat,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathMetricRangeNV"]
+ pub static mut epoxy_glGetPathMetricRangeNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ metricQueryMask: GLbitfield,
+ firstPathName: GLuint,
+ numPaths: GLsizei,
+ stride: GLsizei,
+ metrics: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathMetricsNV"]
+ pub static mut epoxy_glGetPathMetricsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ metricQueryMask: GLbitfield,
+ numPaths: GLsizei,
+ pathNameType: GLenum,
+ paths: *const ::std::os::raw::c_void,
+ pathBase: GLuint,
+ stride: GLsizei,
+ metrics: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathParameterfvNV"]
+ pub static mut epoxy_glGetPathParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, pname: GLenum, value: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathParameterivNV"]
+ pub static mut epoxy_glGetPathParameterivNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, pname: GLenum, value: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathSpacingNV"]
+ pub static mut epoxy_glGetPathSpacingNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ pathListMode: GLenum,
+ numPaths: GLsizei,
+ pathNameType: GLenum,
+ paths: *const ::std::os::raw::c_void,
+ pathBase: GLuint,
+ advanceScale: GLfloat,
+ kerningScale: GLfloat,
+ transformType: GLenum,
+ returnedSpacing: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathTexGenfvNV"]
+ pub static mut epoxy_glGetPathTexGenfvNV: ::std::option::Option<
+ unsafe extern "C" fn(texCoordSet: GLenum, pname: GLenum, value: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPathTexGenivNV"]
+ pub static mut epoxy_glGetPathTexGenivNV: ::std::option::Option<
+ unsafe extern "C" fn(texCoordSet: GLenum, pname: GLenum, value: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfCounterInfoINTEL"]
+ pub static mut epoxy_glGetPerfCounterInfoINTEL: ::std::option::Option<
+ unsafe extern "C" fn(
+ queryId: GLuint,
+ counterId: GLuint,
+ counterNameLength: GLuint,
+ counterName: *mut GLchar,
+ counterDescLength: GLuint,
+ counterDesc: *mut GLchar,
+ counterOffset: *mut GLuint,
+ counterDataSize: *mut GLuint,
+ counterTypeEnum: *mut GLuint,
+ counterDataTypeEnum: *mut GLuint,
+ rawCounterMaxValue: *mut GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfMonitorCounterDataAMD"]
+ pub static mut epoxy_glGetPerfMonitorCounterDataAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ monitor: GLuint,
+ pname: GLenum,
+ dataSize: GLsizei,
+ data: *mut GLuint,
+ bytesWritten: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfMonitorCounterInfoAMD"]
+ pub static mut epoxy_glGetPerfMonitorCounterInfoAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ group: GLuint,
+ counter: GLuint,
+ pname: GLenum,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfMonitorCounterStringAMD"]
+ pub static mut epoxy_glGetPerfMonitorCounterStringAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ group: GLuint,
+ counter: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ counterString: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfMonitorCountersAMD"]
+ pub static mut epoxy_glGetPerfMonitorCountersAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ group: GLuint,
+ numCounters: *mut GLint,
+ maxActiveCounters: *mut GLint,
+ counterSize: GLsizei,
+ counters: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfMonitorGroupStringAMD"]
+ pub static mut epoxy_glGetPerfMonitorGroupStringAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ group: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ groupString: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfMonitorGroupsAMD"]
+ pub static mut epoxy_glGetPerfMonitorGroupsAMD: ::std::option::Option<
+ unsafe extern "C" fn(numGroups: *mut GLint, groupsSize: GLsizei, groups: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfQueryDataINTEL"]
+ pub static mut epoxy_glGetPerfQueryDataINTEL: ::std::option::Option<
+ unsafe extern "C" fn(
+ queryHandle: GLuint,
+ flags: GLuint,
+ dataSize: GLsizei,
+ data: *mut GLvoid,
+ bytesWritten: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfQueryIdByNameINTEL"]
+ pub static mut epoxy_glGetPerfQueryIdByNameINTEL:
+ ::std::option::Option<unsafe extern "C" fn(queryName: *mut GLchar, queryId: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPerfQueryInfoINTEL"]
+ pub static mut epoxy_glGetPerfQueryInfoINTEL: ::std::option::Option<
+ unsafe extern "C" fn(
+ queryId: GLuint,
+ queryNameLength: GLuint,
+ queryName: *mut GLchar,
+ dataSize: *mut GLuint,
+ noCounters: *mut GLuint,
+ noInstances: *mut GLuint,
+ capsMask: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPixelMapfv"]
+ pub static mut epoxy_glGetPixelMapfv:
+ ::std::option::Option<unsafe extern "C" fn(map: GLenum, values: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPixelMapuiv"]
+ pub static mut epoxy_glGetPixelMapuiv:
+ ::std::option::Option<unsafe extern "C" fn(map: GLenum, values: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPixelMapusv"]
+ pub static mut epoxy_glGetPixelMapusv:
+ ::std::option::Option<unsafe extern "C" fn(map: GLenum, values: *mut GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPixelMapxv"]
+ pub static mut epoxy_glGetPixelMapxv:
+ ::std::option::Option<unsafe extern "C" fn(map: GLenum, size: GLint, values: *mut GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPixelTexGenParameterfvSGIS"]
+ pub static mut epoxy_glGetPixelTexGenParameterfvSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPixelTexGenParameterivSGIS"]
+ pub static mut epoxy_glGetPixelTexGenParameterivSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPixelTransformParameterfvEXT"]
+ pub static mut epoxy_glGetPixelTransformParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPixelTransformParameterivEXT"]
+ pub static mut epoxy_glGetPixelTransformParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPointerIndexedvEXT"]
+ pub static mut epoxy_glGetPointerIndexedvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, data: *mut *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPointeri_vEXT"]
+ pub static mut epoxy_glGetPointeri_vEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ pname: GLenum,
+ index: GLuint,
+ params: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPointerv"]
+ pub static mut epoxy_glGetPointerv: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, params: *mut *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPointervEXT"]
+ pub static mut epoxy_glGetPointervEXT: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, params: *mut *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPointervKHR"]
+ pub static mut epoxy_glGetPointervKHR: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, params: *mut *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetPolygonStipple"]
+ pub static mut epoxy_glGetPolygonStipple:
+ ::std::option::Option<unsafe extern "C" fn(mask: *mut GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramBinary"]
+ pub static mut epoxy_glGetProgramBinary: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ binaryFormat: *mut GLenum,
+ binary: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramBinaryOES"]
+ pub static mut epoxy_glGetProgramBinaryOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ binaryFormat: *mut GLenum,
+ binary: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramEnvParameterIivNV"]
+ pub static mut epoxy_glGetProgramEnvParameterIivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramEnvParameterIuivNV"]
+ pub static mut epoxy_glGetProgramEnvParameterIuivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramEnvParameterdvARB"]
+ pub static mut epoxy_glGetProgramEnvParameterdvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramEnvParameterfvARB"]
+ pub static mut epoxy_glGetProgramEnvParameterfvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramInfoLog"]
+ pub static mut epoxy_glGetProgramInfoLog: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ infoLog: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramInterfaceiv"]
+ pub static mut epoxy_glGetProgramInterfaceiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ programInterface: GLenum,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramLocalParameterIivNV"]
+ pub static mut epoxy_glGetProgramLocalParameterIivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramLocalParameterIuivNV"]
+ pub static mut epoxy_glGetProgramLocalParameterIuivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramLocalParameterdvARB"]
+ pub static mut epoxy_glGetProgramLocalParameterdvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramLocalParameterfvARB"]
+ pub static mut epoxy_glGetProgramLocalParameterfvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramNamedParameterdvNV"]
+ pub static mut epoxy_glGetProgramNamedParameterdvNV: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, len: GLsizei, name: *const GLubyte, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramNamedParameterfvNV"]
+ pub static mut epoxy_glGetProgramNamedParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, len: GLsizei, name: *const GLubyte, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramParameterdvNV"]
+ pub static mut epoxy_glGetProgramParameterdvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, pname: GLenum, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramParameterfvNV"]
+ pub static mut epoxy_glGetProgramParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramPipelineInfoLog"]
+ pub static mut epoxy_glGetProgramPipelineInfoLog: ::std::option::Option<
+ unsafe extern "C" fn(
+ pipeline: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ infoLog: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramPipelineInfoLogEXT"]
+ pub static mut epoxy_glGetProgramPipelineInfoLogEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ pipeline: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ infoLog: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramPipelineiv"]
+ pub static mut epoxy_glGetProgramPipelineiv: ::std::option::Option<
+ unsafe extern "C" fn(pipeline: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramPipelineivEXT"]
+ pub static mut epoxy_glGetProgramPipelineivEXT: ::std::option::Option<
+ unsafe extern "C" fn(pipeline: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramResourceIndex"]
+ pub static mut epoxy_glGetProgramResourceIndex: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ programInterface: GLenum,
+ name: *const GLchar,
+ ) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramResourceLocation"]
+ pub static mut epoxy_glGetProgramResourceLocation: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ programInterface: GLenum,
+ name: *const GLchar,
+ ) -> GLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramResourceLocationIndex"]
+ pub static mut epoxy_glGetProgramResourceLocationIndex: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ programInterface: GLenum,
+ name: *const GLchar,
+ ) -> GLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramResourceLocationIndexEXT"]
+ pub static mut epoxy_glGetProgramResourceLocationIndexEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ programInterface: GLenum,
+ name: *const GLchar,
+ ) -> GLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramResourceName"]
+ pub static mut epoxy_glGetProgramResourceName: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ programInterface: GLenum,
+ index: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ name: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramResourcefvNV"]
+ pub static mut epoxy_glGetProgramResourcefvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ programInterface: GLenum,
+ index: GLuint,
+ propCount: GLsizei,
+ props: *const GLenum,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramResourceiv"]
+ pub static mut epoxy_glGetProgramResourceiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ programInterface: GLenum,
+ index: GLuint,
+ propCount: GLsizei,
+ props: *const GLenum,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramStageiv"]
+ pub static mut epoxy_glGetProgramStageiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ shadertype: GLenum,
+ pname: GLenum,
+ values: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramStringARB"]
+ pub static mut epoxy_glGetProgramStringARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, string: *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramStringNV"]
+ pub static mut epoxy_glGetProgramStringNV: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, pname: GLenum, program: *mut GLubyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramSubroutineParameteruivNV"]
+ pub static mut epoxy_glGetProgramSubroutineParameteruivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, param: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramiv"]
+ pub static mut epoxy_glGetProgramiv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramivARB"]
+ pub static mut epoxy_glGetProgramivARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetProgramivNV"]
+ pub static mut epoxy_glGetProgramivNV:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryBufferObjecti64v"]
+ pub static mut epoxy_glGetQueryBufferObjecti64v: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, buffer: GLuint, pname: GLenum, offset: GLintptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryBufferObjectiv"]
+ pub static mut epoxy_glGetQueryBufferObjectiv: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, buffer: GLuint, pname: GLenum, offset: GLintptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryBufferObjectui64v"]
+ pub static mut epoxy_glGetQueryBufferObjectui64v: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, buffer: GLuint, pname: GLenum, offset: GLintptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryBufferObjectuiv"]
+ pub static mut epoxy_glGetQueryBufferObjectuiv: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, buffer: GLuint, pname: GLenum, offset: GLintptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryIndexediv"]
+ pub static mut epoxy_glGetQueryIndexediv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjecti64v"]
+ pub static mut epoxy_glGetQueryObjecti64v: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjecti64vEXT"]
+ pub static mut epoxy_glGetQueryObjecti64vEXT: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjectiv"]
+ pub static mut epoxy_glGetQueryObjectiv:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjectivARB"]
+ pub static mut epoxy_glGetQueryObjectivARB:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjectivEXT"]
+ pub static mut epoxy_glGetQueryObjectivEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjectui64v"]
+ pub static mut epoxy_glGetQueryObjectui64v: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjectui64vEXT"]
+ pub static mut epoxy_glGetQueryObjectui64vEXT: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjectuiv"]
+ pub static mut epoxy_glGetQueryObjectuiv:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjectuivARB"]
+ pub static mut epoxy_glGetQueryObjectuivARB:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryObjectuivEXT"]
+ pub static mut epoxy_glGetQueryObjectuivEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryiv"]
+ pub static mut epoxy_glGetQueryiv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryivARB"]
+ pub static mut epoxy_glGetQueryivARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetQueryivEXT"]
+ pub static mut epoxy_glGetQueryivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetRenderbufferParameteriv"]
+ pub static mut epoxy_glGetRenderbufferParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetRenderbufferParameterivEXT"]
+ pub static mut epoxy_glGetRenderbufferParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetRenderbufferParameterivOES"]
+ pub static mut epoxy_glGetRenderbufferParameterivOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSamplerParameterIiv"]
+ pub static mut epoxy_glGetSamplerParameterIiv: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSamplerParameterIivEXT"]
+ pub static mut epoxy_glGetSamplerParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSamplerParameterIivOES"]
+ pub static mut epoxy_glGetSamplerParameterIivOES: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSamplerParameterIuiv"]
+ pub static mut epoxy_glGetSamplerParameterIuiv: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSamplerParameterIuivEXT"]
+ pub static mut epoxy_glGetSamplerParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSamplerParameterIuivOES"]
+ pub static mut epoxy_glGetSamplerParameterIuivOES: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSamplerParameterfv"]
+ pub static mut epoxy_glGetSamplerParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSamplerParameteriv"]
+ pub static mut epoxy_glGetSamplerParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSeparableFilter"]
+ pub static mut epoxy_glGetSeparableFilter: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ row: *mut ::std::os::raw::c_void,
+ column: *mut ::std::os::raw::c_void,
+ span: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSeparableFilterEXT"]
+ pub static mut epoxy_glGetSeparableFilterEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ row: *mut ::std::os::raw::c_void,
+ column: *mut ::std::os::raw::c_void,
+ span: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetShaderInfoLog"]
+ pub static mut epoxy_glGetShaderInfoLog: ::std::option::Option<
+ unsafe extern "C" fn(
+ shader: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ infoLog: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetShaderPrecisionFormat"]
+ pub static mut epoxy_glGetShaderPrecisionFormat: ::std::option::Option<
+ unsafe extern "C" fn(
+ shadertype: GLenum,
+ precisiontype: GLenum,
+ range: *mut GLint,
+ precision: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetShaderSource"]
+ pub static mut epoxy_glGetShaderSource: ::std::option::Option<
+ unsafe extern "C" fn(
+ shader: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ source: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetShaderSourceARB"]
+ pub static mut epoxy_glGetShaderSourceARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ obj: GLhandleARB,
+ maxLength: GLsizei,
+ length: *mut GLsizei,
+ source: *mut GLcharARB,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetShaderiv"]
+ pub static mut epoxy_glGetShaderiv: ::std::option::Option<
+ unsafe extern "C" fn(shader: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSharpenTexFuncSGIS"]
+ pub static mut epoxy_glGetSharpenTexFuncSGIS:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, points: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetStageIndexNV"]
+ pub static mut epoxy_glGetStageIndexNV:
+ ::std::option::Option<unsafe extern "C" fn(shadertype: GLenum) -> GLushort>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetString"]
+ pub static mut epoxy_glGetString:
+ ::std::option::Option<unsafe extern "C" fn(name: GLenum) -> *const GLubyte>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetStringi"]
+ pub static mut epoxy_glGetStringi:
+ ::std::option::Option<unsafe extern "C" fn(name: GLenum, index: GLuint) -> *const GLubyte>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSubroutineIndex"]
+ pub static mut epoxy_glGetSubroutineIndex: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, shadertype: GLenum, name: *const GLchar) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSubroutineUniformLocation"]
+ pub static mut epoxy_glGetSubroutineUniformLocation: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, shadertype: GLenum, name: *const GLchar) -> GLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSynciv"]
+ pub static mut epoxy_glGetSynciv: ::std::option::Option<
+ unsafe extern "C" fn(
+ sync: GLsync,
+ pname: GLenum,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ values: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetSyncivAPPLE"]
+ pub static mut epoxy_glGetSyncivAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ sync: GLsync,
+ pname: GLenum,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ values: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexBumpParameterfvATI"]
+ pub static mut epoxy_glGetTexBumpParameterfvATI:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexBumpParameterivATI"]
+ pub static mut epoxy_glGetTexBumpParameterivATI:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexEnvfv"]
+ pub static mut epoxy_glGetTexEnvfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexEnviv"]
+ pub static mut epoxy_glGetTexEnviv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexEnvxv"]
+ pub static mut epoxy_glGetTexEnvxv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexEnvxvOES"]
+ pub static mut epoxy_glGetTexEnvxvOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexFilterFuncSGIS"]
+ pub static mut epoxy_glGetTexFilterFuncSGIS: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, filter: GLenum, weights: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexGendv"]
+ pub static mut epoxy_glGetTexGendv: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexGenfv"]
+ pub static mut epoxy_glGetTexGenfv: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexGenfvOES"]
+ pub static mut epoxy_glGetTexGenfvOES: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexGeniv"]
+ pub static mut epoxy_glGetTexGeniv: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexGenivOES"]
+ pub static mut epoxy_glGetTexGenivOES: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexGenxvOES"]
+ pub static mut epoxy_glGetTexGenxvOES: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexImage"]
+ pub static mut epoxy_glGetTexImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexLevelParameterfv"]
+ pub static mut epoxy_glGetTexLevelParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, level: GLint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexLevelParameteriv"]
+ pub static mut epoxy_glGetTexLevelParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, level: GLint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexLevelParameterxvOES"]
+ pub static mut epoxy_glGetTexLevelParameterxvOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, level: GLint, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterIiv"]
+ pub static mut epoxy_glGetTexParameterIiv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterIivEXT"]
+ pub static mut epoxy_glGetTexParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterIivOES"]
+ pub static mut epoxy_glGetTexParameterIivOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterIuiv"]
+ pub static mut epoxy_glGetTexParameterIuiv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterIuivEXT"]
+ pub static mut epoxy_glGetTexParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterIuivOES"]
+ pub static mut epoxy_glGetTexParameterIuivOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterPointervAPPLE"]
+ pub static mut epoxy_glGetTexParameterPointervAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ pname: GLenum,
+ params: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterfv"]
+ pub static mut epoxy_glGetTexParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameteriv"]
+ pub static mut epoxy_glGetTexParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterxv"]
+ pub static mut epoxy_glGetTexParameterxv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTexParameterxvOES"]
+ pub static mut epoxy_glGetTexParameterxvOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *mut GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureHandleARB"]
+ pub static mut epoxy_glGetTextureHandleARB:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint) -> GLuint64>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureHandleIMG"]
+ pub static mut epoxy_glGetTextureHandleIMG:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint) -> GLuint64>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureHandleNV"]
+ pub static mut epoxy_glGetTextureHandleNV:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint) -> GLuint64>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureImage"]
+ pub static mut epoxy_glGetTextureImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureImageEXT"]
+ pub static mut epoxy_glGetTextureImageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureLevelParameterfv"]
+ pub static mut epoxy_glGetTextureLevelParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, level: GLint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureLevelParameterfvEXT"]
+ pub static mut epoxy_glGetTextureLevelParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ pname: GLenum,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureLevelParameteriv"]
+ pub static mut epoxy_glGetTextureLevelParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, level: GLint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureLevelParameterivEXT"]
+ pub static mut epoxy_glGetTextureLevelParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureParameterIiv"]
+ pub static mut epoxy_glGetTextureParameterIiv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureParameterIivEXT"]
+ pub static mut epoxy_glGetTextureParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureParameterIuiv"]
+ pub static mut epoxy_glGetTextureParameterIuiv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureParameterIuivEXT"]
+ pub static mut epoxy_glGetTextureParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureParameterfv"]
+ pub static mut epoxy_glGetTextureParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureParameterfvEXT"]
+ pub static mut epoxy_glGetTextureParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureParameteriv"]
+ pub static mut epoxy_glGetTextureParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureParameterivEXT"]
+ pub static mut epoxy_glGetTextureParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureSamplerHandleARB"]
+ pub static mut epoxy_glGetTextureSamplerHandleARB:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint, sampler: GLuint) -> GLuint64>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureSamplerHandleIMG"]
+ pub static mut epoxy_glGetTextureSamplerHandleIMG:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint, sampler: GLuint) -> GLuint64>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureSamplerHandleNV"]
+ pub static mut epoxy_glGetTextureSamplerHandleNV:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint, sampler: GLuint) -> GLuint64>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTextureSubImage"]
+ pub static mut epoxy_glGetTextureSubImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTrackMatrixivNV"]
+ pub static mut epoxy_glGetTrackMatrixivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, address: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTransformFeedbackVarying"]
+ pub static mut epoxy_glGetTransformFeedbackVarying: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ index: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ size: *mut GLsizei,
+ type_: *mut GLenum,
+ name: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTransformFeedbackVaryingEXT"]
+ pub static mut epoxy_glGetTransformFeedbackVaryingEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ index: GLuint,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ size: *mut GLsizei,
+ type_: *mut GLenum,
+ name: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTransformFeedbackVaryingNV"]
+ pub static mut epoxy_glGetTransformFeedbackVaryingNV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, index: GLuint, location: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTransformFeedbacki64_v"]
+ pub static mut epoxy_glGetTransformFeedbacki64_v: ::std::option::Option<
+ unsafe extern "C" fn(xfb: GLuint, pname: GLenum, index: GLuint, param: *mut GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTransformFeedbacki_v"]
+ pub static mut epoxy_glGetTransformFeedbacki_v: ::std::option::Option<
+ unsafe extern "C" fn(xfb: GLuint, pname: GLenum, index: GLuint, param: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTransformFeedbackiv"]
+ pub static mut epoxy_glGetTransformFeedbackiv:
+ ::std::option::Option<unsafe extern "C" fn(xfb: GLuint, pname: GLenum, param: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetTranslatedShaderSourceANGLE"]
+ pub static mut epoxy_glGetTranslatedShaderSourceANGLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ shader: GLuint,
+ bufsize: GLsizei,
+ length: *mut GLsizei,
+ source: *mut GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformBlockIndex"]
+ pub static mut epoxy_glGetUniformBlockIndex: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, uniformBlockName: *const GLchar) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformBufferSizeEXT"]
+ pub static mut epoxy_glGetUniformBufferSizeEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformIndices"]
+ pub static mut epoxy_glGetUniformIndices: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ uniformCount: GLsizei,
+ uniformNames: *const *const GLchar,
+ uniformIndices: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformLocation"]
+ pub static mut epoxy_glGetUniformLocation:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, name: *const GLchar) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformLocationARB"]
+ pub static mut epoxy_glGetUniformLocationARB: ::std::option::Option<
+ unsafe extern "C" fn(programObj: GLhandleARB, name: *const GLcharARB) -> GLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformOffsetEXT"]
+ pub static mut epoxy_glGetUniformOffsetEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint) -> GLintptr>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformSubroutineuiv"]
+ pub static mut epoxy_glGetUniformSubroutineuiv: ::std::option::Option<
+ unsafe extern "C" fn(shadertype: GLenum, location: GLint, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformdv"]
+ pub static mut epoxy_glGetUniformdv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformfv"]
+ pub static mut epoxy_glGetUniformfv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformfvARB"]
+ pub static mut epoxy_glGetUniformfvARB: ::std::option::Option<
+ unsafe extern "C" fn(programObj: GLhandleARB, location: GLint, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformi64vARB"]
+ pub static mut epoxy_glGetUniformi64vARB: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformi64vNV"]
+ pub static mut epoxy_glGetUniformi64vNV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformiv"]
+ pub static mut epoxy_glGetUniformiv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformivARB"]
+ pub static mut epoxy_glGetUniformivARB: ::std::option::Option<
+ unsafe extern "C" fn(programObj: GLhandleARB, location: GLint, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformui64vARB"]
+ pub static mut epoxy_glGetUniformui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformui64vNV"]
+ pub static mut epoxy_glGetUniformui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformuiv"]
+ pub static mut epoxy_glGetUniformuiv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetUniformuivEXT"]
+ pub static mut epoxy_glGetUniformuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVariantArrayObjectfvATI"]
+ pub static mut epoxy_glGetVariantArrayObjectfvATI: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVariantArrayObjectivATI"]
+ pub static mut epoxy_glGetVariantArrayObjectivATI:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, pname: GLenum, params: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVariantBooleanvEXT"]
+ pub static mut epoxy_glGetVariantBooleanvEXT: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVariantFloatvEXT"]
+ pub static mut epoxy_glGetVariantFloatvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVariantIntegervEXT"]
+ pub static mut epoxy_glGetVariantIntegervEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVariantPointervEXT"]
+ pub static mut epoxy_glGetVariantPointervEXT: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, value: GLenum, data: *mut *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVaryingLocationNV"]
+ pub static mut epoxy_glGetVaryingLocationNV:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, name: *const GLchar) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexArrayIndexed64iv"]
+ pub static mut epoxy_glGetVertexArrayIndexed64iv: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, index: GLuint, pname: GLenum, param: *mut GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexArrayIndexediv"]
+ pub static mut epoxy_glGetVertexArrayIndexediv: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, index: GLuint, pname: GLenum, param: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexArrayIntegeri_vEXT"]
+ pub static mut epoxy_glGetVertexArrayIntegeri_vEXT: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, index: GLuint, pname: GLenum, param: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexArrayIntegervEXT"]
+ pub static mut epoxy_glGetVertexArrayIntegervEXT: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, pname: GLenum, param: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexArrayPointeri_vEXT"]
+ pub static mut epoxy_glGetVertexArrayPointeri_vEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ index: GLuint,
+ pname: GLenum,
+ param: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexArrayPointervEXT"]
+ pub static mut epoxy_glGetVertexArrayPointervEXT: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, pname: GLenum, param: *mut *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexArrayiv"]
+ pub static mut epoxy_glGetVertexArrayiv: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, pname: GLenum, param: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribArrayObjectfvATI"]
+ pub static mut epoxy_glGetVertexAttribArrayObjectfvATI: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribArrayObjectivATI"]
+ pub static mut epoxy_glGetVertexAttribArrayObjectivATI: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribIiv"]
+ pub static mut epoxy_glGetVertexAttribIiv: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribIivEXT"]
+ pub static mut epoxy_glGetVertexAttribIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribIuiv"]
+ pub static mut epoxy_glGetVertexAttribIuiv: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribIuivEXT"]
+ pub static mut epoxy_glGetVertexAttribIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribLdv"]
+ pub static mut epoxy_glGetVertexAttribLdv: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribLdvEXT"]
+ pub static mut epoxy_glGetVertexAttribLdvEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribLi64vNV"]
+ pub static mut epoxy_glGetVertexAttribLi64vNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribLui64vARB"]
+ pub static mut epoxy_glGetVertexAttribLui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribLui64vNV"]
+ pub static mut epoxy_glGetVertexAttribLui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribPointerv"]
+ pub static mut epoxy_glGetVertexAttribPointerv: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ pname: GLenum,
+ pointer: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribPointervARB"]
+ pub static mut epoxy_glGetVertexAttribPointervARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ pname: GLenum,
+ pointer: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribPointervNV"]
+ pub static mut epoxy_glGetVertexAttribPointervNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ pname: GLenum,
+ pointer: *mut *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribdv"]
+ pub static mut epoxy_glGetVertexAttribdv: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribdvARB"]
+ pub static mut epoxy_glGetVertexAttribdvARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribdvNV"]
+ pub static mut epoxy_glGetVertexAttribdvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribfv"]
+ pub static mut epoxy_glGetVertexAttribfv: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribfvARB"]
+ pub static mut epoxy_glGetVertexAttribfvARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribfvNV"]
+ pub static mut epoxy_glGetVertexAttribfvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribiv"]
+ pub static mut epoxy_glGetVertexAttribiv: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribivARB"]
+ pub static mut epoxy_glGetVertexAttribivARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVertexAttribivNV"]
+ pub static mut epoxy_glGetVertexAttribivNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVideoCaptureStreamdvNV"]
+ pub static mut epoxy_glGetVideoCaptureStreamdvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ stream: GLuint,
+ pname: GLenum,
+ params: *mut GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVideoCaptureStreamfvNV"]
+ pub static mut epoxy_glGetVideoCaptureStreamfvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ stream: GLuint,
+ pname: GLenum,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVideoCaptureStreamivNV"]
+ pub static mut epoxy_glGetVideoCaptureStreamivNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ stream: GLuint,
+ pname: GLenum,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVideoCaptureivNV"]
+ pub static mut epoxy_glGetVideoCaptureivNV: ::std::option::Option<
+ unsafe extern "C" fn(video_capture_slot: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVideoi64vNV"]
+ pub static mut epoxy_glGetVideoi64vNV: ::std::option::Option<
+ unsafe extern "C" fn(video_slot: GLuint, pname: GLenum, params: *mut GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVideoivNV"]
+ pub static mut epoxy_glGetVideoivNV: ::std::option::Option<
+ unsafe extern "C" fn(video_slot: GLuint, pname: GLenum, params: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVideoui64vNV"]
+ pub static mut epoxy_glGetVideoui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(video_slot: GLuint, pname: GLenum, params: *mut GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetVideouivNV"]
+ pub static mut epoxy_glGetVideouivNV: ::std::option::Option<
+ unsafe extern "C" fn(video_slot: GLuint, pname: GLenum, params: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnColorTable"]
+ pub static mut epoxy_glGetnColorTable: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ table: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnColorTableARB"]
+ pub static mut epoxy_glGetnColorTableARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ table: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnCompressedTexImage"]
+ pub static mut epoxy_glGetnCompressedTexImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ lod: GLint,
+ bufSize: GLsizei,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnCompressedTexImageARB"]
+ pub static mut epoxy_glGetnCompressedTexImageARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ lod: GLint,
+ bufSize: GLsizei,
+ img: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnConvolutionFilter"]
+ pub static mut epoxy_glGetnConvolutionFilter: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ image: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnConvolutionFilterARB"]
+ pub static mut epoxy_glGetnConvolutionFilterARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ image: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnHistogram"]
+ pub static mut epoxy_glGetnHistogram: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ reset: GLboolean,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ values: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnHistogramARB"]
+ pub static mut epoxy_glGetnHistogramARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ reset: GLboolean,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ values: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnMapdv"]
+ pub static mut epoxy_glGetnMapdv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, query: GLenum, bufSize: GLsizei, v: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnMapdvARB"]
+ pub static mut epoxy_glGetnMapdvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, query: GLenum, bufSize: GLsizei, v: *mut GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnMapfv"]
+ pub static mut epoxy_glGetnMapfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, query: GLenum, bufSize: GLsizei, v: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnMapfvARB"]
+ pub static mut epoxy_glGetnMapfvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, query: GLenum, bufSize: GLsizei, v: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnMapiv"]
+ pub static mut epoxy_glGetnMapiv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, query: GLenum, bufSize: GLsizei, v: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnMapivARB"]
+ pub static mut epoxy_glGetnMapivARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, query: GLenum, bufSize: GLsizei, v: *mut GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnMinmax"]
+ pub static mut epoxy_glGetnMinmax: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ reset: GLboolean,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ values: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnMinmaxARB"]
+ pub static mut epoxy_glGetnMinmaxARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ reset: GLboolean,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ values: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnPixelMapfv"]
+ pub static mut epoxy_glGetnPixelMapfv: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, bufSize: GLsizei, values: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnPixelMapfvARB"]
+ pub static mut epoxy_glGetnPixelMapfvARB: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, bufSize: GLsizei, values: *mut GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnPixelMapuiv"]
+ pub static mut epoxy_glGetnPixelMapuiv: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, bufSize: GLsizei, values: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnPixelMapuivARB"]
+ pub static mut epoxy_glGetnPixelMapuivARB: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, bufSize: GLsizei, values: *mut GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnPixelMapusv"]
+ pub static mut epoxy_glGetnPixelMapusv: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, bufSize: GLsizei, values: *mut GLushort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnPixelMapusvARB"]
+ pub static mut epoxy_glGetnPixelMapusvARB: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, bufSize: GLsizei, values: *mut GLushort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnPolygonStipple"]
+ pub static mut epoxy_glGetnPolygonStipple:
+ ::std::option::Option<unsafe extern "C" fn(bufSize: GLsizei, pattern: *mut GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnPolygonStippleARB"]
+ pub static mut epoxy_glGetnPolygonStippleARB:
+ ::std::option::Option<unsafe extern "C" fn(bufSize: GLsizei, pattern: *mut GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnSeparableFilter"]
+ pub static mut epoxy_glGetnSeparableFilter: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ rowBufSize: GLsizei,
+ row: *mut ::std::os::raw::c_void,
+ columnBufSize: GLsizei,
+ column: *mut ::std::os::raw::c_void,
+ span: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnSeparableFilterARB"]
+ pub static mut epoxy_glGetnSeparableFilterARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ type_: GLenum,
+ rowBufSize: GLsizei,
+ row: *mut ::std::os::raw::c_void,
+ columnBufSize: GLsizei,
+ column: *mut ::std::os::raw::c_void,
+ span: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnTexImage"]
+ pub static mut epoxy_glGetnTexImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnTexImageARB"]
+ pub static mut epoxy_glGetnTexImageARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ img: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformdv"]
+ pub static mut epoxy_glGetnUniformdv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformdvARB"]
+ pub static mut epoxy_glGetnUniformdvARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformfv"]
+ pub static mut epoxy_glGetnUniformfv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformfvARB"]
+ pub static mut epoxy_glGetnUniformfvARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformfvEXT"]
+ pub static mut epoxy_glGetnUniformfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformfvKHR"]
+ pub static mut epoxy_glGetnUniformfvKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformi64vARB"]
+ pub static mut epoxy_glGetnUniformi64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformiv"]
+ pub static mut epoxy_glGetnUniformiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformivARB"]
+ pub static mut epoxy_glGetnUniformivARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformivEXT"]
+ pub static mut epoxy_glGetnUniformivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformivKHR"]
+ pub static mut epoxy_glGetnUniformivKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformui64vARB"]
+ pub static mut epoxy_glGetnUniformui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformuiv"]
+ pub static mut epoxy_glGetnUniformuiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformuivARB"]
+ pub static mut epoxy_glGetnUniformuivARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGetnUniformuivKHR"]
+ pub static mut epoxy_glGetnUniformuivKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ bufSize: GLsizei,
+ params: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGlobalAlphaFactorbSUN"]
+ pub static mut epoxy_glGlobalAlphaFactorbSUN:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGlobalAlphaFactordSUN"]
+ pub static mut epoxy_glGlobalAlphaFactordSUN:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGlobalAlphaFactorfSUN"]
+ pub static mut epoxy_glGlobalAlphaFactorfSUN:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGlobalAlphaFactoriSUN"]
+ pub static mut epoxy_glGlobalAlphaFactoriSUN:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGlobalAlphaFactorsSUN"]
+ pub static mut epoxy_glGlobalAlphaFactorsSUN:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGlobalAlphaFactorubSUN"]
+ pub static mut epoxy_glGlobalAlphaFactorubSUN:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGlobalAlphaFactoruiSUN"]
+ pub static mut epoxy_glGlobalAlphaFactoruiSUN:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glGlobalAlphaFactorusSUN"]
+ pub static mut epoxy_glGlobalAlphaFactorusSUN:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glHint"]
+ pub static mut epoxy_glHint:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glHintPGI"]
+ pub static mut epoxy_glHintPGI:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, mode: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glHistogram"]
+ pub static mut epoxy_glHistogram: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ width: GLsizei,
+ internalformat: GLenum,
+ sink: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glHistogramEXT"]
+ pub static mut epoxy_glHistogramEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ width: GLsizei,
+ internalformat: GLenum,
+ sink: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIglooInterfaceSGIX"]
+ pub static mut epoxy_glIglooInterfaceSGIX: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, params: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glImageTransformParameterfHP"]
+ pub static mut epoxy_glImageTransformParameterfHP:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glImageTransformParameterfvHP"]
+ pub static mut epoxy_glImageTransformParameterfvHP: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glImageTransformParameteriHP"]
+ pub static mut epoxy_glImageTransformParameteriHP:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glImageTransformParameterivHP"]
+ pub static mut epoxy_glImageTransformParameterivHP: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glImportSyncEXT"]
+ pub static mut epoxy_glImportSyncEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ external_sync_type: GLenum,
+ external_sync: GLintptr,
+ flags: GLbitfield,
+ ) -> GLsync,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexFormatNV"]
+ pub static mut epoxy_glIndexFormatNV:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, stride: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexFuncEXT"]
+ pub static mut epoxy_glIndexFuncEXT:
+ ::std::option::Option<unsafe extern "C" fn(func: GLenum, ref_: GLclampf)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexMask"]
+ pub static mut epoxy_glIndexMask: ::std::option::Option<unsafe extern "C" fn(mask: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexMaterialEXT"]
+ pub static mut epoxy_glIndexMaterialEXT:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexPointer"]
+ pub static mut epoxy_glIndexPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexPointerEXT"]
+ pub static mut epoxy_glIndexPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ count: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexPointerListIBM"]
+ pub static mut epoxy_glIndexPointerListIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLint,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ptrstride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexd"]
+ pub static mut epoxy_glIndexd: ::std::option::Option<unsafe extern "C" fn(c: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexdv"]
+ pub static mut epoxy_glIndexdv: ::std::option::Option<unsafe extern "C" fn(c: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexf"]
+ pub static mut epoxy_glIndexf: ::std::option::Option<unsafe extern "C" fn(c: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexfv"]
+ pub static mut epoxy_glIndexfv: ::std::option::Option<unsafe extern "C" fn(c: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexi"]
+ pub static mut epoxy_glIndexi: ::std::option::Option<unsafe extern "C" fn(c: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexiv"]
+ pub static mut epoxy_glIndexiv: ::std::option::Option<unsafe extern "C" fn(c: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexs"]
+ pub static mut epoxy_glIndexs: ::std::option::Option<unsafe extern "C" fn(c: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexsv"]
+ pub static mut epoxy_glIndexsv: ::std::option::Option<unsafe extern "C" fn(c: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexub"]
+ pub static mut epoxy_glIndexub: ::std::option::Option<unsafe extern "C" fn(c: GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexubv"]
+ pub static mut epoxy_glIndexubv: ::std::option::Option<unsafe extern "C" fn(c: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexxOES"]
+ pub static mut epoxy_glIndexxOES:
+ ::std::option::Option<unsafe extern "C" fn(component: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIndexxvOES"]
+ pub static mut epoxy_glIndexxvOES:
+ ::std::option::Option<unsafe extern "C" fn(component: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInitNames"]
+ pub static mut epoxy_glInitNames: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInsertComponentEXT"]
+ pub static mut epoxy_glInsertComponentEXT:
+ ::std::option::Option<unsafe extern "C" fn(res: GLuint, src: GLuint, num: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInsertEventMarkerEXT"]
+ pub static mut epoxy_glInsertEventMarkerEXT:
+ ::std::option::Option<unsafe extern "C" fn(length: GLsizei, marker: *const GLchar)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInstrumentsBufferSGIX"]
+ pub static mut epoxy_glInstrumentsBufferSGIX:
+ ::std::option::Option<unsafe extern "C" fn(size: GLsizei, buffer: *mut GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInterleavedArrays"]
+ pub static mut epoxy_glInterleavedArrays: ::std::option::Option<
+ unsafe extern "C" fn(
+ format: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInterpolatePathsNV"]
+ pub static mut epoxy_glInterpolatePathsNV: ::std::option::Option<
+ unsafe extern "C" fn(resultPath: GLuint, pathA: GLuint, pathB: GLuint, weight: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInvalidateBufferData"]
+ pub static mut epoxy_glInvalidateBufferData:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInvalidateBufferSubData"]
+ pub static mut epoxy_glInvalidateBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, offset: GLintptr, length: GLsizeiptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInvalidateFramebuffer"]
+ pub static mut epoxy_glInvalidateFramebuffer: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, numAttachments: GLsizei, attachments: *const GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInvalidateNamedFramebufferData"]
+ pub static mut epoxy_glInvalidateNamedFramebufferData: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ numAttachments: GLsizei,
+ attachments: *const GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInvalidateNamedFramebufferSubData"]
+ pub static mut epoxy_glInvalidateNamedFramebufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ numAttachments: GLsizei,
+ attachments: *const GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInvalidateSubFramebuffer"]
+ pub static mut epoxy_glInvalidateSubFramebuffer: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ numAttachments: GLsizei,
+ attachments: *const GLenum,
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInvalidateTexImage"]
+ pub static mut epoxy_glInvalidateTexImage:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint, level: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glInvalidateTexSubImage"]
+ pub static mut epoxy_glInvalidateTexSubImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsAsyncMarkerSGIX"]
+ pub static mut epoxy_glIsAsyncMarkerSGIX:
+ ::std::option::Option<unsafe extern "C" fn(marker: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsBuffer"]
+ pub static mut epoxy_glIsBuffer:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsBufferARB"]
+ pub static mut epoxy_glIsBufferARB:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsBufferResidentNV"]
+ pub static mut epoxy_glIsBufferResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsCommandListNV"]
+ pub static mut epoxy_glIsCommandListNV:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsEnabled"]
+ pub static mut epoxy_glIsEnabled:
+ ::std::option::Option<unsafe extern "C" fn(cap: GLenum) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsEnabledIndexedEXT"]
+ pub static mut epoxy_glIsEnabledIndexedEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsEnabledi"]
+ pub static mut epoxy_glIsEnabledi:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsEnablediEXT"]
+ pub static mut epoxy_glIsEnablediEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsEnablediNV"]
+ pub static mut epoxy_glIsEnablediNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsEnablediOES"]
+ pub static mut epoxy_glIsEnablediOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, index: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsFenceAPPLE"]
+ pub static mut epoxy_glIsFenceAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(fence: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsFenceNV"]
+ pub static mut epoxy_glIsFenceNV:
+ ::std::option::Option<unsafe extern "C" fn(fence: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsFramebuffer"]
+ pub static mut epoxy_glIsFramebuffer:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsFramebufferEXT"]
+ pub static mut epoxy_glIsFramebufferEXT:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsFramebufferOES"]
+ pub static mut epoxy_glIsFramebufferOES:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsImageHandleResidentARB"]
+ pub static mut epoxy_glIsImageHandleResidentARB:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsImageHandleResidentNV"]
+ pub static mut epoxy_glIsImageHandleResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsList"]
+ pub static mut epoxy_glIsList:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsNameAMD"]
+ pub static mut epoxy_glIsNameAMD:
+ ::std::option::Option<unsafe extern "C" fn(identifier: GLenum, name: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsNamedBufferResidentNV"]
+ pub static mut epoxy_glIsNamedBufferResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsNamedStringARB"]
+ pub static mut epoxy_glIsNamedStringARB: ::std::option::Option<
+ unsafe extern "C" fn(namelen: GLint, name: *const GLchar) -> GLboolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsObjectBufferATI"]
+ pub static mut epoxy_glIsObjectBufferATI:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsOcclusionQueryNV"]
+ pub static mut epoxy_glIsOcclusionQueryNV:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsPathNV"]
+ pub static mut epoxy_glIsPathNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsPointInFillPathNV"]
+ pub static mut epoxy_glIsPointInFillPathNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, mask: GLuint, x: GLfloat, y: GLfloat) -> GLboolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsPointInStrokePathNV"]
+ pub static mut epoxy_glIsPointInStrokePathNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, x: GLfloat, y: GLfloat) -> GLboolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsProgram"]
+ pub static mut epoxy_glIsProgram:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsProgramARB"]
+ pub static mut epoxy_glIsProgramARB:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsProgramNV"]
+ pub static mut epoxy_glIsProgramNV:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsProgramPipeline"]
+ pub static mut epoxy_glIsProgramPipeline:
+ ::std::option::Option<unsafe extern "C" fn(pipeline: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsProgramPipelineEXT"]
+ pub static mut epoxy_glIsProgramPipelineEXT:
+ ::std::option::Option<unsafe extern "C" fn(pipeline: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsQuery"]
+ pub static mut epoxy_glIsQuery:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsQueryARB"]
+ pub static mut epoxy_glIsQueryARB:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsQueryEXT"]
+ pub static mut epoxy_glIsQueryEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsRenderbuffer"]
+ pub static mut epoxy_glIsRenderbuffer:
+ ::std::option::Option<unsafe extern "C" fn(renderbuffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsRenderbufferEXT"]
+ pub static mut epoxy_glIsRenderbufferEXT:
+ ::std::option::Option<unsafe extern "C" fn(renderbuffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsRenderbufferOES"]
+ pub static mut epoxy_glIsRenderbufferOES:
+ ::std::option::Option<unsafe extern "C" fn(renderbuffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsSampler"]
+ pub static mut epoxy_glIsSampler:
+ ::std::option::Option<unsafe extern "C" fn(sampler: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsShader"]
+ pub static mut epoxy_glIsShader:
+ ::std::option::Option<unsafe extern "C" fn(shader: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsStateNV"]
+ pub static mut epoxy_glIsStateNV:
+ ::std::option::Option<unsafe extern "C" fn(state: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsSync"]
+ pub static mut epoxy_glIsSync:
+ ::std::option::Option<unsafe extern "C" fn(sync: GLsync) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsSyncAPPLE"]
+ pub static mut epoxy_glIsSyncAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(sync: GLsync) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsTexture"]
+ pub static mut epoxy_glIsTexture:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsTextureEXT"]
+ pub static mut epoxy_glIsTextureEXT:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsTextureHandleResidentARB"]
+ pub static mut epoxy_glIsTextureHandleResidentARB:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsTextureHandleResidentNV"]
+ pub static mut epoxy_glIsTextureHandleResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsTransformFeedback"]
+ pub static mut epoxy_glIsTransformFeedback:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsTransformFeedbackNV"]
+ pub static mut epoxy_glIsTransformFeedbackNV:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsVariantEnabledEXT"]
+ pub static mut epoxy_glIsVariantEnabledEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, cap: GLenum) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsVertexArray"]
+ pub static mut epoxy_glIsVertexArray:
+ ::std::option::Option<unsafe extern "C" fn(array: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsVertexArrayAPPLE"]
+ pub static mut epoxy_glIsVertexArrayAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(array: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsVertexArrayOES"]
+ pub static mut epoxy_glIsVertexArrayOES:
+ ::std::option::Option<unsafe extern "C" fn(array: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glIsVertexAttribEnabledAPPLE"]
+ pub static mut epoxy_glIsVertexAttribEnabledAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, pname: GLenum) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLabelObjectEXT"]
+ pub static mut epoxy_glLabelObjectEXT: ::std::option::Option<
+ unsafe extern "C" fn(type_: GLenum, object: GLuint, length: GLsizei, label: *const GLchar),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightEnviSGIX"]
+ pub static mut epoxy_glLightEnviSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightModelf"]
+ pub static mut epoxy_glLightModelf:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightModelfv"]
+ pub static mut epoxy_glLightModelfv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightModeli"]
+ pub static mut epoxy_glLightModeli:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightModeliv"]
+ pub static mut epoxy_glLightModeliv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightModelx"]
+ pub static mut epoxy_glLightModelx:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightModelxOES"]
+ pub static mut epoxy_glLightModelxOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightModelxv"]
+ pub static mut epoxy_glLightModelxv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightModelxvOES"]
+ pub static mut epoxy_glLightModelxvOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightf"]
+ pub static mut epoxy_glLightf:
+ ::std::option::Option<unsafe extern "C" fn(light: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightfv"]
+ pub static mut epoxy_glLightfv: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLighti"]
+ pub static mut epoxy_glLighti:
+ ::std::option::Option<unsafe extern "C" fn(light: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightiv"]
+ pub static mut epoxy_glLightiv: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightx"]
+ pub static mut epoxy_glLightx:
+ ::std::option::Option<unsafe extern "C" fn(light: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightxOES"]
+ pub static mut epoxy_glLightxOES:
+ ::std::option::Option<unsafe extern "C" fn(light: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightxv"]
+ pub static mut epoxy_glLightxv: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLightxvOES"]
+ pub static mut epoxy_glLightxvOES: ::std::option::Option<
+ unsafe extern "C" fn(light: GLenum, pname: GLenum, params: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLineStipple"]
+ pub static mut epoxy_glLineStipple:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLint, pattern: GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLineWidth"]
+ pub static mut epoxy_glLineWidth: ::std::option::Option<unsafe extern "C" fn(width: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLineWidthx"]
+ pub static mut epoxy_glLineWidthx: ::std::option::Option<unsafe extern "C" fn(width: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLineWidthxOES"]
+ pub static mut epoxy_glLineWidthxOES:
+ ::std::option::Option<unsafe extern "C" fn(width: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLinkProgram"]
+ pub static mut epoxy_glLinkProgram:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLinkProgramARB"]
+ pub static mut epoxy_glLinkProgramARB:
+ ::std::option::Option<unsafe extern "C" fn(programObj: GLhandleARB)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glListBase"]
+ pub static mut epoxy_glListBase: ::std::option::Option<unsafe extern "C" fn(base: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glListDrawCommandsStatesClientNV"]
+ pub static mut epoxy_glListDrawCommandsStatesClientNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ list: GLuint,
+ segment: GLuint,
+ indirects: *mut *const ::std::os::raw::c_void,
+ sizes: *const GLsizei,
+ states: *const GLuint,
+ fbos: *const GLuint,
+ count: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glListParameterfSGIX"]
+ pub static mut epoxy_glListParameterfSGIX:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glListParameterfvSGIX"]
+ pub static mut epoxy_glListParameterfvSGIX: ::std::option::Option<
+ unsafe extern "C" fn(list: GLuint, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glListParameteriSGIX"]
+ pub static mut epoxy_glListParameteriSGIX:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glListParameterivSGIX"]
+ pub static mut epoxy_glListParameterivSGIX: ::std::option::Option<
+ unsafe extern "C" fn(list: GLuint, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadIdentity"]
+ pub static mut epoxy_glLoadIdentity: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadIdentityDeformationMapSGIX"]
+ pub static mut epoxy_glLoadIdentityDeformationMapSGIX:
+ ::std::option::Option<unsafe extern "C" fn(mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadMatrixd"]
+ pub static mut epoxy_glLoadMatrixd:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadMatrixf"]
+ pub static mut epoxy_glLoadMatrixf:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadMatrixx"]
+ pub static mut epoxy_glLoadMatrixx:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadMatrixxOES"]
+ pub static mut epoxy_glLoadMatrixxOES:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadName"]
+ pub static mut epoxy_glLoadName: ::std::option::Option<unsafe extern "C" fn(name: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadPaletteFromModelViewMatrixOES"]
+ pub static mut epoxy_glLoadPaletteFromModelViewMatrixOES:
+ ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadProgramNV"]
+ pub static mut epoxy_glLoadProgramNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, id: GLuint, len: GLsizei, program: *const GLubyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadTransposeMatrixd"]
+ pub static mut epoxy_glLoadTransposeMatrixd:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadTransposeMatrixdARB"]
+ pub static mut epoxy_glLoadTransposeMatrixdARB:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadTransposeMatrixf"]
+ pub static mut epoxy_glLoadTransposeMatrixf:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadTransposeMatrixfARB"]
+ pub static mut epoxy_glLoadTransposeMatrixfARB:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLoadTransposeMatrixxOES"]
+ pub static mut epoxy_glLoadTransposeMatrixxOES:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLockArraysEXT"]
+ pub static mut epoxy_glLockArraysEXT:
+ ::std::option::Option<unsafe extern "C" fn(first: GLint, count: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glLogicOp"]
+ pub static mut epoxy_glLogicOp: ::std::option::Option<unsafe extern "C" fn(opcode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeBufferNonResidentNV"]
+ pub static mut epoxy_glMakeBufferNonResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeBufferResidentNV"]
+ pub static mut epoxy_glMakeBufferResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, access: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeImageHandleNonResidentARB"]
+ pub static mut epoxy_glMakeImageHandleNonResidentARB:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeImageHandleNonResidentNV"]
+ pub static mut epoxy_glMakeImageHandleNonResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeImageHandleResidentARB"]
+ pub static mut epoxy_glMakeImageHandleResidentARB:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64, access: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeImageHandleResidentNV"]
+ pub static mut epoxy_glMakeImageHandleResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64, access: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeNamedBufferNonResidentNV"]
+ pub static mut epoxy_glMakeNamedBufferNonResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeNamedBufferResidentNV"]
+ pub static mut epoxy_glMakeNamedBufferResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint, access: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeTextureHandleNonResidentARB"]
+ pub static mut epoxy_glMakeTextureHandleNonResidentARB:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeTextureHandleNonResidentNV"]
+ pub static mut epoxy_glMakeTextureHandleNonResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeTextureHandleResidentARB"]
+ pub static mut epoxy_glMakeTextureHandleResidentARB:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMakeTextureHandleResidentNV"]
+ pub static mut epoxy_glMakeTextureHandleResidentNV:
+ ::std::option::Option<unsafe extern "C" fn(handle: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMap1d"]
+ pub static mut epoxy_glMap1d: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ u1: GLdouble,
+ u2: GLdouble,
+ stride: GLint,
+ order: GLint,
+ points: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMap1f"]
+ pub static mut epoxy_glMap1f: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ u1: GLfloat,
+ u2: GLfloat,
+ stride: GLint,
+ order: GLint,
+ points: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMap1xOES"]
+ pub static mut epoxy_glMap1xOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ u1: GLfixed,
+ u2: GLfixed,
+ stride: GLint,
+ order: GLint,
+ points: GLfixed,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMap2d"]
+ pub static mut epoxy_glMap2d: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ u1: GLdouble,
+ u2: GLdouble,
+ ustride: GLint,
+ uorder: GLint,
+ v1: GLdouble,
+ v2: GLdouble,
+ vstride: GLint,
+ vorder: GLint,
+ points: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMap2f"]
+ pub static mut epoxy_glMap2f: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ u1: GLfloat,
+ u2: GLfloat,
+ ustride: GLint,
+ uorder: GLint,
+ v1: GLfloat,
+ v2: GLfloat,
+ vstride: GLint,
+ vorder: GLint,
+ points: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMap2xOES"]
+ pub static mut epoxy_glMap2xOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ u1: GLfixed,
+ u2: GLfixed,
+ ustride: GLint,
+ uorder: GLint,
+ v1: GLfixed,
+ v2: GLfixed,
+ vstride: GLint,
+ vorder: GLint,
+ points: GLfixed,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapBuffer"]
+ pub static mut epoxy_glMapBuffer: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, access: GLenum) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapBufferARB"]
+ pub static mut epoxy_glMapBufferARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, access: GLenum) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapBufferOES"]
+ pub static mut epoxy_glMapBufferOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, access: GLenum) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapBufferRange"]
+ pub static mut epoxy_glMapBufferRange: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ offset: GLintptr,
+ length: GLsizeiptr,
+ access: GLbitfield,
+ ) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapBufferRangeEXT"]
+ pub static mut epoxy_glMapBufferRangeEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ offset: GLintptr,
+ length: GLsizeiptr,
+ access: GLbitfield,
+ ) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapControlPointsNV"]
+ pub static mut epoxy_glMapControlPointsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ type_: GLenum,
+ ustride: GLsizei,
+ vstride: GLsizei,
+ uorder: GLint,
+ vorder: GLint,
+ packed: GLboolean,
+ points: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapGrid1d"]
+ pub static mut epoxy_glMapGrid1d:
+ ::std::option::Option<unsafe extern "C" fn(un: GLint, u1: GLdouble, u2: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapGrid1f"]
+ pub static mut epoxy_glMapGrid1f:
+ ::std::option::Option<unsafe extern "C" fn(un: GLint, u1: GLfloat, u2: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapGrid1xOES"]
+ pub static mut epoxy_glMapGrid1xOES:
+ ::std::option::Option<unsafe extern "C" fn(n: GLint, u1: GLfixed, u2: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapGrid2d"]
+ pub static mut epoxy_glMapGrid2d: ::std::option::Option<
+ unsafe extern "C" fn(
+ un: GLint,
+ u1: GLdouble,
+ u2: GLdouble,
+ vn: GLint,
+ v1: GLdouble,
+ v2: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapGrid2f"]
+ pub static mut epoxy_glMapGrid2f: ::std::option::Option<
+ unsafe extern "C" fn(
+ un: GLint,
+ u1: GLfloat,
+ u2: GLfloat,
+ vn: GLint,
+ v1: GLfloat,
+ v2: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapGrid2xOES"]
+ pub static mut epoxy_glMapGrid2xOES: ::std::option::Option<
+ unsafe extern "C" fn(n: GLint, u1: GLfixed, u2: GLfixed, v1: GLfixed, v2: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapNamedBuffer"]
+ pub static mut epoxy_glMapNamedBuffer: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, access: GLenum) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapNamedBufferEXT"]
+ pub static mut epoxy_glMapNamedBufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, access: GLenum) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapNamedBufferRange"]
+ pub static mut epoxy_glMapNamedBufferRange: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ offset: GLintptr,
+ length: GLsizeiptr,
+ access: GLbitfield,
+ ) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapNamedBufferRangeEXT"]
+ pub static mut epoxy_glMapNamedBufferRangeEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ offset: GLintptr,
+ length: GLsizeiptr,
+ access: GLbitfield,
+ ) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapObjectBufferATI"]
+ pub static mut epoxy_glMapObjectBufferATI:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint) -> *mut ::std::os::raw::c_void>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapParameterfvNV"]
+ pub static mut epoxy_glMapParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapParameterivNV"]
+ pub static mut epoxy_glMapParameterivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapTexture2DINTEL"]
+ pub static mut epoxy_glMapTexture2DINTEL: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ access: GLbitfield,
+ stride: *mut GLint,
+ layout: *mut GLenum,
+ ) -> *mut ::std::os::raw::c_void,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapVertexAttrib1dAPPLE"]
+ pub static mut epoxy_glMapVertexAttrib1dAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLuint,
+ u1: GLdouble,
+ u2: GLdouble,
+ stride: GLint,
+ order: GLint,
+ points: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapVertexAttrib1fAPPLE"]
+ pub static mut epoxy_glMapVertexAttrib1fAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLuint,
+ u1: GLfloat,
+ u2: GLfloat,
+ stride: GLint,
+ order: GLint,
+ points: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapVertexAttrib2dAPPLE"]
+ pub static mut epoxy_glMapVertexAttrib2dAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLuint,
+ u1: GLdouble,
+ u2: GLdouble,
+ ustride: GLint,
+ uorder: GLint,
+ v1: GLdouble,
+ v2: GLdouble,
+ vstride: GLint,
+ vorder: GLint,
+ points: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMapVertexAttrib2fAPPLE"]
+ pub static mut epoxy_glMapVertexAttrib2fAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLuint,
+ u1: GLfloat,
+ u2: GLfloat,
+ ustride: GLint,
+ uorder: GLint,
+ v1: GLfloat,
+ v2: GLfloat,
+ vstride: GLint,
+ vorder: GLint,
+ points: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMaterialf"]
+ pub static mut epoxy_glMaterialf:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMaterialfv"]
+ pub static mut epoxy_glMaterialfv: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMateriali"]
+ pub static mut epoxy_glMateriali:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMaterialiv"]
+ pub static mut epoxy_glMaterialiv: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMaterialx"]
+ pub static mut epoxy_glMaterialx:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMaterialxOES"]
+ pub static mut epoxy_glMaterialxOES:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMaterialxv"]
+ pub static mut epoxy_glMaterialxv: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, param: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMaterialxvOES"]
+ pub static mut epoxy_glMaterialxvOES: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, pname: GLenum, param: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixFrustumEXT"]
+ pub static mut epoxy_glMatrixFrustumEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ left: GLdouble,
+ right: GLdouble,
+ bottom: GLdouble,
+ top: GLdouble,
+ zNear: GLdouble,
+ zFar: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixIndexPointerARB"]
+ pub static mut epoxy_glMatrixIndexPointerARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixIndexPointerOES"]
+ pub static mut epoxy_glMatrixIndexPointerOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixIndexubvARB"]
+ pub static mut epoxy_glMatrixIndexubvARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, indices: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixIndexuivARB"]
+ pub static mut epoxy_glMatrixIndexuivARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, indices: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixIndexusvARB"]
+ pub static mut epoxy_glMatrixIndexusvARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, indices: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixLoad3x2fNV"]
+ pub static mut epoxy_glMatrixLoad3x2fNV:
+ ::std::option::Option<unsafe extern "C" fn(matrixMode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixLoad3x3fNV"]
+ pub static mut epoxy_glMatrixLoad3x3fNV:
+ ::std::option::Option<unsafe extern "C" fn(matrixMode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixLoadIdentityEXT"]
+ pub static mut epoxy_glMatrixLoadIdentityEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixLoadTranspose3x3fNV"]
+ pub static mut epoxy_glMatrixLoadTranspose3x3fNV:
+ ::std::option::Option<unsafe extern "C" fn(matrixMode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixLoadTransposedEXT"]
+ pub static mut epoxy_glMatrixLoadTransposedEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixLoadTransposefEXT"]
+ pub static mut epoxy_glMatrixLoadTransposefEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixLoaddEXT"]
+ pub static mut epoxy_glMatrixLoaddEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixLoadfEXT"]
+ pub static mut epoxy_glMatrixLoadfEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixMode"]
+ pub static mut epoxy_glMatrixMode: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixMult3x2fNV"]
+ pub static mut epoxy_glMatrixMult3x2fNV:
+ ::std::option::Option<unsafe extern "C" fn(matrixMode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixMult3x3fNV"]
+ pub static mut epoxy_glMatrixMult3x3fNV:
+ ::std::option::Option<unsafe extern "C" fn(matrixMode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixMultTranspose3x3fNV"]
+ pub static mut epoxy_glMatrixMultTranspose3x3fNV:
+ ::std::option::Option<unsafe extern "C" fn(matrixMode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixMultTransposedEXT"]
+ pub static mut epoxy_glMatrixMultTransposedEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixMultTransposefEXT"]
+ pub static mut epoxy_glMatrixMultTransposefEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixMultdEXT"]
+ pub static mut epoxy_glMatrixMultdEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixMultfEXT"]
+ pub static mut epoxy_glMatrixMultfEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum, m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixOrthoEXT"]
+ pub static mut epoxy_glMatrixOrthoEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ left: GLdouble,
+ right: GLdouble,
+ bottom: GLdouble,
+ top: GLdouble,
+ zNear: GLdouble,
+ zFar: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixPopEXT"]
+ pub static mut epoxy_glMatrixPopEXT: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixPushEXT"]
+ pub static mut epoxy_glMatrixPushEXT: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixRotatedEXT"]
+ pub static mut epoxy_glMatrixRotatedEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, angle: GLdouble, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixRotatefEXT"]
+ pub static mut epoxy_glMatrixRotatefEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, angle: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixScaledEXT"]
+ pub static mut epoxy_glMatrixScaledEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixScalefEXT"]
+ pub static mut epoxy_glMatrixScalefEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixTranslatedEXT"]
+ pub static mut epoxy_glMatrixTranslatedEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMatrixTranslatefEXT"]
+ pub static mut epoxy_glMatrixTranslatefEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMaxShaderCompilerThreadsARB"]
+ pub static mut epoxy_glMaxShaderCompilerThreadsARB:
+ ::std::option::Option<unsafe extern "C" fn(count: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMemoryBarrier"]
+ pub static mut epoxy_glMemoryBarrier:
+ ::std::option::Option<unsafe extern "C" fn(barriers: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMemoryBarrierByRegion"]
+ pub static mut epoxy_glMemoryBarrierByRegion:
+ ::std::option::Option<unsafe extern "C" fn(barriers: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMemoryBarrierEXT"]
+ pub static mut epoxy_glMemoryBarrierEXT:
+ ::std::option::Option<unsafe extern "C" fn(barriers: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMinSampleShading"]
+ pub static mut epoxy_glMinSampleShading:
+ ::std::option::Option<unsafe extern "C" fn(value: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMinSampleShadingARB"]
+ pub static mut epoxy_glMinSampleShadingARB:
+ ::std::option::Option<unsafe extern "C" fn(value: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMinSampleShadingOES"]
+ pub static mut epoxy_glMinSampleShadingOES:
+ ::std::option::Option<unsafe extern "C" fn(value: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMinmax"]
+ pub static mut epoxy_glMinmax: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, internalformat: GLenum, sink: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMinmaxEXT"]
+ pub static mut epoxy_glMinmaxEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, internalformat: GLenum, sink: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultMatrixd"]
+ pub static mut epoxy_glMultMatrixd:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultMatrixf"]
+ pub static mut epoxy_glMultMatrixf:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultMatrixx"]
+ pub static mut epoxy_glMultMatrixx:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultMatrixxOES"]
+ pub static mut epoxy_glMultMatrixxOES:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultTransposeMatrixd"]
+ pub static mut epoxy_glMultTransposeMatrixd:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultTransposeMatrixdARB"]
+ pub static mut epoxy_glMultTransposeMatrixdARB:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultTransposeMatrixf"]
+ pub static mut epoxy_glMultTransposeMatrixf:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultTransposeMatrixfARB"]
+ pub static mut epoxy_glMultTransposeMatrixfARB:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultTransposeMatrixxOES"]
+ pub static mut epoxy_glMultTransposeMatrixxOES:
+ ::std::option::Option<unsafe extern "C" fn(m: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawArrays"]
+ pub static mut epoxy_glMultiDrawArrays: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ first: *const GLint,
+ count: *const GLsizei,
+ drawcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawArraysEXT"]
+ pub static mut epoxy_glMultiDrawArraysEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ first: *const GLint,
+ count: *const GLsizei,
+ primcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawArraysIndirect"]
+ pub static mut epoxy_glMultiDrawArraysIndirect: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ drawcount: GLsizei,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawArraysIndirectAMD"]
+ pub static mut epoxy_glMultiDrawArraysIndirectAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawArraysIndirectBindlessCountNV"]
+ pub static mut epoxy_glMultiDrawArraysIndirectBindlessCountNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ drawCount: GLsizei,
+ maxDrawCount: GLsizei,
+ stride: GLsizei,
+ vertexBufferCount: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawArraysIndirectBindlessNV"]
+ pub static mut epoxy_glMultiDrawArraysIndirectBindlessNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ drawCount: GLsizei,
+ stride: GLsizei,
+ vertexBufferCount: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawArraysIndirectCountARB"]
+ pub static mut epoxy_glMultiDrawArraysIndirectCountARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ indirect: GLintptr,
+ drawcount: GLintptr,
+ maxdrawcount: GLsizei,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawArraysIndirectEXT"]
+ pub static mut epoxy_glMultiDrawArraysIndirectEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ drawcount: GLsizei,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementArrayAPPLE"]
+ pub static mut epoxy_glMultiDrawElementArrayAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ first: *const GLint,
+ count: *const GLsizei,
+ primcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElements"]
+ pub static mut epoxy_glMultiDrawElements: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: *const GLsizei,
+ type_: GLenum,
+ indices: *const *const ::std::os::raw::c_void,
+ drawcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsBaseVertex"]
+ pub static mut epoxy_glMultiDrawElementsBaseVertex: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: *const GLsizei,
+ type_: GLenum,
+ indices: *const *const ::std::os::raw::c_void,
+ drawcount: GLsizei,
+ basevertex: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsBaseVertexEXT"]
+ pub static mut epoxy_glMultiDrawElementsBaseVertexEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: *const GLsizei,
+ type_: GLenum,
+ indices: *const *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ basevertex: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsBaseVertexOES"]
+ pub static mut epoxy_glMultiDrawElementsBaseVertexOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: *const GLsizei,
+ type_: GLenum,
+ indices: *const *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ basevertex: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsEXT"]
+ pub static mut epoxy_glMultiDrawElementsEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ count: *const GLsizei,
+ type_: GLenum,
+ indices: *const *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsIndirect"]
+ pub static mut epoxy_glMultiDrawElementsIndirect: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ type_: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ drawcount: GLsizei,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsIndirectAMD"]
+ pub static mut epoxy_glMultiDrawElementsIndirectAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ type_: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsIndirectBindlessCountNV"]
+ pub static mut epoxy_glMultiDrawElementsIndirectBindlessCountNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ type_: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ drawCount: GLsizei,
+ maxDrawCount: GLsizei,
+ stride: GLsizei,
+ vertexBufferCount: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsIndirectBindlessNV"]
+ pub static mut epoxy_glMultiDrawElementsIndirectBindlessNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ type_: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ drawCount: GLsizei,
+ stride: GLsizei,
+ vertexBufferCount: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsIndirectCountARB"]
+ pub static mut epoxy_glMultiDrawElementsIndirectCountARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ type_: GLenum,
+ indirect: GLintptr,
+ drawcount: GLintptr,
+ maxdrawcount: GLsizei,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawElementsIndirectEXT"]
+ pub static mut epoxy_glMultiDrawElementsIndirectEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ type_: GLenum,
+ indirect: *const ::std::os::raw::c_void,
+ drawcount: GLsizei,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiDrawRangeElementArrayAPPLE"]
+ pub static mut epoxy_glMultiDrawRangeElementArrayAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: GLenum,
+ start: GLuint,
+ end: GLuint,
+ first: *const GLint,
+ count: *const GLsizei,
+ primcount: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiModeDrawArraysIBM"]
+ pub static mut epoxy_glMultiModeDrawArraysIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: *const GLenum,
+ first: *const GLint,
+ count: *const GLsizei,
+ primcount: GLsizei,
+ modestride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiModeDrawElementsIBM"]
+ pub static mut epoxy_glMultiModeDrawElementsIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ mode: *const GLenum,
+ count: *const GLsizei,
+ type_: GLenum,
+ indices: *const *const ::std::os::raw::c_void,
+ primcount: GLsizei,
+ modestride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexBufferEXT"]
+ pub static mut epoxy_glMultiTexBufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ internalformat: GLenum,
+ buffer: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1bOES"]
+ pub static mut epoxy_glMultiTexCoord1bOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, s: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1bvOES"]
+ pub static mut epoxy_glMultiTexCoord1bvOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1d"]
+ pub static mut epoxy_glMultiTexCoord1d:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1dARB"]
+ pub static mut epoxy_glMultiTexCoord1dARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1dv"]
+ pub static mut epoxy_glMultiTexCoord1dv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1dvARB"]
+ pub static mut epoxy_glMultiTexCoord1dvARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1f"]
+ pub static mut epoxy_glMultiTexCoord1f:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1fARB"]
+ pub static mut epoxy_glMultiTexCoord1fARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1fv"]
+ pub static mut epoxy_glMultiTexCoord1fv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1fvARB"]
+ pub static mut epoxy_glMultiTexCoord1fvARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1hNV"]
+ pub static mut epoxy_glMultiTexCoord1hNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1hvNV"]
+ pub static mut epoxy_glMultiTexCoord1hvNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1i"]
+ pub static mut epoxy_glMultiTexCoord1i:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1iARB"]
+ pub static mut epoxy_glMultiTexCoord1iARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1iv"]
+ pub static mut epoxy_glMultiTexCoord1iv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1ivARB"]
+ pub static mut epoxy_glMultiTexCoord1ivARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1s"]
+ pub static mut epoxy_glMultiTexCoord1s:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1sARB"]
+ pub static mut epoxy_glMultiTexCoord1sARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1sv"]
+ pub static mut epoxy_glMultiTexCoord1sv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1svARB"]
+ pub static mut epoxy_glMultiTexCoord1svARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1xOES"]
+ pub static mut epoxy_glMultiTexCoord1xOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, s: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord1xvOES"]
+ pub static mut epoxy_glMultiTexCoord1xvOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2bOES"]
+ pub static mut epoxy_glMultiTexCoord2bOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, s: GLbyte, t: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2bvOES"]
+ pub static mut epoxy_glMultiTexCoord2bvOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2d"]
+ pub static mut epoxy_glMultiTexCoord2d:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLdouble, t: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2dARB"]
+ pub static mut epoxy_glMultiTexCoord2dARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLdouble, t: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2dv"]
+ pub static mut epoxy_glMultiTexCoord2dv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2dvARB"]
+ pub static mut epoxy_glMultiTexCoord2dvARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2f"]
+ pub static mut epoxy_glMultiTexCoord2f:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLfloat, t: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2fARB"]
+ pub static mut epoxy_glMultiTexCoord2fARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLfloat, t: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2fv"]
+ pub static mut epoxy_glMultiTexCoord2fv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2fvARB"]
+ pub static mut epoxy_glMultiTexCoord2fvARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2hNV"]
+ pub static mut epoxy_glMultiTexCoord2hNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLhalfNV, t: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2hvNV"]
+ pub static mut epoxy_glMultiTexCoord2hvNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2i"]
+ pub static mut epoxy_glMultiTexCoord2i:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLint, t: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2iARB"]
+ pub static mut epoxy_glMultiTexCoord2iARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLint, t: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2iv"]
+ pub static mut epoxy_glMultiTexCoord2iv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2ivARB"]
+ pub static mut epoxy_glMultiTexCoord2ivARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2s"]
+ pub static mut epoxy_glMultiTexCoord2s:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLshort, t: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2sARB"]
+ pub static mut epoxy_glMultiTexCoord2sARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLshort, t: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2sv"]
+ pub static mut epoxy_glMultiTexCoord2sv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2svARB"]
+ pub static mut epoxy_glMultiTexCoord2svARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2xOES"]
+ pub static mut epoxy_glMultiTexCoord2xOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, s: GLfixed, t: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord2xvOES"]
+ pub static mut epoxy_glMultiTexCoord2xvOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3bOES"]
+ pub static mut epoxy_glMultiTexCoord3bOES: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, s: GLbyte, t: GLbyte, r: GLbyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3bvOES"]
+ pub static mut epoxy_glMultiTexCoord3bvOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3d"]
+ pub static mut epoxy_glMultiTexCoord3d: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLdouble, t: GLdouble, r: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3dARB"]
+ pub static mut epoxy_glMultiTexCoord3dARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLdouble, t: GLdouble, r: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3dv"]
+ pub static mut epoxy_glMultiTexCoord3dv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3dvARB"]
+ pub static mut epoxy_glMultiTexCoord3dvARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3f"]
+ pub static mut epoxy_glMultiTexCoord3f: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLfloat, t: GLfloat, r: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3fARB"]
+ pub static mut epoxy_glMultiTexCoord3fARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLfloat, t: GLfloat, r: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3fv"]
+ pub static mut epoxy_glMultiTexCoord3fv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3fvARB"]
+ pub static mut epoxy_glMultiTexCoord3fvARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3hNV"]
+ pub static mut epoxy_glMultiTexCoord3hNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLhalfNV, t: GLhalfNV, r: GLhalfNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3hvNV"]
+ pub static mut epoxy_glMultiTexCoord3hvNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3i"]
+ pub static mut epoxy_glMultiTexCoord3i:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLint, t: GLint, r: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3iARB"]
+ pub static mut epoxy_glMultiTexCoord3iARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, s: GLint, t: GLint, r: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3iv"]
+ pub static mut epoxy_glMultiTexCoord3iv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3ivARB"]
+ pub static mut epoxy_glMultiTexCoord3ivARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3s"]
+ pub static mut epoxy_glMultiTexCoord3s: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLshort, t: GLshort, r: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3sARB"]
+ pub static mut epoxy_glMultiTexCoord3sARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLshort, t: GLshort, r: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3sv"]
+ pub static mut epoxy_glMultiTexCoord3sv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3svARB"]
+ pub static mut epoxy_glMultiTexCoord3svARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3xOES"]
+ pub static mut epoxy_glMultiTexCoord3xOES: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, s: GLfixed, t: GLfixed, r: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord3xvOES"]
+ pub static mut epoxy_glMultiTexCoord3xvOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4bOES"]
+ pub static mut epoxy_glMultiTexCoord4bOES: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, s: GLbyte, t: GLbyte, r: GLbyte, q: GLbyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4bvOES"]
+ pub static mut epoxy_glMultiTexCoord4bvOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4d"]
+ pub static mut epoxy_glMultiTexCoord4d: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLdouble, t: GLdouble, r: GLdouble, q: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4dARB"]
+ pub static mut epoxy_glMultiTexCoord4dARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLdouble, t: GLdouble, r: GLdouble, q: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4dv"]
+ pub static mut epoxy_glMultiTexCoord4dv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4dvARB"]
+ pub static mut epoxy_glMultiTexCoord4dvARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4f"]
+ pub static mut epoxy_glMultiTexCoord4f: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLfloat, t: GLfloat, r: GLfloat, q: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4fARB"]
+ pub static mut epoxy_glMultiTexCoord4fARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLfloat, t: GLfloat, r: GLfloat, q: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4fv"]
+ pub static mut epoxy_glMultiTexCoord4fv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4fvARB"]
+ pub static mut epoxy_glMultiTexCoord4fvARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4hNV"]
+ pub static mut epoxy_glMultiTexCoord4hNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLhalfNV, t: GLhalfNV, r: GLhalfNV, q: GLhalfNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4hvNV"]
+ pub static mut epoxy_glMultiTexCoord4hvNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4i"]
+ pub static mut epoxy_glMultiTexCoord4i: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLint, t: GLint, r: GLint, q: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4iARB"]
+ pub static mut epoxy_glMultiTexCoord4iARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLint, t: GLint, r: GLint, q: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4iv"]
+ pub static mut epoxy_glMultiTexCoord4iv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4ivARB"]
+ pub static mut epoxy_glMultiTexCoord4ivARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4s"]
+ pub static mut epoxy_glMultiTexCoord4s: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLshort, t: GLshort, r: GLshort, q: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4sARB"]
+ pub static mut epoxy_glMultiTexCoord4sARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, s: GLshort, t: GLshort, r: GLshort, q: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4sv"]
+ pub static mut epoxy_glMultiTexCoord4sv:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4svARB"]
+ pub static mut epoxy_glMultiTexCoord4svARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4x"]
+ pub static mut epoxy_glMultiTexCoord4x: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, s: GLfixed, t: GLfixed, r: GLfixed, q: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4xOES"]
+ pub static mut epoxy_glMultiTexCoord4xOES: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, s: GLfixed, t: GLfixed, r: GLfixed, q: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoord4xvOES"]
+ pub static mut epoxy_glMultiTexCoord4xvOES:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordP1ui"]
+ pub static mut epoxy_glMultiTexCoordP1ui:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordP1uiv"]
+ pub static mut epoxy_glMultiTexCoordP1uiv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, type_: GLenum, coords: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordP2ui"]
+ pub static mut epoxy_glMultiTexCoordP2ui:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordP2uiv"]
+ pub static mut epoxy_glMultiTexCoordP2uiv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, type_: GLenum, coords: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordP3ui"]
+ pub static mut epoxy_glMultiTexCoordP3ui:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordP3uiv"]
+ pub static mut epoxy_glMultiTexCoordP3uiv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, type_: GLenum, coords: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordP4ui"]
+ pub static mut epoxy_glMultiTexCoordP4ui:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLenum, type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordP4uiv"]
+ pub static mut epoxy_glMultiTexCoordP4uiv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLenum, type_: GLenum, coords: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexCoordPointerEXT"]
+ pub static mut epoxy_glMultiTexCoordPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexEnvfEXT"]
+ pub static mut epoxy_glMultiTexEnvfEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, param: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexEnvfvEXT"]
+ pub static mut epoxy_glMultiTexEnvfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ pname: GLenum,
+ params: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexEnviEXT"]
+ pub static mut epoxy_glMultiTexEnviEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, param: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexEnvivEXT"]
+ pub static mut epoxy_glMultiTexEnvivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexGendEXT"]
+ pub static mut epoxy_glMultiTexGendEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, coord: GLenum, pname: GLenum, param: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexGendvEXT"]
+ pub static mut epoxy_glMultiTexGendvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ coord: GLenum,
+ pname: GLenum,
+ params: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexGenfEXT"]
+ pub static mut epoxy_glMultiTexGenfEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, coord: GLenum, pname: GLenum, param: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexGenfvEXT"]
+ pub static mut epoxy_glMultiTexGenfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, coord: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexGeniEXT"]
+ pub static mut epoxy_glMultiTexGeniEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, coord: GLenum, pname: GLenum, param: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexGenivEXT"]
+ pub static mut epoxy_glMultiTexGenivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, coord: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexImage1DEXT"]
+ pub static mut epoxy_glMultiTexImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexImage2DEXT"]
+ pub static mut epoxy_glMultiTexImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexImage3DEXT"]
+ pub static mut epoxy_glMultiTexImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexParameterIivEXT"]
+ pub static mut epoxy_glMultiTexParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexParameterIuivEXT"]
+ pub static mut epoxy_glMultiTexParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexParameterfEXT"]
+ pub static mut epoxy_glMultiTexParameterfEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, param: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexParameterfvEXT"]
+ pub static mut epoxy_glMultiTexParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ pname: GLenum,
+ params: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexParameteriEXT"]
+ pub static mut epoxy_glMultiTexParameteriEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, param: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexParameterivEXT"]
+ pub static mut epoxy_glMultiTexParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexRenderbufferEXT"]
+ pub static mut epoxy_glMultiTexRenderbufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(texunit: GLenum, target: GLenum, renderbuffer: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexSubImage1DEXT"]
+ pub static mut epoxy_glMultiTexSubImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexSubImage2DEXT"]
+ pub static mut epoxy_glMultiTexSubImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glMultiTexSubImage3DEXT"]
+ pub static mut epoxy_glMultiTexSubImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texunit: GLenum,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedBufferData"]
+ pub static mut epoxy_glNamedBufferData: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ usage: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedBufferDataEXT"]
+ pub static mut epoxy_glNamedBufferDataEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ usage: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedBufferPageCommitmentARB"]
+ pub static mut epoxy_glNamedBufferPageCommitmentARB: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, offset: GLintptr, size: GLsizeiptr, commit: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedBufferPageCommitmentEXT"]
+ pub static mut epoxy_glNamedBufferPageCommitmentEXT: ::std::option::Option<
+ unsafe extern "C" fn(buffer: GLuint, offset: GLintptr, size: GLsizeiptr, commit: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedBufferStorage"]
+ pub static mut epoxy_glNamedBufferStorage: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ flags: GLbitfield,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedBufferStorageEXT"]
+ pub static mut epoxy_glNamedBufferStorageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ flags: GLbitfield,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedBufferSubData"]
+ pub static mut epoxy_glNamedBufferSubData: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedBufferSubDataEXT"]
+ pub static mut epoxy_glNamedBufferSubDataEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ data: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedCopyBufferSubDataEXT"]
+ pub static mut epoxy_glNamedCopyBufferSubDataEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ readBuffer: GLuint,
+ writeBuffer: GLuint,
+ readOffset: GLintptr,
+ writeOffset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferDrawBuffer"]
+ pub static mut epoxy_glNamedFramebufferDrawBuffer:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint, buf: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferDrawBuffers"]
+ pub static mut epoxy_glNamedFramebufferDrawBuffers: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, n: GLsizei, bufs: *const GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferParameteri"]
+ pub static mut epoxy_glNamedFramebufferParameteri: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, pname: GLenum, param: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferParameteriEXT"]
+ pub static mut epoxy_glNamedFramebufferParameteriEXT: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, pname: GLenum, param: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferReadBuffer"]
+ pub static mut epoxy_glNamedFramebufferReadBuffer:
+ ::std::option::Option<unsafe extern "C" fn(framebuffer: GLuint, src: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferRenderbuffer"]
+ pub static mut epoxy_glNamedFramebufferRenderbuffer: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ renderbuffertarget: GLenum,
+ renderbuffer: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferRenderbufferEXT"]
+ pub static mut epoxy_glNamedFramebufferRenderbufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ renderbuffertarget: GLenum,
+ renderbuffer: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferSampleLocationsfvARB"]
+ pub static mut epoxy_glNamedFramebufferSampleLocationsfvARB: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, start: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferSampleLocationsfvNV"]
+ pub static mut epoxy_glNamedFramebufferSampleLocationsfvNV: ::std::option::Option<
+ unsafe extern "C" fn(framebuffer: GLuint, start: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferSamplePositionsfvAMD"]
+ pub static mut epoxy_glNamedFramebufferSamplePositionsfvAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ numsamples: GLuint,
+ pixelindex: GLuint,
+ values: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferTexture"]
+ pub static mut epoxy_glNamedFramebufferTexture: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferTexture1DEXT"]
+ pub static mut epoxy_glNamedFramebufferTexture1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferTexture2DEXT"]
+ pub static mut epoxy_glNamedFramebufferTexture2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferTexture3DEXT"]
+ pub static mut epoxy_glNamedFramebufferTexture3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ textarget: GLenum,
+ texture: GLuint,
+ level: GLint,
+ zoffset: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferTextureEXT"]
+ pub static mut epoxy_glNamedFramebufferTextureEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferTextureFaceEXT"]
+ pub static mut epoxy_glNamedFramebufferTextureFaceEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ face: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferTextureLayer"]
+ pub static mut epoxy_glNamedFramebufferTextureLayer: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ layer: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedFramebufferTextureLayerEXT"]
+ pub static mut epoxy_glNamedFramebufferTextureLayerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ framebuffer: GLuint,
+ attachment: GLenum,
+ texture: GLuint,
+ level: GLint,
+ layer: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameter4dEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameter4dEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ x: GLdouble,
+ y: GLdouble,
+ z: GLdouble,
+ w: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameter4dvEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameter4dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ params: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameter4fEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameter4fEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ w: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameter4fvEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameter4fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ params: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameterI4iEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameterI4iEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ x: GLint,
+ y: GLint,
+ z: GLint,
+ w: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameterI4ivEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameterI4ivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, target: GLenum, index: GLuint, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameterI4uiEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameterI4uiEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ x: GLuint,
+ y: GLuint,
+ z: GLuint,
+ w: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameterI4uivEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameterI4uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, target: GLenum, index: GLuint, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParameters4fvEXT"]
+ pub static mut epoxy_glNamedProgramLocalParameters4fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ count: GLsizei,
+ params: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParametersI4ivEXT"]
+ pub static mut epoxy_glNamedProgramLocalParametersI4ivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ count: GLsizei,
+ params: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramLocalParametersI4uivEXT"]
+ pub static mut epoxy_glNamedProgramLocalParametersI4uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ index: GLuint,
+ count: GLsizei,
+ params: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedProgramStringEXT"]
+ pub static mut epoxy_glNamedProgramStringEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ target: GLenum,
+ format: GLenum,
+ len: GLsizei,
+ string: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedRenderbufferStorage"]
+ pub static mut epoxy_glNamedRenderbufferStorage: ::std::option::Option<
+ unsafe extern "C" fn(
+ renderbuffer: GLuint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedRenderbufferStorageEXT"]
+ pub static mut epoxy_glNamedRenderbufferStorageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ renderbuffer: GLuint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedRenderbufferStorageMultisample"]
+ pub static mut epoxy_glNamedRenderbufferStorageMultisample: ::std::option::Option<
+ unsafe extern "C" fn(
+ renderbuffer: GLuint,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedRenderbufferStorageMultisampleCoverageEXT"]
+ pub static mut epoxy_glNamedRenderbufferStorageMultisampleCoverageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ renderbuffer: GLuint,
+ coverageSamples: GLsizei,
+ colorSamples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedRenderbufferStorageMultisampleEXT"]
+ pub static mut epoxy_glNamedRenderbufferStorageMultisampleEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ renderbuffer: GLuint,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNamedStringARB"]
+ pub static mut epoxy_glNamedStringARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ namelen: GLint,
+ name: *const GLchar,
+ stringlen: GLint,
+ string: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNewList"]
+ pub static mut epoxy_glNewList:
+ ::std::option::Option<unsafe extern "C" fn(list: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNewObjectBufferATI"]
+ pub static mut epoxy_glNewObjectBufferATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ usage: GLenum,
+ ) -> GLuint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3b"]
+ pub static mut epoxy_glNormal3b:
+ ::std::option::Option<unsafe extern "C" fn(nx: GLbyte, ny: GLbyte, nz: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3bv"]
+ pub static mut epoxy_glNormal3bv: ::std::option::Option<unsafe extern "C" fn(v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3d"]
+ pub static mut epoxy_glNormal3d:
+ ::std::option::Option<unsafe extern "C" fn(nx: GLdouble, ny: GLdouble, nz: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3dv"]
+ pub static mut epoxy_glNormal3dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3f"]
+ pub static mut epoxy_glNormal3f:
+ ::std::option::Option<unsafe extern "C" fn(nx: GLfloat, ny: GLfloat, nz: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3fVertex3fSUN"]
+ pub static mut epoxy_glNormal3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3fVertex3fvSUN"]
+ pub static mut epoxy_glNormal3fVertex3fvSUN:
+ ::std::option::Option<unsafe extern "C" fn(n: *const GLfloat, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3fv"]
+ pub static mut epoxy_glNormal3fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3hNV"]
+ pub static mut epoxy_glNormal3hNV:
+ ::std::option::Option<unsafe extern "C" fn(nx: GLhalfNV, ny: GLhalfNV, nz: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3hvNV"]
+ pub static mut epoxy_glNormal3hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3i"]
+ pub static mut epoxy_glNormal3i:
+ ::std::option::Option<unsafe extern "C" fn(nx: GLint, ny: GLint, nz: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3iv"]
+ pub static mut epoxy_glNormal3iv: ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3s"]
+ pub static mut epoxy_glNormal3s:
+ ::std::option::Option<unsafe extern "C" fn(nx: GLshort, ny: GLshort, nz: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3sv"]
+ pub static mut epoxy_glNormal3sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3x"]
+ pub static mut epoxy_glNormal3x:
+ ::std::option::Option<unsafe extern "C" fn(nx: GLfixed, ny: GLfixed, nz: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3xOES"]
+ pub static mut epoxy_glNormal3xOES:
+ ::std::option::Option<unsafe extern "C" fn(nx: GLfixed, ny: GLfixed, nz: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormal3xvOES"]
+ pub static mut epoxy_glNormal3xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalFormatNV"]
+ pub static mut epoxy_glNormalFormatNV:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, stride: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalP3ui"]
+ pub static mut epoxy_glNormalP3ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalP3uiv"]
+ pub static mut epoxy_glNormalP3uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalPointer"]
+ pub static mut epoxy_glNormalPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalPointerEXT"]
+ pub static mut epoxy_glNormalPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ count: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalPointerListIBM"]
+ pub static mut epoxy_glNormalPointerListIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLint,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ptrstride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalPointervINTEL"]
+ pub static mut epoxy_glNormalPointervINTEL: ::std::option::Option<
+ unsafe extern "C" fn(type_: GLenum, pointer: *mut *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3bATI"]
+ pub static mut epoxy_glNormalStream3bATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, nx: GLbyte, ny: GLbyte, nz: GLbyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3bvATI"]
+ pub static mut epoxy_glNormalStream3bvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3dATI"]
+ pub static mut epoxy_glNormalStream3dATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, nx: GLdouble, ny: GLdouble, nz: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3dvATI"]
+ pub static mut epoxy_glNormalStream3dvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3fATI"]
+ pub static mut epoxy_glNormalStream3fATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, nx: GLfloat, ny: GLfloat, nz: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3fvATI"]
+ pub static mut epoxy_glNormalStream3fvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3iATI"]
+ pub static mut epoxy_glNormalStream3iATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, nx: GLint, ny: GLint, nz: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3ivATI"]
+ pub static mut epoxy_glNormalStream3ivATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3sATI"]
+ pub static mut epoxy_glNormalStream3sATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, nx: GLshort, ny: GLshort, nz: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glNormalStream3svATI"]
+ pub static mut epoxy_glNormalStream3svATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glObjectLabel"]
+ pub static mut epoxy_glObjectLabel: ::std::option::Option<
+ unsafe extern "C" fn(
+ identifier: GLenum,
+ name: GLuint,
+ length: GLsizei,
+ label: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glObjectLabelKHR"]
+ pub static mut epoxy_glObjectLabelKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ identifier: GLenum,
+ name: GLuint,
+ length: GLsizei,
+ label: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glObjectPtrLabel"]
+ pub static mut epoxy_glObjectPtrLabel: ::std::option::Option<
+ unsafe extern "C" fn(
+ ptr: *const ::std::os::raw::c_void,
+ length: GLsizei,
+ label: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glObjectPtrLabelKHR"]
+ pub static mut epoxy_glObjectPtrLabelKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ ptr: *const ::std::os::raw::c_void,
+ length: GLsizei,
+ label: *const GLchar,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glObjectPurgeableAPPLE"]
+ pub static mut epoxy_glObjectPurgeableAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(objectType: GLenum, name: GLuint, option: GLenum) -> GLenum,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glObjectUnpurgeableAPPLE"]
+ pub static mut epoxy_glObjectUnpurgeableAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(objectType: GLenum, name: GLuint, option: GLenum) -> GLenum,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glOrtho"]
+ pub static mut epoxy_glOrtho: ::std::option::Option<
+ unsafe extern "C" fn(
+ left: GLdouble,
+ right: GLdouble,
+ bottom: GLdouble,
+ top: GLdouble,
+ zNear: GLdouble,
+ zFar: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glOrthof"]
+ pub static mut epoxy_glOrthof: ::std::option::Option<
+ unsafe extern "C" fn(
+ l: GLfloat,
+ r: GLfloat,
+ b: GLfloat,
+ t: GLfloat,
+ n: GLfloat,
+ f: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glOrthofOES"]
+ pub static mut epoxy_glOrthofOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ l: GLfloat,
+ r: GLfloat,
+ b: GLfloat,
+ t: GLfloat,
+ n: GLfloat,
+ f: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glOrthox"]
+ pub static mut epoxy_glOrthox: ::std::option::Option<
+ unsafe extern "C" fn(
+ l: GLfixed,
+ r: GLfixed,
+ b: GLfixed,
+ t: GLfixed,
+ n: GLfixed,
+ f: GLfixed,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glOrthoxOES"]
+ pub static mut epoxy_glOrthoxOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ l: GLfixed,
+ r: GLfixed,
+ b: GLfixed,
+ t: GLfixed,
+ n: GLfixed,
+ f: GLfixed,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPNTrianglesfATI"]
+ pub static mut epoxy_glPNTrianglesfATI:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPNTrianglesiATI"]
+ pub static mut epoxy_glPNTrianglesiATI:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPassTexCoordATI"]
+ pub static mut epoxy_glPassTexCoordATI:
+ ::std::option::Option<unsafe extern "C" fn(dst: GLuint, coord: GLuint, swizzle: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPassThrough"]
+ pub static mut epoxy_glPassThrough: ::std::option::Option<unsafe extern "C" fn(token: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPassThroughxOES"]
+ pub static mut epoxy_glPassThroughxOES:
+ ::std::option::Option<unsafe extern "C" fn(token: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPatchParameterfv"]
+ pub static mut epoxy_glPatchParameterfv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, values: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPatchParameteri"]
+ pub static mut epoxy_glPatchParameteri:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, value: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPatchParameteriEXT"]
+ pub static mut epoxy_glPatchParameteriEXT:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, value: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPatchParameteriOES"]
+ pub static mut epoxy_glPatchParameteriOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, value: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathColorGenNV"]
+ pub static mut epoxy_glPathColorGenNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ color: GLenum,
+ genMode: GLenum,
+ colorFormat: GLenum,
+ coeffs: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathCommandsNV"]
+ pub static mut epoxy_glPathCommandsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ path: GLuint,
+ numCommands: GLsizei,
+ commands: *const GLubyte,
+ numCoords: GLsizei,
+ coordType: GLenum,
+ coords: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathCoordsNV"]
+ pub static mut epoxy_glPathCoordsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ path: GLuint,
+ numCoords: GLsizei,
+ coordType: GLenum,
+ coords: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathCoverDepthFuncNV"]
+ pub static mut epoxy_glPathCoverDepthFuncNV:
+ ::std::option::Option<unsafe extern "C" fn(func: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathDashArrayNV"]
+ pub static mut epoxy_glPathDashArrayNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, dashCount: GLsizei, dashArray: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathFogGenNV"]
+ pub static mut epoxy_glPathFogGenNV:
+ ::std::option::Option<unsafe extern "C" fn(genMode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathGlyphIndexArrayNV"]
+ pub static mut epoxy_glPathGlyphIndexArrayNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ firstPathName: GLuint,
+ fontTarget: GLenum,
+ fontName: *const ::std::os::raw::c_void,
+ fontStyle: GLbitfield,
+ firstGlyphIndex: GLuint,
+ numGlyphs: GLsizei,
+ pathParameterTemplate: GLuint,
+ emScale: GLfloat,
+ ) -> GLenum,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathGlyphIndexRangeNV"]
+ pub static mut epoxy_glPathGlyphIndexRangeNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ fontTarget: GLenum,
+ fontName: *const ::std::os::raw::c_void,
+ fontStyle: GLbitfield,
+ pathParameterTemplate: GLuint,
+ emScale: GLfloat,
+ baseAndCount: GLuint,
+ ) -> GLenum,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathGlyphRangeNV"]
+ pub static mut epoxy_glPathGlyphRangeNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ firstPathName: GLuint,
+ fontTarget: GLenum,
+ fontName: *const ::std::os::raw::c_void,
+ fontStyle: GLbitfield,
+ firstGlyph: GLuint,
+ numGlyphs: GLsizei,
+ handleMissingGlyphs: GLenum,
+ pathParameterTemplate: GLuint,
+ emScale: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathGlyphsNV"]
+ pub static mut epoxy_glPathGlyphsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ firstPathName: GLuint,
+ fontTarget: GLenum,
+ fontName: *const ::std::os::raw::c_void,
+ fontStyle: GLbitfield,
+ numGlyphs: GLsizei,
+ type_: GLenum,
+ charcodes: *const ::std::os::raw::c_void,
+ handleMissingGlyphs: GLenum,
+ pathParameterTemplate: GLuint,
+ emScale: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathMemoryGlyphIndexArrayNV"]
+ pub static mut epoxy_glPathMemoryGlyphIndexArrayNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ firstPathName: GLuint,
+ fontTarget: GLenum,
+ fontSize: GLsizeiptr,
+ fontData: *const ::std::os::raw::c_void,
+ faceIndex: GLsizei,
+ firstGlyphIndex: GLuint,
+ numGlyphs: GLsizei,
+ pathParameterTemplate: GLuint,
+ emScale: GLfloat,
+ ) -> GLenum,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathParameterfNV"]
+ pub static mut epoxy_glPathParameterfNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, pname: GLenum, value: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathParameterfvNV"]
+ pub static mut epoxy_glPathParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, pname: GLenum, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathParameteriNV"]
+ pub static mut epoxy_glPathParameteriNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, pname: GLenum, value: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathParameterivNV"]
+ pub static mut epoxy_glPathParameterivNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, pname: GLenum, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathStencilDepthOffsetNV"]
+ pub static mut epoxy_glPathStencilDepthOffsetNV:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLfloat, units: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathStencilFuncNV"]
+ pub static mut epoxy_glPathStencilFuncNV:
+ ::std::option::Option<unsafe extern "C" fn(func: GLenum, ref_: GLint, mask: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathStringNV"]
+ pub static mut epoxy_glPathStringNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ path: GLuint,
+ format: GLenum,
+ length: GLsizei,
+ pathString: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathSubCommandsNV"]
+ pub static mut epoxy_glPathSubCommandsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ path: GLuint,
+ commandStart: GLsizei,
+ commandsToDelete: GLsizei,
+ numCommands: GLsizei,
+ commands: *const GLubyte,
+ numCoords: GLsizei,
+ coordType: GLenum,
+ coords: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathSubCoordsNV"]
+ pub static mut epoxy_glPathSubCoordsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ path: GLuint,
+ coordStart: GLsizei,
+ numCoords: GLsizei,
+ coordType: GLenum,
+ coords: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPathTexGenNV"]
+ pub static mut epoxy_glPathTexGenNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ texCoordSet: GLenum,
+ genMode: GLenum,
+ components: GLint,
+ coeffs: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPauseTransformFeedback"]
+ pub static mut epoxy_glPauseTransformFeedback: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPauseTransformFeedbackNV"]
+ pub static mut epoxy_glPauseTransformFeedbackNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelDataRangeNV"]
+ pub static mut epoxy_glPixelDataRangeNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ length: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelMapfv"]
+ pub static mut epoxy_glPixelMapfv: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, mapsize: GLsizei, values: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelMapuiv"]
+ pub static mut epoxy_glPixelMapuiv: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, mapsize: GLsizei, values: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelMapusv"]
+ pub static mut epoxy_glPixelMapusv: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, mapsize: GLsizei, values: *const GLushort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelMapx"]
+ pub static mut epoxy_glPixelMapx: ::std::option::Option<
+ unsafe extern "C" fn(map: GLenum, size: GLint, values: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelStoref"]
+ pub static mut epoxy_glPixelStoref:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelStorei"]
+ pub static mut epoxy_glPixelStorei:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelStorex"]
+ pub static mut epoxy_glPixelStorex:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTexGenParameterfSGIS"]
+ pub static mut epoxy_glPixelTexGenParameterfSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTexGenParameterfvSGIS"]
+ pub static mut epoxy_glPixelTexGenParameterfvSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTexGenParameteriSGIS"]
+ pub static mut epoxy_glPixelTexGenParameteriSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTexGenParameterivSGIS"]
+ pub static mut epoxy_glPixelTexGenParameterivSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTexGenSGIX"]
+ pub static mut epoxy_glPixelTexGenSGIX:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTransferf"]
+ pub static mut epoxy_glPixelTransferf:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTransferi"]
+ pub static mut epoxy_glPixelTransferi:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTransferxOES"]
+ pub static mut epoxy_glPixelTransferxOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTransformParameterfEXT"]
+ pub static mut epoxy_glPixelTransformParameterfEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTransformParameterfvEXT"]
+ pub static mut epoxy_glPixelTransformParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTransformParameteriEXT"]
+ pub static mut epoxy_glPixelTransformParameteriEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelTransformParameterivEXT"]
+ pub static mut epoxy_glPixelTransformParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelZoom"]
+ pub static mut epoxy_glPixelZoom:
+ ::std::option::Option<unsafe extern "C" fn(xfactor: GLfloat, yfactor: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPixelZoomxOES"]
+ pub static mut epoxy_glPixelZoomxOES:
+ ::std::option::Option<unsafe extern "C" fn(xfactor: GLfixed, yfactor: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointAlongPathNV"]
+ pub static mut epoxy_glPointAlongPathNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ path: GLuint,
+ startSegment: GLsizei,
+ numSegments: GLsizei,
+ distance: GLfloat,
+ x: *mut GLfloat,
+ y: *mut GLfloat,
+ tangentX: *mut GLfloat,
+ tangentY: *mut GLfloat,
+ ) -> GLboolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterf"]
+ pub static mut epoxy_glPointParameterf:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterfARB"]
+ pub static mut epoxy_glPointParameterfARB:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterfEXT"]
+ pub static mut epoxy_glPointParameterfEXT:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterfSGIS"]
+ pub static mut epoxy_glPointParameterfSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterfv"]
+ pub static mut epoxy_glPointParameterfv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterfvARB"]
+ pub static mut epoxy_glPointParameterfvARB:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterfvEXT"]
+ pub static mut epoxy_glPointParameterfvEXT:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterfvSGIS"]
+ pub static mut epoxy_glPointParameterfvSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameteri"]
+ pub static mut epoxy_glPointParameteri:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameteriNV"]
+ pub static mut epoxy_glPointParameteriNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameteriv"]
+ pub static mut epoxy_glPointParameteriv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterivNV"]
+ pub static mut epoxy_glPointParameterivNV:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterx"]
+ pub static mut epoxy_glPointParameterx:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterxOES"]
+ pub static mut epoxy_glPointParameterxOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterxv"]
+ pub static mut epoxy_glPointParameterxv:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointParameterxvOES"]
+ pub static mut epoxy_glPointParameterxvOES:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointSize"]
+ pub static mut epoxy_glPointSize: ::std::option::Option<unsafe extern "C" fn(size: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointSizePointerOES"]
+ pub static mut epoxy_glPointSizePointerOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointSizex"]
+ pub static mut epoxy_glPointSizex: ::std::option::Option<unsafe extern "C" fn(size: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPointSizexOES"]
+ pub static mut epoxy_glPointSizexOES:
+ ::std::option::Option<unsafe extern "C" fn(size: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPollAsyncSGIX"]
+ pub static mut epoxy_glPollAsyncSGIX:
+ ::std::option::Option<unsafe extern "C" fn(markerp: *mut GLuint) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPollInstrumentsSGIX"]
+ pub static mut epoxy_glPollInstrumentsSGIX:
+ ::std::option::Option<unsafe extern "C" fn(marker_p: *mut GLint) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPolygonMode"]
+ pub static mut epoxy_glPolygonMode:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPolygonModeNV"]
+ pub static mut epoxy_glPolygonModeNV:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPolygonOffset"]
+ pub static mut epoxy_glPolygonOffset:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLfloat, units: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPolygonOffsetClampEXT"]
+ pub static mut epoxy_glPolygonOffsetClampEXT: ::std::option::Option<
+ unsafe extern "C" fn(factor: GLfloat, units: GLfloat, clamp: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPolygonOffsetEXT"]
+ pub static mut epoxy_glPolygonOffsetEXT:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLfloat, bias: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPolygonOffsetx"]
+ pub static mut epoxy_glPolygonOffsetx:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLfixed, units: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPolygonOffsetxOES"]
+ pub static mut epoxy_glPolygonOffsetxOES:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLfixed, units: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPolygonStipple"]
+ pub static mut epoxy_glPolygonStipple:
+ ::std::option::Option<unsafe extern "C" fn(mask: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPopAttrib"]
+ pub static mut epoxy_glPopAttrib: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPopClientAttrib"]
+ pub static mut epoxy_glPopClientAttrib: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPopDebugGroup"]
+ pub static mut epoxy_glPopDebugGroup: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPopDebugGroupKHR"]
+ pub static mut epoxy_glPopDebugGroupKHR: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPopGroupMarkerEXT"]
+ pub static mut epoxy_glPopGroupMarkerEXT: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPopMatrix"]
+ pub static mut epoxy_glPopMatrix: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPopName"]
+ pub static mut epoxy_glPopName: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPresentFrameDualFillNV"]
+ pub static mut epoxy_glPresentFrameDualFillNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_slot: GLuint,
+ minPresentTime: GLuint64EXT,
+ beginPresentTimeId: GLuint,
+ presentDurationId: GLuint,
+ type_: GLenum,
+ target0: GLenum,
+ fill0: GLuint,
+ target1: GLenum,
+ fill1: GLuint,
+ target2: GLenum,
+ fill2: GLuint,
+ target3: GLenum,
+ fill3: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPresentFrameKeyedNV"]
+ pub static mut epoxy_glPresentFrameKeyedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_slot: GLuint,
+ minPresentTime: GLuint64EXT,
+ beginPresentTimeId: GLuint,
+ presentDurationId: GLuint,
+ type_: GLenum,
+ target0: GLenum,
+ fill0: GLuint,
+ key0: GLuint,
+ target1: GLenum,
+ fill1: GLuint,
+ key1: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrimitiveBoundingBox"]
+ pub static mut epoxy_glPrimitiveBoundingBox: ::std::option::Option<
+ unsafe extern "C" fn(
+ minX: GLfloat,
+ minY: GLfloat,
+ minZ: GLfloat,
+ minW: GLfloat,
+ maxX: GLfloat,
+ maxY: GLfloat,
+ maxZ: GLfloat,
+ maxW: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrimitiveBoundingBoxARB"]
+ pub static mut epoxy_glPrimitiveBoundingBoxARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ minX: GLfloat,
+ minY: GLfloat,
+ minZ: GLfloat,
+ minW: GLfloat,
+ maxX: GLfloat,
+ maxY: GLfloat,
+ maxZ: GLfloat,
+ maxW: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrimitiveBoundingBoxEXT"]
+ pub static mut epoxy_glPrimitiveBoundingBoxEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ minX: GLfloat,
+ minY: GLfloat,
+ minZ: GLfloat,
+ minW: GLfloat,
+ maxX: GLfloat,
+ maxY: GLfloat,
+ maxZ: GLfloat,
+ maxW: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrimitiveBoundingBoxOES"]
+ pub static mut epoxy_glPrimitiveBoundingBoxOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ minX: GLfloat,
+ minY: GLfloat,
+ minZ: GLfloat,
+ minW: GLfloat,
+ maxX: GLfloat,
+ maxY: GLfloat,
+ maxZ: GLfloat,
+ maxW: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrimitiveRestartIndex"]
+ pub static mut epoxy_glPrimitiveRestartIndex:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrimitiveRestartIndexNV"]
+ pub static mut epoxy_glPrimitiveRestartIndexNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrimitiveRestartNV"]
+ pub static mut epoxy_glPrimitiveRestartNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrioritizeTextures"]
+ pub static mut epoxy_glPrioritizeTextures: ::std::option::Option<
+ unsafe extern "C" fn(n: GLsizei, textures: *const GLuint, priorities: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrioritizeTexturesEXT"]
+ pub static mut epoxy_glPrioritizeTexturesEXT: ::std::option::Option<
+ unsafe extern "C" fn(n: GLsizei, textures: *const GLuint, priorities: *const GLclampf),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPrioritizeTexturesxOES"]
+ pub static mut epoxy_glPrioritizeTexturesxOES: ::std::option::Option<
+ unsafe extern "C" fn(n: GLsizei, textures: *const GLuint, priorities: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramBinary"]
+ pub static mut epoxy_glProgramBinary: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ binaryFormat: GLenum,
+ binary: *const ::std::os::raw::c_void,
+ length: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramBinaryOES"]
+ pub static mut epoxy_glProgramBinaryOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ binaryFormat: GLenum,
+ binary: *const ::std::os::raw::c_void,
+ length: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramBufferParametersIivNV"]
+ pub static mut epoxy_glProgramBufferParametersIivNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ bindingIndex: GLuint,
+ wordIndex: GLuint,
+ count: GLsizei,
+ params: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramBufferParametersIuivNV"]
+ pub static mut epoxy_glProgramBufferParametersIuivNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ bindingIndex: GLuint,
+ wordIndex: GLuint,
+ count: GLsizei,
+ params: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramBufferParametersfvNV"]
+ pub static mut epoxy_glProgramBufferParametersfvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ bindingIndex: GLuint,
+ wordIndex: GLuint,
+ count: GLsizei,
+ params: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameter4dARB"]
+ pub static mut epoxy_glProgramEnvParameter4dARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ x: GLdouble,
+ y: GLdouble,
+ z: GLdouble,
+ w: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameter4dvARB"]
+ pub static mut epoxy_glProgramEnvParameter4dvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameter4fARB"]
+ pub static mut epoxy_glProgramEnvParameter4fARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ w: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameter4fvARB"]
+ pub static mut epoxy_glProgramEnvParameter4fvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameterI4iNV"]
+ pub static mut epoxy_glProgramEnvParameterI4iNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameterI4ivNV"]
+ pub static mut epoxy_glProgramEnvParameterI4ivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameterI4uiNV"]
+ pub static mut epoxy_glProgramEnvParameterI4uiNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ x: GLuint,
+ y: GLuint,
+ z: GLuint,
+ w: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameterI4uivNV"]
+ pub static mut epoxy_glProgramEnvParameterI4uivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParameters4fvEXT"]
+ pub static mut epoxy_glProgramEnvParameters4fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, count: GLsizei, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParametersI4ivNV"]
+ pub static mut epoxy_glProgramEnvParametersI4ivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, count: GLsizei, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramEnvParametersI4uivNV"]
+ pub static mut epoxy_glProgramEnvParametersI4uivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, count: GLsizei, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameter4dARB"]
+ pub static mut epoxy_glProgramLocalParameter4dARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ x: GLdouble,
+ y: GLdouble,
+ z: GLdouble,
+ w: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameter4dvARB"]
+ pub static mut epoxy_glProgramLocalParameter4dvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameter4fARB"]
+ pub static mut epoxy_glProgramLocalParameter4fARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ w: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameter4fvARB"]
+ pub static mut epoxy_glProgramLocalParameter4fvARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameterI4iNV"]
+ pub static mut epoxy_glProgramLocalParameterI4iNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameterI4ivNV"]
+ pub static mut epoxy_glProgramLocalParameterI4ivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameterI4uiNV"]
+ pub static mut epoxy_glProgramLocalParameterI4uiNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ x: GLuint,
+ y: GLuint,
+ z: GLuint,
+ w: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameterI4uivNV"]
+ pub static mut epoxy_glProgramLocalParameterI4uivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParameters4fvEXT"]
+ pub static mut epoxy_glProgramLocalParameters4fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, count: GLsizei, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParametersI4ivNV"]
+ pub static mut epoxy_glProgramLocalParametersI4ivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, count: GLsizei, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramLocalParametersI4uivNV"]
+ pub static mut epoxy_glProgramLocalParametersI4uivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, count: GLsizei, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramNamedParameter4dNV"]
+ pub static mut epoxy_glProgramNamedParameter4dNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ id: GLuint,
+ len: GLsizei,
+ name: *const GLubyte,
+ x: GLdouble,
+ y: GLdouble,
+ z: GLdouble,
+ w: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramNamedParameter4dvNV"]
+ pub static mut epoxy_glProgramNamedParameter4dvNV: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, len: GLsizei, name: *const GLubyte, v: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramNamedParameter4fNV"]
+ pub static mut epoxy_glProgramNamedParameter4fNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ id: GLuint,
+ len: GLsizei,
+ name: *const GLubyte,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ w: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramNamedParameter4fvNV"]
+ pub static mut epoxy_glProgramNamedParameter4fvNV: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, len: GLsizei, name: *const GLubyte, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameter4dNV"]
+ pub static mut epoxy_glProgramParameter4dNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ x: GLdouble,
+ y: GLdouble,
+ z: GLdouble,
+ w: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameter4dvNV"]
+ pub static mut epoxy_glProgramParameter4dvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, v: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameter4fNV"]
+ pub static mut epoxy_glProgramParameter4fNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ index: GLuint,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ w: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameter4fvNV"]
+ pub static mut epoxy_glProgramParameter4fvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameteri"]
+ pub static mut epoxy_glProgramParameteri:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, pname: GLenum, value: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameteriARB"]
+ pub static mut epoxy_glProgramParameteriARB:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, pname: GLenum, value: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameteriEXT"]
+ pub static mut epoxy_glProgramParameteriEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, pname: GLenum, value: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameters4dvNV"]
+ pub static mut epoxy_glProgramParameters4dvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, count: GLsizei, v: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramParameters4fvNV"]
+ pub static mut epoxy_glProgramParameters4fvNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, index: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramPathFragmentInputGenNV"]
+ pub static mut epoxy_glProgramPathFragmentInputGenNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ genMode: GLenum,
+ components: GLint,
+ coeffs: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramStringARB"]
+ pub static mut epoxy_glProgramStringARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ format: GLenum,
+ len: GLsizei,
+ string: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramSubroutineParametersuivNV"]
+ pub static mut epoxy_glProgramSubroutineParametersuivNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, count: GLsizei, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1d"]
+ pub static mut epoxy_glProgramUniform1d:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1dEXT"]
+ pub static mut epoxy_glProgramUniform1dEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, x: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1dv"]
+ pub static mut epoxy_glProgramUniform1dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1dvEXT"]
+ pub static mut epoxy_glProgramUniform1dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1f"]
+ pub static mut epoxy_glProgramUniform1f:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1fEXT"]
+ pub static mut epoxy_glProgramUniform1fEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1fv"]
+ pub static mut epoxy_glProgramUniform1fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1fvEXT"]
+ pub static mut epoxy_glProgramUniform1fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1i"]
+ pub static mut epoxy_glProgramUniform1i:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1i64ARB"]
+ pub static mut epoxy_glProgramUniform1i64ARB:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, x: GLint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1i64NV"]
+ pub static mut epoxy_glProgramUniform1i64NV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, x: GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1i64vARB"]
+ pub static mut epoxy_glProgramUniform1i64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1i64vNV"]
+ pub static mut epoxy_glProgramUniform1i64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1iEXT"]
+ pub static mut epoxy_glProgramUniform1iEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1iv"]
+ pub static mut epoxy_glProgramUniform1iv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1ivEXT"]
+ pub static mut epoxy_glProgramUniform1ivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1ui"]
+ pub static mut epoxy_glProgramUniform1ui:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1ui64ARB"]
+ pub static mut epoxy_glProgramUniform1ui64ARB:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, x: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1ui64NV"]
+ pub static mut epoxy_glProgramUniform1ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, x: GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1ui64vARB"]
+ pub static mut epoxy_glProgramUniform1ui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1ui64vNV"]
+ pub static mut epoxy_glProgramUniform1ui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1uiEXT"]
+ pub static mut epoxy_glProgramUniform1uiEXT:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1uiv"]
+ pub static mut epoxy_glProgramUniform1uiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform1uivEXT"]
+ pub static mut epoxy_glProgramUniform1uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2d"]
+ pub static mut epoxy_glProgramUniform2d: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLdouble, v1: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2dEXT"]
+ pub static mut epoxy_glProgramUniform2dEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, x: GLdouble, y: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2dv"]
+ pub static mut epoxy_glProgramUniform2dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2dvEXT"]
+ pub static mut epoxy_glProgramUniform2dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2f"]
+ pub static mut epoxy_glProgramUniform2f: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLfloat, v1: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2fEXT"]
+ pub static mut epoxy_glProgramUniform2fEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLfloat, v1: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2fv"]
+ pub static mut epoxy_glProgramUniform2fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2fvEXT"]
+ pub static mut epoxy_glProgramUniform2fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2i"]
+ pub static mut epoxy_glProgramUniform2i: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLint, v1: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2i64ARB"]
+ pub static mut epoxy_glProgramUniform2i64ARB: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, x: GLint64, y: GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2i64NV"]
+ pub static mut epoxy_glProgramUniform2i64NV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, x: GLint64EXT, y: GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2i64vARB"]
+ pub static mut epoxy_glProgramUniform2i64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2i64vNV"]
+ pub static mut epoxy_glProgramUniform2i64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2iEXT"]
+ pub static mut epoxy_glProgramUniform2iEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLint, v1: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2iv"]
+ pub static mut epoxy_glProgramUniform2iv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2ivEXT"]
+ pub static mut epoxy_glProgramUniform2ivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2ui"]
+ pub static mut epoxy_glProgramUniform2ui: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLuint, v1: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2ui64ARB"]
+ pub static mut epoxy_glProgramUniform2ui64ARB: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, x: GLuint64, y: GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2ui64NV"]
+ pub static mut epoxy_glProgramUniform2ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, x: GLuint64EXT, y: GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2ui64vARB"]
+ pub static mut epoxy_glProgramUniform2ui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2ui64vNV"]
+ pub static mut epoxy_glProgramUniform2ui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2uiEXT"]
+ pub static mut epoxy_glProgramUniform2uiEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLuint, v1: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2uiv"]
+ pub static mut epoxy_glProgramUniform2uiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform2uivEXT"]
+ pub static mut epoxy_glProgramUniform2uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3d"]
+ pub static mut epoxy_glProgramUniform3d: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLdouble,
+ v1: GLdouble,
+ v2: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3dEXT"]
+ pub static mut epoxy_glProgramUniform3dEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLdouble,
+ y: GLdouble,
+ z: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3dv"]
+ pub static mut epoxy_glProgramUniform3dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3dvEXT"]
+ pub static mut epoxy_glProgramUniform3dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3f"]
+ pub static mut epoxy_glProgramUniform3f: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLfloat,
+ v1: GLfloat,
+ v2: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3fEXT"]
+ pub static mut epoxy_glProgramUniform3fEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLfloat,
+ v1: GLfloat,
+ v2: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3fv"]
+ pub static mut epoxy_glProgramUniform3fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3fvEXT"]
+ pub static mut epoxy_glProgramUniform3fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3i"]
+ pub static mut epoxy_glProgramUniform3i: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLint, v1: GLint, v2: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3i64ARB"]
+ pub static mut epoxy_glProgramUniform3i64ARB: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, x: GLint64, y: GLint64, z: GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3i64NV"]
+ pub static mut epoxy_glProgramUniform3i64NV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLint64EXT,
+ y: GLint64EXT,
+ z: GLint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3i64vARB"]
+ pub static mut epoxy_glProgramUniform3i64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3i64vNV"]
+ pub static mut epoxy_glProgramUniform3i64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3iEXT"]
+ pub static mut epoxy_glProgramUniform3iEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLint, v1: GLint, v2: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3iv"]
+ pub static mut epoxy_glProgramUniform3iv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3ivEXT"]
+ pub static mut epoxy_glProgramUniform3ivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3ui"]
+ pub static mut epoxy_glProgramUniform3ui: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3ui64ARB"]
+ pub static mut epoxy_glProgramUniform3ui64ARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLuint64,
+ y: GLuint64,
+ z: GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3ui64NV"]
+ pub static mut epoxy_glProgramUniform3ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLuint64EXT,
+ y: GLuint64EXT,
+ z: GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3ui64vARB"]
+ pub static mut epoxy_glProgramUniform3ui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3ui64vNV"]
+ pub static mut epoxy_glProgramUniform3ui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3uiEXT"]
+ pub static mut epoxy_glProgramUniform3uiEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3uiv"]
+ pub static mut epoxy_glProgramUniform3uiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform3uivEXT"]
+ pub static mut epoxy_glProgramUniform3uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4d"]
+ pub static mut epoxy_glProgramUniform4d: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLdouble,
+ v1: GLdouble,
+ v2: GLdouble,
+ v3: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4dEXT"]
+ pub static mut epoxy_glProgramUniform4dEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLdouble,
+ y: GLdouble,
+ z: GLdouble,
+ w: GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4dv"]
+ pub static mut epoxy_glProgramUniform4dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4dvEXT"]
+ pub static mut epoxy_glProgramUniform4dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4f"]
+ pub static mut epoxy_glProgramUniform4f: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLfloat,
+ v1: GLfloat,
+ v2: GLfloat,
+ v3: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4fEXT"]
+ pub static mut epoxy_glProgramUniform4fEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLfloat,
+ v1: GLfloat,
+ v2: GLfloat,
+ v3: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4fv"]
+ pub static mut epoxy_glProgramUniform4fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4fvEXT"]
+ pub static mut epoxy_glProgramUniform4fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4i"]
+ pub static mut epoxy_glProgramUniform4i: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLint,
+ v1: GLint,
+ v2: GLint,
+ v3: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4i64ARB"]
+ pub static mut epoxy_glProgramUniform4i64ARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLint64,
+ y: GLint64,
+ z: GLint64,
+ w: GLint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4i64NV"]
+ pub static mut epoxy_glProgramUniform4i64NV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLint64EXT,
+ y: GLint64EXT,
+ z: GLint64EXT,
+ w: GLint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4i64vARB"]
+ pub static mut epoxy_glProgramUniform4i64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4i64vNV"]
+ pub static mut epoxy_glProgramUniform4i64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4iEXT"]
+ pub static mut epoxy_glProgramUniform4iEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLint,
+ v1: GLint,
+ v2: GLint,
+ v3: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4iv"]
+ pub static mut epoxy_glProgramUniform4iv: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4ivEXT"]
+ pub static mut epoxy_glProgramUniform4ivEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4ui"]
+ pub static mut epoxy_glProgramUniform4ui: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLuint,
+ v1: GLuint,
+ v2: GLuint,
+ v3: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4ui64ARB"]
+ pub static mut epoxy_glProgramUniform4ui64ARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLuint64,
+ y: GLuint64,
+ z: GLuint64,
+ w: GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4ui64NV"]
+ pub static mut epoxy_glProgramUniform4ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ x: GLuint64EXT,
+ y: GLuint64EXT,
+ z: GLuint64EXT,
+ w: GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4ui64vARB"]
+ pub static mut epoxy_glProgramUniform4ui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4ui64vNV"]
+ pub static mut epoxy_glProgramUniform4ui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4uiEXT"]
+ pub static mut epoxy_glProgramUniform4uiEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ v0: GLuint,
+ v1: GLuint,
+ v2: GLuint,
+ v3: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4uiv"]
+ pub static mut epoxy_glProgramUniform4uiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniform4uivEXT"]
+ pub static mut epoxy_glProgramUniform4uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformHandleui64ARB"]
+ pub static mut epoxy_glProgramUniformHandleui64ARB: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, value: GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformHandleui64IMG"]
+ pub static mut epoxy_glProgramUniformHandleui64IMG: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, value: GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformHandleui64NV"]
+ pub static mut epoxy_glProgramUniformHandleui64NV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, value: GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformHandleui64vARB"]
+ pub static mut epoxy_glProgramUniformHandleui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ values: *const GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformHandleui64vIMG"]
+ pub static mut epoxy_glProgramUniformHandleui64vIMG: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ values: *const GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformHandleui64vNV"]
+ pub static mut epoxy_glProgramUniformHandleui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ values: *const GLuint64,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2dv"]
+ pub static mut epoxy_glProgramUniformMatrix2dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix2dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2fv"]
+ pub static mut epoxy_glProgramUniformMatrix2fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix2fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2x3dv"]
+ pub static mut epoxy_glProgramUniformMatrix2x3dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2x3dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix2x3dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2x3fv"]
+ pub static mut epoxy_glProgramUniformMatrix2x3fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2x3fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix2x3fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2x4dv"]
+ pub static mut epoxy_glProgramUniformMatrix2x4dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2x4dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix2x4dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2x4fv"]
+ pub static mut epoxy_glProgramUniformMatrix2x4fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix2x4fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix2x4fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3dv"]
+ pub static mut epoxy_glProgramUniformMatrix3dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix3dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3fv"]
+ pub static mut epoxy_glProgramUniformMatrix3fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix3fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3x2dv"]
+ pub static mut epoxy_glProgramUniformMatrix3x2dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3x2dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix3x2dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3x2fv"]
+ pub static mut epoxy_glProgramUniformMatrix3x2fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3x2fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix3x2fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3x4dv"]
+ pub static mut epoxy_glProgramUniformMatrix3x4dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3x4dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix3x4dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3x4fv"]
+ pub static mut epoxy_glProgramUniformMatrix3x4fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix3x4fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix3x4fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4dv"]
+ pub static mut epoxy_glProgramUniformMatrix4dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix4dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4fv"]
+ pub static mut epoxy_glProgramUniformMatrix4fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix4fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4x2dv"]
+ pub static mut epoxy_glProgramUniformMatrix4x2dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4x2dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix4x2dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4x2fv"]
+ pub static mut epoxy_glProgramUniformMatrix4x2fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4x2fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix4x2fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4x3dv"]
+ pub static mut epoxy_glProgramUniformMatrix4x3dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4x3dvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix4x3dvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4x3fv"]
+ pub static mut epoxy_glProgramUniformMatrix4x3fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformMatrix4x3fvEXT"]
+ pub static mut epoxy_glProgramUniformMatrix4x3fvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformui64NV"]
+ pub static mut epoxy_glProgramUniformui64NV: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, value: GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramUniformui64vNV"]
+ pub static mut epoxy_glProgramUniformui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ location: GLint,
+ count: GLsizei,
+ value: *const GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProgramVertexLimitNV"]
+ pub static mut epoxy_glProgramVertexLimitNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, limit: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProvokingVertex"]
+ pub static mut epoxy_glProvokingVertex:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glProvokingVertexEXT"]
+ pub static mut epoxy_glProvokingVertexEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPushAttrib"]
+ pub static mut epoxy_glPushAttrib:
+ ::std::option::Option<unsafe extern "C" fn(mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPushClientAttrib"]
+ pub static mut epoxy_glPushClientAttrib:
+ ::std::option::Option<unsafe extern "C" fn(mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPushClientAttribDefaultEXT"]
+ pub static mut epoxy_glPushClientAttribDefaultEXT:
+ ::std::option::Option<unsafe extern "C" fn(mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPushDebugGroup"]
+ pub static mut epoxy_glPushDebugGroup: ::std::option::Option<
+ unsafe extern "C" fn(source: GLenum, id: GLuint, length: GLsizei, message: *const GLchar),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPushDebugGroupKHR"]
+ pub static mut epoxy_glPushDebugGroupKHR: ::std::option::Option<
+ unsafe extern "C" fn(source: GLenum, id: GLuint, length: GLsizei, message: *const GLchar),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPushGroupMarkerEXT"]
+ pub static mut epoxy_glPushGroupMarkerEXT:
+ ::std::option::Option<unsafe extern "C" fn(length: GLsizei, marker: *const GLchar)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPushMatrix"]
+ pub static mut epoxy_glPushMatrix: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glPushName"]
+ pub static mut epoxy_glPushName: ::std::option::Option<unsafe extern "C" fn(name: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glQueryCounter"]
+ pub static mut epoxy_glQueryCounter:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glQueryCounterEXT"]
+ pub static mut epoxy_glQueryCounterEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glQueryMatrixxOES"]
+ pub static mut epoxy_glQueryMatrixxOES: ::std::option::Option<
+ unsafe extern "C" fn(mantissa: *mut GLfixed, exponent: *mut GLint) -> GLbitfield,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glQueryObjectParameteruiAMD"]
+ pub static mut epoxy_glQueryObjectParameteruiAMD: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, id: GLuint, pname: GLenum, param: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2d"]
+ pub static mut epoxy_glRasterPos2d:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2dv"]
+ pub static mut epoxy_glRasterPos2dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2f"]
+ pub static mut epoxy_glRasterPos2f:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2fv"]
+ pub static mut epoxy_glRasterPos2fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2i"]
+ pub static mut epoxy_glRasterPos2i:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2iv"]
+ pub static mut epoxy_glRasterPos2iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2s"]
+ pub static mut epoxy_glRasterPos2s:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2sv"]
+ pub static mut epoxy_glRasterPos2sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2xOES"]
+ pub static mut epoxy_glRasterPos2xOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos2xvOES"]
+ pub static mut epoxy_glRasterPos2xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3d"]
+ pub static mut epoxy_glRasterPos3d:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3dv"]
+ pub static mut epoxy_glRasterPos3dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3f"]
+ pub static mut epoxy_glRasterPos3f:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3fv"]
+ pub static mut epoxy_glRasterPos3fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3i"]
+ pub static mut epoxy_glRasterPos3i:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint, z: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3iv"]
+ pub static mut epoxy_glRasterPos3iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3s"]
+ pub static mut epoxy_glRasterPos3s:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3sv"]
+ pub static mut epoxy_glRasterPos3sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3xOES"]
+ pub static mut epoxy_glRasterPos3xOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed, z: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos3xvOES"]
+ pub static mut epoxy_glRasterPos3xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4d"]
+ pub static mut epoxy_glRasterPos4d: ::std::option::Option<
+ unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4dv"]
+ pub static mut epoxy_glRasterPos4dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4f"]
+ pub static mut epoxy_glRasterPos4f:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4fv"]
+ pub static mut epoxy_glRasterPos4fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4i"]
+ pub static mut epoxy_glRasterPos4i:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint, z: GLint, w: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4iv"]
+ pub static mut epoxy_glRasterPos4iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4s"]
+ pub static mut epoxy_glRasterPos4s:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort, w: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4sv"]
+ pub static mut epoxy_glRasterPos4sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4xOES"]
+ pub static mut epoxy_glRasterPos4xOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed, z: GLfixed, w: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterPos4xvOES"]
+ pub static mut epoxy_glRasterPos4xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRasterSamplesEXT"]
+ pub static mut epoxy_glRasterSamplesEXT: ::std::option::Option<
+ unsafe extern "C" fn(samples: GLuint, fixedsamplelocations: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadBuffer"]
+ pub static mut epoxy_glReadBuffer: ::std::option::Option<unsafe extern "C" fn(src: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadBufferIndexedEXT"]
+ pub static mut epoxy_glReadBufferIndexedEXT:
+ ::std::option::Option<unsafe extern "C" fn(src: GLenum, index: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadBufferNV"]
+ pub static mut epoxy_glReadBufferNV: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadInstrumentsSGIX"]
+ pub static mut epoxy_glReadInstrumentsSGIX:
+ ::std::option::Option<unsafe extern "C" fn(marker: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadPixels"]
+ pub static mut epoxy_glReadPixels: ::std::option::Option<
+ unsafe extern "C" fn(
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadnPixels"]
+ pub static mut epoxy_glReadnPixels: ::std::option::Option<
+ unsafe extern "C" fn(
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadnPixelsARB"]
+ pub static mut epoxy_glReadnPixelsARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadnPixelsEXT"]
+ pub static mut epoxy_glReadnPixelsEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReadnPixelsKHR"]
+ pub static mut epoxy_glReadnPixelsKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ x: GLint,
+ y: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ bufSize: GLsizei,
+ data: *mut ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRectd"]
+ pub static mut epoxy_glRectd: ::std::option::Option<
+ unsafe extern "C" fn(x1: GLdouble, y1: GLdouble, x2: GLdouble, y2: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRectdv"]
+ pub static mut epoxy_glRectdv:
+ ::std::option::Option<unsafe extern "C" fn(v1: *const GLdouble, v2: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRectf"]
+ pub static mut epoxy_glRectf: ::std::option::Option<
+ unsafe extern "C" fn(x1: GLfloat, y1: GLfloat, x2: GLfloat, y2: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRectfv"]
+ pub static mut epoxy_glRectfv:
+ ::std::option::Option<unsafe extern "C" fn(v1: *const GLfloat, v2: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRecti"]
+ pub static mut epoxy_glRecti:
+ ::std::option::Option<unsafe extern "C" fn(x1: GLint, y1: GLint, x2: GLint, y2: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRectiv"]
+ pub static mut epoxy_glRectiv:
+ ::std::option::Option<unsafe extern "C" fn(v1: *const GLint, v2: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRects"]
+ pub static mut epoxy_glRects: ::std::option::Option<
+ unsafe extern "C" fn(x1: GLshort, y1: GLshort, x2: GLshort, y2: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRectsv"]
+ pub static mut epoxy_glRectsv:
+ ::std::option::Option<unsafe extern "C" fn(v1: *const GLshort, v2: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRectxOES"]
+ pub static mut epoxy_glRectxOES: ::std::option::Option<
+ unsafe extern "C" fn(x1: GLfixed, y1: GLfixed, x2: GLfixed, y2: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRectxvOES"]
+ pub static mut epoxy_glRectxvOES:
+ ::std::option::Option<unsafe extern "C" fn(v1: *const GLfixed, v2: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReferencePlaneSGIX"]
+ pub static mut epoxy_glReferencePlaneSGIX:
+ ::std::option::Option<unsafe extern "C" fn(equation: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReleaseShaderCompiler"]
+ pub static mut epoxy_glReleaseShaderCompiler: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderMode"]
+ pub static mut epoxy_glRenderMode:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum) -> GLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorage"]
+ pub static mut epoxy_glRenderbufferStorage: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageEXT"]
+ pub static mut epoxy_glRenderbufferStorageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageMultisample"]
+ pub static mut epoxy_glRenderbufferStorageMultisample: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageMultisampleANGLE"]
+ pub static mut epoxy_glRenderbufferStorageMultisampleANGLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageMultisampleAPPLE"]
+ pub static mut epoxy_glRenderbufferStorageMultisampleAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageMultisampleCoverageNV"]
+ pub static mut epoxy_glRenderbufferStorageMultisampleCoverageNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ coverageSamples: GLsizei,
+ colorSamples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageMultisampleEXT"]
+ pub static mut epoxy_glRenderbufferStorageMultisampleEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageMultisampleIMG"]
+ pub static mut epoxy_glRenderbufferStorageMultisampleIMG: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageMultisampleNV"]
+ pub static mut epoxy_glRenderbufferStorageMultisampleNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRenderbufferStorageOES"]
+ pub static mut epoxy_glRenderbufferStorageOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodePointerSUN"]
+ pub static mut epoxy_glReplacementCodePointerSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeubSUN"]
+ pub static mut epoxy_glReplacementCodeubSUN:
+ ::std::option::Option<unsafe extern "C" fn(code: GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeubvSUN"]
+ pub static mut epoxy_glReplacementCodeubvSUN:
+ ::std::option::Option<unsafe extern "C" fn(code: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiColor3fVertex3fSUN"]
+ pub static mut epoxy_glReplacementCodeuiColor3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: GLuint,
+ r: GLfloat,
+ g: GLfloat,
+ b: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiColor3fVertex3fvSUN"]
+ pub static mut epoxy_glReplacementCodeuiColor3fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(rc: *const GLuint, c: *const GLfloat, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiColor4fNormal3fVertex3fSUN"]
+ pub static mut epoxy_glReplacementCodeuiColor4fNormal3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: GLuint,
+ r: GLfloat,
+ g: GLfloat,
+ b: GLfloat,
+ a: GLfloat,
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiColor4fNormal3fVertex3fvSUN"]
+ pub static mut epoxy_glReplacementCodeuiColor4fNormal3fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: *const GLuint,
+ c: *const GLfloat,
+ n: *const GLfloat,
+ v: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiColor4ubVertex3fSUN"]
+ pub static mut epoxy_glReplacementCodeuiColor4ubVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: GLuint,
+ r: GLubyte,
+ g: GLubyte,
+ b: GLubyte,
+ a: GLubyte,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiColor4ubVertex3fvSUN"]
+ pub static mut epoxy_glReplacementCodeuiColor4ubVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(rc: *const GLuint, c: *const GLubyte, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiNormal3fVertex3fSUN"]
+ pub static mut epoxy_glReplacementCodeuiNormal3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: GLuint,
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiNormal3fVertex3fvSUN"]
+ pub static mut epoxy_glReplacementCodeuiNormal3fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(rc: *const GLuint, n: *const GLfloat, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiSUN"]
+ pub static mut epoxy_glReplacementCodeuiSUN:
+ ::std::option::Option<unsafe extern "C" fn(code: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN"]
+ pub static mut epoxy_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN:
+ ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: GLuint,
+ s: GLfloat,
+ t: GLfloat,
+ r: GLfloat,
+ g: GLfloat,
+ b: GLfloat,
+ a: GLfloat,
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN"]
+ pub static mut epoxy_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN:
+ ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: *const GLuint,
+ tc: *const GLfloat,
+ c: *const GLfloat,
+ n: *const GLfloat,
+ v: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN"]
+ pub static mut epoxy_glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: GLuint,
+ s: GLfloat,
+ t: GLfloat,
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN"]
+ pub static mut epoxy_glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: *const GLuint,
+ tc: *const GLfloat,
+ n: *const GLfloat,
+ v: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiTexCoord2fVertex3fSUN"]
+ pub static mut epoxy_glReplacementCodeuiTexCoord2fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ rc: GLuint,
+ s: GLfloat,
+ t: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiTexCoord2fVertex3fvSUN"]
+ pub static mut epoxy_glReplacementCodeuiTexCoord2fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(rc: *const GLuint, tc: *const GLfloat, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiVertex3fSUN"]
+ pub static mut epoxy_glReplacementCodeuiVertex3fSUN:
+ ::std::option::Option<unsafe extern "C" fn(rc: GLuint, x: GLfloat, y: GLfloat, z: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuiVertex3fvSUN"]
+ pub static mut epoxy_glReplacementCodeuiVertex3fvSUN:
+ ::std::option::Option<unsafe extern "C" fn(rc: *const GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeuivSUN"]
+ pub static mut epoxy_glReplacementCodeuivSUN:
+ ::std::option::Option<unsafe extern "C" fn(code: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeusSUN"]
+ pub static mut epoxy_glReplacementCodeusSUN:
+ ::std::option::Option<unsafe extern "C" fn(code: GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glReplacementCodeusvSUN"]
+ pub static mut epoxy_glReplacementCodeusvSUN:
+ ::std::option::Option<unsafe extern "C" fn(code: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRequestResidentProgramsNV"]
+ pub static mut epoxy_glRequestResidentProgramsNV:
+ ::std::option::Option<unsafe extern "C" fn(n: GLsizei, programs: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResetHistogram"]
+ pub static mut epoxy_glResetHistogram:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResetHistogramEXT"]
+ pub static mut epoxy_glResetHistogramEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResetMinmax"]
+ pub static mut epoxy_glResetMinmax: ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResetMinmaxEXT"]
+ pub static mut epoxy_glResetMinmaxEXT:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResizeBuffersMESA"]
+ pub static mut epoxy_glResizeBuffersMESA: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResolveDepthValuesNV"]
+ pub static mut epoxy_glResolveDepthValuesNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResolveMultisampleFramebufferAPPLE"]
+ pub static mut epoxy_glResolveMultisampleFramebufferAPPLE:
+ ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResumeTransformFeedback"]
+ pub static mut epoxy_glResumeTransformFeedback: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glResumeTransformFeedbackNV"]
+ pub static mut epoxy_glResumeTransformFeedbackNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRotated"]
+ pub static mut epoxy_glRotated: ::std::option::Option<
+ unsafe extern "C" fn(angle: GLdouble, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRotatef"]
+ pub static mut epoxy_glRotatef: ::std::option::Option<
+ unsafe extern "C" fn(angle: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRotatex"]
+ pub static mut epoxy_glRotatex: ::std::option::Option<
+ unsafe extern "C" fn(angle: GLfixed, x: GLfixed, y: GLfixed, z: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glRotatexOES"]
+ pub static mut epoxy_glRotatexOES: ::std::option::Option<
+ unsafe extern "C" fn(angle: GLfixed, x: GLfixed, y: GLfixed, z: GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleCoverage"]
+ pub static mut epoxy_glSampleCoverage:
+ ::std::option::Option<unsafe extern "C" fn(value: GLfloat, invert: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleCoverageARB"]
+ pub static mut epoxy_glSampleCoverageARB:
+ ::std::option::Option<unsafe extern "C" fn(value: GLfloat, invert: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleCoveragex"]
+ pub static mut epoxy_glSampleCoveragex:
+ ::std::option::Option<unsafe extern "C" fn(value: GLclampx, invert: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleCoveragexOES"]
+ pub static mut epoxy_glSampleCoveragexOES:
+ ::std::option::Option<unsafe extern "C" fn(value: GLclampx, invert: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleMapATI"]
+ pub static mut epoxy_glSampleMapATI:
+ ::std::option::Option<unsafe extern "C" fn(dst: GLuint, interp: GLuint, swizzle: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleMaskEXT"]
+ pub static mut epoxy_glSampleMaskEXT:
+ ::std::option::Option<unsafe extern "C" fn(value: GLclampf, invert: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleMaskIndexedNV"]
+ pub static mut epoxy_glSampleMaskIndexedNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleMaskSGIS"]
+ pub static mut epoxy_glSampleMaskSGIS:
+ ::std::option::Option<unsafe extern "C" fn(value: GLclampf, invert: GLboolean)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSampleMaski"]
+ pub static mut epoxy_glSampleMaski:
+ ::std::option::Option<unsafe extern "C" fn(maskNumber: GLuint, mask: GLbitfield)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplePatternEXT"]
+ pub static mut epoxy_glSamplePatternEXT:
+ ::std::option::Option<unsafe extern "C" fn(pattern: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplePatternSGIS"]
+ pub static mut epoxy_glSamplePatternSGIS:
+ ::std::option::Option<unsafe extern "C" fn(pattern: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameterIiv"]
+ pub static mut epoxy_glSamplerParameterIiv: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameterIivEXT"]
+ pub static mut epoxy_glSamplerParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameterIivOES"]
+ pub static mut epoxy_glSamplerParameterIivOES: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameterIuiv"]
+ pub static mut epoxy_glSamplerParameterIuiv: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameterIuivEXT"]
+ pub static mut epoxy_glSamplerParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameterIuivOES"]
+ pub static mut epoxy_glSamplerParameterIuivOES: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameterf"]
+ pub static mut epoxy_glSamplerParameterf:
+ ::std::option::Option<unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameterfv"]
+ pub static mut epoxy_glSamplerParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameteri"]
+ pub static mut epoxy_glSamplerParameteri:
+ ::std::option::Option<unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSamplerParameteriv"]
+ pub static mut epoxy_glSamplerParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(sampler: GLuint, pname: GLenum, param: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScaled"]
+ pub static mut epoxy_glScaled:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScalef"]
+ pub static mut epoxy_glScalef:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScalex"]
+ pub static mut epoxy_glScalex:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed, z: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScalexOES"]
+ pub static mut epoxy_glScalexOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed, z: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissor"]
+ pub static mut epoxy_glScissor: ::std::option::Option<
+ unsafe extern "C" fn(x: GLint, y: GLint, width: GLsizei, height: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorArrayv"]
+ pub static mut epoxy_glScissorArrayv:
+ ::std::option::Option<unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorArrayvNV"]
+ pub static mut epoxy_glScissorArrayvNV:
+ ::std::option::Option<unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorArrayvOES"]
+ pub static mut epoxy_glScissorArrayvOES:
+ ::std::option::Option<unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorIndexed"]
+ pub static mut epoxy_glScissorIndexed: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ left: GLint,
+ bottom: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorIndexedNV"]
+ pub static mut epoxy_glScissorIndexedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ left: GLint,
+ bottom: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorIndexedOES"]
+ pub static mut epoxy_glScissorIndexedOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ left: GLint,
+ bottom: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorIndexedv"]
+ pub static mut epoxy_glScissorIndexedv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorIndexedvNV"]
+ pub static mut epoxy_glScissorIndexedvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glScissorIndexedvOES"]
+ pub static mut epoxy_glScissorIndexedvOES:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3b"]
+ pub static mut epoxy_glSecondaryColor3b:
+ ::std::option::Option<unsafe extern "C" fn(red: GLbyte, green: GLbyte, blue: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3bEXT"]
+ pub static mut epoxy_glSecondaryColor3bEXT:
+ ::std::option::Option<unsafe extern "C" fn(red: GLbyte, green: GLbyte, blue: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3bv"]
+ pub static mut epoxy_glSecondaryColor3bv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3bvEXT"]
+ pub static mut epoxy_glSecondaryColor3bvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3d"]
+ pub static mut epoxy_glSecondaryColor3d:
+ ::std::option::Option<unsafe extern "C" fn(red: GLdouble, green: GLdouble, blue: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3dEXT"]
+ pub static mut epoxy_glSecondaryColor3dEXT:
+ ::std::option::Option<unsafe extern "C" fn(red: GLdouble, green: GLdouble, blue: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3dv"]
+ pub static mut epoxy_glSecondaryColor3dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3dvEXT"]
+ pub static mut epoxy_glSecondaryColor3dvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3f"]
+ pub static mut epoxy_glSecondaryColor3f:
+ ::std::option::Option<unsafe extern "C" fn(red: GLfloat, green: GLfloat, blue: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3fEXT"]
+ pub static mut epoxy_glSecondaryColor3fEXT:
+ ::std::option::Option<unsafe extern "C" fn(red: GLfloat, green: GLfloat, blue: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3fv"]
+ pub static mut epoxy_glSecondaryColor3fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3fvEXT"]
+ pub static mut epoxy_glSecondaryColor3fvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3hNV"]
+ pub static mut epoxy_glSecondaryColor3hNV:
+ ::std::option::Option<unsafe extern "C" fn(red: GLhalfNV, green: GLhalfNV, blue: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3hvNV"]
+ pub static mut epoxy_glSecondaryColor3hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3i"]
+ pub static mut epoxy_glSecondaryColor3i:
+ ::std::option::Option<unsafe extern "C" fn(red: GLint, green: GLint, blue: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3iEXT"]
+ pub static mut epoxy_glSecondaryColor3iEXT:
+ ::std::option::Option<unsafe extern "C" fn(red: GLint, green: GLint, blue: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3iv"]
+ pub static mut epoxy_glSecondaryColor3iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3ivEXT"]
+ pub static mut epoxy_glSecondaryColor3ivEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3s"]
+ pub static mut epoxy_glSecondaryColor3s:
+ ::std::option::Option<unsafe extern "C" fn(red: GLshort, green: GLshort, blue: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3sEXT"]
+ pub static mut epoxy_glSecondaryColor3sEXT:
+ ::std::option::Option<unsafe extern "C" fn(red: GLshort, green: GLshort, blue: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3sv"]
+ pub static mut epoxy_glSecondaryColor3sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3svEXT"]
+ pub static mut epoxy_glSecondaryColor3svEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3ub"]
+ pub static mut epoxy_glSecondaryColor3ub:
+ ::std::option::Option<unsafe extern "C" fn(red: GLubyte, green: GLubyte, blue: GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3ubEXT"]
+ pub static mut epoxy_glSecondaryColor3ubEXT:
+ ::std::option::Option<unsafe extern "C" fn(red: GLubyte, green: GLubyte, blue: GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3ubv"]
+ pub static mut epoxy_glSecondaryColor3ubv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3ubvEXT"]
+ pub static mut epoxy_glSecondaryColor3ubvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3ui"]
+ pub static mut epoxy_glSecondaryColor3ui:
+ ::std::option::Option<unsafe extern "C" fn(red: GLuint, green: GLuint, blue: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3uiEXT"]
+ pub static mut epoxy_glSecondaryColor3uiEXT:
+ ::std::option::Option<unsafe extern "C" fn(red: GLuint, green: GLuint, blue: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3uiv"]
+ pub static mut epoxy_glSecondaryColor3uiv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3uivEXT"]
+ pub static mut epoxy_glSecondaryColor3uivEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3us"]
+ pub static mut epoxy_glSecondaryColor3us:
+ ::std::option::Option<unsafe extern "C" fn(red: GLushort, green: GLushort, blue: GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3usEXT"]
+ pub static mut epoxy_glSecondaryColor3usEXT:
+ ::std::option::Option<unsafe extern "C" fn(red: GLushort, green: GLushort, blue: GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3usv"]
+ pub static mut epoxy_glSecondaryColor3usv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColor3usvEXT"]
+ pub static mut epoxy_glSecondaryColor3usvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColorFormatNV"]
+ pub static mut epoxy_glSecondaryColorFormatNV:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, type_: GLenum, stride: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColorP3ui"]
+ pub static mut epoxy_glSecondaryColorP3ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, color: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColorP3uiv"]
+ pub static mut epoxy_glSecondaryColorP3uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, color: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColorPointer"]
+ pub static mut epoxy_glSecondaryColorPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColorPointerEXT"]
+ pub static mut epoxy_glSecondaryColorPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSecondaryColorPointerListIBM"]
+ pub static mut epoxy_glSecondaryColorPointerListIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLint,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ptrstride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSelectBuffer"]
+ pub static mut epoxy_glSelectBuffer:
+ ::std::option::Option<unsafe extern "C" fn(size: GLsizei, buffer: *mut GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSelectPerfMonitorCountersAMD"]
+ pub static mut epoxy_glSelectPerfMonitorCountersAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ monitor: GLuint,
+ enable: GLboolean,
+ group: GLuint,
+ numCounters: GLint,
+ counterList: *mut GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSeparableFilter2D"]
+ pub static mut epoxy_glSeparableFilter2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ row: *const ::std::os::raw::c_void,
+ column: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSeparableFilter2DEXT"]
+ pub static mut epoxy_glSeparableFilter2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ row: *const ::std::os::raw::c_void,
+ column: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSetFenceAPPLE"]
+ pub static mut epoxy_glSetFenceAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(fence: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSetFenceNV"]
+ pub static mut epoxy_glSetFenceNV:
+ ::std::option::Option<unsafe extern "C" fn(fence: GLuint, condition: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSetFragmentShaderConstantATI"]
+ pub static mut epoxy_glSetFragmentShaderConstantATI:
+ ::std::option::Option<unsafe extern "C" fn(dst: GLuint, value: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSetInvariantEXT"]
+ pub static mut epoxy_glSetInvariantEXT: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, type_: GLenum, addr: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSetLocalConstantEXT"]
+ pub static mut epoxy_glSetLocalConstantEXT: ::std::option::Option<
+ unsafe extern "C" fn(id: GLuint, type_: GLenum, addr: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSetMultisamplefvAMD"]
+ pub static mut epoxy_glSetMultisamplefvAMD: ::std::option::Option<
+ unsafe extern "C" fn(pname: GLenum, index: GLuint, val: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glShadeModel"]
+ pub static mut epoxy_glShadeModel: ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glShaderBinary"]
+ pub static mut epoxy_glShaderBinary: ::std::option::Option<
+ unsafe extern "C" fn(
+ count: GLsizei,
+ shaders: *const GLuint,
+ binaryformat: GLenum,
+ binary: *const ::std::os::raw::c_void,
+ length: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glShaderOp1EXT"]
+ pub static mut epoxy_glShaderOp1EXT:
+ ::std::option::Option<unsafe extern "C" fn(op: GLenum, res: GLuint, arg1: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glShaderOp2EXT"]
+ pub static mut epoxy_glShaderOp2EXT: ::std::option::Option<
+ unsafe extern "C" fn(op: GLenum, res: GLuint, arg1: GLuint, arg2: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glShaderOp3EXT"]
+ pub static mut epoxy_glShaderOp3EXT: ::std::option::Option<
+ unsafe extern "C" fn(op: GLenum, res: GLuint, arg1: GLuint, arg2: GLuint, arg3: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glShaderSource"]
+ pub static mut epoxy_glShaderSource: ::std::option::Option<
+ unsafe extern "C" fn(
+ shader: GLuint,
+ count: GLsizei,
+ string: *const *const GLchar,
+ length: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glShaderSourceARB"]
+ pub static mut epoxy_glShaderSourceARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ shaderObj: GLhandleARB,
+ count: GLsizei,
+ string: *mut *const GLcharARB,
+ length: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glShaderStorageBlockBinding"]
+ pub static mut epoxy_glShaderStorageBlockBinding: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ storageBlockIndex: GLuint,
+ storageBlockBinding: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSharpenTexFuncSGIS"]
+ pub static mut epoxy_glSharpenTexFuncSGIS: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, n: GLsizei, points: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSpriteParameterfSGIX"]
+ pub static mut epoxy_glSpriteParameterfSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSpriteParameterfvSGIX"]
+ pub static mut epoxy_glSpriteParameterfvSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSpriteParameteriSGIX"]
+ pub static mut epoxy_glSpriteParameteriSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSpriteParameterivSGIX"]
+ pub static mut epoxy_glSpriteParameterivSGIX:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, params: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStartInstrumentsSGIX"]
+ pub static mut epoxy_glStartInstrumentsSGIX: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStartTilingQCOM"]
+ pub static mut epoxy_glStartTilingQCOM: ::std::option::Option<
+ unsafe extern "C" fn(
+ x: GLuint,
+ y: GLuint,
+ width: GLuint,
+ height: GLuint,
+ preserveMask: GLbitfield,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStateCaptureNV"]
+ pub static mut epoxy_glStateCaptureNV:
+ ::std::option::Option<unsafe extern "C" fn(state: GLuint, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilClearTagEXT"]
+ pub static mut epoxy_glStencilClearTagEXT: ::std::option::Option<
+ unsafe extern "C" fn(stencilTagBits: GLsizei, stencilClearTag: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilFillPathInstancedNV"]
+ pub static mut epoxy_glStencilFillPathInstancedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ numPaths: GLsizei,
+ pathNameType: GLenum,
+ paths: *const ::std::os::raw::c_void,
+ pathBase: GLuint,
+ fillMode: GLenum,
+ mask: GLuint,
+ transformType: GLenum,
+ transformValues: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilFillPathNV"]
+ pub static mut epoxy_glStencilFillPathNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, fillMode: GLenum, mask: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilFunc"]
+ pub static mut epoxy_glStencilFunc:
+ ::std::option::Option<unsafe extern "C" fn(func: GLenum, ref_: GLint, mask: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilFuncSeparate"]
+ pub static mut epoxy_glStencilFuncSeparate: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, func: GLenum, ref_: GLint, mask: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilFuncSeparateATI"]
+ pub static mut epoxy_glStencilFuncSeparateATI: ::std::option::Option<
+ unsafe extern "C" fn(frontfunc: GLenum, backfunc: GLenum, ref_: GLint, mask: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilMask"]
+ pub static mut epoxy_glStencilMask: ::std::option::Option<unsafe extern "C" fn(mask: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilMaskSeparate"]
+ pub static mut epoxy_glStencilMaskSeparate:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, mask: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilOp"]
+ pub static mut epoxy_glStencilOp:
+ ::std::option::Option<unsafe extern "C" fn(fail: GLenum, zfail: GLenum, zpass: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilOpSeparate"]
+ pub static mut epoxy_glStencilOpSeparate: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, sfail: GLenum, dpfail: GLenum, dppass: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilOpSeparateATI"]
+ pub static mut epoxy_glStencilOpSeparateATI: ::std::option::Option<
+ unsafe extern "C" fn(face: GLenum, sfail: GLenum, dpfail: GLenum, dppass: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilOpValueAMD"]
+ pub static mut epoxy_glStencilOpValueAMD:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, value: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilStrokePathInstancedNV"]
+ pub static mut epoxy_glStencilStrokePathInstancedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ numPaths: GLsizei,
+ pathNameType: GLenum,
+ paths: *const ::std::os::raw::c_void,
+ pathBase: GLuint,
+ reference: GLint,
+ mask: GLuint,
+ transformType: GLenum,
+ transformValues: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilStrokePathNV"]
+ pub static mut epoxy_glStencilStrokePathNV:
+ ::std::option::Option<unsafe extern "C" fn(path: GLuint, reference: GLint, mask: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilThenCoverFillPathInstancedNV"]
+ pub static mut epoxy_glStencilThenCoverFillPathInstancedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ numPaths: GLsizei,
+ pathNameType: GLenum,
+ paths: *const ::std::os::raw::c_void,
+ pathBase: GLuint,
+ fillMode: GLenum,
+ mask: GLuint,
+ coverMode: GLenum,
+ transformType: GLenum,
+ transformValues: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilThenCoverFillPathNV"]
+ pub static mut epoxy_glStencilThenCoverFillPathNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, fillMode: GLenum, mask: GLuint, coverMode: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilThenCoverStrokePathInstancedNV"]
+ pub static mut epoxy_glStencilThenCoverStrokePathInstancedNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ numPaths: GLsizei,
+ pathNameType: GLenum,
+ paths: *const ::std::os::raw::c_void,
+ pathBase: GLuint,
+ reference: GLint,
+ mask: GLuint,
+ coverMode: GLenum,
+ transformType: GLenum,
+ transformValues: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStencilThenCoverStrokePathNV"]
+ pub static mut epoxy_glStencilThenCoverStrokePathNV: ::std::option::Option<
+ unsafe extern "C" fn(path: GLuint, reference: GLint, mask: GLuint, coverMode: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStopInstrumentsSGIX"]
+ pub static mut epoxy_glStopInstrumentsSGIX:
+ ::std::option::Option<unsafe extern "C" fn(marker: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glStringMarkerGREMEDY"]
+ pub static mut epoxy_glStringMarkerGREMEDY: ::std::option::Option<
+ unsafe extern "C" fn(len: GLsizei, string: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSubpixelPrecisionBiasNV"]
+ pub static mut epoxy_glSubpixelPrecisionBiasNV:
+ ::std::option::Option<unsafe extern "C" fn(xbits: GLuint, ybits: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSwizzleEXT"]
+ pub static mut epoxy_glSwizzleEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ res: GLuint,
+ in_: GLuint,
+ outX: GLenum,
+ outY: GLenum,
+ outZ: GLenum,
+ outW: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glSyncTextureINTEL"]
+ pub static mut epoxy_glSyncTextureINTEL:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTagSampleBufferSGIX"]
+ pub static mut epoxy_glTagSampleBufferSGIX: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3bEXT"]
+ pub static mut epoxy_glTangent3bEXT:
+ ::std::option::Option<unsafe extern "C" fn(tx: GLbyte, ty: GLbyte, tz: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3bvEXT"]
+ pub static mut epoxy_glTangent3bvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3dEXT"]
+ pub static mut epoxy_glTangent3dEXT:
+ ::std::option::Option<unsafe extern "C" fn(tx: GLdouble, ty: GLdouble, tz: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3dvEXT"]
+ pub static mut epoxy_glTangent3dvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3fEXT"]
+ pub static mut epoxy_glTangent3fEXT:
+ ::std::option::Option<unsafe extern "C" fn(tx: GLfloat, ty: GLfloat, tz: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3fvEXT"]
+ pub static mut epoxy_glTangent3fvEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3iEXT"]
+ pub static mut epoxy_glTangent3iEXT:
+ ::std::option::Option<unsafe extern "C" fn(tx: GLint, ty: GLint, tz: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3ivEXT"]
+ pub static mut epoxy_glTangent3ivEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3sEXT"]
+ pub static mut epoxy_glTangent3sEXT:
+ ::std::option::Option<unsafe extern "C" fn(tx: GLshort, ty: GLshort, tz: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangent3svEXT"]
+ pub static mut epoxy_glTangent3svEXT:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTangentPointerEXT"]
+ pub static mut epoxy_glTangentPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTbufferMask3DFX"]
+ pub static mut epoxy_glTbufferMask3DFX:
+ ::std::option::Option<unsafe extern "C" fn(mask: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTessellationFactorAMD"]
+ pub static mut epoxy_glTessellationFactorAMD:
+ ::std::option::Option<unsafe extern "C" fn(factor: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTessellationModeAMD"]
+ pub static mut epoxy_glTessellationModeAMD:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTestFenceAPPLE"]
+ pub static mut epoxy_glTestFenceAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(fence: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTestFenceNV"]
+ pub static mut epoxy_glTestFenceNV:
+ ::std::option::Option<unsafe extern "C" fn(fence: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTestObjectAPPLE"]
+ pub static mut epoxy_glTestObjectAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(object: GLenum, name: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBuffer"]
+ pub static mut epoxy_glTexBuffer: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, internalformat: GLenum, buffer: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBufferARB"]
+ pub static mut epoxy_glTexBufferARB: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, internalformat: GLenum, buffer: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBufferEXT"]
+ pub static mut epoxy_glTexBufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, internalformat: GLenum, buffer: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBufferOES"]
+ pub static mut epoxy_glTexBufferOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, internalformat: GLenum, buffer: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBufferRange"]
+ pub static mut epoxy_glTexBufferRange: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBufferRangeEXT"]
+ pub static mut epoxy_glTexBufferRangeEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBufferRangeOES"]
+ pub static mut epoxy_glTexBufferRangeOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalformat: GLenum,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBumpParameterfvATI"]
+ pub static mut epoxy_glTexBumpParameterfvATI:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexBumpParameterivATI"]
+ pub static mut epoxy_glTexBumpParameterivATI:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1bOES"]
+ pub static mut epoxy_glTexCoord1bOES: ::std::option::Option<unsafe extern "C" fn(s: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1bvOES"]
+ pub static mut epoxy_glTexCoord1bvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1d"]
+ pub static mut epoxy_glTexCoord1d: ::std::option::Option<unsafe extern "C" fn(s: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1dv"]
+ pub static mut epoxy_glTexCoord1dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1f"]
+ pub static mut epoxy_glTexCoord1f: ::std::option::Option<unsafe extern "C" fn(s: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1fv"]
+ pub static mut epoxy_glTexCoord1fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1hNV"]
+ pub static mut epoxy_glTexCoord1hNV: ::std::option::Option<unsafe extern "C" fn(s: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1hvNV"]
+ pub static mut epoxy_glTexCoord1hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1i"]
+ pub static mut epoxy_glTexCoord1i: ::std::option::Option<unsafe extern "C" fn(s: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1iv"]
+ pub static mut epoxy_glTexCoord1iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1s"]
+ pub static mut epoxy_glTexCoord1s: ::std::option::Option<unsafe extern "C" fn(s: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1sv"]
+ pub static mut epoxy_glTexCoord1sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1xOES"]
+ pub static mut epoxy_glTexCoord1xOES: ::std::option::Option<unsafe extern "C" fn(s: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord1xvOES"]
+ pub static mut epoxy_glTexCoord1xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2bOES"]
+ pub static mut epoxy_glTexCoord2bOES:
+ ::std::option::Option<unsafe extern "C" fn(s: GLbyte, t: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2bvOES"]
+ pub static mut epoxy_glTexCoord2bvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2d"]
+ pub static mut epoxy_glTexCoord2d:
+ ::std::option::Option<unsafe extern "C" fn(s: GLdouble, t: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2dv"]
+ pub static mut epoxy_glTexCoord2dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2f"]
+ pub static mut epoxy_glTexCoord2f:
+ ::std::option::Option<unsafe extern "C" fn(s: GLfloat, t: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fColor3fVertex3fSUN"]
+ pub static mut epoxy_glTexCoord2fColor3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ s: GLfloat,
+ t: GLfloat,
+ r: GLfloat,
+ g: GLfloat,
+ b: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fColor3fVertex3fvSUN"]
+ pub static mut epoxy_glTexCoord2fColor3fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(tc: *const GLfloat, c: *const GLfloat, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fColor4fNormal3fVertex3fSUN"]
+ pub static mut epoxy_glTexCoord2fColor4fNormal3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ s: GLfloat,
+ t: GLfloat,
+ r: GLfloat,
+ g: GLfloat,
+ b: GLfloat,
+ a: GLfloat,
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fColor4fNormal3fVertex3fvSUN"]
+ pub static mut epoxy_glTexCoord2fColor4fNormal3fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ tc: *const GLfloat,
+ c: *const GLfloat,
+ n: *const GLfloat,
+ v: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fColor4ubVertex3fSUN"]
+ pub static mut epoxy_glTexCoord2fColor4ubVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ s: GLfloat,
+ t: GLfloat,
+ r: GLubyte,
+ g: GLubyte,
+ b: GLubyte,
+ a: GLubyte,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fColor4ubVertex3fvSUN"]
+ pub static mut epoxy_glTexCoord2fColor4ubVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(tc: *const GLfloat, c: *const GLubyte, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fNormal3fVertex3fSUN"]
+ pub static mut epoxy_glTexCoord2fNormal3fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ s: GLfloat,
+ t: GLfloat,
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fNormal3fVertex3fvSUN"]
+ pub static mut epoxy_glTexCoord2fNormal3fVertex3fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(tc: *const GLfloat, n: *const GLfloat, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fVertex3fSUN"]
+ pub static mut epoxy_glTexCoord2fVertex3fSUN: ::std::option::Option<
+ unsafe extern "C" fn(s: GLfloat, t: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fVertex3fvSUN"]
+ pub static mut epoxy_glTexCoord2fVertex3fvSUN:
+ ::std::option::Option<unsafe extern "C" fn(tc: *const GLfloat, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2fv"]
+ pub static mut epoxy_glTexCoord2fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2hNV"]
+ pub static mut epoxy_glTexCoord2hNV:
+ ::std::option::Option<unsafe extern "C" fn(s: GLhalfNV, t: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2hvNV"]
+ pub static mut epoxy_glTexCoord2hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2i"]
+ pub static mut epoxy_glTexCoord2i:
+ ::std::option::Option<unsafe extern "C" fn(s: GLint, t: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2iv"]
+ pub static mut epoxy_glTexCoord2iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2s"]
+ pub static mut epoxy_glTexCoord2s:
+ ::std::option::Option<unsafe extern "C" fn(s: GLshort, t: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2sv"]
+ pub static mut epoxy_glTexCoord2sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2xOES"]
+ pub static mut epoxy_glTexCoord2xOES:
+ ::std::option::Option<unsafe extern "C" fn(s: GLfixed, t: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord2xvOES"]
+ pub static mut epoxy_glTexCoord2xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3bOES"]
+ pub static mut epoxy_glTexCoord3bOES:
+ ::std::option::Option<unsafe extern "C" fn(s: GLbyte, t: GLbyte, r: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3bvOES"]
+ pub static mut epoxy_glTexCoord3bvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3d"]
+ pub static mut epoxy_glTexCoord3d:
+ ::std::option::Option<unsafe extern "C" fn(s: GLdouble, t: GLdouble, r: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3dv"]
+ pub static mut epoxy_glTexCoord3dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3f"]
+ pub static mut epoxy_glTexCoord3f:
+ ::std::option::Option<unsafe extern "C" fn(s: GLfloat, t: GLfloat, r: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3fv"]
+ pub static mut epoxy_glTexCoord3fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3hNV"]
+ pub static mut epoxy_glTexCoord3hNV:
+ ::std::option::Option<unsafe extern "C" fn(s: GLhalfNV, t: GLhalfNV, r: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3hvNV"]
+ pub static mut epoxy_glTexCoord3hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3i"]
+ pub static mut epoxy_glTexCoord3i:
+ ::std::option::Option<unsafe extern "C" fn(s: GLint, t: GLint, r: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3iv"]
+ pub static mut epoxy_glTexCoord3iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3s"]
+ pub static mut epoxy_glTexCoord3s:
+ ::std::option::Option<unsafe extern "C" fn(s: GLshort, t: GLshort, r: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3sv"]
+ pub static mut epoxy_glTexCoord3sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3xOES"]
+ pub static mut epoxy_glTexCoord3xOES:
+ ::std::option::Option<unsafe extern "C" fn(s: GLfixed, t: GLfixed, r: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord3xvOES"]
+ pub static mut epoxy_glTexCoord3xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4bOES"]
+ pub static mut epoxy_glTexCoord4bOES:
+ ::std::option::Option<unsafe extern "C" fn(s: GLbyte, t: GLbyte, r: GLbyte, q: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4bvOES"]
+ pub static mut epoxy_glTexCoord4bvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4d"]
+ pub static mut epoxy_glTexCoord4d: ::std::option::Option<
+ unsafe extern "C" fn(s: GLdouble, t: GLdouble, r: GLdouble, q: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4dv"]
+ pub static mut epoxy_glTexCoord4dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4f"]
+ pub static mut epoxy_glTexCoord4f:
+ ::std::option::Option<unsafe extern "C" fn(s: GLfloat, t: GLfloat, r: GLfloat, q: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4fColor4fNormal3fVertex4fSUN"]
+ pub static mut epoxy_glTexCoord4fColor4fNormal3fVertex4fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ s: GLfloat,
+ t: GLfloat,
+ p: GLfloat,
+ q: GLfloat,
+ r: GLfloat,
+ g: GLfloat,
+ b: GLfloat,
+ a: GLfloat,
+ nx: GLfloat,
+ ny: GLfloat,
+ nz: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ w: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4fColor4fNormal3fVertex4fvSUN"]
+ pub static mut epoxy_glTexCoord4fColor4fNormal3fVertex4fvSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ tc: *const GLfloat,
+ c: *const GLfloat,
+ n: *const GLfloat,
+ v: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4fVertex4fSUN"]
+ pub static mut epoxy_glTexCoord4fVertex4fSUN: ::std::option::Option<
+ unsafe extern "C" fn(
+ s: GLfloat,
+ t: GLfloat,
+ p: GLfloat,
+ q: GLfloat,
+ x: GLfloat,
+ y: GLfloat,
+ z: GLfloat,
+ w: GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4fVertex4fvSUN"]
+ pub static mut epoxy_glTexCoord4fVertex4fvSUN:
+ ::std::option::Option<unsafe extern "C" fn(tc: *const GLfloat, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4fv"]
+ pub static mut epoxy_glTexCoord4fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4hNV"]
+ pub static mut epoxy_glTexCoord4hNV: ::std::option::Option<
+ unsafe extern "C" fn(s: GLhalfNV, t: GLhalfNV, r: GLhalfNV, q: GLhalfNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4hvNV"]
+ pub static mut epoxy_glTexCoord4hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4i"]
+ pub static mut epoxy_glTexCoord4i:
+ ::std::option::Option<unsafe extern "C" fn(s: GLint, t: GLint, r: GLint, q: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4iv"]
+ pub static mut epoxy_glTexCoord4iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4s"]
+ pub static mut epoxy_glTexCoord4s:
+ ::std::option::Option<unsafe extern "C" fn(s: GLshort, t: GLshort, r: GLshort, q: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4sv"]
+ pub static mut epoxy_glTexCoord4sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4xOES"]
+ pub static mut epoxy_glTexCoord4xOES:
+ ::std::option::Option<unsafe extern "C" fn(s: GLfixed, t: GLfixed, r: GLfixed, q: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoord4xvOES"]
+ pub static mut epoxy_glTexCoord4xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordFormatNV"]
+ pub static mut epoxy_glTexCoordFormatNV:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, type_: GLenum, stride: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordP1ui"]
+ pub static mut epoxy_glTexCoordP1ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordP1uiv"]
+ pub static mut epoxy_glTexCoordP1uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordP2ui"]
+ pub static mut epoxy_glTexCoordP2ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordP2uiv"]
+ pub static mut epoxy_glTexCoordP2uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordP3ui"]
+ pub static mut epoxy_glTexCoordP3ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordP3uiv"]
+ pub static mut epoxy_glTexCoordP3uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordP4ui"]
+ pub static mut epoxy_glTexCoordP4ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordP4uiv"]
+ pub static mut epoxy_glTexCoordP4uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, coords: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordPointer"]
+ pub static mut epoxy_glTexCoordPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordPointerEXT"]
+ pub static mut epoxy_glTexCoordPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ count: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordPointerListIBM"]
+ pub static mut epoxy_glTexCoordPointerListIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLint,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ptrstride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexCoordPointervINTEL"]
+ pub static mut epoxy_glTexCoordPointervINTEL: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexEnvf"]
+ pub static mut epoxy_glTexEnvf:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexEnvfv"]
+ pub static mut epoxy_glTexEnvfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexEnvi"]
+ pub static mut epoxy_glTexEnvi:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexEnviv"]
+ pub static mut epoxy_glTexEnviv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexEnvx"]
+ pub static mut epoxy_glTexEnvx:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexEnvxOES"]
+ pub static mut epoxy_glTexEnvxOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexEnvxv"]
+ pub static mut epoxy_glTexEnvxv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexEnvxvOES"]
+ pub static mut epoxy_glTexEnvxvOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexFilterFuncSGIS"]
+ pub static mut epoxy_glTexFilterFuncSGIS: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, filter: GLenum, n: GLsizei, weights: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGend"]
+ pub static mut epoxy_glTexGend:
+ ::std::option::Option<unsafe extern "C" fn(coord: GLenum, pname: GLenum, param: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGendv"]
+ pub static mut epoxy_glTexGendv: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGenf"]
+ pub static mut epoxy_glTexGenf:
+ ::std::option::Option<unsafe extern "C" fn(coord: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGenfOES"]
+ pub static mut epoxy_glTexGenfOES:
+ ::std::option::Option<unsafe extern "C" fn(coord: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGenfv"]
+ pub static mut epoxy_glTexGenfv: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGenfvOES"]
+ pub static mut epoxy_glTexGenfvOES: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGeni"]
+ pub static mut epoxy_glTexGeni:
+ ::std::option::Option<unsafe extern "C" fn(coord: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGeniOES"]
+ pub static mut epoxy_glTexGeniOES:
+ ::std::option::Option<unsafe extern "C" fn(coord: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGeniv"]
+ pub static mut epoxy_glTexGeniv: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGenivOES"]
+ pub static mut epoxy_glTexGenivOES: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGenxOES"]
+ pub static mut epoxy_glTexGenxOES:
+ ::std::option::Option<unsafe extern "C" fn(coord: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexGenxvOES"]
+ pub static mut epoxy_glTexGenxvOES: ::std::option::Option<
+ unsafe extern "C" fn(coord: GLenum, pname: GLenum, params: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage1D"]
+ pub static mut epoxy_glTexImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage2D"]
+ pub static mut epoxy_glTexImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage2DMultisample"]
+ pub static mut epoxy_glTexImage2DMultisample: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage2DMultisampleCoverageNV"]
+ pub static mut epoxy_glTexImage2DMultisampleCoverageNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ coverageSamples: GLsizei,
+ colorSamples: GLsizei,
+ internalFormat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ fixedSampleLocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage3D"]
+ pub static mut epoxy_glTexImage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage3DEXT"]
+ pub static mut epoxy_glTexImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage3DMultisample"]
+ pub static mut epoxy_glTexImage3DMultisample: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage3DMultisampleCoverageNV"]
+ pub static mut epoxy_glTexImage3DMultisampleCoverageNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ coverageSamples: GLsizei,
+ colorSamples: GLsizei,
+ internalFormat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ fixedSampleLocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage3DOES"]
+ pub static mut epoxy_glTexImage3DOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexImage4DSGIS"]
+ pub static mut epoxy_glTexImage4DSGIS: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ size4d: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexPageCommitmentARB"]
+ pub static mut epoxy_glTexPageCommitmentARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ commit: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexPageCommitmentEXT"]
+ pub static mut epoxy_glTexPageCommitmentEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ commit: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterIiv"]
+ pub static mut epoxy_glTexParameterIiv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterIivEXT"]
+ pub static mut epoxy_glTexParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterIivOES"]
+ pub static mut epoxy_glTexParameterIivOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterIuiv"]
+ pub static mut epoxy_glTexParameterIuiv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterIuivEXT"]
+ pub static mut epoxy_glTexParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterIuivOES"]
+ pub static mut epoxy_glTexParameterIuivOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterf"]
+ pub static mut epoxy_glTexParameterf:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterfv"]
+ pub static mut epoxy_glTexParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameteri"]
+ pub static mut epoxy_glTexParameteri:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameteriv"]
+ pub static mut epoxy_glTexParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterx"]
+ pub static mut epoxy_glTexParameterx:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterxOES"]
+ pub static mut epoxy_glTexParameterxOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, pname: GLenum, param: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterxv"]
+ pub static mut epoxy_glTexParameterxv: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexParameterxvOES"]
+ pub static mut epoxy_glTexParameterxvOES: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, pname: GLenum, params: *const GLfixed),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexRenderbufferNV"]
+ pub static mut epoxy_glTexRenderbufferNV:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum, renderbuffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage1D"]
+ pub static mut epoxy_glTexStorage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage1DEXT"]
+ pub static mut epoxy_glTexStorage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage2D"]
+ pub static mut epoxy_glTexStorage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage2DEXT"]
+ pub static mut epoxy_glTexStorage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage2DMultisample"]
+ pub static mut epoxy_glTexStorage2DMultisample: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage3D"]
+ pub static mut epoxy_glTexStorage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage3DEXT"]
+ pub static mut epoxy_glTexStorage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage3DMultisample"]
+ pub static mut epoxy_glTexStorage3DMultisample: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorage3DMultisampleOES"]
+ pub static mut epoxy_glTexStorage3DMultisampleOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexStorageSparseAMD"]
+ pub static mut epoxy_glTexStorageSparseAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ internalFormat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ layers: GLsizei,
+ flags: GLbitfield,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexSubImage1D"]
+ pub static mut epoxy_glTexSubImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexSubImage1DEXT"]
+ pub static mut epoxy_glTexSubImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexSubImage2D"]
+ pub static mut epoxy_glTexSubImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexSubImage2DEXT"]
+ pub static mut epoxy_glTexSubImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexSubImage3D"]
+ pub static mut epoxy_glTexSubImage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexSubImage3DEXT"]
+ pub static mut epoxy_glTexSubImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexSubImage3DOES"]
+ pub static mut epoxy_glTexSubImage3DOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexSubImage4DSGIS"]
+ pub static mut epoxy_glTexSubImage4DSGIS: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ woffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ size4d: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureBarrier"]
+ pub static mut epoxy_glTextureBarrier: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureBarrierNV"]
+ pub static mut epoxy_glTextureBarrierNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureBuffer"]
+ pub static mut epoxy_glTextureBuffer: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, internalformat: GLenum, buffer: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureBufferEXT"]
+ pub static mut epoxy_glTextureBufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ internalformat: GLenum,
+ buffer: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureBufferRange"]
+ pub static mut epoxy_glTextureBufferRange: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ internalformat: GLenum,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureBufferRangeEXT"]
+ pub static mut epoxy_glTextureBufferRangeEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ internalformat: GLenum,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureColorMaskSGIS"]
+ pub static mut epoxy_glTextureColorMaskSGIS: ::std::option::Option<
+ unsafe extern "C" fn(red: GLboolean, green: GLboolean, blue: GLboolean, alpha: GLboolean),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureImage1DEXT"]
+ pub static mut epoxy_glTextureImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureImage2DEXT"]
+ pub static mut epoxy_glTextureImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureImage2DMultisampleCoverageNV"]
+ pub static mut epoxy_glTextureImage2DMultisampleCoverageNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ coverageSamples: GLsizei,
+ colorSamples: GLsizei,
+ internalFormat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ fixedSampleLocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureImage2DMultisampleNV"]
+ pub static mut epoxy_glTextureImage2DMultisampleNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ samples: GLsizei,
+ internalFormat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ fixedSampleLocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureImage3DEXT"]
+ pub static mut epoxy_glTextureImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ internalformat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ border: GLint,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureImage3DMultisampleCoverageNV"]
+ pub static mut epoxy_glTextureImage3DMultisampleCoverageNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ coverageSamples: GLsizei,
+ colorSamples: GLsizei,
+ internalFormat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ fixedSampleLocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureImage3DMultisampleNV"]
+ pub static mut epoxy_glTextureImage3DMultisampleNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ samples: GLsizei,
+ internalFormat: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ fixedSampleLocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureLightEXT"]
+ pub static mut epoxy_glTextureLightEXT:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureMaterialEXT"]
+ pub static mut epoxy_glTextureMaterialEXT:
+ ::std::option::Option<unsafe extern "C" fn(face: GLenum, mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureNormalEXT"]
+ pub static mut epoxy_glTextureNormalEXT:
+ ::std::option::Option<unsafe extern "C" fn(mode: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTexturePageCommitmentEXT"]
+ pub static mut epoxy_glTexturePageCommitmentEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ commit: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterIiv"]
+ pub static mut epoxy_glTextureParameterIiv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterIivEXT"]
+ pub static mut epoxy_glTextureParameterIivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterIuiv"]
+ pub static mut epoxy_glTextureParameterIuiv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, pname: GLenum, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterIuivEXT"]
+ pub static mut epoxy_glTextureParameterIuivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, params: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterf"]
+ pub static mut epoxy_glTextureParameterf:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint, pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterfEXT"]
+ pub static mut epoxy_glTextureParameterfEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, param: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterfv"]
+ pub static mut epoxy_glTextureParameterfv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, pname: GLenum, param: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterfvEXT"]
+ pub static mut epoxy_glTextureParameterfvEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ pname: GLenum,
+ params: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameteri"]
+ pub static mut epoxy_glTextureParameteri:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameteriEXT"]
+ pub static mut epoxy_glTextureParameteriEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, param: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameteriv"]
+ pub static mut epoxy_glTextureParameteriv: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, pname: GLenum, param: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureParameterivEXT"]
+ pub static mut epoxy_glTextureParameterivEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, pname: GLenum, params: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureRangeAPPLE"]
+ pub static mut epoxy_glTextureRangeAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ target: GLenum,
+ length: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureRenderbufferEXT"]
+ pub static mut epoxy_glTextureRenderbufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(texture: GLuint, target: GLenum, renderbuffer: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage1D"]
+ pub static mut epoxy_glTextureStorage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage1DEXT"]
+ pub static mut epoxy_glTextureStorage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage2D"]
+ pub static mut epoxy_glTextureStorage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage2DEXT"]
+ pub static mut epoxy_glTextureStorage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage2DMultisample"]
+ pub static mut epoxy_glTextureStorage2DMultisample: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage2DMultisampleEXT"]
+ pub static mut epoxy_glTextureStorage2DMultisampleEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage3D"]
+ pub static mut epoxy_glTextureStorage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage3DEXT"]
+ pub static mut epoxy_glTextureStorage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ levels: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage3DMultisample"]
+ pub static mut epoxy_glTextureStorage3DMultisample: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorage3DMultisampleEXT"]
+ pub static mut epoxy_glTextureStorage3DMultisampleEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ samples: GLsizei,
+ internalformat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ fixedsamplelocations: GLboolean,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureStorageSparseAMD"]
+ pub static mut epoxy_glTextureStorageSparseAMD: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ internalFormat: GLenum,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ layers: GLsizei,
+ flags: GLbitfield,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureSubImage1D"]
+ pub static mut epoxy_glTextureSubImage1D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureSubImage1DEXT"]
+ pub static mut epoxy_glTextureSubImage1DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ width: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureSubImage2D"]
+ pub static mut epoxy_glTextureSubImage2D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureSubImage2DEXT"]
+ pub static mut epoxy_glTextureSubImage2DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureSubImage3D"]
+ pub static mut epoxy_glTextureSubImage3D: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureSubImage3DEXT"]
+ pub static mut epoxy_glTextureSubImage3DEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ level: GLint,
+ xoffset: GLint,
+ yoffset: GLint,
+ zoffset: GLint,
+ width: GLsizei,
+ height: GLsizei,
+ depth: GLsizei,
+ format: GLenum,
+ type_: GLenum,
+ pixels: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureView"]
+ pub static mut epoxy_glTextureView: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ origtexture: GLuint,
+ internalformat: GLenum,
+ minlevel: GLuint,
+ numlevels: GLuint,
+ minlayer: GLuint,
+ numlayers: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureViewEXT"]
+ pub static mut epoxy_glTextureViewEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ origtexture: GLuint,
+ internalformat: GLenum,
+ minlevel: GLuint,
+ numlevels: GLuint,
+ minlayer: GLuint,
+ numlayers: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTextureViewOES"]
+ pub static mut epoxy_glTextureViewOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ texture: GLuint,
+ target: GLenum,
+ origtexture: GLuint,
+ internalformat: GLenum,
+ minlevel: GLuint,
+ numlevels: GLuint,
+ minlayer: GLuint,
+ numlayers: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTrackMatrixNV"]
+ pub static mut epoxy_glTrackMatrixNV: ::std::option::Option<
+ unsafe extern "C" fn(target: GLenum, address: GLuint, matrix: GLenum, transform: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTransformFeedbackAttribsNV"]
+ pub static mut epoxy_glTransformFeedbackAttribsNV: ::std::option::Option<
+ unsafe extern "C" fn(count: GLsizei, attribs: *const GLint, bufferMode: GLenum),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTransformFeedbackBufferBase"]
+ pub static mut epoxy_glTransformFeedbackBufferBase:
+ ::std::option::Option<unsafe extern "C" fn(xfb: GLuint, index: GLuint, buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTransformFeedbackBufferRange"]
+ pub static mut epoxy_glTransformFeedbackBufferRange: ::std::option::Option<
+ unsafe extern "C" fn(
+ xfb: GLuint,
+ index: GLuint,
+ buffer: GLuint,
+ offset: GLintptr,
+ size: GLsizeiptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTransformFeedbackStreamAttribsNV"]
+ pub static mut epoxy_glTransformFeedbackStreamAttribsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ count: GLsizei,
+ attribs: *const GLint,
+ nbuffers: GLsizei,
+ bufstreams: *const GLint,
+ bufferMode: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTransformFeedbackVaryings"]
+ pub static mut epoxy_glTransformFeedbackVaryings: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ count: GLsizei,
+ varyings: *const *const GLchar,
+ bufferMode: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTransformFeedbackVaryingsEXT"]
+ pub static mut epoxy_glTransformFeedbackVaryingsEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ count: GLsizei,
+ varyings: *const *const GLchar,
+ bufferMode: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTransformFeedbackVaryingsNV"]
+ pub static mut epoxy_glTransformFeedbackVaryingsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ count: GLsizei,
+ locations: *const GLint,
+ bufferMode: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTransformPathNV"]
+ pub static mut epoxy_glTransformPathNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ resultPath: GLuint,
+ srcPath: GLuint,
+ transformType: GLenum,
+ transformValues: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTranslated"]
+ pub static mut epoxy_glTranslated:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTranslatef"]
+ pub static mut epoxy_glTranslatef:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTranslatex"]
+ pub static mut epoxy_glTranslatex:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed, z: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glTranslatexOES"]
+ pub static mut epoxy_glTranslatexOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed, z: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1d"]
+ pub static mut epoxy_glUniform1d:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1dv"]
+ pub static mut epoxy_glUniform1dv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1f"]
+ pub static mut epoxy_glUniform1f:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1fARB"]
+ pub static mut epoxy_glUniform1fARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1fv"]
+ pub static mut epoxy_glUniform1fv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1fvARB"]
+ pub static mut epoxy_glUniform1fvARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1i"]
+ pub static mut epoxy_glUniform1i:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1i64ARB"]
+ pub static mut epoxy_glUniform1i64ARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1i64NV"]
+ pub static mut epoxy_glUniform1i64NV:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1i64vARB"]
+ pub static mut epoxy_glUniform1i64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1i64vNV"]
+ pub static mut epoxy_glUniform1i64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1iARB"]
+ pub static mut epoxy_glUniform1iARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1iv"]
+ pub static mut epoxy_glUniform1iv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1ivARB"]
+ pub static mut epoxy_glUniform1ivARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1ui"]
+ pub static mut epoxy_glUniform1ui:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1ui64ARB"]
+ pub static mut epoxy_glUniform1ui64ARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1ui64NV"]
+ pub static mut epoxy_glUniform1ui64NV:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1ui64vARB"]
+ pub static mut epoxy_glUniform1ui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1ui64vNV"]
+ pub static mut epoxy_glUniform1ui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1uiEXT"]
+ pub static mut epoxy_glUniform1uiEXT:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1uiv"]
+ pub static mut epoxy_glUniform1uiv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform1uivEXT"]
+ pub static mut epoxy_glUniform1uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2d"]
+ pub static mut epoxy_glUniform2d:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2dv"]
+ pub static mut epoxy_glUniform2dv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2f"]
+ pub static mut epoxy_glUniform2f:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLfloat, v1: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2fARB"]
+ pub static mut epoxy_glUniform2fARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLfloat, v1: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2fv"]
+ pub static mut epoxy_glUniform2fv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2fvARB"]
+ pub static mut epoxy_glUniform2fvARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2i"]
+ pub static mut epoxy_glUniform2i:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLint, v1: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2i64ARB"]
+ pub static mut epoxy_glUniform2i64ARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLint64, y: GLint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2i64NV"]
+ pub static mut epoxy_glUniform2i64NV:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLint64EXT, y: GLint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2i64vARB"]
+ pub static mut epoxy_glUniform2i64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2i64vNV"]
+ pub static mut epoxy_glUniform2i64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2iARB"]
+ pub static mut epoxy_glUniform2iARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLint, v1: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2iv"]
+ pub static mut epoxy_glUniform2iv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2ivARB"]
+ pub static mut epoxy_glUniform2ivARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2ui"]
+ pub static mut epoxy_glUniform2ui:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLuint, v1: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2ui64ARB"]
+ pub static mut epoxy_glUniform2ui64ARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, x: GLuint64, y: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2ui64NV"]
+ pub static mut epoxy_glUniform2ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLuint64EXT, y: GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2ui64vARB"]
+ pub static mut epoxy_glUniform2ui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2ui64vNV"]
+ pub static mut epoxy_glUniform2ui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2uiEXT"]
+ pub static mut epoxy_glUniform2uiEXT:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, v0: GLuint, v1: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2uiv"]
+ pub static mut epoxy_glUniform2uiv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform2uivEXT"]
+ pub static mut epoxy_glUniform2uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3d"]
+ pub static mut epoxy_glUniform3d: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3dv"]
+ pub static mut epoxy_glUniform3dv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3f"]
+ pub static mut epoxy_glUniform3f: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3fARB"]
+ pub static mut epoxy_glUniform3fARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3fv"]
+ pub static mut epoxy_glUniform3fv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3fvARB"]
+ pub static mut epoxy_glUniform3fvARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3i"]
+ pub static mut epoxy_glUniform3i: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLint, v1: GLint, v2: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3i64ARB"]
+ pub static mut epoxy_glUniform3i64ARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLint64, y: GLint64, z: GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3i64NV"]
+ pub static mut epoxy_glUniform3i64NV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLint64EXT, y: GLint64EXT, z: GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3i64vARB"]
+ pub static mut epoxy_glUniform3i64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3i64vNV"]
+ pub static mut epoxy_glUniform3i64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3iARB"]
+ pub static mut epoxy_glUniform3iARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLint, v1: GLint, v2: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3iv"]
+ pub static mut epoxy_glUniform3iv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3ivARB"]
+ pub static mut epoxy_glUniform3ivARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3ui"]
+ pub static mut epoxy_glUniform3ui: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLuint, v1: GLuint, v2: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3ui64ARB"]
+ pub static mut epoxy_glUniform3ui64ARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLuint64, y: GLuint64, z: GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3ui64NV"]
+ pub static mut epoxy_glUniform3ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLuint64EXT, y: GLuint64EXT, z: GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3ui64vARB"]
+ pub static mut epoxy_glUniform3ui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3ui64vNV"]
+ pub static mut epoxy_glUniform3ui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3uiEXT"]
+ pub static mut epoxy_glUniform3uiEXT: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLuint, v1: GLuint, v2: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3uiv"]
+ pub static mut epoxy_glUniform3uiv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform3uivEXT"]
+ pub static mut epoxy_glUniform3uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4d"]
+ pub static mut epoxy_glUniform4d: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4dv"]
+ pub static mut epoxy_glUniform4dv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4f"]
+ pub static mut epoxy_glUniform4f: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4fARB"]
+ pub static mut epoxy_glUniform4fARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4fv"]
+ pub static mut epoxy_glUniform4fv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4fvARB"]
+ pub static mut epoxy_glUniform4fvARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4i"]
+ pub static mut epoxy_glUniform4i: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLint, v1: GLint, v2: GLint, v3: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4i64ARB"]
+ pub static mut epoxy_glUniform4i64ARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLint64, y: GLint64, z: GLint64, w: GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4i64NV"]
+ pub static mut epoxy_glUniform4i64NV: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ x: GLint64EXT,
+ y: GLint64EXT,
+ z: GLint64EXT,
+ w: GLint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4i64vARB"]
+ pub static mut epoxy_glUniform4i64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4i64vNV"]
+ pub static mut epoxy_glUniform4i64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4iARB"]
+ pub static mut epoxy_glUniform4iARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLint, v1: GLint, v2: GLint, v3: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4iv"]
+ pub static mut epoxy_glUniform4iv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4ivARB"]
+ pub static mut epoxy_glUniform4ivARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4ui"]
+ pub static mut epoxy_glUniform4ui: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLuint, v1: GLuint, v2: GLuint, v3: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4ui64ARB"]
+ pub static mut epoxy_glUniform4ui64ARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, x: GLuint64, y: GLuint64, z: GLuint64, w: GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4ui64NV"]
+ pub static mut epoxy_glUniform4ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ x: GLuint64EXT,
+ y: GLuint64EXT,
+ z: GLuint64EXT,
+ w: GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4ui64vARB"]
+ pub static mut epoxy_glUniform4ui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4ui64vNV"]
+ pub static mut epoxy_glUniform4ui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4uiEXT"]
+ pub static mut epoxy_glUniform4uiEXT: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, v0: GLuint, v1: GLuint, v2: GLuint, v3: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4uiv"]
+ pub static mut epoxy_glUniform4uiv: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniform4uivEXT"]
+ pub static mut epoxy_glUniform4uivEXT: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformBlockBinding"]
+ pub static mut epoxy_glUniformBlockBinding: ::std::option::Option<
+ unsafe extern "C" fn(
+ program: GLuint,
+ uniformBlockIndex: GLuint,
+ uniformBlockBinding: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformBufferEXT"]
+ pub static mut epoxy_glUniformBufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(program: GLuint, location: GLint, buffer: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformHandleui64ARB"]
+ pub static mut epoxy_glUniformHandleui64ARB:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, value: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformHandleui64IMG"]
+ pub static mut epoxy_glUniformHandleui64IMG:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, value: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformHandleui64NV"]
+ pub static mut epoxy_glUniformHandleui64NV:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, value: GLuint64)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformHandleui64vARB"]
+ pub static mut epoxy_glUniformHandleui64vARB: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformHandleui64vIMG"]
+ pub static mut epoxy_glUniformHandleui64vIMG: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformHandleui64vNV"]
+ pub static mut epoxy_glUniformHandleui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2dv"]
+ pub static mut epoxy_glUniformMatrix2dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2fv"]
+ pub static mut epoxy_glUniformMatrix2fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2fvARB"]
+ pub static mut epoxy_glUniformMatrix2fvARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2x3dv"]
+ pub static mut epoxy_glUniformMatrix2x3dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2x3fv"]
+ pub static mut epoxy_glUniformMatrix2x3fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2x3fvNV"]
+ pub static mut epoxy_glUniformMatrix2x3fvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2x4dv"]
+ pub static mut epoxy_glUniformMatrix2x4dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2x4fv"]
+ pub static mut epoxy_glUniformMatrix2x4fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix2x4fvNV"]
+ pub static mut epoxy_glUniformMatrix2x4fvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3dv"]
+ pub static mut epoxy_glUniformMatrix3dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3fv"]
+ pub static mut epoxy_glUniformMatrix3fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3fvARB"]
+ pub static mut epoxy_glUniformMatrix3fvARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3x2dv"]
+ pub static mut epoxy_glUniformMatrix3x2dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3x2fv"]
+ pub static mut epoxy_glUniformMatrix3x2fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3x2fvNV"]
+ pub static mut epoxy_glUniformMatrix3x2fvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3x4dv"]
+ pub static mut epoxy_glUniformMatrix3x4dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3x4fv"]
+ pub static mut epoxy_glUniformMatrix3x4fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix3x4fvNV"]
+ pub static mut epoxy_glUniformMatrix3x4fvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4dv"]
+ pub static mut epoxy_glUniformMatrix4dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4fv"]
+ pub static mut epoxy_glUniformMatrix4fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4fvARB"]
+ pub static mut epoxy_glUniformMatrix4fvARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4x2dv"]
+ pub static mut epoxy_glUniformMatrix4x2dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4x2fv"]
+ pub static mut epoxy_glUniformMatrix4x2fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4x2fvNV"]
+ pub static mut epoxy_glUniformMatrix4x2fvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4x3dv"]
+ pub static mut epoxy_glUniformMatrix4x3dv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4x3fv"]
+ pub static mut epoxy_glUniformMatrix4x3fv: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformMatrix4x3fvNV"]
+ pub static mut epoxy_glUniformMatrix4x3fvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ location: GLint,
+ count: GLsizei,
+ transpose: GLboolean,
+ value: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformSubroutinesuiv"]
+ pub static mut epoxy_glUniformSubroutinesuiv: ::std::option::Option<
+ unsafe extern "C" fn(shadertype: GLenum, count: GLsizei, indices: *const GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformui64NV"]
+ pub static mut epoxy_glUniformui64NV:
+ ::std::option::Option<unsafe extern "C" fn(location: GLint, value: GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUniformui64vNV"]
+ pub static mut epoxy_glUniformui64vNV: ::std::option::Option<
+ unsafe extern "C" fn(location: GLint, count: GLsizei, value: *const GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUnlockArraysEXT"]
+ pub static mut epoxy_glUnlockArraysEXT: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUnmapBuffer"]
+ pub static mut epoxy_glUnmapBuffer:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUnmapBufferARB"]
+ pub static mut epoxy_glUnmapBufferARB:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUnmapBufferOES"]
+ pub static mut epoxy_glUnmapBufferOES:
+ ::std::option::Option<unsafe extern "C" fn(target: GLenum) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUnmapNamedBuffer"]
+ pub static mut epoxy_glUnmapNamedBuffer:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUnmapNamedBufferEXT"]
+ pub static mut epoxy_glUnmapNamedBufferEXT:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUnmapObjectBufferATI"]
+ pub static mut epoxy_glUnmapObjectBufferATI:
+ ::std::option::Option<unsafe extern "C" fn(buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUnmapTexture2DINTEL"]
+ pub static mut epoxy_glUnmapTexture2DINTEL:
+ ::std::option::Option<unsafe extern "C" fn(texture: GLuint, level: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUpdateObjectBufferATI"]
+ pub static mut epoxy_glUpdateObjectBufferATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ buffer: GLuint,
+ offset: GLuint,
+ size: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ preserve: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUseProgram"]
+ pub static mut epoxy_glUseProgram: ::std::option::Option<unsafe extern "C" fn(program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUseProgramObjectARB"]
+ pub static mut epoxy_glUseProgramObjectARB:
+ ::std::option::Option<unsafe extern "C" fn(programObj: GLhandleARB)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUseProgramStages"]
+ pub static mut epoxy_glUseProgramStages: ::std::option::Option<
+ unsafe extern "C" fn(pipeline: GLuint, stages: GLbitfield, program: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUseProgramStagesEXT"]
+ pub static mut epoxy_glUseProgramStagesEXT: ::std::option::Option<
+ unsafe extern "C" fn(pipeline: GLuint, stages: GLbitfield, program: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glUseShaderProgramEXT"]
+ pub static mut epoxy_glUseShaderProgramEXT:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAUFiniNV"]
+ pub static mut epoxy_glVDPAUFiniNV: ::std::option::Option<unsafe extern "C" fn()>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAUGetSurfaceivNV"]
+ pub static mut epoxy_glVDPAUGetSurfaceivNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ surface: GLvdpauSurfaceNV,
+ pname: GLenum,
+ bufSize: GLsizei,
+ length: *mut GLsizei,
+ values: *mut GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAUInitNV"]
+ pub static mut epoxy_glVDPAUInitNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ vdpDevice: *const ::std::os::raw::c_void,
+ getProcAddress: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAUIsSurfaceNV"]
+ pub static mut epoxy_glVDPAUIsSurfaceNV:
+ ::std::option::Option<unsafe extern "C" fn(surface: GLvdpauSurfaceNV) -> GLboolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAUMapSurfacesNV"]
+ pub static mut epoxy_glVDPAUMapSurfacesNV: ::std::option::Option<
+ unsafe extern "C" fn(numSurfaces: GLsizei, surfaces: *const GLvdpauSurfaceNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAURegisterOutputSurfaceNV"]
+ pub static mut epoxy_glVDPAURegisterOutputSurfaceNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ vdpSurface: *const ::std::os::raw::c_void,
+ target: GLenum,
+ numTextureNames: GLsizei,
+ textureNames: *const GLuint,
+ ) -> GLvdpauSurfaceNV,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAURegisterVideoSurfaceNV"]
+ pub static mut epoxy_glVDPAURegisterVideoSurfaceNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ vdpSurface: *const ::std::os::raw::c_void,
+ target: GLenum,
+ numTextureNames: GLsizei,
+ textureNames: *const GLuint,
+ ) -> GLvdpauSurfaceNV,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAUSurfaceAccessNV"]
+ pub static mut epoxy_glVDPAUSurfaceAccessNV:
+ ::std::option::Option<unsafe extern "C" fn(surface: GLvdpauSurfaceNV, access: GLenum)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAUUnmapSurfacesNV"]
+ pub static mut epoxy_glVDPAUUnmapSurfacesNV: ::std::option::Option<
+ unsafe extern "C" fn(numSurface: GLsizei, surfaces: *const GLvdpauSurfaceNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVDPAUUnregisterSurfaceNV"]
+ pub static mut epoxy_glVDPAUUnregisterSurfaceNV:
+ ::std::option::Option<unsafe extern "C" fn(surface: GLvdpauSurfaceNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glValidateProgram"]
+ pub static mut epoxy_glValidateProgram:
+ ::std::option::Option<unsafe extern "C" fn(program: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glValidateProgramARB"]
+ pub static mut epoxy_glValidateProgramARB:
+ ::std::option::Option<unsafe extern "C" fn(programObj: GLhandleARB)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glValidateProgramPipeline"]
+ pub static mut epoxy_glValidateProgramPipeline:
+ ::std::option::Option<unsafe extern "C" fn(pipeline: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glValidateProgramPipelineEXT"]
+ pub static mut epoxy_glValidateProgramPipelineEXT:
+ ::std::option::Option<unsafe extern "C" fn(pipeline: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantArrayObjectATI"]
+ pub static mut epoxy_glVariantArrayObjectATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ id: GLuint,
+ type_: GLenum,
+ stride: GLsizei,
+ buffer: GLuint,
+ offset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantPointerEXT"]
+ pub static mut epoxy_glVariantPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ id: GLuint,
+ type_: GLenum,
+ stride: GLuint,
+ addr: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantbvEXT"]
+ pub static mut epoxy_glVariantbvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, addr: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantdvEXT"]
+ pub static mut epoxy_glVariantdvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, addr: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantfvEXT"]
+ pub static mut epoxy_glVariantfvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, addr: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantivEXT"]
+ pub static mut epoxy_glVariantivEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, addr: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantsvEXT"]
+ pub static mut epoxy_glVariantsvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, addr: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantubvEXT"]
+ pub static mut epoxy_glVariantubvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, addr: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantuivEXT"]
+ pub static mut epoxy_glVariantuivEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, addr: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVariantusvEXT"]
+ pub static mut epoxy_glVariantusvEXT:
+ ::std::option::Option<unsafe extern "C" fn(id: GLuint, addr: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2bOES"]
+ pub static mut epoxy_glVertex2bOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLbyte, y: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2bvOES"]
+ pub static mut epoxy_glVertex2bvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2d"]
+ pub static mut epoxy_glVertex2d:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2dv"]
+ pub static mut epoxy_glVertex2dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2f"]
+ pub static mut epoxy_glVertex2f:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2fv"]
+ pub static mut epoxy_glVertex2fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2hNV"]
+ pub static mut epoxy_glVertex2hNV:
+ ::std::option::Option<unsafe extern "C" fn(x: GLhalfNV, y: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2hvNV"]
+ pub static mut epoxy_glVertex2hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2i"]
+ pub static mut epoxy_glVertex2i:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2iv"]
+ pub static mut epoxy_glVertex2iv: ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2s"]
+ pub static mut epoxy_glVertex2s:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2sv"]
+ pub static mut epoxy_glVertex2sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2xOES"]
+ pub static mut epoxy_glVertex2xOES: ::std::option::Option<unsafe extern "C" fn(x: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex2xvOES"]
+ pub static mut epoxy_glVertex2xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3bOES"]
+ pub static mut epoxy_glVertex3bOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLbyte, y: GLbyte, z: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3bvOES"]
+ pub static mut epoxy_glVertex3bvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3d"]
+ pub static mut epoxy_glVertex3d:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3dv"]
+ pub static mut epoxy_glVertex3dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3f"]
+ pub static mut epoxy_glVertex3f:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3fv"]
+ pub static mut epoxy_glVertex3fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3hNV"]
+ pub static mut epoxy_glVertex3hNV:
+ ::std::option::Option<unsafe extern "C" fn(x: GLhalfNV, y: GLhalfNV, z: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3hvNV"]
+ pub static mut epoxy_glVertex3hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3i"]
+ pub static mut epoxy_glVertex3i:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint, z: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3iv"]
+ pub static mut epoxy_glVertex3iv: ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3s"]
+ pub static mut epoxy_glVertex3s:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3sv"]
+ pub static mut epoxy_glVertex3sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3xOES"]
+ pub static mut epoxy_glVertex3xOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex3xvOES"]
+ pub static mut epoxy_glVertex3xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4bOES"]
+ pub static mut epoxy_glVertex4bOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLbyte, y: GLbyte, z: GLbyte, w: GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4bvOES"]
+ pub static mut epoxy_glVertex4bvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4d"]
+ pub static mut epoxy_glVertex4d: ::std::option::Option<
+ unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4dv"]
+ pub static mut epoxy_glVertex4dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4f"]
+ pub static mut epoxy_glVertex4f:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4fv"]
+ pub static mut epoxy_glVertex4fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4hNV"]
+ pub static mut epoxy_glVertex4hNV: ::std::option::Option<
+ unsafe extern "C" fn(x: GLhalfNV, y: GLhalfNV, z: GLhalfNV, w: GLhalfNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4hvNV"]
+ pub static mut epoxy_glVertex4hvNV:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4i"]
+ pub static mut epoxy_glVertex4i:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint, z: GLint, w: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4iv"]
+ pub static mut epoxy_glVertex4iv: ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4s"]
+ pub static mut epoxy_glVertex4s:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort, w: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4sv"]
+ pub static mut epoxy_glVertex4sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4xOES"]
+ pub static mut epoxy_glVertex4xOES:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfixed, y: GLfixed, z: GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertex4xvOES"]
+ pub static mut epoxy_glVertex4xvOES:
+ ::std::option::Option<unsafe extern "C" fn(coords: *const GLfixed)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayAttribBinding"]
+ pub static mut epoxy_glVertexArrayAttribBinding: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, attribindex: GLuint, bindingindex: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayAttribFormat"]
+ pub static mut epoxy_glVertexArrayAttribFormat: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ normalized: GLboolean,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayAttribIFormat"]
+ pub static mut epoxy_glVertexArrayAttribIFormat: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayAttribLFormat"]
+ pub static mut epoxy_glVertexArrayAttribLFormat: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayBindVertexBufferEXT"]
+ pub static mut epoxy_glVertexArrayBindVertexBufferEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ bindingindex: GLuint,
+ buffer: GLuint,
+ offset: GLintptr,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayBindingDivisor"]
+ pub static mut epoxy_glVertexArrayBindingDivisor: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, bindingindex: GLuint, divisor: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayColorOffsetEXT"]
+ pub static mut epoxy_glVertexArrayColorOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayEdgeFlagOffsetEXT"]
+ pub static mut epoxy_glVertexArrayEdgeFlagOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, buffer: GLuint, stride: GLsizei, offset: GLintptr),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayElementBuffer"]
+ pub static mut epoxy_glVertexArrayElementBuffer:
+ ::std::option::Option<unsafe extern "C" fn(vaobj: GLuint, buffer: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayFogCoordOffsetEXT"]
+ pub static mut epoxy_glVertexArrayFogCoordOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayIndexOffsetEXT"]
+ pub static mut epoxy_glVertexArrayIndexOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayMultiTexCoordOffsetEXT"]
+ pub static mut epoxy_glVertexArrayMultiTexCoordOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ texunit: GLenum,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayNormalOffsetEXT"]
+ pub static mut epoxy_glVertexArrayNormalOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayParameteriAPPLE"]
+ pub static mut epoxy_glVertexArrayParameteriAPPLE:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayRangeAPPLE"]
+ pub static mut epoxy_glVertexArrayRangeAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(length: GLsizei, pointer: *mut ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayRangeNV"]
+ pub static mut epoxy_glVertexArrayRangeNV: ::std::option::Option<
+ unsafe extern "C" fn(length: GLsizei, pointer: *const ::std::os::raw::c_void),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArraySecondaryColorOffsetEXT"]
+ pub static mut epoxy_glVertexArraySecondaryColorOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayTexCoordOffsetEXT"]
+ pub static mut epoxy_glVertexArrayTexCoordOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexAttribBindingEXT"]
+ pub static mut epoxy_glVertexArrayVertexAttribBindingEXT: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, attribindex: GLuint, bindingindex: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexAttribDivisorEXT"]
+ pub static mut epoxy_glVertexArrayVertexAttribDivisorEXT:
+ ::std::option::Option<unsafe extern "C" fn(vaobj: GLuint, index: GLuint, divisor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexAttribFormatEXT"]
+ pub static mut epoxy_glVertexArrayVertexAttribFormatEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ normalized: GLboolean,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexAttribIFormatEXT"]
+ pub static mut epoxy_glVertexArrayVertexAttribIFormatEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexAttribIOffsetEXT"]
+ pub static mut epoxy_glVertexArrayVertexAttribIOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexAttribLFormatEXT"]
+ pub static mut epoxy_glVertexArrayVertexAttribLFormatEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexAttribLOffsetEXT"]
+ pub static mut epoxy_glVertexArrayVertexAttribLOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexAttribOffsetEXT"]
+ pub static mut epoxy_glVertexArrayVertexAttribOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ normalized: GLboolean,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexBindingDivisorEXT"]
+ pub static mut epoxy_glVertexArrayVertexBindingDivisorEXT: ::std::option::Option<
+ unsafe extern "C" fn(vaobj: GLuint, bindingindex: GLuint, divisor: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexBuffer"]
+ pub static mut epoxy_glVertexArrayVertexBuffer: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ bindingindex: GLuint,
+ buffer: GLuint,
+ offset: GLintptr,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexBuffers"]
+ pub static mut epoxy_glVertexArrayVertexBuffers: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ first: GLuint,
+ count: GLsizei,
+ buffers: *const GLuint,
+ offsets: *const GLintptr,
+ strides: *const GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexArrayVertexOffsetEXT"]
+ pub static mut epoxy_glVertexArrayVertexOffsetEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ vaobj: GLuint,
+ buffer: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ offset: GLintptr,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1d"]
+ pub static mut epoxy_glVertexAttrib1d:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1dARB"]
+ pub static mut epoxy_glVertexAttrib1dARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1dNV"]
+ pub static mut epoxy_glVertexAttrib1dNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1dv"]
+ pub static mut epoxy_glVertexAttrib1dv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1dvARB"]
+ pub static mut epoxy_glVertexAttrib1dvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1dvNV"]
+ pub static mut epoxy_glVertexAttrib1dvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1f"]
+ pub static mut epoxy_glVertexAttrib1f:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1fARB"]
+ pub static mut epoxy_glVertexAttrib1fARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1fNV"]
+ pub static mut epoxy_glVertexAttrib1fNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1fv"]
+ pub static mut epoxy_glVertexAttrib1fv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1fvARB"]
+ pub static mut epoxy_glVertexAttrib1fvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1fvNV"]
+ pub static mut epoxy_glVertexAttrib1fvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1hNV"]
+ pub static mut epoxy_glVertexAttrib1hNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1hvNV"]
+ pub static mut epoxy_glVertexAttrib1hvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1s"]
+ pub static mut epoxy_glVertexAttrib1s:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1sARB"]
+ pub static mut epoxy_glVertexAttrib1sARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1sNV"]
+ pub static mut epoxy_glVertexAttrib1sNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1sv"]
+ pub static mut epoxy_glVertexAttrib1sv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1svARB"]
+ pub static mut epoxy_glVertexAttrib1svARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib1svNV"]
+ pub static mut epoxy_glVertexAttrib1svNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2d"]
+ pub static mut epoxy_glVertexAttrib2d:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2dARB"]
+ pub static mut epoxy_glVertexAttrib2dARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2dNV"]
+ pub static mut epoxy_glVertexAttrib2dNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2dv"]
+ pub static mut epoxy_glVertexAttrib2dv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2dvARB"]
+ pub static mut epoxy_glVertexAttrib2dvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2dvNV"]
+ pub static mut epoxy_glVertexAttrib2dvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2f"]
+ pub static mut epoxy_glVertexAttrib2f:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2fARB"]
+ pub static mut epoxy_glVertexAttrib2fARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2fNV"]
+ pub static mut epoxy_glVertexAttrib2fNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2fv"]
+ pub static mut epoxy_glVertexAttrib2fv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2fvARB"]
+ pub static mut epoxy_glVertexAttrib2fvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2fvNV"]
+ pub static mut epoxy_glVertexAttrib2fvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2hNV"]
+ pub static mut epoxy_glVertexAttrib2hNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLhalfNV, y: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2hvNV"]
+ pub static mut epoxy_glVertexAttrib2hvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2s"]
+ pub static mut epoxy_glVertexAttrib2s:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2sARB"]
+ pub static mut epoxy_glVertexAttrib2sARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2sNV"]
+ pub static mut epoxy_glVertexAttrib2sNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2sv"]
+ pub static mut epoxy_glVertexAttrib2sv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2svARB"]
+ pub static mut epoxy_glVertexAttrib2svARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib2svNV"]
+ pub static mut epoxy_glVertexAttrib2svNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3d"]
+ pub static mut epoxy_glVertexAttrib3d: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3dARB"]
+ pub static mut epoxy_glVertexAttrib3dARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3dNV"]
+ pub static mut epoxy_glVertexAttrib3dNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3dv"]
+ pub static mut epoxy_glVertexAttrib3dv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3dvARB"]
+ pub static mut epoxy_glVertexAttrib3dvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3dvNV"]
+ pub static mut epoxy_glVertexAttrib3dvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3f"]
+ pub static mut epoxy_glVertexAttrib3f: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3fARB"]
+ pub static mut epoxy_glVertexAttrib3fARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3fNV"]
+ pub static mut epoxy_glVertexAttrib3fNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3fv"]
+ pub static mut epoxy_glVertexAttrib3fv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3fvARB"]
+ pub static mut epoxy_glVertexAttrib3fvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3fvNV"]
+ pub static mut epoxy_glVertexAttrib3fvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3hNV"]
+ pub static mut epoxy_glVertexAttrib3hNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLhalfNV, y: GLhalfNV, z: GLhalfNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3hvNV"]
+ pub static mut epoxy_glVertexAttrib3hvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3s"]
+ pub static mut epoxy_glVertexAttrib3s: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort, z: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3sARB"]
+ pub static mut epoxy_glVertexAttrib3sARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort, z: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3sNV"]
+ pub static mut epoxy_glVertexAttrib3sNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort, z: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3sv"]
+ pub static mut epoxy_glVertexAttrib3sv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3svARB"]
+ pub static mut epoxy_glVertexAttrib3svARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib3svNV"]
+ pub static mut epoxy_glVertexAttrib3svNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4Nbv"]
+ pub static mut epoxy_glVertexAttrib4Nbv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4NbvARB"]
+ pub static mut epoxy_glVertexAttrib4NbvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4Niv"]
+ pub static mut epoxy_glVertexAttrib4Niv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4NivARB"]
+ pub static mut epoxy_glVertexAttrib4NivARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4Nsv"]
+ pub static mut epoxy_glVertexAttrib4Nsv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4NsvARB"]
+ pub static mut epoxy_glVertexAttrib4NsvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4Nub"]
+ pub static mut epoxy_glVertexAttrib4Nub: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLubyte, y: GLubyte, z: GLubyte, w: GLubyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4NubARB"]
+ pub static mut epoxy_glVertexAttrib4NubARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLubyte, y: GLubyte, z: GLubyte, w: GLubyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4Nubv"]
+ pub static mut epoxy_glVertexAttrib4Nubv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4NubvARB"]
+ pub static mut epoxy_glVertexAttrib4NubvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4Nuiv"]
+ pub static mut epoxy_glVertexAttrib4Nuiv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4NuivARB"]
+ pub static mut epoxy_glVertexAttrib4NuivARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4Nusv"]
+ pub static mut epoxy_glVertexAttrib4Nusv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4NusvARB"]
+ pub static mut epoxy_glVertexAttrib4NusvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4bv"]
+ pub static mut epoxy_glVertexAttrib4bv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4bvARB"]
+ pub static mut epoxy_glVertexAttrib4bvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4d"]
+ pub static mut epoxy_glVertexAttrib4d: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4dARB"]
+ pub static mut epoxy_glVertexAttrib4dARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4dNV"]
+ pub static mut epoxy_glVertexAttrib4dNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4dv"]
+ pub static mut epoxy_glVertexAttrib4dv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4dvARB"]
+ pub static mut epoxy_glVertexAttrib4dvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4dvNV"]
+ pub static mut epoxy_glVertexAttrib4dvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4f"]
+ pub static mut epoxy_glVertexAttrib4f: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4fARB"]
+ pub static mut epoxy_glVertexAttrib4fARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4fNV"]
+ pub static mut epoxy_glVertexAttrib4fNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4fv"]
+ pub static mut epoxy_glVertexAttrib4fv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4fvARB"]
+ pub static mut epoxy_glVertexAttrib4fvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4fvNV"]
+ pub static mut epoxy_glVertexAttrib4fvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4hNV"]
+ pub static mut epoxy_glVertexAttrib4hNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLhalfNV, y: GLhalfNV, z: GLhalfNV, w: GLhalfNV),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4hvNV"]
+ pub static mut epoxy_glVertexAttrib4hvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4iv"]
+ pub static mut epoxy_glVertexAttrib4iv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4ivARB"]
+ pub static mut epoxy_glVertexAttrib4ivARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4s"]
+ pub static mut epoxy_glVertexAttrib4s: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort, z: GLshort, w: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4sARB"]
+ pub static mut epoxy_glVertexAttrib4sARB: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort, z: GLshort, w: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4sNV"]
+ pub static mut epoxy_glVertexAttrib4sNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLshort, y: GLshort, z: GLshort, w: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4sv"]
+ pub static mut epoxy_glVertexAttrib4sv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4svARB"]
+ pub static mut epoxy_glVertexAttrib4svARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4svNV"]
+ pub static mut epoxy_glVertexAttrib4svNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4ubNV"]
+ pub static mut epoxy_glVertexAttrib4ubNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLubyte, y: GLubyte, z: GLubyte, w: GLubyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4ubv"]
+ pub static mut epoxy_glVertexAttrib4ubv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4ubvARB"]
+ pub static mut epoxy_glVertexAttrib4ubvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4ubvNV"]
+ pub static mut epoxy_glVertexAttrib4ubvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4uiv"]
+ pub static mut epoxy_glVertexAttrib4uiv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4uivARB"]
+ pub static mut epoxy_glVertexAttrib4uivARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4usv"]
+ pub static mut epoxy_glVertexAttrib4usv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttrib4usvARB"]
+ pub static mut epoxy_glVertexAttrib4usvARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribArrayObjectATI"]
+ pub static mut epoxy_glVertexAttribArrayObjectATI: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ normalized: GLboolean,
+ stride: GLsizei,
+ buffer: GLuint,
+ offset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribBinding"]
+ pub static mut epoxy_glVertexAttribBinding:
+ ::std::option::Option<unsafe extern "C" fn(attribindex: GLuint, bindingindex: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribDivisor"]
+ pub static mut epoxy_glVertexAttribDivisor:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, divisor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribDivisorANGLE"]
+ pub static mut epoxy_glVertexAttribDivisorANGLE:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, divisor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribDivisorARB"]
+ pub static mut epoxy_glVertexAttribDivisorARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, divisor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribDivisorEXT"]
+ pub static mut epoxy_glVertexAttribDivisorEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, divisor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribDivisorNV"]
+ pub static mut epoxy_glVertexAttribDivisorNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, divisor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribFormat"]
+ pub static mut epoxy_glVertexAttribFormat: ::std::option::Option<
+ unsafe extern "C" fn(
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ normalized: GLboolean,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribFormatNV"]
+ pub static mut epoxy_glVertexAttribFormatNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ normalized: GLboolean,
+ stride: GLsizei,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI1i"]
+ pub static mut epoxy_glVertexAttribI1i:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI1iEXT"]
+ pub static mut epoxy_glVertexAttribI1iEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI1iv"]
+ pub static mut epoxy_glVertexAttribI1iv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI1ivEXT"]
+ pub static mut epoxy_glVertexAttribI1ivEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI1ui"]
+ pub static mut epoxy_glVertexAttribI1ui:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI1uiEXT"]
+ pub static mut epoxy_glVertexAttribI1uiEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI1uiv"]
+ pub static mut epoxy_glVertexAttribI1uiv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI1uivEXT"]
+ pub static mut epoxy_glVertexAttribI1uivEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI2i"]
+ pub static mut epoxy_glVertexAttribI2i:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLint, y: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI2iEXT"]
+ pub static mut epoxy_glVertexAttribI2iEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLint, y: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI2iv"]
+ pub static mut epoxy_glVertexAttribI2iv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI2ivEXT"]
+ pub static mut epoxy_glVertexAttribI2ivEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI2ui"]
+ pub static mut epoxy_glVertexAttribI2ui:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint, y: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI2uiEXT"]
+ pub static mut epoxy_glVertexAttribI2uiEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint, y: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI2uiv"]
+ pub static mut epoxy_glVertexAttribI2uiv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI2uivEXT"]
+ pub static mut epoxy_glVertexAttribI2uivEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI3i"]
+ pub static mut epoxy_glVertexAttribI3i:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLint, y: GLint, z: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI3iEXT"]
+ pub static mut epoxy_glVertexAttribI3iEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLint, y: GLint, z: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI3iv"]
+ pub static mut epoxy_glVertexAttribI3iv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI3ivEXT"]
+ pub static mut epoxy_glVertexAttribI3ivEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI3ui"]
+ pub static mut epoxy_glVertexAttribI3ui:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint, y: GLuint, z: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI3uiEXT"]
+ pub static mut epoxy_glVertexAttribI3uiEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint, y: GLuint, z: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI3uiv"]
+ pub static mut epoxy_glVertexAttribI3uiv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI3uivEXT"]
+ pub static mut epoxy_glVertexAttribI3uivEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4bv"]
+ pub static mut epoxy_glVertexAttribI4bv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4bvEXT"]
+ pub static mut epoxy_glVertexAttribI4bvEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4i"]
+ pub static mut epoxy_glVertexAttribI4i: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4iEXT"]
+ pub static mut epoxy_glVertexAttribI4iEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4iv"]
+ pub static mut epoxy_glVertexAttribI4iv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4ivEXT"]
+ pub static mut epoxy_glVertexAttribI4ivEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4sv"]
+ pub static mut epoxy_glVertexAttribI4sv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4svEXT"]
+ pub static mut epoxy_glVertexAttribI4svEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4ubv"]
+ pub static mut epoxy_glVertexAttribI4ubv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4ubvEXT"]
+ pub static mut epoxy_glVertexAttribI4ubvEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4ui"]
+ pub static mut epoxy_glVertexAttribI4ui: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLuint, y: GLuint, z: GLuint, w: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4uiEXT"]
+ pub static mut epoxy_glVertexAttribI4uiEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLuint, y: GLuint, z: GLuint, w: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4uiv"]
+ pub static mut epoxy_glVertexAttribI4uiv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4uivEXT"]
+ pub static mut epoxy_glVertexAttribI4uivEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4usv"]
+ pub static mut epoxy_glVertexAttribI4usv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribI4usvEXT"]
+ pub static mut epoxy_glVertexAttribI4usvEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribIFormat"]
+ pub static mut epoxy_glVertexAttribIFormat: ::std::option::Option<
+ unsafe extern "C" fn(
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribIFormatNV"]
+ pub static mut epoxy_glVertexAttribIFormatNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, size: GLint, type_: GLenum, stride: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribIPointer"]
+ pub static mut epoxy_glVertexAttribIPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribIPointerEXT"]
+ pub static mut epoxy_glVertexAttribIPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1d"]
+ pub static mut epoxy_glVertexAttribL1d:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1dEXT"]
+ pub static mut epoxy_glVertexAttribL1dEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1dv"]
+ pub static mut epoxy_glVertexAttribL1dv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1dvEXT"]
+ pub static mut epoxy_glVertexAttribL1dvEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1i64NV"]
+ pub static mut epoxy_glVertexAttribL1i64NV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1i64vNV"]
+ pub static mut epoxy_glVertexAttribL1i64vNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1ui64ARB"]
+ pub static mut epoxy_glVertexAttribL1ui64ARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1ui64NV"]
+ pub static mut epoxy_glVertexAttribL1ui64NV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1ui64vARB"]
+ pub static mut epoxy_glVertexAttribL1ui64vARB:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL1ui64vNV"]
+ pub static mut epoxy_glVertexAttribL1ui64vNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL2d"]
+ pub static mut epoxy_glVertexAttribL2d:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL2dEXT"]
+ pub static mut epoxy_glVertexAttribL2dEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL2dv"]
+ pub static mut epoxy_glVertexAttribL2dv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL2dvEXT"]
+ pub static mut epoxy_glVertexAttribL2dvEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL2i64NV"]
+ pub static mut epoxy_glVertexAttribL2i64NV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLint64EXT, y: GLint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL2i64vNV"]
+ pub static mut epoxy_glVertexAttribL2i64vNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL2ui64NV"]
+ pub static mut epoxy_glVertexAttribL2ui64NV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, x: GLuint64EXT, y: GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL2ui64vNV"]
+ pub static mut epoxy_glVertexAttribL2ui64vNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL3d"]
+ pub static mut epoxy_glVertexAttribL3d: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL3dEXT"]
+ pub static mut epoxy_glVertexAttribL3dEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL3dv"]
+ pub static mut epoxy_glVertexAttribL3dv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL3dvEXT"]
+ pub static mut epoxy_glVertexAttribL3dvEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL3i64NV"]
+ pub static mut epoxy_glVertexAttribL3i64NV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLint64EXT, y: GLint64EXT, z: GLint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL3i64vNV"]
+ pub static mut epoxy_glVertexAttribL3i64vNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL3ui64NV"]
+ pub static mut epoxy_glVertexAttribL3ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLuint64EXT, y: GLuint64EXT, z: GLuint64EXT),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL3ui64vNV"]
+ pub static mut epoxy_glVertexAttribL3ui64vNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL4d"]
+ pub static mut epoxy_glVertexAttribL4d: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL4dEXT"]
+ pub static mut epoxy_glVertexAttribL4dEXT: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL4dv"]
+ pub static mut epoxy_glVertexAttribL4dv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL4dvEXT"]
+ pub static mut epoxy_glVertexAttribL4dvEXT:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL4i64NV"]
+ pub static mut epoxy_glVertexAttribL4i64NV: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ x: GLint64EXT,
+ y: GLint64EXT,
+ z: GLint64EXT,
+ w: GLint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL4i64vNV"]
+ pub static mut epoxy_glVertexAttribL4i64vNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL4ui64NV"]
+ pub static mut epoxy_glVertexAttribL4ui64NV: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ x: GLuint64EXT,
+ y: GLuint64EXT,
+ z: GLuint64EXT,
+ w: GLuint64EXT,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribL4ui64vNV"]
+ pub static mut epoxy_glVertexAttribL4ui64vNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLuint64EXT)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribLFormat"]
+ pub static mut epoxy_glVertexAttribLFormat: ::std::option::Option<
+ unsafe extern "C" fn(
+ attribindex: GLuint,
+ size: GLint,
+ type_: GLenum,
+ relativeoffset: GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribLFormatNV"]
+ pub static mut epoxy_glVertexAttribLFormatNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, size: GLint, type_: GLenum, stride: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribLPointer"]
+ pub static mut epoxy_glVertexAttribLPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribLPointerEXT"]
+ pub static mut epoxy_glVertexAttribLPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribP1ui"]
+ pub static mut epoxy_glVertexAttribP1ui: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, type_: GLenum, normalized: GLboolean, value: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribP1uiv"]
+ pub static mut epoxy_glVertexAttribP1uiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ type_: GLenum,
+ normalized: GLboolean,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribP2ui"]
+ pub static mut epoxy_glVertexAttribP2ui: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, type_: GLenum, normalized: GLboolean, value: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribP2uiv"]
+ pub static mut epoxy_glVertexAttribP2uiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ type_: GLenum,
+ normalized: GLboolean,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribP3ui"]
+ pub static mut epoxy_glVertexAttribP3ui: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, type_: GLenum, normalized: GLboolean, value: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribP3uiv"]
+ pub static mut epoxy_glVertexAttribP3uiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ type_: GLenum,
+ normalized: GLboolean,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribP4ui"]
+ pub static mut epoxy_glVertexAttribP4ui: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, type_: GLenum, normalized: GLboolean, value: GLuint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribP4uiv"]
+ pub static mut epoxy_glVertexAttribP4uiv: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ type_: GLenum,
+ normalized: GLboolean,
+ value: *const GLuint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribParameteriAMD"]
+ pub static mut epoxy_glVertexAttribParameteriAMD:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribPointer"]
+ pub static mut epoxy_glVertexAttribPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ normalized: GLboolean,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribPointerARB"]
+ pub static mut epoxy_glVertexAttribPointerARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ size: GLint,
+ type_: GLenum,
+ normalized: GLboolean,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribPointerNV"]
+ pub static mut epoxy_glVertexAttribPointerNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ fsize: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs1dvNV"]
+ pub static mut epoxy_glVertexAttribs1dvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs1fvNV"]
+ pub static mut epoxy_glVertexAttribs1fvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs1hvNV"]
+ pub static mut epoxy_glVertexAttribs1hvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, n: GLsizei, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs1svNV"]
+ pub static mut epoxy_glVertexAttribs1svNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs2dvNV"]
+ pub static mut epoxy_glVertexAttribs2dvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs2fvNV"]
+ pub static mut epoxy_glVertexAttribs2fvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs2hvNV"]
+ pub static mut epoxy_glVertexAttribs2hvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, n: GLsizei, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs2svNV"]
+ pub static mut epoxy_glVertexAttribs2svNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs3dvNV"]
+ pub static mut epoxy_glVertexAttribs3dvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs3fvNV"]
+ pub static mut epoxy_glVertexAttribs3fvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs3hvNV"]
+ pub static mut epoxy_glVertexAttribs3hvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, n: GLsizei, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs3svNV"]
+ pub static mut epoxy_glVertexAttribs3svNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs4dvNV"]
+ pub static mut epoxy_glVertexAttribs4dvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs4fvNV"]
+ pub static mut epoxy_glVertexAttribs4fvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs4hvNV"]
+ pub static mut epoxy_glVertexAttribs4hvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, n: GLsizei, v: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs4svNV"]
+ pub static mut epoxy_glVertexAttribs4svNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexAttribs4ubvNV"]
+ pub static mut epoxy_glVertexAttribs4ubvNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, count: GLsizei, v: *const GLubyte),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexBindingDivisor"]
+ pub static mut epoxy_glVertexBindingDivisor:
+ ::std::option::Option<unsafe extern "C" fn(bindingindex: GLuint, divisor: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexBlendARB"]
+ pub static mut epoxy_glVertexBlendARB:
+ ::std::option::Option<unsafe extern "C" fn(count: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexBlendEnvfATI"]
+ pub static mut epoxy_glVertexBlendEnvfATI:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexBlendEnviATI"]
+ pub static mut epoxy_glVertexBlendEnviATI:
+ ::std::option::Option<unsafe extern "C" fn(pname: GLenum, param: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexFormatNV"]
+ pub static mut epoxy_glVertexFormatNV:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, type_: GLenum, stride: GLsizei)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexP2ui"]
+ pub static mut epoxy_glVertexP2ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, value: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexP2uiv"]
+ pub static mut epoxy_glVertexP2uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, value: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexP3ui"]
+ pub static mut epoxy_glVertexP3ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, value: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexP3uiv"]
+ pub static mut epoxy_glVertexP3uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, value: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexP4ui"]
+ pub static mut epoxy_glVertexP4ui:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, value: GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexP4uiv"]
+ pub static mut epoxy_glVertexP4uiv:
+ ::std::option::Option<unsafe extern "C" fn(type_: GLenum, value: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexPointer"]
+ pub static mut epoxy_glVertexPointer: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexPointerEXT"]
+ pub static mut epoxy_glVertexPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ count: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexPointerListIBM"]
+ pub static mut epoxy_glVertexPointerListIBM: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLint,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ptrstride: GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexPointervINTEL"]
+ pub static mut epoxy_glVertexPointervINTEL: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ pointer: *mut *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream1dATI"]
+ pub static mut epoxy_glVertexStream1dATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream1dvATI"]
+ pub static mut epoxy_glVertexStream1dvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream1fATI"]
+ pub static mut epoxy_glVertexStream1fATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream1fvATI"]
+ pub static mut epoxy_glVertexStream1fvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream1iATI"]
+ pub static mut epoxy_glVertexStream1iATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream1ivATI"]
+ pub static mut epoxy_glVertexStream1ivATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream1sATI"]
+ pub static mut epoxy_glVertexStream1sATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream1svATI"]
+ pub static mut epoxy_glVertexStream1svATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream2dATI"]
+ pub static mut epoxy_glVertexStream2dATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream2dvATI"]
+ pub static mut epoxy_glVertexStream2dvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream2fATI"]
+ pub static mut epoxy_glVertexStream2fATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream2fvATI"]
+ pub static mut epoxy_glVertexStream2fvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream2iATI"]
+ pub static mut epoxy_glVertexStream2iATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLint, y: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream2ivATI"]
+ pub static mut epoxy_glVertexStream2ivATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream2sATI"]
+ pub static mut epoxy_glVertexStream2sATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream2svATI"]
+ pub static mut epoxy_glVertexStream2svATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream3dATI"]
+ pub static mut epoxy_glVertexStream3dATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, x: GLdouble, y: GLdouble, z: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream3dvATI"]
+ pub static mut epoxy_glVertexStream3dvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream3fATI"]
+ pub static mut epoxy_glVertexStream3fATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, x: GLfloat, y: GLfloat, z: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream3fvATI"]
+ pub static mut epoxy_glVertexStream3fvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream3iATI"]
+ pub static mut epoxy_glVertexStream3iATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, x: GLint, y: GLint, z: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream3ivATI"]
+ pub static mut epoxy_glVertexStream3ivATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream3sATI"]
+ pub static mut epoxy_glVertexStream3sATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, x: GLshort, y: GLshort, z: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream3svATI"]
+ pub static mut epoxy_glVertexStream3svATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream4dATI"]
+ pub static mut epoxy_glVertexStream4dATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream4dvATI"]
+ pub static mut epoxy_glVertexStream4dvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream4fATI"]
+ pub static mut epoxy_glVertexStream4fATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream4fvATI"]
+ pub static mut epoxy_glVertexStream4fvATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream4iATI"]
+ pub static mut epoxy_glVertexStream4iATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, x: GLint, y: GLint, z: GLint, w: GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream4ivATI"]
+ pub static mut epoxy_glVertexStream4ivATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream4sATI"]
+ pub static mut epoxy_glVertexStream4sATI: ::std::option::Option<
+ unsafe extern "C" fn(stream: GLenum, x: GLshort, y: GLshort, z: GLshort, w: GLshort),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexStream4svATI"]
+ pub static mut epoxy_glVertexStream4svATI:
+ ::std::option::Option<unsafe extern "C" fn(stream: GLenum, coords: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexWeightPointerEXT"]
+ pub static mut epoxy_glVertexWeightPointerEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexWeightfEXT"]
+ pub static mut epoxy_glVertexWeightfEXT:
+ ::std::option::Option<unsafe extern "C" fn(weight: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexWeightfvEXT"]
+ pub static mut epoxy_glVertexWeightfvEXT:
+ ::std::option::Option<unsafe extern "C" fn(weight: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexWeighthNV"]
+ pub static mut epoxy_glVertexWeighthNV:
+ ::std::option::Option<unsafe extern "C" fn(weight: GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVertexWeighthvNV"]
+ pub static mut epoxy_glVertexWeighthvNV:
+ ::std::option::Option<unsafe extern "C" fn(weight: *const GLhalfNV)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVideoCaptureNV"]
+ pub static mut epoxy_glVideoCaptureNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ sequence_num: *mut GLuint,
+ capture_time: *mut GLuint64EXT,
+ ) -> GLenum,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVideoCaptureStreamParameterdvNV"]
+ pub static mut epoxy_glVideoCaptureStreamParameterdvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ stream: GLuint,
+ pname: GLenum,
+ params: *const GLdouble,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVideoCaptureStreamParameterfvNV"]
+ pub static mut epoxy_glVideoCaptureStreamParameterfvNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ stream: GLuint,
+ pname: GLenum,
+ params: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glVideoCaptureStreamParameterivNV"]
+ pub static mut epoxy_glVideoCaptureStreamParameterivNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ video_capture_slot: GLuint,
+ stream: GLuint,
+ pname: GLenum,
+ params: *const GLint,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewport"]
+ pub static mut epoxy_glViewport: ::std::option::Option<
+ unsafe extern "C" fn(x: GLint, y: GLint, width: GLsizei, height: GLsizei),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportArrayv"]
+ pub static mut epoxy_glViewportArrayv: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportArrayvNV"]
+ pub static mut epoxy_glViewportArrayvNV: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportArrayvOES"]
+ pub static mut epoxy_glViewportArrayvOES: ::std::option::Option<
+ unsafe extern "C" fn(first: GLuint, count: GLsizei, v: *const GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportIndexedf"]
+ pub static mut epoxy_glViewportIndexedf: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, w: GLfloat, h: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportIndexedfNV"]
+ pub static mut epoxy_glViewportIndexedfNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, w: GLfloat, h: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportIndexedfOES"]
+ pub static mut epoxy_glViewportIndexedfOES: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, x: GLfloat, y: GLfloat, w: GLfloat, h: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportIndexedfv"]
+ pub static mut epoxy_glViewportIndexedfv:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportIndexedfvNV"]
+ pub static mut epoxy_glViewportIndexedfvNV:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportIndexedfvOES"]
+ pub static mut epoxy_glViewportIndexedfvOES:
+ ::std::option::Option<unsafe extern "C" fn(index: GLuint, v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportPositionWScaleNV"]
+ pub static mut epoxy_glViewportPositionWScaleNV: ::std::option::Option<
+ unsafe extern "C" fn(index: GLuint, xcoeff: GLfloat, ycoeff: GLfloat),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glViewportSwizzleNV"]
+ pub static mut epoxy_glViewportSwizzleNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ index: GLuint,
+ swizzlex: GLenum,
+ swizzley: GLenum,
+ swizzlez: GLenum,
+ swizzlew: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWaitSync"]
+ pub static mut epoxy_glWaitSync: ::std::option::Option<
+ unsafe extern "C" fn(sync: GLsync, flags: GLbitfield, timeout: GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWaitSyncAPPLE"]
+ pub static mut epoxy_glWaitSyncAPPLE: ::std::option::Option<
+ unsafe extern "C" fn(sync: GLsync, flags: GLbitfield, timeout: GLuint64),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightPathsNV"]
+ pub static mut epoxy_glWeightPathsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ resultPath: GLuint,
+ numPaths: GLsizei,
+ paths: *const GLuint,
+ weights: *const GLfloat,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightPointerARB"]
+ pub static mut epoxy_glWeightPointerARB: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightPointerOES"]
+ pub static mut epoxy_glWeightPointerOES: ::std::option::Option<
+ unsafe extern "C" fn(
+ size: GLint,
+ type_: GLenum,
+ stride: GLsizei,
+ pointer: *const ::std::os::raw::c_void,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightbvARB"]
+ pub static mut epoxy_glWeightbvARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, weights: *const GLbyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightdvARB"]
+ pub static mut epoxy_glWeightdvARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, weights: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightfvARB"]
+ pub static mut epoxy_glWeightfvARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, weights: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightivARB"]
+ pub static mut epoxy_glWeightivARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, weights: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightsvARB"]
+ pub static mut epoxy_glWeightsvARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, weights: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightubvARB"]
+ pub static mut epoxy_glWeightubvARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, weights: *const GLubyte)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightuivARB"]
+ pub static mut epoxy_glWeightuivARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, weights: *const GLuint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWeightusvARB"]
+ pub static mut epoxy_glWeightusvARB:
+ ::std::option::Option<unsafe extern "C" fn(size: GLint, weights: *const GLushort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2d"]
+ pub static mut epoxy_glWindowPos2d:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2dARB"]
+ pub static mut epoxy_glWindowPos2dARB:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2dMESA"]
+ pub static mut epoxy_glWindowPos2dMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2dv"]
+ pub static mut epoxy_glWindowPos2dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2dvARB"]
+ pub static mut epoxy_glWindowPos2dvARB:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2dvMESA"]
+ pub static mut epoxy_glWindowPos2dvMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2f"]
+ pub static mut epoxy_glWindowPos2f:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2fARB"]
+ pub static mut epoxy_glWindowPos2fARB:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2fMESA"]
+ pub static mut epoxy_glWindowPos2fMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2fv"]
+ pub static mut epoxy_glWindowPos2fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2fvARB"]
+ pub static mut epoxy_glWindowPos2fvARB:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2fvMESA"]
+ pub static mut epoxy_glWindowPos2fvMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2i"]
+ pub static mut epoxy_glWindowPos2i:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2iARB"]
+ pub static mut epoxy_glWindowPos2iARB:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2iMESA"]
+ pub static mut epoxy_glWindowPos2iMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2iv"]
+ pub static mut epoxy_glWindowPos2iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2ivARB"]
+ pub static mut epoxy_glWindowPos2ivARB:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2ivMESA"]
+ pub static mut epoxy_glWindowPos2ivMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2s"]
+ pub static mut epoxy_glWindowPos2s:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2sARB"]
+ pub static mut epoxy_glWindowPos2sARB:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2sMESA"]
+ pub static mut epoxy_glWindowPos2sMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2sv"]
+ pub static mut epoxy_glWindowPos2sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2svARB"]
+ pub static mut epoxy_glWindowPos2svARB:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos2svMESA"]
+ pub static mut epoxy_glWindowPos2svMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3d"]
+ pub static mut epoxy_glWindowPos3d:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3dARB"]
+ pub static mut epoxy_glWindowPos3dARB:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3dMESA"]
+ pub static mut epoxy_glWindowPos3dMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3dv"]
+ pub static mut epoxy_glWindowPos3dv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3dvARB"]
+ pub static mut epoxy_glWindowPos3dvARB:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3dvMESA"]
+ pub static mut epoxy_glWindowPos3dvMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3f"]
+ pub static mut epoxy_glWindowPos3f:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3fARB"]
+ pub static mut epoxy_glWindowPos3fARB:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3fMESA"]
+ pub static mut epoxy_glWindowPos3fMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3fv"]
+ pub static mut epoxy_glWindowPos3fv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3fvARB"]
+ pub static mut epoxy_glWindowPos3fvARB:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3fvMESA"]
+ pub static mut epoxy_glWindowPos3fvMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3i"]
+ pub static mut epoxy_glWindowPos3i:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint, z: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3iARB"]
+ pub static mut epoxy_glWindowPos3iARB:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint, z: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3iMESA"]
+ pub static mut epoxy_glWindowPos3iMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint, z: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3iv"]
+ pub static mut epoxy_glWindowPos3iv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3ivARB"]
+ pub static mut epoxy_glWindowPos3ivARB:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3ivMESA"]
+ pub static mut epoxy_glWindowPos3ivMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3s"]
+ pub static mut epoxy_glWindowPos3s:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3sARB"]
+ pub static mut epoxy_glWindowPos3sARB:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3sMESA"]
+ pub static mut epoxy_glWindowPos3sMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3sv"]
+ pub static mut epoxy_glWindowPos3sv:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3svARB"]
+ pub static mut epoxy_glWindowPos3svARB:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos3svMESA"]
+ pub static mut epoxy_glWindowPos3svMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos4dMESA"]
+ pub static mut epoxy_glWindowPos4dMESA: ::std::option::Option<
+ unsafe extern "C" fn(x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos4dvMESA"]
+ pub static mut epoxy_glWindowPos4dvMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLdouble)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos4fMESA"]
+ pub static mut epoxy_glWindowPos4fMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos4fvMESA"]
+ pub static mut epoxy_glWindowPos4fvMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLfloat)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos4iMESA"]
+ pub static mut epoxy_glWindowPos4iMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLint, y: GLint, z: GLint, w: GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos4ivMESA"]
+ pub static mut epoxy_glWindowPos4ivMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLint)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos4sMESA"]
+ pub static mut epoxy_glWindowPos4sMESA:
+ ::std::option::Option<unsafe extern "C" fn(x: GLshort, y: GLshort, z: GLshort, w: GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowPos4svMESA"]
+ pub static mut epoxy_glWindowPos4svMESA:
+ ::std::option::Option<unsafe extern "C" fn(v: *const GLshort)>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWindowRectanglesEXT"]
+ pub static mut epoxy_glWindowRectanglesEXT: ::std::option::Option<
+ unsafe extern "C" fn(mode: GLenum, count: GLsizei, box_: *const GLint),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_glWriteMaskEXT"]
+ pub static mut epoxy_glWriteMaskEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ res: GLuint,
+ in_: GLuint,
+ outX: GLenum,
+ outY: GLenum,
+ outZ: GLenum,
+ outW: GLenum,
+ ),
+ >;
+}
+extern "C" {
+ pub fn epoxy_has_gl_extension(extension: *const ::std::os::raw::c_char) -> bool;
+}
+extern "C" {
+ pub fn epoxy_is_desktop_gl() -> bool;
+}
+extern "C" {
+ pub fn epoxy_gl_version() -> ::std::os::raw::c_int;
+}
+pub type XID = ::std::os::raw::c_ulong;
+pub type Window = XID;
+pub type Pixmap = XID;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct _XDisplay {
+ _unused: [u8; 0],
+}
+pub type Display = _XDisplay;
+pub type EGLNativeDisplayType = *mut Display;
+pub type EGLNativePixmapType = Pixmap;
+pub type EGLNativeWindowType = Window;
+pub type EGLint = khronos_int32_t;
+pub type EGLBoolean = ::std::os::raw::c_uint;
+pub type EGLenum = ::std::os::raw::c_uint;
+pub type EGLAttribKHR = isize;
+pub type EGLAttrib = isize;
+pub type EGLClientBuffer = *mut ::std::os::raw::c_void;
+pub type EGLConfig = *mut ::std::os::raw::c_void;
+pub type EGLContext = *mut ::std::os::raw::c_void;
+pub type EGLDeviceEXT = *mut ::std::os::raw::c_void;
+pub type EGLDisplay = *mut ::std::os::raw::c_void;
+pub type EGLImage = *mut ::std::os::raw::c_void;
+pub type EGLImageKHR = *mut ::std::os::raw::c_void;
+pub type EGLLabelKHR = *mut ::std::os::raw::c_void;
+pub type EGLObjectKHR = *mut ::std::os::raw::c_void;
+pub type EGLOutputLayerEXT = *mut ::std::os::raw::c_void;
+pub type EGLOutputPortEXT = *mut ::std::os::raw::c_void;
+pub type EGLStreamKHR = *mut ::std::os::raw::c_void;
+pub type EGLSurface = *mut ::std::os::raw::c_void;
+pub type EGLSync = *mut ::std::os::raw::c_void;
+pub type EGLSyncKHR = *mut ::std::os::raw::c_void;
+pub type EGLSyncNV = *mut ::std::os::raw::c_void;
+pub type __eglMustCastToProperFunctionPointerType = ::std::option::Option<unsafe extern "C" fn()>;
+pub type EGLTimeKHR = khronos_utime_nanoseconds_t;
+pub type EGLTime = khronos_utime_nanoseconds_t;
+pub type EGLTimeNV = khronos_utime_nanoseconds_t;
+pub type EGLuint64NV = khronos_utime_nanoseconds_t;
+pub type EGLuint64KHR = khronos_uint64_t;
+pub type EGLnsecsANDROID = khronos_stime_nanoseconds_t;
+pub type EGLNativeFileDescriptorKHR = ::std::os::raw::c_int;
+pub type EGLsizeiANDROID = khronos_ssize_t;
+pub type EGLSetBlobFuncANDROID = ::std::option::Option<
+ unsafe extern "C" fn(
+ key: *const ::std::os::raw::c_void,
+ keySize: EGLsizeiANDROID,
+ value: *const ::std::os::raw::c_void,
+ valueSize: EGLsizeiANDROID,
+ ),
+>;
+pub type EGLGetBlobFuncANDROID = ::std::option::Option<
+ unsafe extern "C" fn(
+ key: *const ::std::os::raw::c_void,
+ keySize: EGLsizeiANDROID,
+ value: *mut ::std::os::raw::c_void,
+ valueSize: EGLsizeiANDROID,
+ ) -> EGLsizeiANDROID,
+>;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct EGLClientPixmapHI {
+ pub pData: *mut ::std::os::raw::c_void,
+ pub iWidth: EGLint,
+ pub iHeight: EGLint,
+ pub iStride: EGLint,
+}
+pub type EGLDEBUGPROCKHR = ::std::option::Option<
+ unsafe extern "C" fn(
+ error: EGLenum,
+ command: *const ::std::os::raw::c_char,
+ messageType: EGLint,
+ threadLabel: EGLLabelKHR,
+ objectLabel: EGLLabelKHR,
+ message: *const ::std::os::raw::c_char,
+ ),
+>;
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglBindAPI"]
+ pub static mut epoxy_eglBindAPI:
+ ::std::option::Option<unsafe extern "C" fn(api: EGLenum) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglBindTexImage"]
+ pub static mut epoxy_eglBindTexImage: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, surface: EGLSurface, buffer: EGLint) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglChooseConfig"]
+ pub static mut epoxy_eglChooseConfig: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ attrib_list: *const EGLint,
+ configs: *mut EGLConfig,
+ config_size: EGLint,
+ num_config: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglClientWaitSync"]
+ pub static mut epoxy_eglClientWaitSync: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ sync: EGLSync,
+ flags: EGLint,
+ timeout: EGLTime,
+ ) -> EGLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglClientWaitSyncKHR"]
+ pub static mut epoxy_eglClientWaitSyncKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ sync: EGLSyncKHR,
+ flags: EGLint,
+ timeout: EGLTimeKHR,
+ ) -> EGLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglClientWaitSyncNV"]
+ pub static mut epoxy_eglClientWaitSyncNV: ::std::option::Option<
+ unsafe extern "C" fn(sync: EGLSyncNV, flags: EGLint, timeout: EGLTimeNV) -> EGLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCopyBuffers"]
+ pub static mut epoxy_eglCopyBuffers: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ target: EGLNativePixmapType,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateContext"]
+ pub static mut epoxy_eglCreateContext: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ share_context: EGLContext,
+ attrib_list: *const EGLint,
+ ) -> EGLContext,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateDRMImageMESA"]
+ pub static mut epoxy_eglCreateDRMImageMESA: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, attrib_list: *const EGLint) -> EGLImageKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateFenceSyncNV"]
+ pub static mut epoxy_eglCreateFenceSyncNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ condition: EGLenum,
+ attrib_list: *const EGLint,
+ ) -> EGLSyncNV,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateImage"]
+ pub static mut epoxy_eglCreateImage: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ ctx: EGLContext,
+ target: EGLenum,
+ buffer: EGLClientBuffer,
+ attrib_list: *const EGLAttrib,
+ ) -> EGLImage,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateImageKHR"]
+ pub static mut epoxy_eglCreateImageKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ ctx: EGLContext,
+ target: EGLenum,
+ buffer: EGLClientBuffer,
+ attrib_list: *const EGLint,
+ ) -> EGLImageKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateNativeClientBufferANDROID"]
+ pub static mut epoxy_eglCreateNativeClientBufferANDROID:
+ ::std::option::Option<unsafe extern "C" fn(attrib_list: *const EGLint) -> EGLClientBuffer>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreatePbufferFromClientBuffer"]
+ pub static mut epoxy_eglCreatePbufferFromClientBuffer: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ buftype: EGLenum,
+ buffer: EGLClientBuffer,
+ config: EGLConfig,
+ attrib_list: *const EGLint,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreatePbufferSurface"]
+ pub static mut epoxy_eglCreatePbufferSurface: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ attrib_list: *const EGLint,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreatePixmapSurface"]
+ pub static mut epoxy_eglCreatePixmapSurface: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ pixmap: EGLNativePixmapType,
+ attrib_list: *const EGLint,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreatePixmapSurfaceHI"]
+ pub static mut epoxy_eglCreatePixmapSurfaceHI: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ pixmap: *mut EGLClientPixmapHI,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreatePlatformPixmapSurface"]
+ pub static mut epoxy_eglCreatePlatformPixmapSurface: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ native_pixmap: *mut ::std::os::raw::c_void,
+ attrib_list: *const EGLAttrib,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreatePlatformPixmapSurfaceEXT"]
+ pub static mut epoxy_eglCreatePlatformPixmapSurfaceEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ native_pixmap: *mut ::std::os::raw::c_void,
+ attrib_list: *const EGLint,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreatePlatformWindowSurface"]
+ pub static mut epoxy_eglCreatePlatformWindowSurface: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ native_window: *mut ::std::os::raw::c_void,
+ attrib_list: *const EGLAttrib,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreatePlatformWindowSurfaceEXT"]
+ pub static mut epoxy_eglCreatePlatformWindowSurfaceEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ native_window: *mut ::std::os::raw::c_void,
+ attrib_list: *const EGLint,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateStreamAttribKHR"]
+ pub static mut epoxy_eglCreateStreamAttribKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, attrib_list: *const EGLAttrib) -> EGLStreamKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateStreamFromFileDescriptorKHR"]
+ pub static mut epoxy_eglCreateStreamFromFileDescriptorKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ file_descriptor: EGLNativeFileDescriptorKHR,
+ ) -> EGLStreamKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateStreamKHR"]
+ pub static mut epoxy_eglCreateStreamKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, attrib_list: *const EGLint) -> EGLStreamKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateStreamProducerSurfaceKHR"]
+ pub static mut epoxy_eglCreateStreamProducerSurfaceKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ stream: EGLStreamKHR,
+ attrib_list: *const EGLint,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateStreamSyncNV"]
+ pub static mut epoxy_eglCreateStreamSyncNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ type_: EGLenum,
+ attrib_list: *const EGLint,
+ ) -> EGLSyncKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateSync"]
+ pub static mut epoxy_eglCreateSync: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ type_: EGLenum,
+ attrib_list: *const EGLAttrib,
+ ) -> EGLSync,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateSync64KHR"]
+ pub static mut epoxy_eglCreateSync64KHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ type_: EGLenum,
+ attrib_list: *const EGLAttribKHR,
+ ) -> EGLSyncKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateSyncKHR"]
+ pub static mut epoxy_eglCreateSyncKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ type_: EGLenum,
+ attrib_list: *const EGLint,
+ ) -> EGLSyncKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglCreateWindowSurface"]
+ pub static mut epoxy_eglCreateWindowSurface: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ win: EGLNativeWindowType,
+ attrib_list: *const EGLint,
+ ) -> EGLSurface,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDebugMessageControlKHR"]
+ pub static mut epoxy_eglDebugMessageControlKHR: ::std::option::Option<
+ unsafe extern "C" fn(callback: EGLDEBUGPROCKHR, attrib_list: *const EGLAttrib) -> EGLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDestroyContext"]
+ pub static mut epoxy_eglDestroyContext:
+ ::std::option::Option<unsafe extern "C" fn(dpy: EGLDisplay, ctx: EGLContext) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDestroyImage"]
+ pub static mut epoxy_eglDestroyImage:
+ ::std::option::Option<unsafe extern "C" fn(dpy: EGLDisplay, image: EGLImage) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDestroyImageKHR"]
+ pub static mut epoxy_eglDestroyImageKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, image: EGLImageKHR) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDestroyStreamKHR"]
+ pub static mut epoxy_eglDestroyStreamKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, stream: EGLStreamKHR) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDestroySurface"]
+ pub static mut epoxy_eglDestroySurface: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, surface: EGLSurface) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDestroySync"]
+ pub static mut epoxy_eglDestroySync:
+ ::std::option::Option<unsafe extern "C" fn(dpy: EGLDisplay, sync: EGLSync) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDestroySyncKHR"]
+ pub static mut epoxy_eglDestroySyncKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, sync: EGLSyncKHR) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDestroySyncNV"]
+ pub static mut epoxy_eglDestroySyncNV:
+ ::std::option::Option<unsafe extern "C" fn(sync: EGLSyncNV) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglDupNativeFenceFDANDROID"]
+ pub static mut epoxy_eglDupNativeFenceFDANDROID:
+ ::std::option::Option<unsafe extern "C" fn(dpy: EGLDisplay, sync: EGLSyncKHR) -> EGLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglExportDMABUFImageMESA"]
+ pub static mut epoxy_eglExportDMABUFImageMESA: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ image: EGLImageKHR,
+ fds: *mut ::std::os::raw::c_int,
+ strides: *mut EGLint,
+ offsets: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglExportDMABUFImageQueryMESA"]
+ pub static mut epoxy_eglExportDMABUFImageQueryMESA: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ image: EGLImageKHR,
+ fourcc: *mut ::std::os::raw::c_int,
+ num_planes: *mut ::std::os::raw::c_int,
+ modifiers: *mut EGLuint64KHR,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglExportDRMImageMESA"]
+ pub static mut epoxy_eglExportDRMImageMESA: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ image: EGLImageKHR,
+ name: *mut EGLint,
+ handle: *mut EGLint,
+ stride: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglFenceNV"]
+ pub static mut epoxy_eglFenceNV:
+ ::std::option::Option<unsafe extern "C" fn(sync: EGLSyncNV) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetConfigAttrib"]
+ pub static mut epoxy_eglGetConfigAttrib: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ attribute: EGLint,
+ value: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetConfigs"]
+ pub static mut epoxy_eglGetConfigs: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ configs: *mut EGLConfig,
+ config_size: EGLint,
+ num_config: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetCurrentContext"]
+ pub static mut epoxy_eglGetCurrentContext:
+ ::std::option::Option<unsafe extern "C" fn() -> EGLContext>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetCurrentDisplay"]
+ pub static mut epoxy_eglGetCurrentDisplay:
+ ::std::option::Option<unsafe extern "C" fn() -> EGLDisplay>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetCurrentSurface"]
+ pub static mut epoxy_eglGetCurrentSurface:
+ ::std::option::Option<unsafe extern "C" fn(readdraw: EGLint) -> EGLSurface>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetDisplay"]
+ pub static mut epoxy_eglGetDisplay:
+ ::std::option::Option<unsafe extern "C" fn(display_id: EGLNativeDisplayType) -> EGLDisplay>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetError"]
+ pub static mut epoxy_eglGetError: ::std::option::Option<unsafe extern "C" fn() -> EGLint>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetOutputLayersEXT"]
+ pub static mut epoxy_eglGetOutputLayersEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ attrib_list: *const EGLAttrib,
+ layers: *mut EGLOutputLayerEXT,
+ max_layers: EGLint,
+ num_layers: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetOutputPortsEXT"]
+ pub static mut epoxy_eglGetOutputPortsEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ attrib_list: *const EGLAttrib,
+ ports: *mut EGLOutputPortEXT,
+ max_ports: EGLint,
+ num_ports: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetPlatformDisplay"]
+ pub static mut epoxy_eglGetPlatformDisplay: ::std::option::Option<
+ unsafe extern "C" fn(
+ platform: EGLenum,
+ native_display: *mut ::std::os::raw::c_void,
+ attrib_list: *const EGLAttrib,
+ ) -> EGLDisplay,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetPlatformDisplayEXT"]
+ pub static mut epoxy_eglGetPlatformDisplayEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ platform: EGLenum,
+ native_display: *mut ::std::os::raw::c_void,
+ attrib_list: *const EGLint,
+ ) -> EGLDisplay,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetProcAddress"]
+ pub static mut epoxy_eglGetProcAddress: ::std::option::Option<
+ unsafe extern "C" fn(
+ procname: *const ::std::os::raw::c_char,
+ ) -> __eglMustCastToProperFunctionPointerType,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetStreamFileDescriptorKHR"]
+ pub static mut epoxy_eglGetStreamFileDescriptorKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, stream: EGLStreamKHR) -> EGLNativeFileDescriptorKHR,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetSyncAttrib"]
+ pub static mut epoxy_eglGetSyncAttrib: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ sync: EGLSync,
+ attribute: EGLint,
+ value: *mut EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetSyncAttribKHR"]
+ pub static mut epoxy_eglGetSyncAttribKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ sync: EGLSyncKHR,
+ attribute: EGLint,
+ value: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetSyncAttribNV"]
+ pub static mut epoxy_eglGetSyncAttribNV: ::std::option::Option<
+ unsafe extern "C" fn(sync: EGLSyncNV, attribute: EGLint, value: *mut EGLint) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetSystemTimeFrequencyNV"]
+ pub static mut epoxy_eglGetSystemTimeFrequencyNV:
+ ::std::option::Option<unsafe extern "C" fn() -> EGLuint64NV>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglGetSystemTimeNV"]
+ pub static mut epoxy_eglGetSystemTimeNV:
+ ::std::option::Option<unsafe extern "C" fn() -> EGLuint64NV>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglInitialize"]
+ pub static mut epoxy_eglInitialize: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, major: *mut EGLint, minor: *mut EGLint) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglLabelObjectKHR"]
+ pub static mut epoxy_eglLabelObjectKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ display: EGLDisplay,
+ objectType: EGLenum,
+ object: EGLObjectKHR,
+ label: EGLLabelKHR,
+ ) -> EGLint,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglLockSurfaceKHR"]
+ pub static mut epoxy_eglLockSurfaceKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ attrib_list: *const EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglMakeCurrent"]
+ pub static mut epoxy_eglMakeCurrent: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ draw: EGLSurface,
+ read: EGLSurface,
+ ctx: EGLContext,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglOutputLayerAttribEXT"]
+ pub static mut epoxy_eglOutputLayerAttribEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ layer: EGLOutputLayerEXT,
+ attribute: EGLint,
+ value: EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglOutputPortAttribEXT"]
+ pub static mut epoxy_eglOutputPortAttribEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ port: EGLOutputPortEXT,
+ attribute: EGLint,
+ value: EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglPostSubBufferNV"]
+ pub static mut epoxy_eglPostSubBufferNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ x: EGLint,
+ y: EGLint,
+ width: EGLint,
+ height: EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglPresentationTimeANDROID"]
+ pub static mut epoxy_eglPresentationTimeANDROID: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ time: EGLnsecsANDROID,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryAPI"]
+ pub static mut epoxy_eglQueryAPI: ::std::option::Option<unsafe extern "C" fn() -> EGLenum>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryContext"]
+ pub static mut epoxy_eglQueryContext: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ ctx: EGLContext,
+ attribute: EGLint,
+ value: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryDebugKHR"]
+ pub static mut epoxy_eglQueryDebugKHR: ::std::option::Option<
+ unsafe extern "C" fn(attribute: EGLint, value: *mut EGLAttrib) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryDeviceAttribEXT"]
+ pub static mut epoxy_eglQueryDeviceAttribEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ device: EGLDeviceEXT,
+ attribute: EGLint,
+ value: *mut EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryDeviceStringEXT"]
+ pub static mut epoxy_eglQueryDeviceStringEXT: ::std::option::Option<
+ unsafe extern "C" fn(device: EGLDeviceEXT, name: EGLint) -> *const ::std::os::raw::c_char,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryDevicesEXT"]
+ pub static mut epoxy_eglQueryDevicesEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ max_devices: EGLint,
+ devices: *mut EGLDeviceEXT,
+ num_devices: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryDisplayAttribEXT"]
+ pub static mut epoxy_eglQueryDisplayAttribEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ attribute: EGLint,
+ value: *mut EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryDisplayAttribNV"]
+ pub static mut epoxy_eglQueryDisplayAttribNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ attribute: EGLint,
+ value: *mut EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryDmaBufFormatsEXT"]
+ pub static mut epoxy_eglQueryDmaBufFormatsEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ max_formats: EGLint,
+ formats: *mut EGLint,
+ num_formats: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryDmaBufModifiersEXT"]
+ pub static mut epoxy_eglQueryDmaBufModifiersEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ format: EGLint,
+ max_modifiers: EGLint,
+ modifiers: *mut EGLuint64KHR,
+ external_only: *mut EGLBoolean,
+ num_modifiers: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryNativeDisplayNV"]
+ pub static mut epoxy_eglQueryNativeDisplayNV: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, display_id: *mut EGLNativeDisplayType) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryNativePixmapNV"]
+ pub static mut epoxy_eglQueryNativePixmapNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surf: EGLSurface,
+ pixmap: *mut EGLNativePixmapType,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryNativeWindowNV"]
+ pub static mut epoxy_eglQueryNativeWindowNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surf: EGLSurface,
+ window: *mut EGLNativeWindowType,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryOutputLayerAttribEXT"]
+ pub static mut epoxy_eglQueryOutputLayerAttribEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ layer: EGLOutputLayerEXT,
+ attribute: EGLint,
+ value: *mut EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryOutputLayerStringEXT"]
+ pub static mut epoxy_eglQueryOutputLayerStringEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ layer: EGLOutputLayerEXT,
+ name: EGLint,
+ ) -> *const ::std::os::raw::c_char,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryOutputPortAttribEXT"]
+ pub static mut epoxy_eglQueryOutputPortAttribEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ port: EGLOutputPortEXT,
+ attribute: EGLint,
+ value: *mut EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryOutputPortStringEXT"]
+ pub static mut epoxy_eglQueryOutputPortStringEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ port: EGLOutputPortEXT,
+ name: EGLint,
+ ) -> *const ::std::os::raw::c_char,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryStreamAttribKHR"]
+ pub static mut epoxy_eglQueryStreamAttribKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attribute: EGLenum,
+ value: *mut EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryStreamKHR"]
+ pub static mut epoxy_eglQueryStreamKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attribute: EGLenum,
+ value: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryStreamMetadataNV"]
+ pub static mut epoxy_eglQueryStreamMetadataNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ name: EGLenum,
+ n: EGLint,
+ offset: EGLint,
+ size: EGLint,
+ data: *mut ::std::os::raw::c_void,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryStreamTimeKHR"]
+ pub static mut epoxy_eglQueryStreamTimeKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attribute: EGLenum,
+ value: *mut EGLTimeKHR,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryStreamu64KHR"]
+ pub static mut epoxy_eglQueryStreamu64KHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attribute: EGLenum,
+ value: *mut EGLuint64KHR,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQueryString"]
+ pub static mut epoxy_eglQueryString: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, name: EGLint) -> *const ::std::os::raw::c_char,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQuerySurface"]
+ pub static mut epoxy_eglQuerySurface: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ attribute: EGLint,
+ value: *mut EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQuerySurface64KHR"]
+ pub static mut epoxy_eglQuerySurface64KHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ attribute: EGLint,
+ value: *mut EGLAttribKHR,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglQuerySurfacePointerANGLE"]
+ pub static mut epoxy_eglQuerySurfacePointerANGLE: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ attribute: EGLint,
+ value: *mut *mut ::std::os::raw::c_void,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglReleaseTexImage"]
+ pub static mut epoxy_eglReleaseTexImage: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, surface: EGLSurface, buffer: EGLint) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglReleaseThread"]
+ pub static mut epoxy_eglReleaseThread:
+ ::std::option::Option<unsafe extern "C" fn() -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglResetStreamNV"]
+ pub static mut epoxy_eglResetStreamNV: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, stream: EGLStreamKHR) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSetBlobCacheFuncsANDROID"]
+ pub static mut epoxy_eglSetBlobCacheFuncsANDROID: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ set: EGLSetBlobFuncANDROID,
+ get: EGLGetBlobFuncANDROID,
+ ),
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSetDamageRegionKHR"]
+ pub static mut epoxy_eglSetDamageRegionKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ rects: *mut EGLint,
+ n_rects: EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSetStreamAttribKHR"]
+ pub static mut epoxy_eglSetStreamAttribKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attribute: EGLenum,
+ value: EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSetStreamMetadataNV"]
+ pub static mut epoxy_eglSetStreamMetadataNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ n: EGLint,
+ offset: EGLint,
+ size: EGLint,
+ data: *const ::std::os::raw::c_void,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSignalSyncKHR"]
+ pub static mut epoxy_eglSignalSyncKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, sync: EGLSyncKHR, mode: EGLenum) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSignalSyncNV"]
+ pub static mut epoxy_eglSignalSyncNV:
+ ::std::option::Option<unsafe extern "C" fn(sync: EGLSyncNV, mode: EGLenum) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglStreamAttribKHR"]
+ pub static mut epoxy_eglStreamAttribKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attribute: EGLenum,
+ value: EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglStreamConsumerAcquireAttribKHR"]
+ pub static mut epoxy_eglStreamConsumerAcquireAttribKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attrib_list: *const EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglStreamConsumerAcquireKHR"]
+ pub static mut epoxy_eglStreamConsumerAcquireKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, stream: EGLStreamKHR) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglStreamConsumerGLTextureExternalAttribsNV"]
+ pub static mut epoxy_eglStreamConsumerGLTextureExternalAttribsNV: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attrib_list: *mut EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglStreamConsumerGLTextureExternalKHR"]
+ pub static mut epoxy_eglStreamConsumerGLTextureExternalKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, stream: EGLStreamKHR) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglStreamConsumerOutputEXT"]
+ pub static mut epoxy_eglStreamConsumerOutputEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ layer: EGLOutputLayerEXT,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglStreamConsumerReleaseAttribKHR"]
+ pub static mut epoxy_eglStreamConsumerReleaseAttribKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ stream: EGLStreamKHR,
+ attrib_list: *const EGLAttrib,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglStreamConsumerReleaseKHR"]
+ pub static mut epoxy_eglStreamConsumerReleaseKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, stream: EGLStreamKHR) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSurfaceAttrib"]
+ pub static mut epoxy_eglSurfaceAttrib: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ attribute: EGLint,
+ value: EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSwapBuffers"]
+ pub static mut epoxy_eglSwapBuffers: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, surface: EGLSurface) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSwapBuffersRegion2NOK"]
+ pub static mut epoxy_eglSwapBuffersRegion2NOK: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ numRects: EGLint,
+ rects: *const EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSwapBuffersRegionNOK"]
+ pub static mut epoxy_eglSwapBuffersRegionNOK: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ numRects: EGLint,
+ rects: *const EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSwapBuffersWithDamageEXT"]
+ pub static mut epoxy_eglSwapBuffersWithDamageEXT: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ rects: *mut EGLint,
+ n_rects: EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSwapBuffersWithDamageKHR"]
+ pub static mut epoxy_eglSwapBuffersWithDamageKHR: ::std::option::Option<
+ unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ surface: EGLSurface,
+ rects: *mut EGLint,
+ n_rects: EGLint,
+ ) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglSwapInterval"]
+ pub static mut epoxy_eglSwapInterval: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, interval: EGLint) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglTerminate"]
+ pub static mut epoxy_eglTerminate:
+ ::std::option::Option<unsafe extern "C" fn(dpy: EGLDisplay) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglUnlockSurfaceKHR"]
+ pub static mut epoxy_eglUnlockSurfaceKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, surface: EGLSurface) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglWaitClient"]
+ pub static mut epoxy_eglWaitClient: ::std::option::Option<unsafe extern "C" fn() -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglWaitGL"]
+ pub static mut epoxy_eglWaitGL: ::std::option::Option<unsafe extern "C" fn() -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglWaitNative"]
+ pub static mut epoxy_eglWaitNative:
+ ::std::option::Option<unsafe extern "C" fn(engine: EGLint) -> EGLBoolean>;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglWaitSync"]
+ pub static mut epoxy_eglWaitSync: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, sync: EGLSync, flags: EGLint) -> EGLBoolean,
+ >;
+}
+extern "C" {
+ #[link_name = "\u{1}epoxy_eglWaitSyncKHR"]
+ pub static mut epoxy_eglWaitSyncKHR: ::std::option::Option<
+ unsafe extern "C" fn(dpy: EGLDisplay, sync: EGLSyncKHR, flags: EGLint) -> EGLint,
+ >;
+}
+extern "C" {
+ pub fn epoxy_has_egl_extension(
+ dpy: EGLDisplay,
+ extension: *const ::std::os::raw::c_char,
+ ) -> bool;
+}
+extern "C" {
+ pub fn epoxy_egl_version(dpy: EGLDisplay) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn epoxy_has_egl() -> bool;
+}
diff --git a/gpu_renderer/src/generated/generate b/gpu_renderer/src/generated/generate
new file mode 120000
index 0000000..d4bb1f1
--- /dev/null
+++ b/gpu_renderer/src/generated/generate
@@ -0,0 +1 @@
+generate.py
\ No newline at end of file
diff --git a/gpu_renderer/src/generated/generate.py b/gpu_renderer/src/generated/generate.py
new file mode 100755
index 0000000..923cd70
--- /dev/null
+++ b/gpu_renderer/src/generated/generate.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python3
+# Copyright 2018 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.
+
+"""Generates bindings that are used gpu_renderer.
+
+A sysroot and virglrenderer source checkout is required. The defaults to the
+root directory.
+"""
+
+from __future__ import print_function
+import argparse
+import multiprocessing.pool
+import os
+import subprocess
+import sys
+import tempfile
+
+# Bright green.
+PASS_COLOR = '\033[1;32m'
+# Bright red.
+FAIL_COLOR = '\033[1;31m'
+# Default color.
+END_COLOR = '\033[0m'
+
+verbose = False
+
+def generate_module(module_name, whitelist, header, clang_args, lib_name):
+ args = [
+ 'bindgen',
+ '--no-layout-tests',
+ '--whitelist-function', whitelist,
+ '--whitelist-var', whitelist,
+ '--whitelist-type', whitelist,
+ '--no-prepend-enum-name',
+ '--no-rustfmt-bindings',
+ '-o', module_name + '.rs',
+ ];
+
+ if lib_name:
+ args.extend(['--raw-line',
+ '#[link(name = "{}")] extern {{}}'.format(lib_name)])
+
+ args.extend([header, '--'])
+ args.extend(clang_args)
+
+ if verbose:
+ print(' '.join(args))
+
+ if subprocess.Popen(args).wait() == 0:
+ return 'pass'
+ else:
+ return 'bindgen failed'
+
+
+def download_virgl(src, dst, branch):
+ virgl_src = tempfile.TemporaryDirectory(prefix='virglrenderer-src')
+
+ args = ['git', 'clone']
+
+ if branch:
+ args.extend(['-b', branch])
+
+ args.extend([src, dst])
+
+ if verbose:
+ print(' '.join(args))
+
+ if subprocess.Popen(args).wait() == 0:
+ return True
+ else:
+
+ return False
+
+
+def get_parser():
+ """Gets the argument parser"""
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--sysroot',
+ default='/',
+ help='sysroot directory (default=%(default)s)')
+ parser.add_argument('--virglrenderer',
+ default='git://git.freedesktop.org/git/virglrenderer',
+ help='virglrenderer src dir/repo (default=%(default)s)')
+ parser.add_argument('--virgl_branch',
+ default='master',
+ help='virglrenderer branch name (default=%(default)s)')
+ parser.add_argument('--verbose', '-v',
+ action='store_true',
+ help='enable verbose output (default=%(default)s)')
+ return parser
+
+
+def main(argv):
+ global verbose
+ os.chdir(os.path.dirname(sys.argv[0]))
+ opts = get_parser().parse_args(argv)
+ if opts.verbose:
+ verbose = True
+
+ virgl_src_dir = opts.virglrenderer
+ virgl_src_dir_temp = None
+ if '://' in opts.virglrenderer:
+ virgl_src_dir_temp = tempfile.TemporaryDirectory(prefix='virglrenderer-src')
+ virgl_src_dir = virgl_src_dir_temp.name
+ if not download_virgl(opts.virglrenderer, virgl_src_dir, opts.virgl_branch):
+ print('failed to clone \'{}\' to \'{}\''.format(virgl_src_dir,
+ opts.virgl_branch))
+ sys.exit(1)
+
+ clang_args = ['-I', os.path.join(opts.sysroot, 'usr/include')]
+
+ modules = (
+ (
+ 'virglrenderer',
+ '(virgl|VIRGL)_.+',
+ os.path.join(opts.sysroot, 'usr/include/virgl/virglrenderer.h'),
+ clang_args,
+ 'virglrenderer',
+ ),
+ (
+ 'epoxy_egl',
+ '(E?GL)|(epoxy)_.+',
+ os.path.join(opts.sysroot, 'usr/include/epoxy/egl.h'),
+ clang_args,
+ 'epoxy',
+ ),
+ (
+ 'virgl_protocol',
+ '(virgl)|(VIRGL)_.+',
+ os.path.join(virgl_src_dir, 'src/virgl_protocol.h'),
+ clang_args,
+ None,
+ ),
+ (
+ 'p_defines',
+ '(pipe)|(PIPE).+',
+ os.path.join(virgl_src_dir, 'src/gallium/include/pipe/p_defines.h'),
+ clang_args,
+ None,
+ ),
+ (
+ 'p_format',
+ 'pipe_format',
+ os.path.join(virgl_src_dir, 'src/gallium/include/pipe/p_format.h'),
+ clang_args,
+ None,
+ ),
+ )
+
+ pool = multiprocessing.pool.Pool(len(modules))
+ results = pool.starmap(generate_module, modules, 1)
+
+ return_fail = False
+ print('---')
+ print('generate module summary:')
+ for module, result in zip(modules, results):
+ result_color = FAIL_COLOR
+ if result == 'pass':
+ result_color = PASS_COLOR
+ else:
+ return_fail = True
+
+ print('%15s: %s%s%s' %
+ (module[0], result_color, result, END_COLOR))
+
+ if return_fail:
+ sys.exit(1)
+
+ with open('mod.rs', 'w') as f:
+ print('/* generated by generate.py */', file=f)
+ print('#![allow(dead_code)]', file=f)
+ print('#![allow(non_camel_case_types)]', file=f)
+ print('#![allow(non_snake_case)]', file=f)
+ print('#![allow(non_upper_case_globals)]', file=f)
+ for module in modules:
+ print('pub mod', module[0] + ';', file=f)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/gpu_renderer/src/generated/mod.rs b/gpu_renderer/src/generated/mod.rs
new file mode 100644
index 0000000..d35ed8d
--- /dev/null
+++ b/gpu_renderer/src/generated/mod.rs
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* generated by generate.py */
+#![allow(dead_code)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+pub mod epoxy_egl;
+pub mod p_defines;
+pub mod p_format;
+pub mod virgl_protocol;
+pub mod virglrenderer;
diff --git a/gpu_renderer/src/generated/p_defines.rs b/gpu_renderer/src/generated/p_defines.rs
new file mode 100644
index 0000000..637179a
--- /dev/null
+++ b/gpu_renderer/src/generated/p_defines.rs
@@ -0,0 +1,634 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+pub const _POSIX_PIPE_BUF: u32 = 512;
+pub const PIPE_BUF: u32 = 4096;
+pub const PIPE_BLENDFACTOR_ONE: u32 = 1;
+pub const PIPE_BLENDFACTOR_SRC_COLOR: u32 = 2;
+pub const PIPE_BLENDFACTOR_SRC_ALPHA: u32 = 3;
+pub const PIPE_BLENDFACTOR_DST_ALPHA: u32 = 4;
+pub const PIPE_BLENDFACTOR_DST_COLOR: u32 = 5;
+pub const PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE: u32 = 6;
+pub const PIPE_BLENDFACTOR_CONST_COLOR: u32 = 7;
+pub const PIPE_BLENDFACTOR_CONST_ALPHA: u32 = 8;
+pub const PIPE_BLENDFACTOR_SRC1_COLOR: u32 = 9;
+pub const PIPE_BLENDFACTOR_SRC1_ALPHA: u32 = 10;
+pub const PIPE_BLENDFACTOR_ZERO: u32 = 17;
+pub const PIPE_BLENDFACTOR_INV_SRC_COLOR: u32 = 18;
+pub const PIPE_BLENDFACTOR_INV_SRC_ALPHA: u32 = 19;
+pub const PIPE_BLENDFACTOR_INV_DST_ALPHA: u32 = 20;
+pub const PIPE_BLENDFACTOR_INV_DST_COLOR: u32 = 21;
+pub const PIPE_BLENDFACTOR_INV_CONST_COLOR: u32 = 23;
+pub const PIPE_BLENDFACTOR_INV_CONST_ALPHA: u32 = 24;
+pub const PIPE_BLENDFACTOR_INV_SRC1_COLOR: u32 = 25;
+pub const PIPE_BLENDFACTOR_INV_SRC1_ALPHA: u32 = 26;
+pub const PIPE_BLEND_ADD: u32 = 0;
+pub const PIPE_BLEND_SUBTRACT: u32 = 1;
+pub const PIPE_BLEND_REVERSE_SUBTRACT: u32 = 2;
+pub const PIPE_BLEND_MIN: u32 = 3;
+pub const PIPE_BLEND_MAX: u32 = 4;
+pub const PIPE_LOGICOP_CLEAR: u32 = 0;
+pub const PIPE_LOGICOP_NOR: u32 = 1;
+pub const PIPE_LOGICOP_AND_INVERTED: u32 = 2;
+pub const PIPE_LOGICOP_COPY_INVERTED: u32 = 3;
+pub const PIPE_LOGICOP_AND_REVERSE: u32 = 4;
+pub const PIPE_LOGICOP_INVERT: u32 = 5;
+pub const PIPE_LOGICOP_XOR: u32 = 6;
+pub const PIPE_LOGICOP_NAND: u32 = 7;
+pub const PIPE_LOGICOP_AND: u32 = 8;
+pub const PIPE_LOGICOP_EQUIV: u32 = 9;
+pub const PIPE_LOGICOP_NOOP: u32 = 10;
+pub const PIPE_LOGICOP_OR_INVERTED: u32 = 11;
+pub const PIPE_LOGICOP_COPY: u32 = 12;
+pub const PIPE_LOGICOP_OR_REVERSE: u32 = 13;
+pub const PIPE_LOGICOP_OR: u32 = 14;
+pub const PIPE_LOGICOP_SET: u32 = 15;
+pub const PIPE_MASK_R: u32 = 1;
+pub const PIPE_MASK_G: u32 = 2;
+pub const PIPE_MASK_B: u32 = 4;
+pub const PIPE_MASK_A: u32 = 8;
+pub const PIPE_MASK_RGBA: u32 = 15;
+pub const PIPE_MASK_Z: u32 = 16;
+pub const PIPE_MASK_S: u32 = 32;
+pub const PIPE_MASK_ZS: u32 = 48;
+pub const PIPE_MASK_RGBAZS: u32 = 63;
+pub const PIPE_FUNC_NEVER: u32 = 0;
+pub const PIPE_FUNC_LESS: u32 = 1;
+pub const PIPE_FUNC_EQUAL: u32 = 2;
+pub const PIPE_FUNC_LEQUAL: u32 = 3;
+pub const PIPE_FUNC_GREATER: u32 = 4;
+pub const PIPE_FUNC_NOTEQUAL: u32 = 5;
+pub const PIPE_FUNC_GEQUAL: u32 = 6;
+pub const PIPE_FUNC_ALWAYS: u32 = 7;
+pub const PIPE_POLYGON_MODE_FILL: u32 = 0;
+pub const PIPE_POLYGON_MODE_LINE: u32 = 1;
+pub const PIPE_POLYGON_MODE_POINT: u32 = 2;
+pub const PIPE_FACE_NONE: u32 = 0;
+pub const PIPE_FACE_FRONT: u32 = 1;
+pub const PIPE_FACE_BACK: u32 = 2;
+pub const PIPE_FACE_FRONT_AND_BACK: u32 = 3;
+pub const PIPE_STENCIL_OP_KEEP: u32 = 0;
+pub const PIPE_STENCIL_OP_ZERO: u32 = 1;
+pub const PIPE_STENCIL_OP_REPLACE: u32 = 2;
+pub const PIPE_STENCIL_OP_INCR: u32 = 3;
+pub const PIPE_STENCIL_OP_DECR: u32 = 4;
+pub const PIPE_STENCIL_OP_INCR_WRAP: u32 = 5;
+pub const PIPE_STENCIL_OP_DECR_WRAP: u32 = 6;
+pub const PIPE_STENCIL_OP_INVERT: u32 = 7;
+pub const PIPE_TEX_FACE_POS_X: u32 = 0;
+pub const PIPE_TEX_FACE_NEG_X: u32 = 1;
+pub const PIPE_TEX_FACE_POS_Y: u32 = 2;
+pub const PIPE_TEX_FACE_NEG_Y: u32 = 3;
+pub const PIPE_TEX_FACE_POS_Z: u32 = 4;
+pub const PIPE_TEX_FACE_NEG_Z: u32 = 5;
+pub const PIPE_TEX_FACE_MAX: u32 = 6;
+pub const PIPE_TEX_WRAP_REPEAT: u32 = 0;
+pub const PIPE_TEX_WRAP_CLAMP: u32 = 1;
+pub const PIPE_TEX_WRAP_CLAMP_TO_EDGE: u32 = 2;
+pub const PIPE_TEX_WRAP_CLAMP_TO_BORDER: u32 = 3;
+pub const PIPE_TEX_WRAP_MIRROR_REPEAT: u32 = 4;
+pub const PIPE_TEX_WRAP_MIRROR_CLAMP: u32 = 5;
+pub const PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE: u32 = 6;
+pub const PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER: u32 = 7;
+pub const PIPE_TEX_MIPFILTER_NEAREST: u32 = 0;
+pub const PIPE_TEX_MIPFILTER_LINEAR: u32 = 1;
+pub const PIPE_TEX_MIPFILTER_NONE: u32 = 2;
+pub const PIPE_TEX_FILTER_NEAREST: u32 = 0;
+pub const PIPE_TEX_FILTER_LINEAR: u32 = 1;
+pub const PIPE_TEX_COMPARE_NONE: u32 = 0;
+pub const PIPE_TEX_COMPARE_R_TO_TEXTURE: u32 = 1;
+pub const PIPE_CLEAR_DEPTH: u32 = 1;
+pub const PIPE_CLEAR_STENCIL: u32 = 2;
+pub const PIPE_CLEAR_COLOR0: u32 = 4;
+pub const PIPE_CLEAR_COLOR1: u32 = 8;
+pub const PIPE_CLEAR_COLOR2: u32 = 16;
+pub const PIPE_CLEAR_COLOR3: u32 = 32;
+pub const PIPE_CLEAR_COLOR4: u32 = 64;
+pub const PIPE_CLEAR_COLOR5: u32 = 128;
+pub const PIPE_CLEAR_COLOR6: u32 = 256;
+pub const PIPE_CLEAR_COLOR7: u32 = 512;
+pub const PIPE_CLEAR_COLOR: u32 = 1020;
+pub const PIPE_CLEAR_DEPTHSTENCIL: u32 = 3;
+pub const PIPE_BARRIER_MAPPED_BUFFER: u32 = 1;
+pub const PIPE_BARRIER_SHADER_BUFFER: u32 = 2;
+pub const PIPE_BARRIER_QUERY_BUFFER: u32 = 4;
+pub const PIPE_BARRIER_VERTEX_BUFFER: u32 = 8;
+pub const PIPE_BARRIER_INDEX_BUFFER: u32 = 16;
+pub const PIPE_BARRIER_CONSTANT_BUFFER: u32 = 32;
+pub const PIPE_BARRIER_INDIRECT_BUFFER: u32 = 64;
+pub const PIPE_BARRIER_TEXTURE: u32 = 128;
+pub const PIPE_BARRIER_IMAGE: u32 = 256;
+pub const PIPE_BARRIER_FRAMEBUFFER: u32 = 512;
+pub const PIPE_BARRIER_STREAMOUT_BUFFER: u32 = 1024;
+pub const PIPE_BARRIER_GLOBAL_BUFFER: u32 = 2048;
+pub const PIPE_BARRIER_ALL: u32 = 4095;
+pub const PIPE_TEXTURE_BARRIER_SAMPLER: u32 = 1;
+pub const PIPE_TEXTURE_BARRIER_FRAMEBUFFER: u32 = 2;
+pub const PIPE_BIND_DEPTH_STENCIL: u32 = 1;
+pub const PIPE_BIND_RENDER_TARGET: u32 = 2;
+pub const PIPE_BIND_BLENDABLE: u32 = 4;
+pub const PIPE_BIND_SAMPLER_VIEW: u32 = 8;
+pub const PIPE_BIND_VERTEX_BUFFER: u32 = 16;
+pub const PIPE_BIND_INDEX_BUFFER: u32 = 32;
+pub const PIPE_BIND_CONSTANT_BUFFER: u32 = 64;
+pub const PIPE_BIND_DISPLAY_TARGET: u32 = 256;
+pub const PIPE_BIND_TRANSFER_WRITE: u32 = 512;
+pub const PIPE_BIND_TRANSFER_READ: u32 = 1024;
+pub const PIPE_BIND_STREAM_OUTPUT: u32 = 2048;
+pub const PIPE_BIND_CURSOR: u32 = 65536;
+pub const PIPE_BIND_CUSTOM: u32 = 131072;
+pub const PIPE_BIND_GLOBAL: u32 = 262144;
+pub const PIPE_BIND_SHADER_RESOURCE: u32 = 524288;
+pub const PIPE_BIND_COMPUTE_RESOURCE: u32 = 1048576;
+pub const PIPE_BIND_COMMAND_ARGS_BUFFER: u32 = 2097152;
+pub const PIPE_BIND_QUERY_BUFFER: u32 = 4194304;
+pub const PIPE_BIND_SCANOUT: u32 = 16384;
+pub const PIPE_BIND_SHARED: u32 = 32768;
+pub const PIPE_BIND_LINEAR: u32 = 2097152;
+pub const PIPE_RESOURCE_FLAG_MAP_PERSISTENT: u32 = 1;
+pub const PIPE_RESOURCE_FLAG_MAP_COHERENT: u32 = 2;
+pub const PIPE_RESOURCE_FLAG_DRV_PRIV: u32 = 65536;
+pub const PIPE_RESOURCE_FLAG_ST_PRIV: u32 = 16777216;
+pub const PIPE_USAGE_DEFAULT: u32 = 0;
+pub const PIPE_USAGE_IMMUTABLE: u32 = 1;
+pub const PIPE_USAGE_DYNAMIC: u32 = 2;
+pub const PIPE_USAGE_STREAM: u32 = 3;
+pub const PIPE_USAGE_STAGING: u32 = 4;
+pub const PIPE_SHADER_VERTEX: u32 = 0;
+pub const PIPE_SHADER_FRAGMENT: u32 = 1;
+pub const PIPE_SHADER_GEOMETRY: u32 = 2;
+pub const PIPE_SHADER_TESS_CTRL: u32 = 3;
+pub const PIPE_SHADER_TESS_EVAL: u32 = 4;
+pub const PIPE_SHADER_COMPUTE: u32 = 5;
+pub const PIPE_SHADER_TYPES: u32 = 6;
+pub const PIPE_PRIM_POINTS: u32 = 0;
+pub const PIPE_PRIM_LINES: u32 = 1;
+pub const PIPE_PRIM_LINE_LOOP: u32 = 2;
+pub const PIPE_PRIM_LINE_STRIP: u32 = 3;
+pub const PIPE_PRIM_TRIANGLES: u32 = 4;
+pub const PIPE_PRIM_TRIANGLE_STRIP: u32 = 5;
+pub const PIPE_PRIM_TRIANGLE_FAN: u32 = 6;
+pub const PIPE_PRIM_QUADS: u32 = 7;
+pub const PIPE_PRIM_QUAD_STRIP: u32 = 8;
+pub const PIPE_PRIM_POLYGON: u32 = 9;
+pub const PIPE_PRIM_LINES_ADJACENCY: u32 = 10;
+pub const PIPE_PRIM_LINE_STRIP_ADJACENCY: u32 = 11;
+pub const PIPE_PRIM_TRIANGLES_ADJACENCY: u32 = 12;
+pub const PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY: u32 = 13;
+pub const PIPE_PRIM_PATCHES: u32 = 14;
+pub const PIPE_PRIM_MAX: u32 = 15;
+pub const PIPE_TESS_SPACING_FRACTIONAL_ODD: u32 = 0;
+pub const PIPE_TESS_SPACING_FRACTIONAL_EVEN: u32 = 1;
+pub const PIPE_TESS_SPACING_EQUAL: u32 = 2;
+pub const PIPE_QUERY_OCCLUSION_COUNTER: u32 = 0;
+pub const PIPE_QUERY_OCCLUSION_PREDICATE: u32 = 1;
+pub const PIPE_QUERY_TIMESTAMP: u32 = 2;
+pub const PIPE_QUERY_TIMESTAMP_DISJOINT: u32 = 3;
+pub const PIPE_QUERY_TIME_ELAPSED: u32 = 4;
+pub const PIPE_QUERY_PRIMITIVES_GENERATED: u32 = 5;
+pub const PIPE_QUERY_PRIMITIVES_EMITTED: u32 = 6;
+pub const PIPE_QUERY_SO_STATISTICS: u32 = 7;
+pub const PIPE_QUERY_SO_OVERFLOW_PREDICATE: u32 = 8;
+pub const PIPE_QUERY_GPU_FINISHED: u32 = 9;
+pub const PIPE_QUERY_PIPELINE_STATISTICS: u32 = 10;
+pub const PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE: u32 = 11;
+pub const PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE: u32 = 12;
+pub const PIPE_QUERY_TYPES: u32 = 13;
+pub const PIPE_QUERY_DRIVER_SPECIFIC: u32 = 256;
+pub const PIPE_RENDER_COND_WAIT: u32 = 0;
+pub const PIPE_RENDER_COND_NO_WAIT: u32 = 1;
+pub const PIPE_RENDER_COND_BY_REGION_WAIT: u32 = 2;
+pub const PIPE_RENDER_COND_BY_REGION_NO_WAIT: u32 = 3;
+pub const PIPE_SPRITE_COORD_UPPER_LEFT: u32 = 0;
+pub const PIPE_SPRITE_COORD_LOWER_LEFT: u32 = 1;
+pub const PIPE_SWIZZLE_RED: u32 = 0;
+pub const PIPE_SWIZZLE_GREEN: u32 = 1;
+pub const PIPE_SWIZZLE_BLUE: u32 = 2;
+pub const PIPE_SWIZZLE_ALPHA: u32 = 3;
+pub const PIPE_SWIZZLE_ZERO: u32 = 4;
+pub const PIPE_SWIZZLE_ONE: u32 = 5;
+pub const PIPE_TIMEOUT_INFINITE: i32 = -1;
+pub const PIPE_IMAGE_ACCESS_READ: u32 = 1;
+pub const PIPE_IMAGE_ACCESS_WRITE: u32 = 2;
+pub const PIPE_IMAGE_ACCESS_READ_WRITE: u32 = 3;
+pub const PIPE_QUIRK_TEXTURE_BORDER_COLOR_SWIZZLE_NV50: u32 = 1;
+pub const PIPE_QUIRK_TEXTURE_BORDER_COLOR_SWIZZLE_R600: u32 = 2;
+pub type __uint64_t = ::std::os::raw::c_ulong;
+pub type boolean = ::std::os::raw::c_uchar;
+pub const PIPE_OK: pipe_error = 0;
+/// < Generic error
+pub const PIPE_ERROR: pipe_error = -1;
+pub const PIPE_ERROR_BAD_INPUT: pipe_error = -2;
+pub const PIPE_ERROR_OUT_OF_MEMORY: pipe_error = -3;
+pub const PIPE_ERROR_RETRY: pipe_error = -4;
+/// Gallium error codes.
+///
+/// - A zero value always means success.
+/// - A negative value always means failure.
+/// - The meaning of a positive value is function dependent.
+pub type pipe_error = i32;
+pub const PIPE_BUFFER: pipe_texture_target = 0;
+pub const PIPE_TEXTURE_1D: pipe_texture_target = 1;
+pub const PIPE_TEXTURE_2D: pipe_texture_target = 2;
+pub const PIPE_TEXTURE_3D: pipe_texture_target = 3;
+pub const PIPE_TEXTURE_CUBE: pipe_texture_target = 4;
+pub const PIPE_TEXTURE_RECT: pipe_texture_target = 5;
+pub const PIPE_TEXTURE_1D_ARRAY: pipe_texture_target = 6;
+pub const PIPE_TEXTURE_2D_ARRAY: pipe_texture_target = 7;
+pub const PIPE_TEXTURE_CUBE_ARRAY: pipe_texture_target = 8;
+pub const PIPE_MAX_TEXTURE_TYPES: pipe_texture_target = 9;
+/// Texture types.
+/// See the documentation for info on PIPE_TEXTURE_RECT vs PIPE_TEXTURE_2D
+pub type pipe_texture_target = u32;
+/// Resource contents read back (or accessed directly) at transfer
+/// create time.
+pub const PIPE_TRANSFER_READ: pipe_transfer_usage = 1;
+/// Resource contents will be written back at transfer_unmap
+/// time (or modified as a result of being accessed directly).
+pub const PIPE_TRANSFER_WRITE: pipe_transfer_usage = 2;
+/// Read/modify/write
+pub const PIPE_TRANSFER_READ_WRITE: pipe_transfer_usage = 3;
+/// The transfer should map the texture storage directly. The driver may
+/// return NULL if that isn't possible, and the state tracker needs to cope
+/// with that and use an alternative path without this flag.
+///
+/// E.g. the state tracker could have a simpler path which maps textures and
+/// does read/modify/write cycles on them directly, and a more complicated
+/// path which uses minimal read and write transfers.
+pub const PIPE_TRANSFER_MAP_DIRECTLY: pipe_transfer_usage = 4;
+/// Discards the memory within the mapped region.
+///
+/// It should not be used with PIPE_TRANSFER_READ.
+///
+/// See also:
+/// - OpenGL's ARB_map_buffer_range extension, MAP_INVALIDATE_RANGE_BIT flag.
+pub const PIPE_TRANSFER_DISCARD_RANGE: pipe_transfer_usage = 256;
+/// Fail if the resource cannot be mapped immediately.
+///
+/// See also:
+/// - Direct3D's D3DLOCK_DONOTWAIT flag.
+/// - Mesa3D's MESA_MAP_NOWAIT_BIT flag.
+/// - WDDM's D3DDDICB_LOCKFLAGS.DonotWait flag.
+pub const PIPE_TRANSFER_DONTBLOCK: pipe_transfer_usage = 512;
+/// Do not attempt to synchronize pending operations on the resource when mapping.
+///
+/// It should not be used with PIPE_TRANSFER_READ.
+///
+/// See also:
+/// - OpenGL's ARB_map_buffer_range extension, MAP_UNSYNCHRONIZED_BIT flag.
+/// - Direct3D's D3DLOCK_NOOVERWRITE flag.
+/// - WDDM's D3DDDICB_LOCKFLAGS.IgnoreSync flag.
+pub const PIPE_TRANSFER_UNSYNCHRONIZED: pipe_transfer_usage = 1024;
+/// Written ranges will be notified later with
+/// pipe_context::transfer_flush_region.
+///
+/// It should not be used with PIPE_TRANSFER_READ.
+///
+/// See also:
+/// - pipe_context::transfer_flush_region
+/// - OpenGL's ARB_map_buffer_range extension, MAP_FLUSH_EXPLICIT_BIT flag.
+pub const PIPE_TRANSFER_FLUSH_EXPLICIT: pipe_transfer_usage = 2048;
+/// Discards all memory backing the resource.
+///
+/// It should not be used with PIPE_TRANSFER_READ.
+///
+/// This is equivalent to:
+/// - OpenGL's ARB_map_buffer_range extension, MAP_INVALIDATE_BUFFER_BIT
+/// - BufferData(NULL) on a GL buffer
+/// - Direct3D's D3DLOCK_DISCARD flag.
+/// - WDDM's D3DDDICB_LOCKFLAGS.Discard flag.
+/// - D3D10 DDI's D3D10_DDI_MAP_WRITE_DISCARD flag
+/// - D3D10's D3D10_MAP_WRITE_DISCARD flag.
+pub const PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE: pipe_transfer_usage = 4096;
+/// Allows the resource to be used for rendering while mapped.
+///
+/// PIPE_RESOURCE_FLAG_MAP_PERSISTENT must be set when creating
+/// the resource.
+///
+/// If COHERENT is not set, memory_barrier(PIPE_BARRIER_MAPPED_BUFFER)
+/// must be called to ensure the device can see what the CPU has written.
+pub const PIPE_TRANSFER_PERSISTENT: pipe_transfer_usage = 8192;
+/// If PERSISTENT is set, this ensures any writes done by the device are
+/// immediately visible to the CPU and vice versa.
+///
+/// PIPE_RESOURCE_FLAG_MAP_COHERENT must be set when creating
+/// the resource.
+pub const PIPE_TRANSFER_COHERENT: pipe_transfer_usage = 16384;
+/// Transfer object usage flags
+pub type pipe_transfer_usage = u32;
+pub const PIPE_FLUSH_END_OF_FRAME: pipe_flush_flags = 1;
+/// Flags for the flush function.
+pub type pipe_flush_flags = u32;
+pub const PIPE_CAP_NPOT_TEXTURES: pipe_cap = 1;
+pub const PIPE_CAP_TWO_SIDED_STENCIL: pipe_cap = 2;
+pub const PIPE_CAP_MAX_DUAL_SOURCE_RENDER_TARGETS: pipe_cap = 4;
+pub const PIPE_CAP_ANISOTROPIC_FILTER: pipe_cap = 5;
+pub const PIPE_CAP_POINT_SPRITE: pipe_cap = 6;
+pub const PIPE_CAP_MAX_RENDER_TARGETS: pipe_cap = 7;
+pub const PIPE_CAP_OCCLUSION_QUERY: pipe_cap = 8;
+pub const PIPE_CAP_QUERY_TIME_ELAPSED: pipe_cap = 9;
+pub const PIPE_CAP_TEXTURE_SHADOW_MAP: pipe_cap = 10;
+pub const PIPE_CAP_TEXTURE_SWIZZLE: pipe_cap = 11;
+pub const PIPE_CAP_MAX_TEXTURE_2D_LEVELS: pipe_cap = 12;
+pub const PIPE_CAP_MAX_TEXTURE_3D_LEVELS: pipe_cap = 13;
+pub const PIPE_CAP_MAX_TEXTURE_CUBE_LEVELS: pipe_cap = 14;
+pub const PIPE_CAP_TEXTURE_MIRROR_CLAMP: pipe_cap = 25;
+pub const PIPE_CAP_BLEND_EQUATION_SEPARATE: pipe_cap = 28;
+pub const PIPE_CAP_SM3: pipe_cap = 29;
+pub const PIPE_CAP_MAX_STREAM_OUTPUT_BUFFERS: pipe_cap = 30;
+pub const PIPE_CAP_PRIMITIVE_RESTART: pipe_cap = 31;
+/// blend enables and write masks per rendertarget
+pub const PIPE_CAP_INDEP_BLEND_ENABLE: pipe_cap = 33;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_INDEP_BLEND_FUNC: pipe_cap = 34;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_TEXTURE_ARRAY_LAYERS: pipe_cap = 36;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_FS_COORD_ORIGIN_UPPER_LEFT: pipe_cap = 37;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_FS_COORD_ORIGIN_LOWER_LEFT: pipe_cap = 38;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_FS_COORD_PIXEL_CENTER_HALF_INTEGER: pipe_cap = 39;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_FS_COORD_PIXEL_CENTER_INTEGER: pipe_cap = 40;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_DEPTH_CLIP_DISABLE: pipe_cap = 41;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_SHADER_STENCIL_EXPORT: pipe_cap = 42;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_INSTANCEID: pipe_cap = 43;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VERTEX_ELEMENT_INSTANCE_DIVISOR: pipe_cap = 44;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_FRAGMENT_COLOR_CLAMPED: pipe_cap = 45;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MIXED_COLORBUFFER_FORMATS: pipe_cap = 46;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_SEAMLESS_CUBE_MAP: pipe_cap = 47;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_SEAMLESS_CUBE_MAP_PER_TEXTURE: pipe_cap = 48;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MIN_TEXEL_OFFSET: pipe_cap = 50;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_TEXEL_OFFSET: pipe_cap = 51;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_CONDITIONAL_RENDER: pipe_cap = 52;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TEXTURE_BARRIER: pipe_cap = 53;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_STREAM_OUTPUT_SEPARATE_COMPONENTS: pipe_cap = 55;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_STREAM_OUTPUT_INTERLEAVED_COMPONENTS: pipe_cap = 56;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_STREAM_OUTPUT_PAUSE_RESUME: pipe_cap = 57;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_CAN_COMPACT_CONSTANTS: pipe_cap = 59;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VERTEX_COLOR_UNCLAMPED: pipe_cap = 60;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VERTEX_COLOR_CLAMPED: pipe_cap = 61;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_GLSL_FEATURE_LEVEL: pipe_cap = 62;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION: pipe_cap = 63;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_USER_VERTEX_BUFFERS: pipe_cap = 64;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VERTEX_BUFFER_OFFSET_4BYTE_ALIGNED_ONLY: pipe_cap = 65;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VERTEX_BUFFER_STRIDE_4BYTE_ALIGNED_ONLY: pipe_cap = 66;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VERTEX_ELEMENT_SRC_OFFSET_4BYTE_ALIGNED_ONLY: pipe_cap = 67;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_COMPUTE: pipe_cap = 68;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_USER_INDEX_BUFFERS: pipe_cap = 69;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_USER_CONSTANT_BUFFERS: pipe_cap = 70;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_CONSTANT_BUFFER_OFFSET_ALIGNMENT: pipe_cap = 71;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_START_INSTANCE: pipe_cap = 72;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_QUERY_TIMESTAMP: pipe_cap = 73;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TEXTURE_MULTISAMPLE: pipe_cap = 74;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MIN_MAP_BUFFER_ALIGNMENT: pipe_cap = 75;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_CUBE_MAP_ARRAY: pipe_cap = 76;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TEXTURE_BUFFER_OBJECTS: pipe_cap = 77;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TEXTURE_BUFFER_OFFSET_ALIGNMENT: pipe_cap = 78;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_TEXCOORD: pipe_cap = 79;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_PREFER_BLIT_BASED_TEXTURE_TRANSFER: pipe_cap = 80;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_QUERY_PIPELINE_STATISTICS: pipe_cap = 81;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TEXTURE_BORDER_COLOR_QUIRK: pipe_cap = 82;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_TEXTURE_BUFFER_SIZE: pipe_cap = 83;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_VIEWPORTS: pipe_cap = 84;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_ENDIANNESS: pipe_cap = 85;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MIXED_FRAMEBUFFER_SIZES: pipe_cap = 86;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_VS_LAYER_VIEWPORT: pipe_cap = 87;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_GEOMETRY_OUTPUT_VERTICES: pipe_cap = 88;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS: pipe_cap = 89;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_TEXTURE_GATHER_COMPONENTS: pipe_cap = 90;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TEXTURE_GATHER_SM5: pipe_cap = 91;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT: pipe_cap = 92;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_FAKE_SW_MSAA: pipe_cap = 93;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TEXTURE_QUERY_LOD: pipe_cap = 94;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MIN_TEXTURE_GATHER_OFFSET: pipe_cap = 95;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_TEXTURE_GATHER_OFFSET: pipe_cap = 96;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_SAMPLE_SHADING: pipe_cap = 97;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TEXTURE_GATHER_OFFSETS: pipe_cap = 98;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_VS_WINDOW_SPACE_POSITION: pipe_cap = 99;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_VERTEX_STREAMS: pipe_cap = 100;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_DRAW_INDIRECT: pipe_cap = 101;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_TGSI_FS_FINE_DERIVATIVE: pipe_cap = 102;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VENDOR_ID: pipe_cap = 103;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_DEVICE_ID: pipe_cap = 104;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_ACCELERATED: pipe_cap = 105;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VIDEO_MEMORY: pipe_cap = 106;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_UMA: pipe_cap = 107;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_CONDITIONAL_RENDER_INVERTED: pipe_cap = 108;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_MAX_VERTEX_ATTRIB_STRIDE: pipe_cap = 109;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_SAMPLER_VIEW_TARGET: pipe_cap = 110;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_CLIP_HALFZ: pipe_cap = 111;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_VERTEXID_NOBASE: pipe_cap = 112;
+/// different blend funcs per rendertarget
+pub const PIPE_CAP_POLYGON_OFFSET_CLAMP: pipe_cap = 113;
+/// Implementation capabilities/limits which are queried through
+/// pipe_screen::get_param()
+pub type pipe_cap = u32;
+pub const PIPE_ENDIAN_LITTLE: pipe_endian = 0;
+pub const PIPE_ENDIAN_BIG: pipe_endian = 1;
+pub const PIPE_ENDIAN_NATIVE: pipe_endian = 0;
+pub type pipe_endian = u32;
+pub const PIPE_CAPF_MAX_LINE_WIDTH: pipe_capf = 0;
+pub const PIPE_CAPF_MAX_LINE_WIDTH_AA: pipe_capf = 1;
+pub const PIPE_CAPF_MAX_POINT_WIDTH: pipe_capf = 2;
+pub const PIPE_CAPF_MAX_POINT_WIDTH_AA: pipe_capf = 3;
+pub const PIPE_CAPF_MAX_TEXTURE_ANISOTROPY: pipe_capf = 4;
+pub const PIPE_CAPF_MAX_TEXTURE_LOD_BIAS: pipe_capf = 5;
+pub const PIPE_CAPF_GUARD_BAND_LEFT: pipe_capf = 6;
+pub const PIPE_CAPF_GUARD_BAND_TOP: pipe_capf = 7;
+pub const PIPE_CAPF_GUARD_BAND_RIGHT: pipe_capf = 8;
+pub const PIPE_CAPF_GUARD_BAND_BOTTOM: pipe_capf = 9;
+/// Implementation limits which are queried through
+/// pipe_screen::get_paramf()
+pub type pipe_capf = u32;
+pub const PIPE_SHADER_CAP_MAX_INSTRUCTIONS: pipe_shader_cap = 0;
+pub const PIPE_SHADER_CAP_MAX_ALU_INSTRUCTIONS: pipe_shader_cap = 1;
+pub const PIPE_SHADER_CAP_MAX_TEX_INSTRUCTIONS: pipe_shader_cap = 2;
+pub const PIPE_SHADER_CAP_MAX_TEX_INDIRECTIONS: pipe_shader_cap = 3;
+pub const PIPE_SHADER_CAP_MAX_CONTROL_FLOW_DEPTH: pipe_shader_cap = 4;
+pub const PIPE_SHADER_CAP_MAX_INPUTS: pipe_shader_cap = 5;
+pub const PIPE_SHADER_CAP_MAX_OUTPUTS: pipe_shader_cap = 6;
+pub const PIPE_SHADER_CAP_MAX_CONST_BUFFER_SIZE: pipe_shader_cap = 7;
+pub const PIPE_SHADER_CAP_MAX_CONST_BUFFERS: pipe_shader_cap = 8;
+pub const PIPE_SHADER_CAP_MAX_TEMPS: pipe_shader_cap = 9;
+pub const PIPE_SHADER_CAP_MAX_PREDS: pipe_shader_cap = 10;
+pub const PIPE_SHADER_CAP_TGSI_CONT_SUPPORTED: pipe_shader_cap = 11;
+pub const PIPE_SHADER_CAP_INDIRECT_INPUT_ADDR: pipe_shader_cap = 12;
+pub const PIPE_SHADER_CAP_INDIRECT_OUTPUT_ADDR: pipe_shader_cap = 13;
+pub const PIPE_SHADER_CAP_INDIRECT_TEMP_ADDR: pipe_shader_cap = 14;
+pub const PIPE_SHADER_CAP_INDIRECT_CONST_ADDR: pipe_shader_cap = 15;
+pub const PIPE_SHADER_CAP_SUBROUTINES: pipe_shader_cap = 16;
+pub const PIPE_SHADER_CAP_INTEGERS: pipe_shader_cap = 17;
+pub const PIPE_SHADER_CAP_MAX_TEXTURE_SAMPLERS: pipe_shader_cap = 18;
+pub const PIPE_SHADER_CAP_PREFERRED_IR: pipe_shader_cap = 19;
+pub const PIPE_SHADER_CAP_TGSI_SQRT_SUPPORTED: pipe_shader_cap = 20;
+pub const PIPE_SHADER_CAP_MAX_SAMPLER_VIEWS: pipe_shader_cap = 21;
+pub const PIPE_SHADER_CAP_DOUBLES: pipe_shader_cap = 22;
+pub type pipe_shader_cap = u32;
+pub const PIPE_SHADER_IR_TGSI: pipe_shader_ir = 0;
+pub const PIPE_SHADER_IR_LLVM: pipe_shader_ir = 1;
+pub const PIPE_SHADER_IR_NATIVE: pipe_shader_ir = 2;
+/// Shader intermediate representation.
+pub type pipe_shader_ir = u32;
+pub const PIPE_COMPUTE_CAP_IR_TARGET: pipe_compute_cap = 0;
+pub const PIPE_COMPUTE_CAP_GRID_DIMENSION: pipe_compute_cap = 1;
+pub const PIPE_COMPUTE_CAP_MAX_GRID_SIZE: pipe_compute_cap = 2;
+pub const PIPE_COMPUTE_CAP_MAX_BLOCK_SIZE: pipe_compute_cap = 3;
+pub const PIPE_COMPUTE_CAP_MAX_THREADS_PER_BLOCK: pipe_compute_cap = 4;
+pub const PIPE_COMPUTE_CAP_MAX_GLOBAL_SIZE: pipe_compute_cap = 5;
+pub const PIPE_COMPUTE_CAP_MAX_LOCAL_SIZE: pipe_compute_cap = 6;
+pub const PIPE_COMPUTE_CAP_MAX_PRIVATE_SIZE: pipe_compute_cap = 7;
+pub const PIPE_COMPUTE_CAP_MAX_INPUT_SIZE: pipe_compute_cap = 8;
+pub const PIPE_COMPUTE_CAP_MAX_MEM_ALLOC_SIZE: pipe_compute_cap = 9;
+pub const PIPE_COMPUTE_CAP_MAX_CLOCK_FREQUENCY: pipe_compute_cap = 10;
+pub const PIPE_COMPUTE_CAP_MAX_COMPUTE_UNITS: pipe_compute_cap = 11;
+pub const PIPE_COMPUTE_CAP_IMAGES_SUPPORTED: pipe_compute_cap = 12;
+/// Compute-specific implementation capability. They can be queried
+/// using pipe_screen::get_compute_param.
+pub type pipe_compute_cap = u32;
+/// Query result for PIPE_QUERY_SO_STATISTICS.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pipe_query_data_so_statistics {
+ pub num_primitives_written: u64,
+ pub primitives_storage_needed: u64,
+}
+/// Query result for PIPE_QUERY_TIMESTAMP_DISJOINT.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pipe_query_data_timestamp_disjoint {
+ pub frequency: u64,
+ pub disjoint: boolean,
+}
+/// Query result for PIPE_QUERY_PIPELINE_STATISTICS.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pipe_query_data_pipeline_statistics {
+ /// < Num vertices read by the vertex fetcher.
+ pub ia_vertices: u64,
+ /// < Num primitives read by the vertex fetcher.
+ pub ia_primitives: u64,
+ /// < Num vertex shader invocations.
+ pub vs_invocations: u64,
+ /// < Num geometry shader invocations.
+ pub gs_invocations: u64,
+ /// < Num primitives output by a geometry shader.
+ pub gs_primitives: u64,
+ /// < Num primitives sent to the rasterizer.
+ pub c_invocations: u64,
+ /// < Num primitives that were rendered.
+ pub c_primitives: u64,
+ /// < Num pixel shader invocations.
+ pub ps_invocations: u64,
+ /// < Num hull shader invocations.
+ pub hs_invocations: u64,
+ /// < Num domain shader invocations.
+ pub ds_invocations: u64,
+ /// < Num compute shader invocations.
+ pub cs_invocations: u64,
+}
+/// Query result (returned by pipe_context::get_query_result).
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pipe_query_result {
+ pub b: boolean,
+ pub u64: u64,
+ pub so_statistics: pipe_query_data_so_statistics,
+ pub timestamp_disjoint: pipe_query_data_timestamp_disjoint,
+ pub pipeline_statistics: pipe_query_data_pipeline_statistics,
+ _bindgen_union_align: [u64; 11usize],
+}
+pub const PIPE_QUERY_TYPE_I32: pipe_query_value_type = 0;
+pub const PIPE_QUERY_TYPE_U32: pipe_query_value_type = 1;
+pub const PIPE_QUERY_TYPE_I64: pipe_query_value_type = 2;
+pub const PIPE_QUERY_TYPE_U64: pipe_query_value_type = 3;
+pub type pipe_query_value_type = u32;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pipe_color_union {
+ pub f: [f32; 4usize],
+ pub i: [::std::os::raw::c_int; 4usize],
+ pub ui: [::std::os::raw::c_uint; 4usize],
+ _bindgen_union_align: [u32; 4usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pipe_driver_query_info {
+ pub name: *const ::std::os::raw::c_char,
+ pub query_type: ::std::os::raw::c_uint,
+ pub max_value: u64,
+ pub uses_byte_units: boolean,
+}
diff --git a/gpu_renderer/src/generated/p_format.rs b/gpu_renderer/src/generated/p_format.rs
new file mode 100644
index 0000000..709fa4d
--- /dev/null
+++ b/gpu_renderer/src/generated/p_format.rs
@@ -0,0 +1,323 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+pub const PIPE_FORMAT_NONE: pipe_format = 0;
+pub const PIPE_FORMAT_B8G8R8A8_UNORM: pipe_format = 1;
+pub const PIPE_FORMAT_B8G8R8X8_UNORM: pipe_format = 2;
+pub const PIPE_FORMAT_A8R8G8B8_UNORM: pipe_format = 3;
+pub const PIPE_FORMAT_X8R8G8B8_UNORM: pipe_format = 4;
+pub const PIPE_FORMAT_B5G5R5A1_UNORM: pipe_format = 5;
+pub const PIPE_FORMAT_B4G4R4A4_UNORM: pipe_format = 6;
+pub const PIPE_FORMAT_B5G6R5_UNORM: pipe_format = 7;
+pub const PIPE_FORMAT_R10G10B10A2_UNORM: pipe_format = 8;
+/// < ubyte luminance
+pub const PIPE_FORMAT_L8_UNORM: pipe_format = 9;
+/// < ubyte alpha
+pub const PIPE_FORMAT_A8_UNORM: pipe_format = 10;
+/// < ubyte intensity
+pub const PIPE_FORMAT_I8_UNORM: pipe_format = 11;
+/// < ubyte alpha, luminance
+pub const PIPE_FORMAT_L8A8_UNORM: pipe_format = 12;
+/// < ushort luminance
+pub const PIPE_FORMAT_L16_UNORM: pipe_format = 13;
+pub const PIPE_FORMAT_UYVY: pipe_format = 14;
+pub const PIPE_FORMAT_YUYV: pipe_format = 15;
+pub const PIPE_FORMAT_Z16_UNORM: pipe_format = 16;
+pub const PIPE_FORMAT_Z32_UNORM: pipe_format = 17;
+pub const PIPE_FORMAT_Z32_FLOAT: pipe_format = 18;
+pub const PIPE_FORMAT_Z24_UNORM_S8_UINT: pipe_format = 19;
+pub const PIPE_FORMAT_S8_UINT_Z24_UNORM: pipe_format = 20;
+pub const PIPE_FORMAT_Z24X8_UNORM: pipe_format = 21;
+pub const PIPE_FORMAT_X8Z24_UNORM: pipe_format = 22;
+/// < ubyte stencil
+pub const PIPE_FORMAT_S8_UINT: pipe_format = 23;
+pub const PIPE_FORMAT_R64_FLOAT: pipe_format = 24;
+pub const PIPE_FORMAT_R64G64_FLOAT: pipe_format = 25;
+pub const PIPE_FORMAT_R64G64B64_FLOAT: pipe_format = 26;
+pub const PIPE_FORMAT_R64G64B64A64_FLOAT: pipe_format = 27;
+pub const PIPE_FORMAT_R32_FLOAT: pipe_format = 28;
+pub const PIPE_FORMAT_R32G32_FLOAT: pipe_format = 29;
+pub const PIPE_FORMAT_R32G32B32_FLOAT: pipe_format = 30;
+pub const PIPE_FORMAT_R32G32B32A32_FLOAT: pipe_format = 31;
+pub const PIPE_FORMAT_R32_UNORM: pipe_format = 32;
+pub const PIPE_FORMAT_R32G32_UNORM: pipe_format = 33;
+pub const PIPE_FORMAT_R32G32B32_UNORM: pipe_format = 34;
+pub const PIPE_FORMAT_R32G32B32A32_UNORM: pipe_format = 35;
+pub const PIPE_FORMAT_R32_USCALED: pipe_format = 36;
+pub const PIPE_FORMAT_R32G32_USCALED: pipe_format = 37;
+pub const PIPE_FORMAT_R32G32B32_USCALED: pipe_format = 38;
+pub const PIPE_FORMAT_R32G32B32A32_USCALED: pipe_format = 39;
+pub const PIPE_FORMAT_R32_SNORM: pipe_format = 40;
+pub const PIPE_FORMAT_R32G32_SNORM: pipe_format = 41;
+pub const PIPE_FORMAT_R32G32B32_SNORM: pipe_format = 42;
+pub const PIPE_FORMAT_R32G32B32A32_SNORM: pipe_format = 43;
+pub const PIPE_FORMAT_R32_SSCALED: pipe_format = 44;
+pub const PIPE_FORMAT_R32G32_SSCALED: pipe_format = 45;
+pub const PIPE_FORMAT_R32G32B32_SSCALED: pipe_format = 46;
+pub const PIPE_FORMAT_R32G32B32A32_SSCALED: pipe_format = 47;
+pub const PIPE_FORMAT_R16_UNORM: pipe_format = 48;
+pub const PIPE_FORMAT_R16G16_UNORM: pipe_format = 49;
+pub const PIPE_FORMAT_R16G16B16_UNORM: pipe_format = 50;
+pub const PIPE_FORMAT_R16G16B16A16_UNORM: pipe_format = 51;
+pub const PIPE_FORMAT_R16_USCALED: pipe_format = 52;
+pub const PIPE_FORMAT_R16G16_USCALED: pipe_format = 53;
+pub const PIPE_FORMAT_R16G16B16_USCALED: pipe_format = 54;
+pub const PIPE_FORMAT_R16G16B16A16_USCALED: pipe_format = 55;
+pub const PIPE_FORMAT_R16_SNORM: pipe_format = 56;
+pub const PIPE_FORMAT_R16G16_SNORM: pipe_format = 57;
+pub const PIPE_FORMAT_R16G16B16_SNORM: pipe_format = 58;
+pub const PIPE_FORMAT_R16G16B16A16_SNORM: pipe_format = 59;
+pub const PIPE_FORMAT_R16_SSCALED: pipe_format = 60;
+pub const PIPE_FORMAT_R16G16_SSCALED: pipe_format = 61;
+pub const PIPE_FORMAT_R16G16B16_SSCALED: pipe_format = 62;
+pub const PIPE_FORMAT_R16G16B16A16_SSCALED: pipe_format = 63;
+pub const PIPE_FORMAT_R8_UNORM: pipe_format = 64;
+pub const PIPE_FORMAT_R8G8_UNORM: pipe_format = 65;
+pub const PIPE_FORMAT_R8G8B8_UNORM: pipe_format = 66;
+pub const PIPE_FORMAT_R8G8B8A8_UNORM: pipe_format = 67;
+pub const PIPE_FORMAT_X8B8G8R8_UNORM: pipe_format = 68;
+pub const PIPE_FORMAT_R8_USCALED: pipe_format = 69;
+pub const PIPE_FORMAT_R8G8_USCALED: pipe_format = 70;
+pub const PIPE_FORMAT_R8G8B8_USCALED: pipe_format = 71;
+pub const PIPE_FORMAT_R8G8B8A8_USCALED: pipe_format = 72;
+pub const PIPE_FORMAT_R8_SNORM: pipe_format = 74;
+pub const PIPE_FORMAT_R8G8_SNORM: pipe_format = 75;
+pub const PIPE_FORMAT_R8G8B8_SNORM: pipe_format = 76;
+pub const PIPE_FORMAT_R8G8B8A8_SNORM: pipe_format = 77;
+pub const PIPE_FORMAT_R8_SSCALED: pipe_format = 82;
+pub const PIPE_FORMAT_R8G8_SSCALED: pipe_format = 83;
+pub const PIPE_FORMAT_R8G8B8_SSCALED: pipe_format = 84;
+pub const PIPE_FORMAT_R8G8B8A8_SSCALED: pipe_format = 85;
+pub const PIPE_FORMAT_R32_FIXED: pipe_format = 87;
+pub const PIPE_FORMAT_R32G32_FIXED: pipe_format = 88;
+pub const PIPE_FORMAT_R32G32B32_FIXED: pipe_format = 89;
+pub const PIPE_FORMAT_R32G32B32A32_FIXED: pipe_format = 90;
+pub const PIPE_FORMAT_R16_FLOAT: pipe_format = 91;
+pub const PIPE_FORMAT_R16G16_FLOAT: pipe_format = 92;
+pub const PIPE_FORMAT_R16G16B16_FLOAT: pipe_format = 93;
+pub const PIPE_FORMAT_R16G16B16A16_FLOAT: pipe_format = 94;
+pub const PIPE_FORMAT_L8_SRGB: pipe_format = 95;
+pub const PIPE_FORMAT_L8A8_SRGB: pipe_format = 96;
+pub const PIPE_FORMAT_R8G8B8_SRGB: pipe_format = 97;
+pub const PIPE_FORMAT_A8B8G8R8_SRGB: pipe_format = 98;
+pub const PIPE_FORMAT_X8B8G8R8_SRGB: pipe_format = 99;
+pub const PIPE_FORMAT_B8G8R8A8_SRGB: pipe_format = 100;
+pub const PIPE_FORMAT_B8G8R8X8_SRGB: pipe_format = 101;
+pub const PIPE_FORMAT_A8R8G8B8_SRGB: pipe_format = 102;
+pub const PIPE_FORMAT_X8R8G8B8_SRGB: pipe_format = 103;
+pub const PIPE_FORMAT_R8G8B8A8_SRGB: pipe_format = 104;
+pub const PIPE_FORMAT_DXT1_RGB: pipe_format = 105;
+pub const PIPE_FORMAT_DXT1_RGBA: pipe_format = 106;
+pub const PIPE_FORMAT_DXT3_RGBA: pipe_format = 107;
+pub const PIPE_FORMAT_DXT5_RGBA: pipe_format = 108;
+pub const PIPE_FORMAT_DXT1_SRGB: pipe_format = 109;
+pub const PIPE_FORMAT_DXT1_SRGBA: pipe_format = 110;
+pub const PIPE_FORMAT_DXT3_SRGBA: pipe_format = 111;
+pub const PIPE_FORMAT_DXT5_SRGBA: pipe_format = 112;
+pub const PIPE_FORMAT_RGTC1_UNORM: pipe_format = 113;
+pub const PIPE_FORMAT_RGTC1_SNORM: pipe_format = 114;
+pub const PIPE_FORMAT_RGTC2_UNORM: pipe_format = 115;
+pub const PIPE_FORMAT_RGTC2_SNORM: pipe_format = 116;
+pub const PIPE_FORMAT_R8G8_B8G8_UNORM: pipe_format = 117;
+pub const PIPE_FORMAT_G8R8_G8B8_UNORM: pipe_format = 118;
+pub const PIPE_FORMAT_R8SG8SB8UX8U_NORM: pipe_format = 119;
+pub const PIPE_FORMAT_R5SG5SB6U_NORM: pipe_format = 120;
+pub const PIPE_FORMAT_A8B8G8R8_UNORM: pipe_format = 121;
+pub const PIPE_FORMAT_B5G5R5X1_UNORM: pipe_format = 122;
+pub const PIPE_FORMAT_R10G10B10A2_USCALED: pipe_format = 123;
+pub const PIPE_FORMAT_R11G11B10_FLOAT: pipe_format = 124;
+pub const PIPE_FORMAT_R9G9B9E5_FLOAT: pipe_format = 125;
+pub const PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: pipe_format = 126;
+pub const PIPE_FORMAT_R1_UNORM: pipe_format = 127;
+pub const PIPE_FORMAT_R10G10B10X2_USCALED: pipe_format = 128;
+pub const PIPE_FORMAT_R10G10B10X2_SNORM: pipe_format = 129;
+pub const PIPE_FORMAT_L4A4_UNORM: pipe_format = 130;
+pub const PIPE_FORMAT_B10G10R10A2_UNORM: pipe_format = 131;
+pub const PIPE_FORMAT_R10SG10SB10SA2U_NORM: pipe_format = 132;
+pub const PIPE_FORMAT_R8G8Bx_SNORM: pipe_format = 133;
+pub const PIPE_FORMAT_R8G8B8X8_UNORM: pipe_format = 134;
+pub const PIPE_FORMAT_B4G4R4X4_UNORM: pipe_format = 135;
+pub const PIPE_FORMAT_X24S8_UINT: pipe_format = 136;
+pub const PIPE_FORMAT_S8X24_UINT: pipe_format = 137;
+pub const PIPE_FORMAT_X32_S8X24_UINT: pipe_format = 138;
+pub const PIPE_FORMAT_B2G3R3_UNORM: pipe_format = 139;
+pub const PIPE_FORMAT_L16A16_UNORM: pipe_format = 140;
+pub const PIPE_FORMAT_A16_UNORM: pipe_format = 141;
+pub const PIPE_FORMAT_I16_UNORM: pipe_format = 142;
+pub const PIPE_FORMAT_LATC1_UNORM: pipe_format = 143;
+pub const PIPE_FORMAT_LATC1_SNORM: pipe_format = 144;
+pub const PIPE_FORMAT_LATC2_UNORM: pipe_format = 145;
+pub const PIPE_FORMAT_LATC2_SNORM: pipe_format = 146;
+pub const PIPE_FORMAT_A8_SNORM: pipe_format = 147;
+pub const PIPE_FORMAT_L8_SNORM: pipe_format = 148;
+pub const PIPE_FORMAT_L8A8_SNORM: pipe_format = 149;
+pub const PIPE_FORMAT_I8_SNORM: pipe_format = 150;
+pub const PIPE_FORMAT_A16_SNORM: pipe_format = 151;
+pub const PIPE_FORMAT_L16_SNORM: pipe_format = 152;
+pub const PIPE_FORMAT_L16A16_SNORM: pipe_format = 153;
+pub const PIPE_FORMAT_I16_SNORM: pipe_format = 154;
+pub const PIPE_FORMAT_A16_FLOAT: pipe_format = 155;
+pub const PIPE_FORMAT_L16_FLOAT: pipe_format = 156;
+pub const PIPE_FORMAT_L16A16_FLOAT: pipe_format = 157;
+pub const PIPE_FORMAT_I16_FLOAT: pipe_format = 158;
+pub const PIPE_FORMAT_A32_FLOAT: pipe_format = 159;
+pub const PIPE_FORMAT_L32_FLOAT: pipe_format = 160;
+pub const PIPE_FORMAT_L32A32_FLOAT: pipe_format = 161;
+pub const PIPE_FORMAT_I32_FLOAT: pipe_format = 162;
+pub const PIPE_FORMAT_YV12: pipe_format = 163;
+pub const PIPE_FORMAT_YV16: pipe_format = 164;
+/// < aka I420
+pub const PIPE_FORMAT_IYUV: pipe_format = 165;
+pub const PIPE_FORMAT_NV12: pipe_format = 166;
+pub const PIPE_FORMAT_NV21: pipe_format = 167;
+pub const PIPE_FORMAT_A4R4_UNORM: pipe_format = 168;
+pub const PIPE_FORMAT_R4A4_UNORM: pipe_format = 169;
+pub const PIPE_FORMAT_R8A8_UNORM: pipe_format = 170;
+pub const PIPE_FORMAT_A8R8_UNORM: pipe_format = 171;
+pub const PIPE_FORMAT_R10G10B10A2_SSCALED: pipe_format = 172;
+pub const PIPE_FORMAT_R10G10B10A2_SNORM: pipe_format = 173;
+pub const PIPE_FORMAT_B10G10R10A2_USCALED: pipe_format = 174;
+pub const PIPE_FORMAT_B10G10R10A2_SSCALED: pipe_format = 175;
+pub const PIPE_FORMAT_B10G10R10A2_SNORM: pipe_format = 176;
+pub const PIPE_FORMAT_R8_UINT: pipe_format = 177;
+pub const PIPE_FORMAT_R8G8_UINT: pipe_format = 178;
+pub const PIPE_FORMAT_R8G8B8_UINT: pipe_format = 179;
+pub const PIPE_FORMAT_R8G8B8A8_UINT: pipe_format = 180;
+pub const PIPE_FORMAT_R8_SINT: pipe_format = 181;
+pub const PIPE_FORMAT_R8G8_SINT: pipe_format = 182;
+pub const PIPE_FORMAT_R8G8B8_SINT: pipe_format = 183;
+pub const PIPE_FORMAT_R8G8B8A8_SINT: pipe_format = 184;
+pub const PIPE_FORMAT_R16_UINT: pipe_format = 185;
+pub const PIPE_FORMAT_R16G16_UINT: pipe_format = 186;
+pub const PIPE_FORMAT_R16G16B16_UINT: pipe_format = 187;
+pub const PIPE_FORMAT_R16G16B16A16_UINT: pipe_format = 188;
+pub const PIPE_FORMAT_R16_SINT: pipe_format = 189;
+pub const PIPE_FORMAT_R16G16_SINT: pipe_format = 190;
+pub const PIPE_FORMAT_R16G16B16_SINT: pipe_format = 191;
+pub const PIPE_FORMAT_R16G16B16A16_SINT: pipe_format = 192;
+pub const PIPE_FORMAT_R32_UINT: pipe_format = 193;
+pub const PIPE_FORMAT_R32G32_UINT: pipe_format = 194;
+pub const PIPE_FORMAT_R32G32B32_UINT: pipe_format = 195;
+pub const PIPE_FORMAT_R32G32B32A32_UINT: pipe_format = 196;
+pub const PIPE_FORMAT_R32_SINT: pipe_format = 197;
+pub const PIPE_FORMAT_R32G32_SINT: pipe_format = 198;
+pub const PIPE_FORMAT_R32G32B32_SINT: pipe_format = 199;
+pub const PIPE_FORMAT_R32G32B32A32_SINT: pipe_format = 200;
+pub const PIPE_FORMAT_A8_UINT: pipe_format = 201;
+pub const PIPE_FORMAT_I8_UINT: pipe_format = 202;
+pub const PIPE_FORMAT_L8_UINT: pipe_format = 203;
+pub const PIPE_FORMAT_L8A8_UINT: pipe_format = 204;
+pub const PIPE_FORMAT_A8_SINT: pipe_format = 205;
+pub const PIPE_FORMAT_I8_SINT: pipe_format = 206;
+pub const PIPE_FORMAT_L8_SINT: pipe_format = 207;
+pub const PIPE_FORMAT_L8A8_SINT: pipe_format = 208;
+pub const PIPE_FORMAT_A16_UINT: pipe_format = 209;
+pub const PIPE_FORMAT_I16_UINT: pipe_format = 210;
+pub const PIPE_FORMAT_L16_UINT: pipe_format = 211;
+pub const PIPE_FORMAT_L16A16_UINT: pipe_format = 212;
+pub const PIPE_FORMAT_A16_SINT: pipe_format = 213;
+pub const PIPE_FORMAT_I16_SINT: pipe_format = 214;
+pub const PIPE_FORMAT_L16_SINT: pipe_format = 215;
+pub const PIPE_FORMAT_L16A16_SINT: pipe_format = 216;
+pub const PIPE_FORMAT_A32_UINT: pipe_format = 217;
+pub const PIPE_FORMAT_I32_UINT: pipe_format = 218;
+pub const PIPE_FORMAT_L32_UINT: pipe_format = 219;
+pub const PIPE_FORMAT_L32A32_UINT: pipe_format = 220;
+pub const PIPE_FORMAT_A32_SINT: pipe_format = 221;
+pub const PIPE_FORMAT_I32_SINT: pipe_format = 222;
+pub const PIPE_FORMAT_L32_SINT: pipe_format = 223;
+pub const PIPE_FORMAT_L32A32_SINT: pipe_format = 224;
+pub const PIPE_FORMAT_B10G10R10A2_UINT: pipe_format = 225;
+pub const PIPE_FORMAT_ETC1_RGB8: pipe_format = 226;
+pub const PIPE_FORMAT_R8G8_R8B8_UNORM: pipe_format = 227;
+pub const PIPE_FORMAT_G8R8_B8R8_UNORM: pipe_format = 228;
+pub const PIPE_FORMAT_R8G8B8X8_SNORM: pipe_format = 229;
+pub const PIPE_FORMAT_R8G8B8X8_SRGB: pipe_format = 230;
+pub const PIPE_FORMAT_R8G8B8X8_UINT: pipe_format = 231;
+pub const PIPE_FORMAT_R8G8B8X8_SINT: pipe_format = 232;
+pub const PIPE_FORMAT_B10G10R10X2_UNORM: pipe_format = 233;
+pub const PIPE_FORMAT_R16G16B16X16_UNORM: pipe_format = 234;
+pub const PIPE_FORMAT_R16G16B16X16_SNORM: pipe_format = 235;
+pub const PIPE_FORMAT_R16G16B16X16_FLOAT: pipe_format = 236;
+pub const PIPE_FORMAT_R16G16B16X16_UINT: pipe_format = 237;
+pub const PIPE_FORMAT_R16G16B16X16_SINT: pipe_format = 238;
+pub const PIPE_FORMAT_R32G32B32X32_FLOAT: pipe_format = 239;
+pub const PIPE_FORMAT_R32G32B32X32_UINT: pipe_format = 240;
+pub const PIPE_FORMAT_R32G32B32X32_SINT: pipe_format = 241;
+pub const PIPE_FORMAT_R8A8_SNORM: pipe_format = 242;
+pub const PIPE_FORMAT_R16A16_UNORM: pipe_format = 243;
+pub const PIPE_FORMAT_R16A16_SNORM: pipe_format = 244;
+pub const PIPE_FORMAT_R16A16_FLOAT: pipe_format = 245;
+pub const PIPE_FORMAT_R32A32_FLOAT: pipe_format = 246;
+pub const PIPE_FORMAT_R8A8_UINT: pipe_format = 247;
+pub const PIPE_FORMAT_R8A8_SINT: pipe_format = 248;
+pub const PIPE_FORMAT_R16A16_UINT: pipe_format = 249;
+pub const PIPE_FORMAT_R16A16_SINT: pipe_format = 250;
+pub const PIPE_FORMAT_R32A32_UINT: pipe_format = 251;
+pub const PIPE_FORMAT_R32A32_SINT: pipe_format = 252;
+pub const PIPE_FORMAT_R10G10B10A2_UINT: pipe_format = 253;
+pub const PIPE_FORMAT_B5G6R5_SRGB: pipe_format = 254;
+pub const PIPE_FORMAT_BPTC_RGBA_UNORM: pipe_format = 255;
+pub const PIPE_FORMAT_BPTC_SRGBA: pipe_format = 256;
+pub const PIPE_FORMAT_BPTC_RGB_FLOAT: pipe_format = 257;
+pub const PIPE_FORMAT_BPTC_RGB_UFLOAT: pipe_format = 258;
+pub const PIPE_FORMAT_A8L8_UNORM: pipe_format = 259;
+pub const PIPE_FORMAT_A8L8_SNORM: pipe_format = 260;
+pub const PIPE_FORMAT_A8L8_SRGB: pipe_format = 261;
+pub const PIPE_FORMAT_A16L16_UNORM: pipe_format = 262;
+pub const PIPE_FORMAT_G8R8_UNORM: pipe_format = 263;
+pub const PIPE_FORMAT_G8R8_SNORM: pipe_format = 264;
+pub const PIPE_FORMAT_G16R16_UNORM: pipe_format = 265;
+pub const PIPE_FORMAT_G16R16_SNORM: pipe_format = 266;
+pub const PIPE_FORMAT_A8B8G8R8_SNORM: pipe_format = 267;
+pub const PIPE_FORMAT_X8B8G8R8_SNORM: pipe_format = 268;
+pub const PIPE_FORMAT_ETC2_RGB8: pipe_format = 269;
+pub const PIPE_FORMAT_ETC2_SRGB8: pipe_format = 270;
+pub const PIPE_FORMAT_ETC2_RGB8A1: pipe_format = 271;
+pub const PIPE_FORMAT_ETC2_SRGB8A1: pipe_format = 272;
+pub const PIPE_FORMAT_ETC2_RGBA8: pipe_format = 273;
+pub const PIPE_FORMAT_ETC2_SRGBA8: pipe_format = 274;
+pub const PIPE_FORMAT_ETC2_R11_UNORM: pipe_format = 275;
+pub const PIPE_FORMAT_ETC2_R11_SNORM: pipe_format = 276;
+pub const PIPE_FORMAT_ETC2_RG11_UNORM: pipe_format = 277;
+pub const PIPE_FORMAT_ETC2_RG11_SNORM: pipe_format = 278;
+pub const PIPE_FORMAT_ASTC_4x4: pipe_format = 279;
+pub const PIPE_FORMAT_ASTC_5x4: pipe_format = 280;
+pub const PIPE_FORMAT_ASTC_5x5: pipe_format = 281;
+pub const PIPE_FORMAT_ASTC_6x5: pipe_format = 282;
+pub const PIPE_FORMAT_ASTC_6x6: pipe_format = 283;
+pub const PIPE_FORMAT_ASTC_8x5: pipe_format = 284;
+pub const PIPE_FORMAT_ASTC_8x6: pipe_format = 285;
+pub const PIPE_FORMAT_ASTC_8x8: pipe_format = 286;
+pub const PIPE_FORMAT_ASTC_10x5: pipe_format = 287;
+pub const PIPE_FORMAT_ASTC_10x6: pipe_format = 288;
+pub const PIPE_FORMAT_ASTC_10x8: pipe_format = 289;
+pub const PIPE_FORMAT_ASTC_10x10: pipe_format = 290;
+pub const PIPE_FORMAT_ASTC_12x10: pipe_format = 291;
+pub const PIPE_FORMAT_ASTC_12x12: pipe_format = 292;
+pub const PIPE_FORMAT_ASTC_4x4_SRGB: pipe_format = 293;
+pub const PIPE_FORMAT_ASTC_5x4_SRGB: pipe_format = 294;
+pub const PIPE_FORMAT_ASTC_5x5_SRGB: pipe_format = 295;
+pub const PIPE_FORMAT_ASTC_6x5_SRGB: pipe_format = 296;
+pub const PIPE_FORMAT_ASTC_6x6_SRGB: pipe_format = 297;
+pub const PIPE_FORMAT_ASTC_8x5_SRGB: pipe_format = 298;
+pub const PIPE_FORMAT_ASTC_8x6_SRGB: pipe_format = 299;
+pub const PIPE_FORMAT_ASTC_8x8_SRGB: pipe_format = 300;
+pub const PIPE_FORMAT_ASTC_10x5_SRGB: pipe_format = 301;
+pub const PIPE_FORMAT_ASTC_10x6_SRGB: pipe_format = 302;
+pub const PIPE_FORMAT_ASTC_10x8_SRGB: pipe_format = 303;
+pub const PIPE_FORMAT_ASTC_10x10_SRGB: pipe_format = 304;
+pub const PIPE_FORMAT_ASTC_12x10_SRGB: pipe_format = 305;
+pub const PIPE_FORMAT_ASTC_12x12_SRGB: pipe_format = 306;
+pub const PIPE_FORMAT_P016: pipe_format = 307;
+pub const PIPE_FORMAT_R10G10B10X2_UNORM: pipe_format = 308;
+pub const PIPE_FORMAT_A1B5G5R5_UNORM: pipe_format = 309;
+pub const PIPE_FORMAT_X1B5G5R5_UNORM: pipe_format = 310;
+pub const PIPE_FORMAT_A4B4G4R4_UNORM: pipe_format = 311;
+pub const PIPE_FORMAT_R8_SRGB: pipe_format = 312;
+pub const PIPE_FORMAT_COUNT: pipe_format = 313;
+/// Formats for textures, surfaces and vertex data
+pub type pipe_format = u32;
diff --git a/gpu_renderer/src/generated/virgl_protocol.rs b/gpu_renderer/src/generated/virgl_protocol.rs
new file mode 100644
index 0000000..8d09d61
--- /dev/null
+++ b/gpu_renderer/src/generated/virgl_protocol.rs
@@ -0,0 +1,310 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+pub const VIRGL_QUERY_STATE_NEW: u32 = 0;
+pub const VIRGL_QUERY_STATE_DONE: u32 = 1;
+pub const VIRGL_QUERY_STATE_WAIT_HOST: u32 = 2;
+pub const VIRGL_MAX_COLOR_BUFS: u32 = 8;
+pub const VIRGL_MAX_CLIP_PLANES: u32 = 8;
+pub const VIRGL_OBJ_CREATE_HEADER: u32 = 0;
+pub const VIRGL_OBJ_CREATE_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_BIND_HEADER: u32 = 0;
+pub const VIRGL_OBJ_BIND_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_DESTROY_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_BLEND_SIZE: u32 = 11;
+pub const VIRGL_OBJ_BLEND_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_BLEND_S0: u32 = 2;
+pub const VIRGL_OBJ_BLEND_S1: u32 = 3;
+pub const VIRGL_OBJ_DSA_SIZE: u32 = 5;
+pub const VIRGL_OBJ_DSA_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_DSA_S0: u32 = 2;
+pub const VIRGL_OBJ_DSA_S1: u32 = 3;
+pub const VIRGL_OBJ_DSA_S2: u32 = 4;
+pub const VIRGL_OBJ_DSA_ALPHA_REF: u32 = 5;
+pub const VIRGL_OBJ_RS_SIZE: u32 = 9;
+pub const VIRGL_OBJ_RS_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_RS_S0: u32 = 2;
+pub const VIRGL_OBJ_RS_POINT_SIZE: u32 = 3;
+pub const VIRGL_OBJ_RS_SPRITE_COORD_ENABLE: u32 = 4;
+pub const VIRGL_OBJ_RS_S3: u32 = 5;
+pub const VIRGL_OBJ_RS_LINE_WIDTH: u32 = 6;
+pub const VIRGL_OBJ_RS_OFFSET_UNITS: u32 = 7;
+pub const VIRGL_OBJ_RS_OFFSET_SCALE: u32 = 8;
+pub const VIRGL_OBJ_RS_OFFSET_CLAMP: u32 = 9;
+pub const VIRGL_OBJ_CLEAR_SIZE: u32 = 8;
+pub const VIRGL_OBJ_CLEAR_BUFFERS: u32 = 1;
+pub const VIRGL_OBJ_CLEAR_COLOR_0: u32 = 2;
+pub const VIRGL_OBJ_CLEAR_COLOR_1: u32 = 3;
+pub const VIRGL_OBJ_CLEAR_COLOR_2: u32 = 4;
+pub const VIRGL_OBJ_CLEAR_COLOR_3: u32 = 5;
+pub const VIRGL_OBJ_CLEAR_DEPTH_0: u32 = 6;
+pub const VIRGL_OBJ_CLEAR_DEPTH_1: u32 = 7;
+pub const VIRGL_OBJ_CLEAR_STENCIL: u32 = 8;
+pub const VIRGL_OBJ_SHADER_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_SHADER_TYPE: u32 = 2;
+pub const VIRGL_OBJ_SHADER_OFFSET: u32 = 3;
+pub const VIRGL_OBJ_SHADER_OFFSET_CONT: u32 = 2147483648;
+pub const VIRGL_OBJ_SHADER_NUM_TOKENS: u32 = 4;
+pub const VIRGL_OBJ_SHADER_SO_NUM_OUTPUTS: u32 = 5;
+pub const VIRGL_SET_VIEWPORT_START_SLOT: u32 = 1;
+pub const VIRGL_SET_FRAMEBUFFER_STATE_NR_CBUFS: u32 = 1;
+pub const VIRGL_SET_FRAMEBUFFER_STATE_NR_ZSURF_HANDLE: u32 = 2;
+pub const VIRGL_OBJ_VERTEX_ELEMENTS_HANDLE: u32 = 1;
+pub const VIRGL_SET_INDEX_BUFFER_HANDLE: u32 = 1;
+pub const VIRGL_SET_INDEX_BUFFER_INDEX_SIZE: u32 = 2;
+pub const VIRGL_SET_INDEX_BUFFER_OFFSET: u32 = 3;
+pub const VIRGL_SET_CONSTANT_BUFFER_SHADER_TYPE: u32 = 1;
+pub const VIRGL_SET_CONSTANT_BUFFER_INDEX: u32 = 2;
+pub const VIRGL_SET_CONSTANT_BUFFER_DATA_START: u32 = 3;
+pub const VIRGL_SET_UNIFORM_BUFFER_SIZE: u32 = 5;
+pub const VIRGL_SET_UNIFORM_BUFFER_SHADER_TYPE: u32 = 1;
+pub const VIRGL_SET_UNIFORM_BUFFER_INDEX: u32 = 2;
+pub const VIRGL_SET_UNIFORM_BUFFER_OFFSET: u32 = 3;
+pub const VIRGL_SET_UNIFORM_BUFFER_LENGTH: u32 = 4;
+pub const VIRGL_SET_UNIFORM_BUFFER_RES_HANDLE: u32 = 5;
+pub const VIRGL_DRAW_VBO_SIZE: u32 = 12;
+pub const VIRGL_DRAW_VBO_SIZE_TESS: u32 = 14;
+pub const VIRGL_DRAW_VBO_SIZE_INDIRECT: u32 = 20;
+pub const VIRGL_DRAW_VBO_START: u32 = 1;
+pub const VIRGL_DRAW_VBO_COUNT: u32 = 2;
+pub const VIRGL_DRAW_VBO_MODE: u32 = 3;
+pub const VIRGL_DRAW_VBO_INDEXED: u32 = 4;
+pub const VIRGL_DRAW_VBO_INSTANCE_COUNT: u32 = 5;
+pub const VIRGL_DRAW_VBO_INDEX_BIAS: u32 = 6;
+pub const VIRGL_DRAW_VBO_START_INSTANCE: u32 = 7;
+pub const VIRGL_DRAW_VBO_PRIMITIVE_RESTART: u32 = 8;
+pub const VIRGL_DRAW_VBO_RESTART_INDEX: u32 = 9;
+pub const VIRGL_DRAW_VBO_MIN_INDEX: u32 = 10;
+pub const VIRGL_DRAW_VBO_MAX_INDEX: u32 = 11;
+pub const VIRGL_DRAW_VBO_COUNT_FROM_SO: u32 = 12;
+pub const VIRGL_DRAW_VBO_VERTICES_PER_PATCH: u32 = 13;
+pub const VIRGL_DRAW_VBO_DRAWID: u32 = 14;
+pub const VIRGL_DRAW_VBO_INDIRECT_HANDLE: u32 = 15;
+pub const VIRGL_DRAW_VBO_INDIRECT_OFFSET: u32 = 16;
+pub const VIRGL_DRAW_VBO_INDIRECT_STRIDE: u32 = 17;
+pub const VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT: u32 = 18;
+pub const VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT_OFFSET: u32 = 19;
+pub const VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT_HANDLE: u32 = 20;
+pub const VIRGL_OBJ_SURFACE_SIZE: u32 = 5;
+pub const VIRGL_OBJ_SURFACE_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_SURFACE_RES_HANDLE: u32 = 2;
+pub const VIRGL_OBJ_SURFACE_FORMAT: u32 = 3;
+pub const VIRGL_OBJ_SURFACE_BUFFER_FIRST_ELEMENT: u32 = 4;
+pub const VIRGL_OBJ_SURFACE_BUFFER_LAST_ELEMENT: u32 = 5;
+pub const VIRGL_OBJ_SURFACE_TEXTURE_LEVEL: u32 = 4;
+pub const VIRGL_OBJ_SURFACE_TEXTURE_LAYERS: u32 = 5;
+pub const VIRGL_OBJ_STREAMOUT_SIZE: u32 = 4;
+pub const VIRGL_OBJ_STREAMOUT_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_STREAMOUT_RES_HANDLE: u32 = 2;
+pub const VIRGL_OBJ_STREAMOUT_BUFFER_OFFSET: u32 = 3;
+pub const VIRGL_OBJ_STREAMOUT_BUFFER_SIZE: u32 = 4;
+pub const VIRGL_OBJ_SAMPLER_STATE_SIZE: u32 = 9;
+pub const VIRGL_OBJ_SAMPLER_STATE_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_SAMPLER_STATE_S0: u32 = 2;
+pub const VIRGL_OBJ_SAMPLER_STATE_LOD_BIAS: u32 = 3;
+pub const VIRGL_OBJ_SAMPLER_STATE_MIN_LOD: u32 = 4;
+pub const VIRGL_OBJ_SAMPLER_STATE_MAX_LOD: u32 = 5;
+pub const VIRGL_OBJ_SAMPLER_VIEW_SIZE: u32 = 6;
+pub const VIRGL_OBJ_SAMPLER_VIEW_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_SAMPLER_VIEW_RES_HANDLE: u32 = 2;
+pub const VIRGL_OBJ_SAMPLER_VIEW_FORMAT: u32 = 3;
+pub const VIRGL_OBJ_SAMPLER_VIEW_BUFFER_FIRST_ELEMENT: u32 = 4;
+pub const VIRGL_OBJ_SAMPLER_VIEW_BUFFER_LAST_ELEMENT: u32 = 5;
+pub const VIRGL_OBJ_SAMPLER_VIEW_TEXTURE_LAYER: u32 = 4;
+pub const VIRGL_OBJ_SAMPLER_VIEW_TEXTURE_LEVEL: u32 = 5;
+pub const VIRGL_OBJ_SAMPLER_VIEW_SWIZZLE: u32 = 6;
+pub const VIRGL_SET_SAMPLER_VIEWS_SHADER_TYPE: u32 = 1;
+pub const VIRGL_SET_SAMPLER_VIEWS_START_SLOT: u32 = 2;
+pub const VIRGL_SET_SAMPLER_VIEWS_V0_HANDLE: u32 = 3;
+pub const VIRGL_BIND_SAMPLER_STATES_SHADER_TYPE: u32 = 1;
+pub const VIRGL_BIND_SAMPLER_STATES_START_SLOT: u32 = 2;
+pub const VIRGL_BIND_SAMPLER_STATES_S0_HANDLE: u32 = 3;
+pub const VIRGL_SET_STENCIL_REF_SIZE: u32 = 1;
+pub const VIRGL_SET_STENCIL_REF: u32 = 1;
+pub const VIRGL_SET_BLEND_COLOR_SIZE: u32 = 4;
+pub const VIRGL_SET_SCISSOR_START_SLOT: u32 = 1;
+pub const VIRGL_CMD_RESOURCE_COPY_REGION_SIZE: u32 = 13;
+pub const VIRGL_CMD_RCR_DST_RES_HANDLE: u32 = 1;
+pub const VIRGL_CMD_RCR_DST_LEVEL: u32 = 2;
+pub const VIRGL_CMD_RCR_DST_X: u32 = 3;
+pub const VIRGL_CMD_RCR_DST_Y: u32 = 4;
+pub const VIRGL_CMD_RCR_DST_Z: u32 = 5;
+pub const VIRGL_CMD_RCR_SRC_RES_HANDLE: u32 = 6;
+pub const VIRGL_CMD_RCR_SRC_LEVEL: u32 = 7;
+pub const VIRGL_CMD_RCR_SRC_X: u32 = 8;
+pub const VIRGL_CMD_RCR_SRC_Y: u32 = 9;
+pub const VIRGL_CMD_RCR_SRC_Z: u32 = 10;
+pub const VIRGL_CMD_RCR_SRC_W: u32 = 11;
+pub const VIRGL_CMD_RCR_SRC_H: u32 = 12;
+pub const VIRGL_CMD_RCR_SRC_D: u32 = 13;
+pub const VIRGL_CMD_BLIT_SIZE: u32 = 21;
+pub const VIRGL_CMD_BLIT_S0: u32 = 1;
+pub const VIRGL_CMD_BLIT_SCISSOR_MINX_MINY: u32 = 2;
+pub const VIRGL_CMD_BLIT_SCISSOR_MAXX_MAXY: u32 = 3;
+pub const VIRGL_CMD_BLIT_DST_RES_HANDLE: u32 = 4;
+pub const VIRGL_CMD_BLIT_DST_LEVEL: u32 = 5;
+pub const VIRGL_CMD_BLIT_DST_FORMAT: u32 = 6;
+pub const VIRGL_CMD_BLIT_DST_X: u32 = 7;
+pub const VIRGL_CMD_BLIT_DST_Y: u32 = 8;
+pub const VIRGL_CMD_BLIT_DST_Z: u32 = 9;
+pub const VIRGL_CMD_BLIT_DST_W: u32 = 10;
+pub const VIRGL_CMD_BLIT_DST_H: u32 = 11;
+pub const VIRGL_CMD_BLIT_DST_D: u32 = 12;
+pub const VIRGL_CMD_BLIT_SRC_RES_HANDLE: u32 = 13;
+pub const VIRGL_CMD_BLIT_SRC_LEVEL: u32 = 14;
+pub const VIRGL_CMD_BLIT_SRC_FORMAT: u32 = 15;
+pub const VIRGL_CMD_BLIT_SRC_X: u32 = 16;
+pub const VIRGL_CMD_BLIT_SRC_Y: u32 = 17;
+pub const VIRGL_CMD_BLIT_SRC_Z: u32 = 18;
+pub const VIRGL_CMD_BLIT_SRC_W: u32 = 19;
+pub const VIRGL_CMD_BLIT_SRC_H: u32 = 20;
+pub const VIRGL_CMD_BLIT_SRC_D: u32 = 21;
+pub const VIRGL_OBJ_QUERY_SIZE: u32 = 4;
+pub const VIRGL_OBJ_QUERY_HANDLE: u32 = 1;
+pub const VIRGL_OBJ_QUERY_TYPE_INDEX: u32 = 2;
+pub const VIRGL_OBJ_QUERY_OFFSET: u32 = 3;
+pub const VIRGL_OBJ_QUERY_RES_HANDLE: u32 = 4;
+pub const VIRGL_QUERY_BEGIN_HANDLE: u32 = 1;
+pub const VIRGL_QUERY_END_HANDLE: u32 = 1;
+pub const VIRGL_QUERY_RESULT_HANDLE: u32 = 1;
+pub const VIRGL_QUERY_RESULT_WAIT: u32 = 2;
+pub const VIRGL_RENDER_CONDITION_SIZE: u32 = 3;
+pub const VIRGL_RENDER_CONDITION_HANDLE: u32 = 1;
+pub const VIRGL_RENDER_CONDITION_CONDITION: u32 = 2;
+pub const VIRGL_RENDER_CONDITION_MODE: u32 = 3;
+pub const VIRGL_RESOURCE_IW_RES_HANDLE: u32 = 1;
+pub const VIRGL_RESOURCE_IW_LEVEL: u32 = 2;
+pub const VIRGL_RESOURCE_IW_USAGE: u32 = 3;
+pub const VIRGL_RESOURCE_IW_STRIDE: u32 = 4;
+pub const VIRGL_RESOURCE_IW_LAYER_STRIDE: u32 = 5;
+pub const VIRGL_RESOURCE_IW_X: u32 = 6;
+pub const VIRGL_RESOURCE_IW_Y: u32 = 7;
+pub const VIRGL_RESOURCE_IW_Z: u32 = 8;
+pub const VIRGL_RESOURCE_IW_W: u32 = 9;
+pub const VIRGL_RESOURCE_IW_H: u32 = 10;
+pub const VIRGL_RESOURCE_IW_D: u32 = 11;
+pub const VIRGL_RESOURCE_IW_DATA_START: u32 = 12;
+pub const VIRGL_SET_STREAMOUT_TARGETS_APPEND_BITMASK: u32 = 1;
+pub const VIRGL_SET_STREAMOUT_TARGETS_H0: u32 = 2;
+pub const VIRGL_SET_SAMPLE_MASK_SIZE: u32 = 1;
+pub const VIRGL_SET_SAMPLE_MASK_MASK: u32 = 1;
+pub const VIRGL_SET_CLIP_STATE_SIZE: u32 = 32;
+pub const VIRGL_SET_CLIP_STATE_C0: u32 = 1;
+pub const VIRGL_POLYGON_STIPPLE_SIZE: u32 = 32;
+pub const VIRGL_POLYGON_STIPPLE_P0: u32 = 1;
+pub const VIRGL_BIND_SHADER_SIZE: u32 = 2;
+pub const VIRGL_BIND_SHADER_HANDLE: u32 = 1;
+pub const VIRGL_BIND_SHADER_TYPE: u32 = 2;
+pub const VIRGL_TESS_STATE_SIZE: u32 = 6;
+pub const VIRGL_SET_MIN_SAMPLES_SIZE: u32 = 1;
+pub const VIRGL_SET_MIN_SAMPLES_MASK: u32 = 1;
+pub const VIRGL_SET_SHADER_BUFFER_ELEMENT_SIZE: u32 = 3;
+pub const VIRGL_SET_SHADER_BUFFER_SHADER_TYPE: u32 = 1;
+pub const VIRGL_SET_SHADER_BUFFER_START_SLOT: u32 = 2;
+pub const VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE: u32 = 5;
+pub const VIRGL_SET_SHADER_IMAGE_SHADER_TYPE: u32 = 1;
+pub const VIRGL_SET_SHADER_IMAGE_START_SLOT: u32 = 2;
+pub const VIRGL_MEMORY_BARRIER_SIZE: u32 = 1;
+pub const VIRGL_MEMORY_BARRIER_FLAGS: u32 = 1;
+pub const VIRGL_LAUNCH_GRID_SIZE: u32 = 8;
+pub const VIRGL_LAUNCH_BLOCK_X: u32 = 1;
+pub const VIRGL_LAUNCH_BLOCK_Y: u32 = 2;
+pub const VIRGL_LAUNCH_BLOCK_Z: u32 = 3;
+pub const VIRGL_LAUNCH_GRID_X: u32 = 4;
+pub const VIRGL_LAUNCH_GRID_Y: u32 = 5;
+pub const VIRGL_LAUNCH_GRID_Z: u32 = 6;
+pub const VIRGL_LAUNCH_INDIRECT_HANDLE: u32 = 7;
+pub const VIRGL_LAUNCH_INDIRECT_OFFSET: u32 = 8;
+pub const VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_SIZE: u32 = 2;
+pub const VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_WIDTH_HEIGHT: u32 = 1;
+pub const VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_LAYERS_SAMPLES: u32 = 2;
+pub const VIRGL_TEXTURE_BARRIER_SIZE: u32 = 1;
+pub const VIRGL_TEXTURE_BARRIER_FLAGS: u32 = 1;
+pub const VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE: u32 = 3;
+pub const VIRGL_SET_ATOMIC_BUFFER_START_SLOT: u32 = 1;
+pub const VIRGL_SET_DEBUG_FLAGS_MIN_SIZE: u32 = 2;
+pub const VIRGL_SET_DEBUG_FLAGSTRING_OFFSET: u32 = 1;
+pub const VIRGL_QUERY_RESULT_QBO_SIZE: u32 = 6;
+pub const VIRGL_QUERY_RESULT_QBO_HANDLE: u32 = 1;
+pub const VIRGL_QUERY_RESULT_QBO_QBO_HANDLE: u32 = 2;
+pub const VIRGL_QUERY_RESULT_QBO_WAIT: u32 = 3;
+pub const VIRGL_QUERY_RESULT_QBO_RESULT_TYPE: u32 = 4;
+pub const VIRGL_QUERY_RESULT_QBO_OFFSET: u32 = 5;
+pub const VIRGL_QUERY_RESULT_QBO_INDEX: u32 = 6;
+pub const VIRGL_TRANSFER_TO_HOST: u32 = 1;
+pub const VIRGL_TRANSFER_FROM_HOST: u32 = 2;
+pub const VIRGL_TRANSFER3D_SIZE: u32 = 13;
+pub const VIRGL_TRANSFER3D_DATA_OFFSET: u32 = 12;
+pub const VIRGL_TRANSFER3D_DIRECTION: u32 = 13;
+pub type __uint32_t = ::std::os::raw::c_uint;
+pub type __uint64_t = ::std::os::raw::c_ulong;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct virgl_host_query_state {
+ pub query_state: u32,
+ pub result_size: u32,
+ pub result: u64,
+}
+pub const VIRGL_OBJECT_NULL: virgl_object_type = 0;
+pub const VIRGL_OBJECT_BLEND: virgl_object_type = 1;
+pub const VIRGL_OBJECT_RASTERIZER: virgl_object_type = 2;
+pub const VIRGL_OBJECT_DSA: virgl_object_type = 3;
+pub const VIRGL_OBJECT_SHADER: virgl_object_type = 4;
+pub const VIRGL_OBJECT_VERTEX_ELEMENTS: virgl_object_type = 5;
+pub const VIRGL_OBJECT_SAMPLER_VIEW: virgl_object_type = 6;
+pub const VIRGL_OBJECT_SAMPLER_STATE: virgl_object_type = 7;
+pub const VIRGL_OBJECT_SURFACE: virgl_object_type = 8;
+pub const VIRGL_OBJECT_QUERY: virgl_object_type = 9;
+pub const VIRGL_OBJECT_STREAMOUT_TARGET: virgl_object_type = 10;
+pub const VIRGL_MAX_OBJECTS: virgl_object_type = 11;
+pub type virgl_object_type = u32;
+pub const VIRGL_CCMD_NOP: virgl_context_cmd = 0;
+pub const VIRGL_CCMD_CREATE_OBJECT: virgl_context_cmd = 1;
+pub const VIRGL_CCMD_BIND_OBJECT: virgl_context_cmd = 2;
+pub const VIRGL_CCMD_DESTROY_OBJECT: virgl_context_cmd = 3;
+pub const VIRGL_CCMD_SET_VIEWPORT_STATE: virgl_context_cmd = 4;
+pub const VIRGL_CCMD_SET_FRAMEBUFFER_STATE: virgl_context_cmd = 5;
+pub const VIRGL_CCMD_SET_VERTEX_BUFFERS: virgl_context_cmd = 6;
+pub const VIRGL_CCMD_CLEAR: virgl_context_cmd = 7;
+pub const VIRGL_CCMD_DRAW_VBO: virgl_context_cmd = 8;
+pub const VIRGL_CCMD_RESOURCE_INLINE_WRITE: virgl_context_cmd = 9;
+pub const VIRGL_CCMD_SET_SAMPLER_VIEWS: virgl_context_cmd = 10;
+pub const VIRGL_CCMD_SET_INDEX_BUFFER: virgl_context_cmd = 11;
+pub const VIRGL_CCMD_SET_CONSTANT_BUFFER: virgl_context_cmd = 12;
+pub const VIRGL_CCMD_SET_STENCIL_REF: virgl_context_cmd = 13;
+pub const VIRGL_CCMD_SET_BLEND_COLOR: virgl_context_cmd = 14;
+pub const VIRGL_CCMD_SET_SCISSOR_STATE: virgl_context_cmd = 15;
+pub const VIRGL_CCMD_BLIT: virgl_context_cmd = 16;
+pub const VIRGL_CCMD_RESOURCE_COPY_REGION: virgl_context_cmd = 17;
+pub const VIRGL_CCMD_BIND_SAMPLER_STATES: virgl_context_cmd = 18;
+pub const VIRGL_CCMD_BEGIN_QUERY: virgl_context_cmd = 19;
+pub const VIRGL_CCMD_END_QUERY: virgl_context_cmd = 20;
+pub const VIRGL_CCMD_GET_QUERY_RESULT: virgl_context_cmd = 21;
+pub const VIRGL_CCMD_SET_POLYGON_STIPPLE: virgl_context_cmd = 22;
+pub const VIRGL_CCMD_SET_CLIP_STATE: virgl_context_cmd = 23;
+pub const VIRGL_CCMD_SET_SAMPLE_MASK: virgl_context_cmd = 24;
+pub const VIRGL_CCMD_SET_STREAMOUT_TARGETS: virgl_context_cmd = 25;
+pub const VIRGL_CCMD_SET_RENDER_CONDITION: virgl_context_cmd = 26;
+pub const VIRGL_CCMD_SET_UNIFORM_BUFFER: virgl_context_cmd = 27;
+pub const VIRGL_CCMD_SET_SUB_CTX: virgl_context_cmd = 28;
+pub const VIRGL_CCMD_CREATE_SUB_CTX: virgl_context_cmd = 29;
+pub const VIRGL_CCMD_DESTROY_SUB_CTX: virgl_context_cmd = 30;
+pub const VIRGL_CCMD_BIND_SHADER: virgl_context_cmd = 31;
+pub const VIRGL_CCMD_SET_TESS_STATE: virgl_context_cmd = 32;
+pub const VIRGL_CCMD_SET_MIN_SAMPLES: virgl_context_cmd = 33;
+pub const VIRGL_CCMD_SET_SHADER_BUFFERS: virgl_context_cmd = 34;
+pub const VIRGL_CCMD_SET_SHADER_IMAGES: virgl_context_cmd = 35;
+pub const VIRGL_CCMD_MEMORY_BARRIER: virgl_context_cmd = 36;
+pub const VIRGL_CCMD_LAUNCH_GRID: virgl_context_cmd = 37;
+pub const VIRGL_CCMD_SET_FRAMEBUFFER_STATE_NO_ATTACH: virgl_context_cmd = 38;
+pub const VIRGL_CCMD_TEXTURE_BARRIER: virgl_context_cmd = 39;
+pub const VIRGL_CCMD_SET_ATOMIC_BUFFERS: virgl_context_cmd = 40;
+pub const VIRGL_CCMD_SET_DEBUG_FLAGS: virgl_context_cmd = 41;
+pub const VIRGL_CCMD_GET_QUERY_RESULT_QBO: virgl_context_cmd = 42;
+pub const VIRGL_CCMD_TRANSFER3D: virgl_context_cmd = 43;
+pub const VIRGL_CCMD_END_TRANSFERS: virgl_context_cmd = 44;
+pub const VIRGL_MAX_COMMANDS: virgl_context_cmd = 45;
+pub type virgl_context_cmd = u32;
diff --git a/gpu_renderer/src/generated/virglrenderer.rs b/gpu_renderer/src/generated/virglrenderer.rs
new file mode 100644
index 0000000..eb4d4d6
--- /dev/null
+++ b/gpu_renderer/src/generated/virglrenderer.rs
@@ -0,0 +1,282 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[link(name = "virglrenderer")]
+extern "C" {}
+
+pub const VIRGL_RENDERER_CALLBACKS_VERSION: u32 = 2;
+pub const VIRGL_RENDERER_USE_EGL: u32 = 1;
+pub const VIRGL_RENDERER_THREAD_SYNC: u32 = 2;
+pub const VIRGL_RENDERER_USE_GLX: u32 = 4;
+pub const VIRGL_RENDERER_USE_SURFACELESS: u32 = 8;
+pub const VIRGL_RENDERER_USE_GLES: u32 = 16;
+pub const VIRGL_RES_BIND_DEPTH_STENCIL: u32 = 1;
+pub const VIRGL_RES_BIND_RENDER_TARGET: u32 = 2;
+pub const VIRGL_RES_BIND_SAMPLER_VIEW: u32 = 8;
+pub const VIRGL_RES_BIND_VERTEX_BUFFER: u32 = 16;
+pub const VIRGL_RES_BIND_INDEX_BUFFER: u32 = 32;
+pub const VIRGL_RES_BIND_CONSTANT_BUFFER: u32 = 64;
+pub const VIRGL_RES_BIND_STREAM_OUTPUT: u32 = 2048;
+pub const VIRGL_RES_BIND_CURSOR: u32 = 65536;
+pub const VIRGL_RES_BIND_CUSTOM: u32 = 131072;
+pub const VIRGL_RES_BIND_SCANOUT: u32 = 262144;
+pub type __uint32_t = ::std::os::raw::c_uint;
+pub type __uint64_t = ::std::os::raw::c_ulong;
+pub type va_list = __builtin_va_list;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct virgl_box {
+ _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct iovec {
+ _unused: [u8; 0],
+}
+pub type virgl_renderer_gl_context = *mut ::std::os::raw::c_void;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct virgl_renderer_gl_ctx_param {
+ pub version: ::std::os::raw::c_int,
+ pub shared: bool,
+ pub major_ver: ::std::os::raw::c_int,
+ pub minor_ver: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct virgl_renderer_callbacks {
+ pub version: ::std::os::raw::c_int,
+ pub write_fence: ::std::option::Option<
+ unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void, fence: u32),
+ >,
+ pub create_gl_context: ::std::option::Option<
+ unsafe extern "C" fn(
+ cookie: *mut ::std::os::raw::c_void,
+ scanout_idx: ::std::os::raw::c_int,
+ param: *mut virgl_renderer_gl_ctx_param,
+ ) -> virgl_renderer_gl_context,
+ >,
+ pub destroy_gl_context: ::std::option::Option<
+ unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void, ctx: virgl_renderer_gl_context),
+ >,
+ pub make_current: ::std::option::Option<
+ unsafe extern "C" fn(
+ cookie: *mut ::std::os::raw::c_void,
+ scanout_idx: ::std::os::raw::c_int,
+ ctx: virgl_renderer_gl_context,
+ ) -> ::std::os::raw::c_int,
+ >,
+ pub get_drm_fd: ::std::option::Option<
+ unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int,
+ >,
+}
+extern "C" {
+ pub fn virgl_renderer_init(
+ cookie: *mut ::std::os::raw::c_void,
+ flags: ::std::os::raw::c_int,
+ cb: *mut virgl_renderer_callbacks,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_poll();
+}
+extern "C" {
+ pub fn virgl_renderer_get_cursor_data(
+ resource_id: u32,
+ width: *mut u32,
+ height: *mut u32,
+ ) -> *mut ::std::os::raw::c_void;
+}
+extern "C" {
+ pub fn virgl_renderer_get_rect(
+ resource_id: ::std::os::raw::c_int,
+ iov: *mut iovec,
+ num_iovs: ::std::os::raw::c_uint,
+ offset: u32,
+ x: ::std::os::raw::c_int,
+ y: ::std::os::raw::c_int,
+ width: ::std::os::raw::c_int,
+ height: ::std::os::raw::c_int,
+ );
+}
+extern "C" {
+ pub fn virgl_renderer_get_fd_for_texture(
+ tex_id: u32,
+ fd: *mut ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_get_fd_for_texture2(
+ tex_id: u32,
+ fd: *mut ::std::os::raw::c_int,
+ stride: *mut ::std::os::raw::c_int,
+ offset: *mut ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct virgl_renderer_resource_create_args {
+ pub handle: u32,
+ pub target: u32,
+ pub format: u32,
+ pub bind: u32,
+ pub width: u32,
+ pub height: u32,
+ pub depth: u32,
+ pub array_size: u32,
+ pub last_level: u32,
+ pub nr_samples: u32,
+ pub flags: u32,
+}
+pub type virgl_debug_callback_type = ::std::option::Option<
+ unsafe extern "C" fn(fmt: *const ::std::os::raw::c_char, ap: *mut __va_list_tag),
+>;
+extern "C" {
+ pub fn virgl_renderer_resource_create(
+ args: *mut virgl_renderer_resource_create_args,
+ iov: *mut iovec,
+ num_iovs: u32,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_resource_import_eglimage(
+ args: *mut virgl_renderer_resource_create_args,
+ image: *mut ::std::os::raw::c_void,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_resource_unref(res_handle: u32);
+}
+extern "C" {
+ pub fn virgl_renderer_resource_set_priv(res_handle: u32, priv_: *mut ::std::os::raw::c_void);
+}
+extern "C" {
+ pub fn virgl_renderer_resource_get_priv(res_handle: u32) -> *mut ::std::os::raw::c_void;
+}
+extern "C" {
+ pub fn virgl_renderer_context_create(
+ handle: u32,
+ nlen: u32,
+ name: *const ::std::os::raw::c_char,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_context_destroy(handle: u32);
+}
+extern "C" {
+ pub fn virgl_renderer_submit_cmd(
+ buffer: *mut ::std::os::raw::c_void,
+ ctx_id: ::std::os::raw::c_int,
+ ndw: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_transfer_read_iov(
+ handle: u32,
+ ctx_id: u32,
+ level: u32,
+ stride: u32,
+ layer_stride: u32,
+ box_: *mut virgl_box,
+ offset: u64,
+ iov: *mut iovec,
+ iovec_cnt: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_transfer_write_iov(
+ handle: u32,
+ ctx_id: u32,
+ level: ::std::os::raw::c_int,
+ stride: u32,
+ layer_stride: u32,
+ box_: *mut virgl_box,
+ offset: u64,
+ iovec: *mut iovec,
+ iovec_cnt: ::std::os::raw::c_uint,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32);
+}
+extern "C" {
+ pub fn virgl_renderer_fill_caps(set: u32, version: u32, caps: *mut ::std::os::raw::c_void);
+}
+extern "C" {
+ pub fn virgl_renderer_resource_attach_iov(
+ res_handle: ::std::os::raw::c_int,
+ iov: *mut iovec,
+ num_iovs: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_resource_detach_iov(
+ res_handle: ::std::os::raw::c_int,
+ iov: *mut *mut iovec,
+ num_iovs: *mut ::std::os::raw::c_int,
+ );
+}
+extern "C" {
+ pub fn virgl_renderer_create_fence(
+ client_fence_id: ::std::os::raw::c_int,
+ ctx_id: u32,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_force_ctx_0();
+}
+extern "C" {
+ pub fn virgl_renderer_ctx_attach_resource(
+ ctx_id: ::std::os::raw::c_int,
+ res_handle: ::std::os::raw::c_int,
+ );
+}
+extern "C" {
+ pub fn virgl_renderer_ctx_detach_resource(
+ ctx_id: ::std::os::raw::c_int,
+ res_handle: ::std::os::raw::c_int,
+ );
+}
+extern "C" {
+ pub fn virgl_set_debug_callback(cb: virgl_debug_callback_type) -> virgl_debug_callback_type;
+}
+#[repr(C)]
+#[derive(Default, Debug, Copy, Clone)]
+pub struct virgl_renderer_resource_info {
+ pub handle: u32,
+ pub virgl_format: u32,
+ pub width: u32,
+ pub height: u32,
+ pub depth: u32,
+ pub flags: u32,
+ pub tex_id: u32,
+ pub stride: u32,
+ pub drm_fourcc: ::std::os::raw::c_int,
+}
+extern "C" {
+ pub fn virgl_renderer_resource_get_info(
+ res_handle: ::std::os::raw::c_int,
+ info: *mut virgl_renderer_resource_info,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn virgl_renderer_cleanup(cookie: *mut ::std::os::raw::c_void);
+}
+extern "C" {
+ pub fn virgl_renderer_reset();
+}
+extern "C" {
+ pub fn virgl_renderer_get_poll_fd() -> ::std::os::raw::c_int;
+}
+pub type __builtin_va_list = [__va_list_tag; 1usize];
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __va_list_tag {
+ pub gp_offset: ::std::os::raw::c_uint,
+ pub fp_offset: ::std::os::raw::c_uint,
+ pub overflow_arg_area: *mut ::std::os::raw::c_void,
+ pub reg_save_area: *mut ::std::os::raw::c_void,
+}
diff --git a/gpu_renderer/src/lib.rs b/gpu_renderer/src/lib.rs
new file mode 100644
index 0000000..4b8274d
--- /dev/null
+++ b/gpu_renderer/src/lib.rs
@@ -0,0 +1,1009 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! A crate for using hardware acceleration to render virtio-gpu's virgl command streams.
+
+mod command_buffer;
+mod generated;
+mod pipe_format_fourcc;
+
+use std::cell::RefCell;
+use std::ffi::CStr;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::marker::PhantomData;
+use std::mem::{size_of, transmute};
+use std::ops::Deref;
+use std::os::raw::{c_char, c_int, c_uint, c_void};
+use std::os::unix::io::{FromRawFd, RawFd};
+use std::ptr::{null, null_mut};
+use std::rc::Rc;
+use std::result;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+use data_model::{VolatileMemory, VolatileSlice};
+use sys_util::{GuestAddress, GuestMemory};
+
+use crate::generated::epoxy_egl::{
+ EGLAttrib, EGLBoolean, EGLClientBuffer, EGLConfig, EGLContext, EGLDisplay, EGLImageKHR,
+ EGLNativeDisplayType, EGLSurface, EGLenum, EGLint, EGLuint64KHR, EGLDEBUGPROCKHR,
+ EGL_CONTEXT_CLIENT_VERSION, EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT,
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_GL_TEXTURE_2D_KHR, EGL_HEIGHT, EGL_LINUX_DMA_BUF_EXT,
+ EGL_LINUX_DRM_FOURCC_EXT, EGL_NONE, EGL_OPENGL_ES_API, EGL_SURFACE_TYPE, EGL_WIDTH,
+};
+use crate::generated::p_defines::{PIPE_BIND_SAMPLER_VIEW, PIPE_TEXTURE_1D, PIPE_TEXTURE_2D};
+use crate::generated::p_format::PIPE_FORMAT_B8G8R8X8_UNORM;
+use crate::generated::virglrenderer::*;
+
+pub use crate::command_buffer::CommandBufferBuilder;
+pub use crate::generated::virglrenderer::{
+ virgl_renderer_resource_create_args, virgl_renderer_resource_info, VIRGL_RES_BIND_SCANOUT,
+};
+pub use crate::pipe_format_fourcc::pipe_format_fourcc as format_fourcc;
+
+/// Arguments used in `Renderer::create_resource`..
+pub type ResourceCreateArgs = virgl_renderer_resource_create_args;
+/// Information returned from `Resource::get_info`.
+pub type ResourceInfo = virgl_renderer_resource_info;
+
+/// An error generated while using this crate.
+#[derive(Debug)]
+pub enum Error {
+ /// Inidcates `Renderer` was already initialized, and only one renderer per process is allowed.
+ AlreadyInitialized,
+ /// Indicates libeopoxy was unable to load the EGL function with the given name.
+ MissingEGLFunction(&'static str),
+ /// A call to eglGetDisplay indicated failure.
+ EGLGetDisplay,
+ /// A call to eglInitialize indicated failure.
+ EGLInitialize,
+ /// A call to eglChooseConfig indicated failure.
+ EGLChooseConfig,
+ /// A call to eglBindAPI indicated failure.
+ EGLBindAPI,
+ /// A call to eglCreateContext indicated failure.
+ EGLCreateContext,
+ /// A call to eglMakeCurrent indicated failure.
+ EGLMakeCurrent,
+ /// An internal virglrenderer error was returned.
+ Virglrenderer(i32),
+ /// An EGLIMageKHR could not be created, indicating a EGL driver error.
+ CreateImage,
+ /// The EGL driver failed to export an EGLImageKHR as a dmabuf.
+ ExportedResourceDmabuf,
+ /// The indicated region of guest memory is invalid.
+ InvalidIovec,
+ /// A command size was submitted that was invalid.
+ InvalidCommandSize(usize),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ AlreadyInitialized => write!(f, "global gpu renderer was already initailized"),
+ MissingEGLFunction(name) => write!(f, "egl function `{}` was missing", name),
+ EGLGetDisplay => write!(f, "call to eglGetDisplay failed"),
+ EGLInitialize => write!(f, "call to eglInitialize failed"),
+ EGLChooseConfig => write!(f, "call to eglChooseConfig failed"),
+ EGLBindAPI => write!(f, "call to eglBindAPI failed"),
+ EGLCreateContext => write!(f, "call to eglCreateContext failed"),
+ EGLMakeCurrent => write!(f, "call to eglMakeCurrent failed"),
+ Virglrenderer(ret) => write!(f, "virglrenderer failed with error {}", ret),
+ CreateImage => write!(f, "failed to create EGLImage"),
+ ExportedResourceDmabuf => write!(f, "failed to export dmabuf from EGLImageKHR"),
+ InvalidIovec => write!(f, "an iovec is outside of guest memory's range"),
+ InvalidCommandSize(s) => write!(f, "command buffer submitted with invalid size: {}", s),
+ }
+ }
+}
+
+/// The result of an operation in this crate.
+pub type Result<T> = result::Result<T, Error>;
+
+fn ret_to_res(ret: i32) -> Result<()> {
+ match ret {
+ 0 => Ok(()),
+ _ => Err(Error::Virglrenderer(ret)),
+ }
+}
+
+#[derive(Debug)]
+#[repr(C)]
+struct VirglVec {
+ base: *mut c_void,
+ len: usize,
+}
+
+/// An axis aligned box in 3 dimensional space.
+#[derive(Debug)]
+#[repr(C)]
+pub struct Box3 {
+ pub x: u32,
+ pub y: u32,
+ pub z: u32,
+ pub w: u32,
+ pub h: u32,
+ pub d: u32,
+}
+
+impl Box3 {
+ /// Constructs a 2 dimensional XY box in 3 dimensional space with unit depth and zero
+ /// displacement on the Z axis.
+ pub fn new_2d(x: u32, w: u32, y: u32, h: u32) -> Box3 {
+ Box3 {
+ x,
+ y,
+ z: 0,
+ w,
+ h,
+ d: 1,
+ }
+ }
+}
+
+struct FenceState {
+ latest_fence: u32,
+}
+impl FenceState {
+ pub fn write(&mut self, latest_fence: u32) {
+ if latest_fence > self.latest_fence {
+ self.latest_fence = latest_fence;
+ }
+ }
+}
+
+struct VirglCookie {
+ fence_state: Rc<RefCell<FenceState>>,
+}
+
+extern "C" fn write_fence(cookie: *mut c_void, fence: u32) {
+ assert!(!cookie.is_null());
+ let cookie = unsafe { &*(cookie as *mut VirglCookie) };
+
+ // Track the most recent fence.
+ let mut fence_state = cookie.fence_state.borrow_mut();
+ fence_state.write(fence);
+}
+
+const VIRGL_RENDERER_CALLBACKS: &virgl_renderer_callbacks = &virgl_renderer_callbacks {
+ version: 1,
+ write_fence: Some(write_fence),
+ create_gl_context: None,
+ destroy_gl_context: None,
+ make_current: None,
+ get_drm_fd: None,
+};
+
+unsafe extern "C" fn error_callback(
+ error: c_uint,
+ command: *const c_char,
+ _: c_int,
+ _: *mut c_void,
+ _: *mut c_void,
+ message: *const c_char,
+) {
+ eprint!("EGL ERROR {}: {:?}", error, CStr::from_ptr(command));
+ if !message.is_null() {
+ eprint!(": {:?}", CStr::from_ptr(message));
+ }
+ eprintln!();
+}
+
+#[allow(non_snake_case)]
+struct EGLFunctionsInner {
+ BindAPI: unsafe extern "C" fn(api: EGLenum) -> EGLBoolean,
+ ChooseConfig: unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ attrib_list: *const EGLint,
+ configs: *mut EGLConfig,
+ config_size: EGLint,
+ num_config: *mut EGLint,
+ ) -> EGLBoolean,
+ CreateContext: unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ config: EGLConfig,
+ share_context: EGLContext,
+ attrib_list: *const EGLint,
+ ) -> EGLContext,
+ CreateImageKHR: unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ ctx: EGLContext,
+ target: EGLenum,
+ buffer: EGLClientBuffer,
+ attrib_list: *const EGLint,
+ ) -> EGLImageKHR,
+ DebugMessageControlKHR:
+ unsafe extern "C" fn(callback: EGLDEBUGPROCKHR, attrib_list: *const EGLAttrib) -> EGLint,
+ DestroyImageKHR: unsafe extern "C" fn(dpy: EGLDisplay, image: EGLImageKHR) -> EGLBoolean,
+ ExportDRMImageMESA: unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ image: EGLImageKHR,
+ fds: *mut ::std::os::raw::c_int,
+ strides: *mut EGLint,
+ offsets: *mut EGLint,
+ ) -> EGLBoolean,
+ ExportDMABUFImageQueryMESA: unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ image: EGLImageKHR,
+ fourcc: *mut ::std::os::raw::c_int,
+ num_planes: *mut ::std::os::raw::c_int,
+ modifiers: *mut EGLuint64KHR,
+ ) -> EGLBoolean,
+ GetCurrentContext: unsafe extern "C" fn() -> EGLContext,
+ GetCurrentDisplay: unsafe extern "C" fn() -> EGLDisplay,
+ GetDisplay: unsafe extern "C" fn(display_id: EGLNativeDisplayType) -> EGLDisplay,
+ Initialize:
+ unsafe extern "C" fn(dpy: EGLDisplay, major: *mut EGLint, minor: *mut EGLint) -> EGLBoolean,
+ MakeCurrent: unsafe extern "C" fn(
+ dpy: EGLDisplay,
+ draw: EGLSurface,
+ read: EGLSurface,
+ ctx: EGLContext,
+ ) -> EGLBoolean,
+ no_sync_send: PhantomData<*mut ()>,
+}
+
+#[derive(Clone)]
+struct EGLFunctions(Rc<EGLFunctionsInner>);
+
+impl EGLFunctions {
+ fn new() -> Result<EGLFunctions> {
+ use crate::generated::epoxy_egl::{
+ epoxy_eglBindAPI, epoxy_eglChooseConfig, epoxy_eglCreateContext,
+ epoxy_eglCreateImageKHR, epoxy_eglDebugMessageControlKHR, epoxy_eglDestroyImageKHR,
+ epoxy_eglExportDMABUFImageQueryMESA, epoxy_eglExportDRMImageMESA,
+ epoxy_eglGetCurrentContext, epoxy_eglGetCurrentDisplay, epoxy_eglGetDisplay,
+ epoxy_eglInitialize, epoxy_eglMakeCurrent,
+ };
+ // This is unsafe because it is reading mutable static variables exported by epoxy. These
+ // variables are initialized during the binary's init and never modified again, so it should
+ // be safe to read them now.
+ unsafe {
+ Ok(EGLFunctions(Rc::new(EGLFunctionsInner {
+ BindAPI: epoxy_eglBindAPI.ok_or(Error::MissingEGLFunction("eglBindAPI"))?,
+ ChooseConfig: epoxy_eglChooseConfig
+ .ok_or(Error::MissingEGLFunction("eglChooseConfig"))?,
+ CreateContext: epoxy_eglCreateContext
+ .ok_or(Error::MissingEGLFunction("eglCreateContext"))?,
+ CreateImageKHR: epoxy_eglCreateImageKHR
+ .ok_or(Error::MissingEGLFunction("eglCreateImageKHR"))?,
+ DebugMessageControlKHR: epoxy_eglDebugMessageControlKHR
+ .ok_or(Error::MissingEGLFunction("eglDebugMessageControlKHR"))?,
+ DestroyImageKHR: epoxy_eglDestroyImageKHR
+ .ok_or(Error::MissingEGLFunction("eglDestroyImageKHR"))?,
+ ExportDRMImageMESA: epoxy_eglExportDRMImageMESA
+ .ok_or(Error::MissingEGLFunction("eglExportDRMImageMESA"))?,
+ ExportDMABUFImageQueryMESA: epoxy_eglExportDMABUFImageQueryMESA
+ .ok_or(Error::MissingEGLFunction("eglExportDMABUFImageQueryMESA"))?,
+ GetCurrentContext: epoxy_eglGetCurrentContext
+ .ok_or(Error::MissingEGLFunction("eglGetCurrentContext"))?,
+ GetCurrentDisplay: epoxy_eglGetCurrentDisplay
+ .ok_or(Error::MissingEGLFunction("eglGetCurrentDisplay"))?,
+ GetDisplay: epoxy_eglGetDisplay
+ .ok_or(Error::MissingEGLFunction("eglGetDisplay"))?,
+ Initialize: epoxy_eglInitialize
+ .ok_or(Error::MissingEGLFunction("eglInitialize"))?,
+ MakeCurrent: epoxy_eglMakeCurrent
+ .ok_or(Error::MissingEGLFunction("eglMakeCurrent"))?,
+ no_sync_send: PhantomData,
+ })))
+ }
+ }
+}
+
+impl Deref for EGLFunctions {
+ type Target = EGLFunctionsInner;
+ fn deref(&self) -> &EGLFunctionsInner {
+ self.0.deref()
+ }
+}
+
+/// The global renderer handle used to query capability sets, and create resources and contexts.
+pub struct Renderer {
+ no_sync_send: PhantomData<*mut ()>,
+ egl_funcs: EGLFunctions,
+ display: EGLDisplay,
+ fence_state: Rc<RefCell<FenceState>>,
+}
+
+impl Renderer {
+ /// Initializes the renderer and returns a handle to it.
+ ///
+ /// This may only be called once per process. Calls after the first will return an error.
+ pub fn init() -> Result<Renderer> {
+ // virglrenderer is a global state backed library that uses thread bound OpenGL contexts.
+ // Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied
+ // to whichever thread called this function first.
+ static INIT_ONCE: AtomicBool = AtomicBool::new(false);
+ if INIT_ONCE.compare_and_swap(false, true, Ordering::Acquire) {
+ return Err(Error::AlreadyInitialized);
+ }
+
+ let egl_funcs = EGLFunctions::new()?;
+
+ // Safe because only valid callbacks are given and only one thread can execute this
+ // function.
+ unsafe {
+ (egl_funcs.DebugMessageControlKHR)(Some(error_callback), null());
+ }
+
+ // Trivially safe.
+ let display = unsafe { (egl_funcs.GetDisplay)(null_mut()) };
+ if display.is_null() {
+ return Err(Error::EGLGetDisplay);
+ }
+
+ // Safe because only a valid display is given.
+ let ret = unsafe { (egl_funcs.Initialize)(display, null_mut(), null_mut()) };
+ if ret == 0 {
+ return Err(Error::EGLInitialize);
+ }
+
+ let config_attribs = [EGL_SURFACE_TYPE as i32, -1, EGL_NONE as i32];
+ let mut egl_config: *mut c_void = null_mut();
+ let mut num_configs = 0;
+ // Safe because only a valid, initialized display is used, along with validly sized
+ // pointers to stack variables.
+ let ret = unsafe {
+ (egl_funcs.ChooseConfig)(
+ display,
+ config_attribs.as_ptr(),
+ &mut egl_config,
+ 1,
+ &mut num_configs, /* unused but can't be null */
+ )
+ };
+ if ret == 0 {
+ return Err(Error::EGLChooseConfig);
+ }
+
+ // Cookie is intentionally never freed because virglrenderer never gets uninitialized.
+ // Otherwise, Resource and Context would become invalid because their lifetime is not tied
+ // to the Renderer instance. Doing so greatly simplifies the ownership for users of this
+ // library.
+
+ let fence_state = Rc::new(RefCell::new(FenceState { latest_fence: 0 }));
+
+ let cookie: *mut VirglCookie = Box::into_raw(Box::new(VirglCookie {
+ fence_state: Rc::clone(&fence_state),
+ }));
+
+ // Safe because EGL was properly initialized before here..
+ let ret = unsafe { (egl_funcs.BindAPI)(EGL_OPENGL_ES_API) };
+ if ret == 0 {
+ return Err(Error::EGLBindAPI);
+ }
+
+ let context_attribs = [EGL_CONTEXT_CLIENT_VERSION as i32, 3, EGL_NONE as i32];
+ // Safe because a valid display, config, and config_attribs pointer are given.
+ let ctx = unsafe {
+ (egl_funcs.CreateContext)(display, egl_config, null_mut(), context_attribs.as_ptr())
+ };
+ if ctx.is_null() {
+ return Err(Error::EGLCreateContext);
+ }
+
+ // Safe because a valid display and context is used, and the two null surfaces are not
+ // used.
+ let ret = unsafe { (egl_funcs.MakeCurrent)(display, null_mut(), null_mut(), ctx) };
+ if ret == 0 {
+ return Err(Error::EGLMakeCurrent);
+ }
+
+ // Safe because a valid cookie and set of callbacks is used and the result is checked for
+ // error.
+ let ret = unsafe {
+ virgl_renderer_init(
+ cookie as *mut c_void,
+ (VIRGL_RENDERER_USE_EGL | VIRGL_RENDERER_USE_SURFACELESS | VIRGL_RENDERER_USE_GLES)
+ as i32,
+ transmute(VIRGL_RENDERER_CALLBACKS),
+ )
+ };
+ ret_to_res(ret)?;
+
+ Ok(Renderer {
+ no_sync_send: PhantomData,
+ egl_funcs,
+ display,
+ fence_state,
+ })
+ }
+
+ /// Gets the version and size for the given capability set ID.
+ pub fn get_cap_set_info(&self, id: u32) -> (u32, u32) {
+ let mut version = 0;
+ let mut size = 0;
+ // Safe because virglrenderer is initialized by now and properly size stack variables are
+ // used for the pointers.
+ unsafe {
+ virgl_renderer_get_cap_set(id, &mut version, &mut size);
+ }
+ (version, size)
+ }
+
+ /// Gets the capability set for the given ID and version.
+ pub fn get_cap_set(&self, id: u32, version: u32) -> Vec<u8> {
+ let (_, max_size) = self.get_cap_set_info(id);
+ let mut buf = vec![0u8; max_size as usize];
+ // Safe because virglrenderer is initialized by now and the given buffer is sized properly
+ // for the given cap id/version.
+ unsafe {
+ virgl_renderer_fill_caps(id, version, buf.as_mut_ptr() as *mut c_void);
+ }
+ buf
+ }
+
+ /// Creates a rendering context with the given id.
+ pub fn create_context(&self, id: u32) -> Result<Context> {
+ const CONTEXT_NAME: &[u8] = b"gpu_renderer";
+ // Safe because virglrenderer is initialized by now and the context name is statically
+ // allocated. The return value is checked before returning a new context.
+ let ret = unsafe {
+ virgl_renderer_context_create(
+ id,
+ CONTEXT_NAME.len() as u32,
+ CONTEXT_NAME.as_ptr() as *const c_char,
+ )
+ };
+ ret_to_res(ret)?;
+ Ok(Context {
+ id,
+ no_sync_send: PhantomData,
+ })
+ }
+
+ /// Creates a resource with the given arguments.
+ pub fn create_resource(
+ &self,
+ mut args: virgl_renderer_resource_create_args,
+ ) -> Result<Resource> {
+ // Safe because virglrenderer is initialized by now, and the return value is checked before
+ // returning a new resource. The backing buffers are not supplied with this call.
+ let ret = unsafe { virgl_renderer_resource_create(&mut args, null_mut(), 0) };
+ ret_to_res(ret)?;
+ Ok(Resource {
+ id: args.handle,
+ backing_iovecs: Vec::new(),
+ backing_mem: None,
+ egl_funcs: self.egl_funcs.clone(),
+ no_sync_send: PhantomData,
+ })
+ }
+
+ /// Imports a resource from an EGLImage.
+ pub fn import_resource(
+ &self,
+ mut args: virgl_renderer_resource_create_args,
+ image: &Image,
+ ) -> Result<Resource> {
+ let ret = unsafe { virgl_renderer_resource_import_eglimage(&mut args, image.image) };
+ ret_to_res(ret)?;
+ Ok(Resource {
+ id: args.handle,
+ backing_iovecs: Vec::new(),
+ backing_mem: None,
+ egl_funcs: self.egl_funcs.clone(),
+ no_sync_send: PhantomData,
+ })
+ }
+
+ /// Helper that creates a simple 1 dimensional resource with basic metadata.
+ pub fn create_tex_1d(&self, id: u32, width: u32) -> Result<Resource> {
+ self.create_resource(virgl_renderer_resource_create_args {
+ handle: id,
+ target: PIPE_TEXTURE_1D,
+ format: PIPE_FORMAT_B8G8R8X8_UNORM,
+ width,
+ height: 1,
+ depth: 1,
+ array_size: 1,
+ last_level: 0,
+ nr_samples: 0,
+ bind: PIPE_BIND_SAMPLER_VIEW,
+ flags: 0,
+ })
+ }
+
+ /// Helper that creates a simple 2 dimensional resource with basic metadata.
+ pub fn create_tex_2d(&self, id: u32, width: u32, height: u32) -> Result<Resource> {
+ self.create_resource(virgl_renderer_resource_create_args {
+ handle: id,
+ target: PIPE_TEXTURE_2D,
+ format: PIPE_FORMAT_B8G8R8X8_UNORM,
+ width,
+ height,
+ depth: 1,
+ array_size: 1,
+ last_level: 0,
+ nr_samples: 0,
+ bind: PIPE_BIND_SAMPLER_VIEW,
+ flags: 0,
+ })
+ }
+
+ /// Creates an EGLImage from a DMA buffer.
+ pub fn image_from_dmabuf(
+ &self,
+ fourcc: u32,
+ width: u32,
+ height: u32,
+ fd: RawFd,
+ offset: u32,
+ stride: u32,
+ ) -> Result<Image> {
+ let mut attrs = [
+ EGL_WIDTH as EGLint,
+ width as EGLint,
+ EGL_HEIGHT as EGLint,
+ height as EGLint,
+ EGL_LINUX_DRM_FOURCC_EXT as EGLint,
+ fourcc as EGLint,
+ EGL_DMA_BUF_PLANE0_FD_EXT as EGLint,
+ fd as EGLint,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT as EGLint,
+ offset as EGLint,
+ EGL_DMA_BUF_PLANE0_PITCH_EXT as EGLint,
+ stride as EGLint,
+ EGL_NONE as EGLint,
+ ];
+
+ let image = unsafe {
+ (self.egl_funcs.CreateImageKHR)(
+ self.display,
+ 0 as EGLContext,
+ EGL_LINUX_DMA_BUF_EXT,
+ null_mut() as EGLClientBuffer,
+ attrs.as_mut_ptr(),
+ )
+ };
+
+ if image.is_null() {
+ return Err(Error::CreateImage);
+ }
+
+ Ok(Image {
+ egl_funcs: self.egl_funcs.clone(),
+ egl_dpy: self.display,
+ image,
+ })
+ }
+
+ pub fn poll(&self) -> u32 {
+ unsafe { virgl_renderer_poll() };
+ self.fence_state.borrow().latest_fence
+ }
+
+ pub fn create_fence(&mut self, fence_id: u32, ctx_id: u32) -> Result<()> {
+ let ret = unsafe { virgl_renderer_create_fence(fence_id as i32, ctx_id) };
+ ret_to_res(ret)
+ }
+
+ pub fn force_ctx_0(&self) {
+ unsafe { virgl_renderer_force_ctx_0() };
+ }
+}
+
+/// A context in which resources can be attached/detached and commands can be submitted.
+pub struct Context {
+ id: u32,
+ no_sync_send: PhantomData<*mut ()>,
+}
+
+impl Context {
+ /// Gets the ID assigned to this context when it was created.
+ pub fn id(&self) -> u32 {
+ self.id
+ }
+
+ /// Submits a command stream to this rendering context.
+ pub fn submit<T: AsMut<[u8]>>(&mut self, mut buf: T) -> Result<()> {
+ let buf = buf.as_mut();
+ if buf.len() % size_of::<u32>() != 0 {
+ return Err(Error::InvalidCommandSize(buf.len()));
+ }
+ let dword_count = (buf.len() / size_of::<u32>()) as i32;
+ // Safe because the context and buffer are valid and virglrenderer will have been
+ // initialized if there are Context instances.
+ let ret = unsafe {
+ virgl_renderer_submit_cmd(buf.as_mut_ptr() as *mut c_void, self.id as i32, dword_count)
+ };
+ ret_to_res(ret)
+ }
+
+ /// Attaches the given resource to this rendering context.
+ pub fn attach(&mut self, res: &Resource) {
+ // The context id and resource id must be valid because the respective instances ensure
+ // their lifetime.
+ unsafe {
+ virgl_renderer_ctx_attach_resource(self.id as i32, res.id() as i32);
+ }
+ }
+
+ /// Detaches a previously attached resource from this rendering context.
+ pub fn detach(&mut self, res: &Resource) {
+ // The context id and resource id must be valid because the respective instances ensure
+ // their lifetime.
+ unsafe {
+ virgl_renderer_ctx_detach_resource(self.id as i32, res.id() as i32);
+ }
+ }
+}
+
+impl Drop for Context {
+ fn drop(&mut self) {
+ // The context is safe to destroy because nothing else can be referencing it.
+ unsafe {
+ virgl_renderer_context_destroy(self.id);
+ }
+ }
+}
+
+/// A wrapper of an EGLImage to manage its destruction.
+pub struct Image {
+ egl_funcs: EGLFunctions,
+ egl_dpy: EGLDisplay,
+ image: EGLImageKHR,
+}
+
+impl Drop for Image {
+ fn drop(&mut self) {
+ unsafe {
+ (self.egl_funcs.DestroyImageKHR)(self.egl_dpy, self.image);
+ }
+ }
+}
+
+/// A DMABUF file descriptor handle and metadata returned from `Resource::export`.
+#[derive(Debug)]
+pub struct ExportedResource {
+ /// The file descriptor that represents the DMABUF kernel object.
+ pub dmabuf: File,
+ /// The width in pixels of the exported resource.
+ pub width: u32,
+ /// The height in pixels of the exported resource.
+ pub height: u32,
+ /// The fourcc identifier for the format of the resource.
+ pub fourcc: u32,
+ /// Extra modifiers for the format.
+ pub modifiers: u64,
+ /// The number of bytes between successive rows in the exported resource.
+ pub stride: u32,
+ /// The number of bytes from the start of the exported resource to the first pixel.
+ pub offset: u32,
+}
+
+/// A resource handle used by the renderer.
+pub struct Resource {
+ id: u32,
+ backing_iovecs: Vec<VirglVec>,
+ backing_mem: Option<GuestMemory>,
+ egl_funcs: EGLFunctions,
+ no_sync_send: PhantomData<*mut ()>,
+}
+
+impl Resource {
+ /// Gets the ID assigned to this resource when it was created.
+ pub fn id(&self) -> u32 {
+ self.id
+ }
+
+ /// Retrieves metadata about this resource.
+ pub fn get_info(&self) -> Result<ResourceInfo> {
+ let mut res_info = Default::default();
+ let ret = unsafe { virgl_renderer_resource_get_info(self.id as i32, &mut res_info) };
+ ret_to_res(ret)?;
+ Ok(res_info)
+ }
+
+ /// Performs an export of this resource so that it may be imported by other processes.
+ pub fn export(&self) -> Result<ExportedResource> {
+ let res_info = self.get_info()?;
+ let mut fourcc = 0;
+ let mut modifiers = 0;
+ let mut fd = -1;
+ let mut stride = 0;
+ let mut offset = 0;
+ // Always safe on the same thread with an already initialized virglrenderer.
+ unsafe {
+ virgl_renderer_force_ctx_0();
+ }
+ // These are trivially safe and always return successfully because we bind the context in
+ // the previous line.
+ let egl_dpy: EGLDisplay = unsafe { (self.egl_funcs.GetCurrentDisplay)() };
+ let egl_ctx: EGLContext = unsafe { (self.egl_funcs.GetCurrentContext)() };
+
+ // Safe because a valid display, context, and texture ID are given. The attribute list is
+ // not needed. The result is checked to ensure the returned image is valid.
+ let image = unsafe {
+ (self.egl_funcs.CreateImageKHR)(
+ egl_dpy,
+ egl_ctx,
+ EGL_GL_TEXTURE_2D_KHR,
+ res_info.tex_id as EGLClientBuffer,
+ null(),
+ )
+ };
+
+ if image.is_null() {
+ return Err(Error::CreateImage);
+ }
+
+ // Safe because the display and image are valid and each function call is checked for
+ // success. The returned image parameters are stored in stack variables of the correct type.
+ let export_success = unsafe {
+ (self.egl_funcs.ExportDMABUFImageQueryMESA)(
+ egl_dpy,
+ image,
+ &mut fourcc,
+ null_mut(),
+ &mut modifiers,
+ ) != 0
+ && (self.egl_funcs.ExportDRMImageMESA)(
+ egl_dpy,
+ image,
+ &mut fd,
+ &mut stride,
+ &mut offset,
+ ) != 0
+ };
+
+ // Safe because we checked that the image was valid and nobody else owns it. The image does
+ // not need to be around for the dmabuf to be valid.
+ unsafe {
+ (self.egl_funcs.DestroyImageKHR)(egl_dpy, image);
+ }
+
+ if !export_success || fd < 0 {
+ return Err(Error::ExportedResourceDmabuf);
+ }
+
+ // Safe because the FD was just returned by a successful EGL call so it must be valid and
+ // owned by us.
+ let dmabuf = unsafe { File::from_raw_fd(fd) };
+ Ok(ExportedResource {
+ dmabuf,
+ width: res_info.width,
+ height: res_info.height,
+ fourcc: fourcc as u32,
+ modifiers,
+ stride: stride as u32,
+ offset: offset as u32,
+ })
+ }
+
+ /// Attaches a scatter-gather mapping of guest memory to this resource which used for transfers.
+ pub fn attach_backing(
+ &mut self,
+ iovecs: &[(GuestAddress, usize)],
+ mem: &GuestMemory,
+ ) -> Result<()> {
+ if iovecs
+ .iter()
+ .any(|&(addr, len)| mem.get_slice(addr.offset(), len as u64).is_err())
+ {
+ return Err(Error::InvalidIovec);
+ }
+ self.detach_backing();
+ self.backing_mem = Some(mem.clone());
+ for &(addr, len) in iovecs {
+ // Unwrap will not panic because we already checked the slices.
+ let slice = mem.get_slice(addr.offset(), len as u64).unwrap();
+ self.backing_iovecs.push(VirglVec {
+ base: slice.as_ptr() as *mut c_void,
+ len,
+ });
+ }
+ // Safe because the backing is into guest memory that we store a reference count for.
+ let ret = unsafe {
+ virgl_renderer_resource_attach_iov(
+ self.id as i32,
+ self.backing_iovecs.as_mut_ptr() as *mut iovec,
+ self.backing_iovecs.len() as i32,
+ )
+ };
+ let res = ret_to_res(ret);
+ if res.is_err() {
+ // Not strictly necessary, but it's good to clear out our collection of pointers to
+ // memory we don't own or need.
+ self.backing_iovecs.clear();
+ self.backing_mem = None;
+ }
+ res
+ }
+
+ /// Detaches previously attached scatter-gather memory from this resource.
+ pub fn detach_backing(&mut self) {
+ // Safe as we don't need the old backing iovecs returned and the reference to the guest
+ // memory can be dropped as it will no longer be needed for this resource.
+ unsafe {
+ virgl_renderer_resource_detach_iov(self.id as i32, null_mut(), null_mut());
+ }
+ self.backing_iovecs.clear();
+ self.backing_mem = None;
+ }
+
+ /// Performs a transfer to the given resource from its backing in guest memory.
+ pub fn transfer_write(
+ &self,
+ ctx: Option<&Context>,
+ level: u32,
+ stride: u32,
+ layer_stride: u32,
+ mut transfer_box: Box3,
+ offset: u64,
+ ) -> Result<()> {
+ // Safe because only stack variables of the appropriate type are used.
+ let ret = unsafe {
+ virgl_renderer_transfer_write_iov(
+ self.id,
+ ctx.map(Context::id).unwrap_or(0),
+ level as i32,
+ stride,
+ layer_stride,
+ &mut transfer_box as *mut Box3 as *mut virgl_box,
+ offset,
+ null_mut(),
+ 0,
+ )
+ };
+ ret_to_res(ret)
+ }
+
+ /// Performs a transfer from the given resource to its backing in guest memory.
+ pub fn transfer_read(
+ &self,
+ ctx: Option<&Context>,
+ level: u32,
+ stride: u32,
+ layer_stride: u32,
+ mut transfer_box: Box3,
+ offset: u64,
+ ) -> Result<()> {
+ // Safe because only stack variables of the appropriate type are used.
+ let ret = unsafe {
+ virgl_renderer_transfer_read_iov(
+ self.id,
+ ctx.map(Context::id).unwrap_or(0),
+ level,
+ stride,
+ layer_stride,
+ &mut transfer_box as *mut Box3 as *mut virgl_box,
+ offset,
+ null_mut(),
+ 0,
+ )
+ };
+ ret_to_res(ret)
+ }
+
+ /// Performs a transfer from the given resource to the provided `buf`
+ pub fn transfer_read_buf(
+ &self,
+ ctx: Option<&Context>,
+ level: u32,
+ stride: u32,
+ layer_stride: u32,
+ mut transfer_box: Box3,
+ offset: u64,
+ buf: &mut [u8],
+ ) -> Result<()> {
+ let mut iov = VirglVec {
+ base: buf.as_mut_ptr() as *mut c_void,
+ len: buf.len(),
+ };
+ // Safe because only stack variables of the appropriate type are used, along with a properly
+ // sized buffer.
+ let ret = unsafe {
+ virgl_renderer_transfer_read_iov(
+ self.id,
+ ctx.map(Context::id).unwrap_or(0),
+ level,
+ stride,
+ layer_stride,
+ &mut transfer_box as *mut Box3 as *mut virgl_box,
+ offset,
+ &mut iov as *mut VirglVec as *mut iovec,
+ 1,
+ )
+ };
+ ret_to_res(ret)
+ }
+
+ /// Reads from this resource to a volatile slice of memory.
+ pub fn read_to_volatile(
+ &self,
+ ctx: Option<&Context>,
+ level: u32,
+ stride: u32,
+ layer_stride: u32,
+ mut transfer_box: Box3,
+ offset: u64,
+ buf: VolatileSlice,
+ ) -> Result<()> {
+ let mut iov = VirglVec {
+ base: buf.as_ptr() as *mut c_void,
+ len: buf.size() as usize,
+ };
+ // Safe because only stack variables of the appropriate type are used, along with a properly
+ // sized buffer.
+ let ret = unsafe {
+ virgl_renderer_transfer_read_iov(
+ self.id,
+ ctx.map(Context::id).unwrap_or(0),
+ level,
+ stride,
+ layer_stride,
+ &mut transfer_box as *mut Box3 as *mut virgl_box,
+ offset,
+ &mut iov as *mut VirglVec as *mut iovec,
+ 1,
+ )
+ };
+ ret_to_res(ret)
+ }
+}
+
+impl Drop for Resource {
+ fn drop(&mut self) {
+ // The resource is safe to unreference destroy because no user of these bindings can still
+ // be holding a reference.
+ unsafe {
+ virgl_renderer_resource_unref(self.id);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::generated::p_defines::PIPE_CLEAR_COLOR0;
+
+ #[test]
+ #[ignore]
+ // Make sure a simple buffer clear works by using a command stream.
+ fn simple_clear() {
+ let render = Renderer::init().expect("failed to initialize virglrenderer");
+ let mut ctx = render.create_context(1).expect("failed to create context");
+
+ // Create a 50x50 texture with id=2.
+ let resource = render
+ .create_tex_2d(2, 50, 50)
+ .expect("failed to create texture");
+ ctx.attach(&resource);
+
+ // Create a command buffer that uses the resource as a render target and clears it.
+ const CLEAR_COLOR: [f32; 4] = [0.5, 0.4, 0.3, 0.2];
+ let mut cbuf = CommandBufferBuilder::new();
+ cbuf.e_create_surface(1, &resource, PIPE_FORMAT_B8G8R8X8_UNORM, 0, 0, 0);
+ cbuf.e_set_fb_state(&[1], None);
+ cbuf.e_clear(PIPE_CLEAR_COLOR0, CLEAR_COLOR, 0.0, 0);
+ ctx.submit(&mut cbuf)
+ .expect("failed to submit command buffer to context");
+
+ // Read the result of the rendering into a buffer.
+ let mut pix_buf = [0; 50 * 50 * 4];
+ resource
+ .transfer_read_buf(
+ Some(&ctx),
+ 0,
+ 50,
+ 0,
+ Box3::new_2d(0, 5, 0, 1),
+ 0,
+ &mut pix_buf[..],
+ )
+ .expect("failed to read back resource data");
+
+ // Check that the pixels are the color we cleared to. The red and blue channels are switched
+ // because the surface was created with the BGR format, but the colors are RGB order in the
+ // command stream.
+ assert_eq!(pix_buf[0], (256.0 * CLEAR_COLOR[2]) as u8);
+ assert_eq!(pix_buf[1], (256.0 * CLEAR_COLOR[1]) as u8);
+ assert_eq!(pix_buf[2], (256.0 * CLEAR_COLOR[0]) as u8);
+ assert_eq!(pix_buf[3], (256.0 * CLEAR_COLOR[3]) as u8);
+ }
+}
diff --git a/gpu_renderer/src/pipe_format_fourcc.rs b/gpu_renderer/src/pipe_format_fourcc.rs
new file mode 100644
index 0000000..5a93167
--- /dev/null
+++ b/gpu_renderer/src/pipe_format_fourcc.rs
@@ -0,0 +1,26 @@
+// Copyright 2018 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::generated::p_format;
+
+macro_rules! fourcc {
+ ($a:expr, $b:expr, $c:expr, $d:expr) => {
+ Some($a as u32 | ($b as u32) << 8 | ($c as u32) << 16 | ($d as u32) << 24)
+ };
+}
+
+/// Gets the fourcc that corresponds to the given pipe format, or `None` if the format is
+/// unrecognized.
+pub fn pipe_format_fourcc(f: p_format::pipe_format) -> Option<u32> {
+ match f {
+ p_format::PIPE_FORMAT_B8G8R8A8_UNORM => fourcc!('A', 'R', '2', '4'),
+ p_format::PIPE_FORMAT_B8G8R8X8_UNORM => fourcc!('X', 'R', '2', '4'),
+ p_format::PIPE_FORMAT_R8G8B8A8_UNORM => fourcc!('A', 'B', '2', '4'),
+ p_format::PIPE_FORMAT_R8G8B8X8_UNORM => fourcc!('X', 'B', '2', '4'),
+ p_format::PIPE_FORMAT_B5G6R5_UNORM => fourcc!('R', 'G', '1', '6'),
+ // p_format::PIPE_FORMAT_R8_UNORM => fourcc!('R', '8', ' ', ' '),
+ // p_format::PIPE_FORMAT_G8R8_UNORM => fourcc!('R', 'G', '8', '8'),
+ _ => None,
+ }
+}
diff --git a/io_jail/Cargo.toml b/io_jail/Cargo.toml
new file mode 100644
index 0000000..5e7ca5d
--- /dev/null
+++ b/io_jail/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "io_jail"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+libc = "*"
diff --git a/io_jail/src/lib.rs b/io_jail/src/lib.rs
new file mode 100644
index 0000000..43698aa
--- /dev/null
+++ b/io_jail/src/lib.rs
@@ -0,0 +1,719 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[allow(dead_code)]
+#[allow(non_camel_case_types)]
+#[allow(non_snake_case)]
+#[allow(non_upper_case_globals)]
+mod libminijail;
+
+use libc::pid_t;
+use std::ffi::CString;
+use std::fmt::{self, Display};
+use std::fs;
+use std::io;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::{Path, PathBuf};
+use std::ptr::{null, null_mut};
+
+#[derive(Debug)]
+pub enum Error {
+ // minijail failed to accept bind mount.
+ BindMount {
+ errno: i32,
+ src: PathBuf,
+ dst: PathBuf,
+ },
+ // minijail failed to accept mount.
+ Mount {
+ errno: i32,
+ src: PathBuf,
+ dest: PathBuf,
+ fstype: String,
+ flags: usize,
+ data: String,
+ },
+ /// Failure to count the number of threads in /proc/self/tasks.
+ CheckingMultiThreaded(io::Error),
+ /// minjail_new failed, this is an allocation failure.
+ CreatingMinijail,
+ /// minijail_fork failed with the given error code.
+ ForkingMinijail(i32),
+ /// Attempt to `fork` while already multithreaded.
+ ForkingWhileMultiThreaded,
+ /// The seccomp policy path doesn't exist.
+ SeccompPath(PathBuf),
+ /// The string passed in didn't parse to a valid CString.
+ StrToCString(String),
+ /// The path passed in didn't parse to a valid CString.
+ PathToCString(PathBuf),
+ /// Failed to call dup2 to set stdin, stdout, or stderr to /dev/null.
+ DupDevNull(i32),
+ /// Failed to set up /dev/null for FDs 0, 1, or 2.
+ OpenDevNull(io::Error),
+ /// Setting the specified alt-syscall table failed with errno. Is the table in the kernel?
+ SetAltSyscallTable { errno: i32, name: String },
+ /// chroot failed with the provided errno.
+ SettingChrootDirectory(i32, PathBuf),
+ /// pivot_root failed with the provided errno.
+ SettingPivotRootDirectory(i32, PathBuf),
+ /// There is an entry in /proc/self/fd that isn't a valid PID.
+ ReadFdDirEntry(io::Error),
+ /// /proc/self/fd failed to open.
+ ReadFdDir(io::Error),
+ /// An entry in /proc/self/fd is not an integer
+ ProcFd(String),
+ /// Minijail refused to preserve an FD in the inherit list of `fork()`.
+ PreservingFd(i32),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ BindMount { src, dst, errno } => write!(
+ f,
+ "failed to accept bind mount {} -> {}: {}",
+ src.display(),
+ dst.display(),
+ io::Error::from_raw_os_error(*errno),
+ ),
+ Mount {
+ errno,
+ src,
+ dest,
+ fstype,
+ flags,
+ data,
+ } => write!(
+ f,
+ "failed to accept mount {} -> {} of type {:?} with flags 0x{:x} \
+ and data {:?}: {}",
+ src.display(),
+ dest.display(),
+ fstype,
+ flags,
+ data,
+ io::Error::from_raw_os_error(*errno),
+ ),
+ CheckingMultiThreaded(e) => write!(
+ f,
+ "Failed to count the number of threads from /proc/self/tasks {}",
+ e
+ ),
+ CreatingMinijail => write!(f, "minjail_new failed due to an allocation failure"),
+ ForkingMinijail(e) => write!(f, "minijail_fork failed with error {}", e),
+ ForkingWhileMultiThreaded => write!(f, "Attempt to call fork() while multithreaded"),
+ SeccompPath(p) => write!(f, "missing seccomp policy path: {}", p.display()),
+ StrToCString(s) => write!(f, "failed to convert string into CString: {}", s),
+ PathToCString(s) => write!(f, "failed to convert path into CString: {}", s.display()),
+ DupDevNull(errno) => write!(
+ f,
+ "failed to call dup2 to set stdin, stdout, or stderr to /dev/null: {}",
+ io::Error::from_raw_os_error(*errno),
+ ),
+ OpenDevNull(e) => write!(
+ f,
+ "fail to open /dev/null for setting FDs 0, 1, or 2: {}",
+ e,
+ ),
+ SetAltSyscallTable { name, errno } => write!(
+ f,
+ "failed to set alt-syscall table {}: {}",
+ name,
+ io::Error::from_raw_os_error(*errno),
+ ),
+ SettingChrootDirectory(errno, p) => write!(
+ f,
+ "failed to set chroot {}: {}",
+ p.display(),
+ io::Error::from_raw_os_error(*errno),
+ ),
+ SettingPivotRootDirectory(errno, p) => write!(
+ f,
+ "failed to set pivot root {}: {}",
+ p.display(),
+ io::Error::from_raw_os_error(*errno),
+ ),
+ ReadFdDirEntry(e) => write!(f, "failed to read an entry in /proc/self/fd: {}", e),
+ ReadFdDir(e) => write!(f, "failed to open /proc/self/fd: {}", e),
+ ProcFd(s) => write!(f, "an entry in /proc/self/fd is not an integer: {}", s),
+ PreservingFd(e) => write!(f, "fork failed in minijail_preserve_fd with error {}", e),
+ }
+ }
+}
+
+impl std::error::Error for Error {}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Configuration to jail a process based on wrapping libminijail.
+///
+/// Intentionally leave out everything related to `minijail_run`. Forking is
+/// hard to reason about w.r.t. memory and resource safety. It is better to avoid
+/// forking from rust code. Leave forking to the library user, who can make
+/// an informed decision about when to fork to minimize risk.
+/// # Examples
+/// * Load seccomp policy - like "minijail0 -n -S myfilter.policy"
+///
+/// ```
+/// # use std::path::Path;
+/// # use io_jail::Minijail;
+/// # fn seccomp_filter_test() -> Result<(), ()> {
+/// let mut j = Minijail::new().map_err(|_| ())?;
+/// j.no_new_privs();
+/// j.parse_seccomp_filters(Path::new("my_filter.policy")).map_err(|_| ())?;
+/// j.use_seccomp_filter();
+/// unsafe { // `fork` will close all the programs FDs.
+/// j.fork(None).map_err(|_| ())?;
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// * Keep stdin, stdout, and stderr open after jailing.
+///
+/// ```
+/// # use io_jail::Minijail;
+/// # use std::os::unix::io::RawFd;
+/// # fn seccomp_filter_test() -> Result<(), ()> {
+/// let j = Minijail::new().map_err(|_| ())?;
+/// let preserve_fds: Vec<RawFd> = vec![0, 1, 2];
+/// unsafe { // `fork` will close all the programs FDs.
+/// j.fork(Some(&preserve_fds)).map_err(|_| ())?;
+/// }
+/// # Ok(())
+/// # }
+/// ```
+/// # Errors
+/// The `fork` function might not return an error if it fails after forking. A
+/// partial jail is not recoverable and will instead result in killing the
+/// process.
+pub struct Minijail {
+ jail: *mut libminijail::minijail,
+}
+
+impl Minijail {
+ /// Creates a new jail configuration.
+ pub fn new() -> Result<Minijail> {
+ let j = unsafe {
+ // libminijail actually owns the minijail structure. It will live until we call
+ // minijail_destroy.
+ libminijail::minijail_new()
+ };
+ if j.is_null() {
+ return Err(Error::CreatingMinijail);
+ }
+ Ok(Minijail { jail: j })
+ }
+
+ // The following functions are safe because they only set values in the
+ // struct already owned by minijail. The struct's lifetime is tied to
+ // `struct Minijail` so it is guaranteed to be valid
+
+ pub fn change_uid(&mut self, uid: libc::uid_t) {
+ unsafe {
+ libminijail::minijail_change_uid(self.jail, uid);
+ }
+ }
+ pub fn change_gid(&mut self, gid: libc::gid_t) {
+ unsafe {
+ libminijail::minijail_change_gid(self.jail, gid);
+ }
+ }
+ pub fn set_supplementary_gids(&mut self, ids: &[libc::gid_t]) {
+ unsafe {
+ libminijail::minijail_set_supplementary_gids(self.jail, ids.len(), ids.as_ptr());
+ }
+ }
+ pub fn keep_supplementary_gids(&mut self) {
+ unsafe {
+ libminijail::minijail_keep_supplementary_gids(self.jail);
+ }
+ }
+ pub fn use_seccomp(&mut self) {
+ unsafe {
+ libminijail::minijail_use_seccomp(self.jail);
+ }
+ }
+ pub fn no_new_privs(&mut self) {
+ unsafe {
+ libminijail::minijail_no_new_privs(self.jail);
+ }
+ }
+ pub fn use_seccomp_filter(&mut self) {
+ unsafe {
+ libminijail::minijail_use_seccomp_filter(self.jail);
+ }
+ }
+ pub fn set_seccomp_filter_tsync(&mut self) {
+ unsafe {
+ libminijail::minijail_set_seccomp_filter_tsync(self.jail);
+ }
+ }
+ pub fn parse_seccomp_filters(&mut self, path: &Path) -> Result<()> {
+ if !path.is_file() {
+ return Err(Error::SeccompPath(path.to_owned()));
+ }
+
+ let pathstring = path
+ .as_os_str()
+ .to_str()
+ .ok_or(Error::PathToCString(path.to_owned()))?;
+ let filename =
+ CString::new(pathstring).map_err(|_| Error::PathToCString(path.to_owned()))?;
+ unsafe {
+ libminijail::minijail_parse_seccomp_filters(self.jail, filename.as_ptr());
+ }
+ Ok(())
+ }
+ pub fn log_seccomp_filter_failures(&mut self) {
+ unsafe {
+ libminijail::minijail_log_seccomp_filter_failures(self.jail);
+ }
+ }
+ pub fn use_caps(&mut self, capmask: u64) {
+ unsafe {
+ libminijail::minijail_use_caps(self.jail, capmask);
+ }
+ }
+ pub fn capbset_drop(&mut self, capmask: u64) {
+ unsafe {
+ libminijail::minijail_capbset_drop(self.jail, capmask);
+ }
+ }
+ pub fn set_ambient_caps(&mut self) {
+ unsafe {
+ libminijail::minijail_set_ambient_caps(self.jail);
+ }
+ }
+ pub fn reset_signal_mask(&mut self) {
+ unsafe {
+ libminijail::minijail_reset_signal_mask(self.jail);
+ }
+ }
+ pub fn run_as_init(&mut self) {
+ unsafe {
+ libminijail::minijail_run_as_init(self.jail);
+ }
+ }
+ pub fn namespace_pids(&mut self) {
+ unsafe {
+ libminijail::minijail_namespace_pids(self.jail);
+ }
+ }
+ pub fn namespace_user(&mut self) {
+ unsafe {
+ libminijail::minijail_namespace_user(self.jail);
+ }
+ }
+ pub fn namespace_user_disable_setgroups(&mut self) {
+ unsafe {
+ libminijail::minijail_namespace_user_disable_setgroups(self.jail);
+ }
+ }
+ pub fn namespace_vfs(&mut self) {
+ unsafe {
+ libminijail::minijail_namespace_vfs(self.jail);
+ }
+ }
+ pub fn new_session_keyring(&mut self) {
+ unsafe {
+ libminijail::minijail_new_session_keyring(self.jail);
+ }
+ }
+ pub fn skip_remount_private(&mut self) {
+ unsafe {
+ libminijail::minijail_skip_remount_private(self.jail);
+ }
+ }
+ pub fn namespace_ipc(&mut self) {
+ unsafe {
+ libminijail::minijail_namespace_ipc(self.jail);
+ }
+ }
+ pub fn namespace_net(&mut self) {
+ unsafe {
+ libminijail::minijail_namespace_net(self.jail);
+ }
+ }
+ pub fn namespace_cgroups(&mut self) {
+ unsafe {
+ libminijail::minijail_namespace_cgroups(self.jail);
+ }
+ }
+ pub fn remount_proc_readonly(&mut self) {
+ unsafe {
+ libminijail::minijail_remount_proc_readonly(self.jail);
+ }
+ }
+ pub fn uidmap(&mut self, uid_map: &str) -> Result<()> {
+ let map_cstring =
+ CString::new(uid_map).map_err(|_| Error::StrToCString(uid_map.to_owned()))?;
+ unsafe {
+ libminijail::minijail_uidmap(self.jail, map_cstring.as_ptr());
+ }
+ Ok(())
+ }
+ pub fn gidmap(&mut self, gid_map: &str) -> Result<()> {
+ let map_cstring =
+ CString::new(gid_map).map_err(|_| Error::StrToCString(gid_map.to_owned()))?;
+ unsafe {
+ libminijail::minijail_gidmap(self.jail, map_cstring.as_ptr());
+ }
+ Ok(())
+ }
+ pub fn inherit_usergroups(&mut self) {
+ unsafe {
+ libminijail::minijail_inherit_usergroups(self.jail);
+ }
+ }
+ pub fn use_alt_syscall(&mut self, table_name: &str) -> Result<()> {
+ let table_name_string =
+ CString::new(table_name).map_err(|_| Error::StrToCString(table_name.to_owned()))?;
+ let ret =
+ unsafe { libminijail::minijail_use_alt_syscall(self.jail, table_name_string.as_ptr()) };
+ if ret < 0 {
+ return Err(Error::SetAltSyscallTable {
+ errno: ret,
+ name: table_name.to_owned(),
+ });
+ }
+ Ok(())
+ }
+ pub fn enter_chroot(&mut self, dir: &Path) -> Result<()> {
+ let pathstring = dir
+ .as_os_str()
+ .to_str()
+ .ok_or(Error::PathToCString(dir.to_owned()))?;
+ let dirname = CString::new(pathstring).map_err(|_| Error::PathToCString(dir.to_owned()))?;
+ let ret = unsafe { libminijail::minijail_enter_chroot(self.jail, dirname.as_ptr()) };
+ if ret < 0 {
+ return Err(Error::SettingChrootDirectory(ret, dir.to_owned()));
+ }
+ Ok(())
+ }
+ pub fn enter_pivot_root(&mut self, dir: &Path) -> Result<()> {
+ let pathstring = dir
+ .as_os_str()
+ .to_str()
+ .ok_or(Error::PathToCString(dir.to_owned()))?;
+ let dirname = CString::new(pathstring).map_err(|_| Error::PathToCString(dir.to_owned()))?;
+ let ret = unsafe { libminijail::minijail_enter_pivot_root(self.jail, dirname.as_ptr()) };
+ if ret < 0 {
+ return Err(Error::SettingPivotRootDirectory(ret, dir.to_owned()));
+ }
+ Ok(())
+ }
+ pub fn mount(&mut self, src: &Path, dest: &Path, fstype: &str, flags: usize) -> Result<()> {
+ self.mount_with_data(src, dest, fstype, flags, "")
+ }
+ pub fn mount_with_data(
+ &mut self,
+ src: &Path,
+ dest: &Path,
+ fstype: &str,
+ flags: usize,
+ data: &str,
+ ) -> Result<()> {
+ let src_os = src
+ .as_os_str()
+ .to_str()
+ .ok_or(Error::PathToCString(src.to_owned()))?;
+ let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?;
+ let dest_os = dest
+ .as_os_str()
+ .to_str()
+ .ok_or(Error::PathToCString(dest.to_owned()))?;
+ let dest_path =
+ CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?;
+ let fstype_string =
+ CString::new(fstype).map_err(|_| Error::StrToCString(fstype.to_owned()))?;
+ let data_string = CString::new(data).map_err(|_| Error::StrToCString(data.to_owned()))?;
+ let ret = unsafe {
+ libminijail::minijail_mount_with_data(
+ self.jail,
+ src_path.as_ptr(),
+ dest_path.as_ptr(),
+ fstype_string.as_ptr(),
+ flags as _,
+ data_string.as_ptr(),
+ )
+ };
+ if ret < 0 {
+ return Err(Error::Mount {
+ errno: ret,
+ src: src.to_owned(),
+ dest: dest.to_owned(),
+ fstype: fstype.to_owned(),
+ flags,
+ data: data.to_owned(),
+ });
+ }
+ Ok(())
+ }
+ pub fn mount_dev(&mut self) {
+ unsafe {
+ libminijail::minijail_mount_dev(self.jail);
+ }
+ }
+ pub fn mount_tmp(&mut self) {
+ unsafe {
+ libminijail::minijail_mount_tmp(self.jail);
+ }
+ }
+ pub fn mount_tmp_size(&mut self, size: usize) {
+ unsafe {
+ libminijail::minijail_mount_tmp_size(self.jail, size);
+ }
+ }
+ pub fn mount_bind(&mut self, src: &Path, dest: &Path, writable: bool) -> Result<()> {
+ let src_os = src
+ .as_os_str()
+ .to_str()
+ .ok_or(Error::PathToCString(src.to_owned()))?;
+ let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?;
+ let dest_os = dest
+ .as_os_str()
+ .to_str()
+ .ok_or(Error::PathToCString(dest.to_owned()))?;
+ let dest_path =
+ CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?;
+ let ret = unsafe {
+ libminijail::minijail_bind(
+ self.jail,
+ src_path.as_ptr(),
+ dest_path.as_ptr(),
+ writable as _,
+ )
+ };
+ if ret < 0 {
+ return Err(Error::BindMount {
+ errno: ret,
+ src: src.to_owned(),
+ dst: dest.to_owned(),
+ });
+ }
+ Ok(())
+ }
+
+ /// Forks and execs a child and puts it in the previously configured minijail.
+ /// FDs 0, 1, and 2 are overwritten with /dev/null FDs unless they are included in the
+ /// inheritable_fds list. This function may abort in the child on error because a partially
+ /// entered jail isn't recoverable.
+ pub fn run(&self, cmd: &Path, inheritable_fds: &[RawFd], args: &[&str]) -> Result<pid_t> {
+ let cmd_os = cmd.to_str().ok_or(Error::PathToCString(cmd.to_owned()))?;
+ let cmd_cstr = CString::new(cmd_os).map_err(|_| Error::StrToCString(cmd_os.to_owned()))?;
+
+ // Converts each incoming `args` string to a `CString`, and then puts each `CString` pointer
+ // into a null terminated array, suitable for use as an argv parameter to `execve`.
+ let mut args_cstr = Vec::with_capacity(args.len());
+ let mut args_array = Vec::with_capacity(args.len());
+ for &arg in args {
+ let arg_cstr = CString::new(arg).map_err(|_| Error::StrToCString(arg.to_owned()))?;
+ args_array.push(arg_cstr.as_ptr());
+ args_cstr.push(arg_cstr);
+ }
+ args_array.push(null());
+
+ for fd in inheritable_fds {
+ let ret = unsafe { libminijail::minijail_preserve_fd(self.jail, *fd, *fd) };
+ if ret < 0 {
+ return Err(Error::PreservingFd(ret));
+ }
+ }
+
+ let dev_null = fs::OpenOptions::new()
+ .read(true)
+ .write(true)
+ .open("/dev/null")
+ .map_err(Error::OpenDevNull)?;
+ // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
+ // These will only be closed when this process exits.
+ for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
+ if !inheritable_fds.contains(io_fd) {
+ let ret = unsafe {
+ libminijail::minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd)
+ };
+ if ret < 0 {
+ return Err(Error::PreservingFd(ret));
+ }
+ }
+ }
+
+ unsafe {
+ libminijail::minijail_close_open_fds(self.jail);
+ }
+
+ let mut pid = 0;
+ let ret = unsafe {
+ libminijail::minijail_run_pid_pipes(
+ self.jail,
+ cmd_cstr.as_ptr(),
+ args_array.as_ptr(),
+ &mut pid,
+ null_mut(),
+ null_mut(),
+ null_mut(),
+ )
+ };
+ if ret < 0 {
+ return Err(Error::ForkingMinijail(ret));
+ }
+ Ok(pid)
+ }
+
+ /// Forks a child and puts it in the previously configured minijail.
+ /// `fork` is unsafe because it closes all open FD for this process. That
+ /// could cause a lot of trouble if not handled carefully. FDs 0, 1, and 2
+ /// are overwritten with /dev/null FDs unless they are included in the
+ /// inheritable_fds list.
+ /// This Function may abort in the child on error because a partially
+ /// entered jail isn't recoverable.
+ pub unsafe fn fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t> {
+ if !is_single_threaded().map_err(Error::CheckingMultiThreaded)? {
+ // This test will fail during `cargo test` because the test harness always spawns a test
+ // thread. We will make an exception for that case because the tests for this module
+ // should always be run in a serial fashion using `--test-threads=1`.
+ #[cfg(not(test))]
+ return Err(Error::ForkingWhileMultiThreaded);
+ }
+
+ if let Some(keep_fds) = inheritable_fds {
+ for fd in keep_fds {
+ let ret = libminijail::minijail_preserve_fd(self.jail, *fd, *fd);
+ if ret < 0 {
+ return Err(Error::PreservingFd(ret));
+ }
+ }
+ }
+
+ let dev_null = fs::OpenOptions::new()
+ .read(true)
+ .write(true)
+ .open("/dev/null")
+ .map_err(Error::OpenDevNull)?;
+ // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
+ // These will only be closed when this process exits.
+ for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
+ if inheritable_fds.is_none() || !inheritable_fds.unwrap().contains(io_fd) {
+ let ret =
+ libminijail::minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd);
+ if ret < 0 {
+ return Err(Error::PreservingFd(ret));
+ }
+ }
+ }
+
+ libminijail::minijail_close_open_fds(self.jail);
+
+ let ret = libminijail::minijail_fork(self.jail);
+ if ret < 0 {
+ return Err(Error::ForkingMinijail(ret));
+ }
+ Ok(ret as pid_t)
+ }
+}
+
+impl Drop for Minijail {
+ /// Frees the Minijail created in Minijail::new.
+ fn drop(&mut self) {
+ unsafe {
+ // Destroys the minijail's memory. It is safe to do here because all references to
+ // this object have been dropped.
+ libminijail::minijail_destroy(self.jail);
+ }
+ }
+}
+
+// Count the number of files in the directory specified by `path`.
+fn count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize> {
+ Ok(fs::read_dir(path)?.count())
+}
+
+// Return true if the current thread is the only thread in the process.
+fn is_single_threaded() -> io::Result<bool> {
+ match count_dir_entries("/proc/self/task") {
+ Ok(1) => Ok(true),
+ Ok(_) => Ok(false),
+ Err(e) => Err(e),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn create_and_free() {
+ unsafe {
+ let j = libminijail::minijail_new();
+ assert_ne!(std::ptr::null_mut(), j);
+ libminijail::minijail_destroy(j);
+ }
+
+ let j = Minijail::new().unwrap();
+ drop(j);
+ }
+
+ #[test]
+ // Test that setting a seccomp filter with no-new-privs works as non-root.
+ // This is equivalent to minijail0 -n -S <seccomp_policy>
+ fn seccomp_no_new_privs() {
+ let mut j = Minijail::new().unwrap();
+ j.no_new_privs();
+ j.parse_seccomp_filters(Path::new("src/test_filter.policy"))
+ .unwrap();
+ j.use_seccomp_filter();
+ unsafe {
+ j.fork(None).unwrap();
+ }
+ }
+
+ #[test]
+ // Test that open FDs get closed and that FDs in the inherit list are left open.
+ fn close_fds() {
+ unsafe {
+ // Using libc to open/close FDs for testing.
+ const FILE_PATH: &[u8] = b"/dev/null\0";
+ let j = Minijail::new().unwrap();
+ let first = libc::open(FILE_PATH.as_ptr() as *const i8, libc::O_RDONLY);
+ assert!(first >= 0);
+ let second = libc::open(FILE_PATH.as_ptr() as *const i8, libc::O_RDONLY);
+ assert!(second >= 0);
+ let fds: Vec<RawFd> = vec![0, 1, 2, first];
+ if j.fork(Some(&fds)).unwrap() == 0 {
+ assert!(libc::close(second) < 0); // Should fail as second should be closed already.
+ assert_eq!(libc::close(first), 0); // Should succeed as first should be untouched.
+ }
+ }
+ }
+
+ #[test]
+ #[ignore] // privileged operation.
+ fn chroot() {
+ let mut j = Minijail::new().unwrap();
+ j.enter_chroot(Path::new(".")).unwrap();
+ unsafe {
+ j.fork(None).unwrap();
+ }
+ }
+
+ #[test]
+ #[ignore] // privileged operation.
+ fn namespace_vfs() {
+ let mut j = Minijail::new().unwrap();
+ j.namespace_vfs();
+ unsafe {
+ j.fork(None).unwrap();
+ }
+ }
+
+ #[test]
+ fn run() {
+ let j = Minijail::new().unwrap();
+ j.run(Path::new("/bin/true"), &[], &[]).unwrap();
+ }
+}
diff --git a/io_jail/src/libminijail.rs b/io_jail/src/libminijail.rs
new file mode 100644
index 0000000..f8c3654
--- /dev/null
+++ b/io_jail/src/libminijail.rs
@@ -0,0 +1,126 @@
+// Copyright 2017 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 libc::{gid_t, pid_t, uid_t};
+use std::os::raw::{c_char, c_int, c_ulong};
+
+/// Struct minijail is an opaque type inside libminijail.
+/// See the minijail man page for a description of functions.
+#[derive(Debug, Copy, Clone)]
+pub enum minijail {}
+
+#[link(name = "minijail")]
+extern "C" {
+ pub fn minijail_new() -> *mut minijail;
+ pub fn minijail_change_uid(j: *mut minijail, uid: uid_t);
+ pub fn minijail_change_gid(j: *mut minijail, gid: gid_t);
+ pub fn minijail_set_supplementary_gids(j: *mut minijail, size: usize, list: *const gid_t);
+ pub fn minijail_keep_supplementary_gids(j: *mut minijail);
+ pub fn minijail_change_user(j: *mut minijail, user: *const c_char) -> c_int;
+ pub fn minijail_change_group(j: *mut minijail, group: *const c_char) -> c_int;
+ pub fn minijail_use_seccomp(j: *mut minijail);
+ pub fn minijail_no_new_privs(j: *mut minijail);
+ pub fn minijail_use_seccomp_filter(j: *mut minijail);
+ pub fn minijail_set_seccomp_filter_tsync(j: *mut minijail);
+ pub fn minijail_parse_seccomp_filters(j: *mut minijail, path: *const c_char);
+ pub fn minijail_parse_seccomp_filters_from_fd(j: *mut minijail, fd: c_int);
+ pub fn minijail_log_seccomp_filter_failures(j: *mut minijail);
+ pub fn minijail_use_caps(j: *mut minijail, capmask: u64);
+ pub fn minijail_capbset_drop(j: *mut minijail, capmask: u64);
+ pub fn minijail_set_ambient_caps(j: *mut minijail);
+ pub fn minijail_reset_signal_mask(j: *mut minijail);
+ pub fn minijail_namespace_vfs(j: *mut minijail);
+ pub fn minijail_namespace_enter_vfs(j: *mut minijail, ns_path: *const c_char);
+ pub fn minijail_new_session_keyring(j: *mut minijail);
+ pub fn minijail_skip_remount_private(j: *mut minijail);
+ pub fn minijail_namespace_ipc(j: *mut minijail);
+ pub fn minijail_namespace_net(j: *mut minijail);
+ pub fn minijail_namespace_enter_net(j: *mut minijail, ns_path: *const c_char);
+ pub fn minijail_namespace_cgroups(j: *mut minijail);
+ pub fn minijail_close_open_fds(j: *mut minijail);
+ pub fn minijail_namespace_pids(j: *mut minijail);
+ pub fn minijail_namespace_user(j: *mut minijail);
+ pub fn minijail_namespace_user_disable_setgroups(j: *mut minijail);
+ pub fn minijail_uidmap(j: *mut minijail, uidmap: *const c_char) -> c_int;
+ pub fn minijail_gidmap(j: *mut minijail, gidmap: *const c_char) -> c_int;
+ pub fn minijail_remount_proc_readonly(j: *mut minijail);
+ pub fn minijail_run_as_init(j: *mut minijail);
+ pub fn minijail_write_pid_file(j: *mut minijail, path: *const c_char) -> c_int;
+ pub fn minijail_inherit_usergroups(j: *mut minijail);
+ pub fn minijail_use_alt_syscall(j: *mut minijail, table: *const c_char) -> c_int;
+ pub fn minijail_add_to_cgroup(j: *mut minijail, path: *const c_char) -> c_int;
+ pub fn minijail_enter_chroot(j: *mut minijail, dir: *const c_char) -> c_int;
+ pub fn minijail_enter_pivot_root(j: *mut minijail, dir: *const c_char) -> c_int;
+ pub fn minijail_fork(j: *mut minijail) -> pid_t;
+ pub fn minijail_get_original_path(j: *mut minijail, chroot_path: *const c_char) -> *mut c_char;
+ pub fn minijail_mount_dev(j: *mut minijail);
+ pub fn minijail_mount_tmp(j: *mut minijail);
+ pub fn minijail_mount_tmp_size(j: *mut minijail, size: usize);
+ pub fn minijail_mount_with_data(
+ j: *mut minijail,
+ src: *const c_char,
+ dest: *const c_char,
+ type_: *const c_char,
+ flags: c_ulong,
+ data: *const c_char,
+ ) -> c_int;
+ pub fn minijail_mount(
+ j: *mut minijail,
+ src: *const c_char,
+ dest: *const c_char,
+ type_: *const c_char,
+ flags: c_ulong,
+ ) -> c_int;
+ pub fn minijail_bind(
+ j: *mut minijail,
+ src: *const c_char,
+ dest: *const c_char,
+ writeable: c_int,
+ ) -> c_int;
+ pub fn minijail_preserve_fd(j: *mut minijail, parent_fd: c_int, child_fd: c_int) -> c_int;
+ pub fn minijail_enter(j: *const minijail);
+ pub fn minijail_run(
+ j: *mut minijail,
+ filename: *const c_char,
+ argv: *const *const c_char,
+ ) -> c_int;
+ pub fn minijail_run_no_preload(
+ j: *mut minijail,
+ filename: *const c_char,
+ argv: *const *const c_char,
+ ) -> c_int;
+ pub fn minijail_run_pid(
+ j: *mut minijail,
+ filename: *const c_char,
+ argv: *const *const c_char,
+ pchild_pid: *mut pid_t,
+ ) -> c_int;
+ pub fn minijail_run_pipe(
+ j: *mut minijail,
+ filename: *const c_char,
+ argv: *const *const c_char,
+ pstdin_fd: *mut c_int,
+ ) -> c_int;
+ pub fn minijail_run_pid_pipes(
+ j: *mut minijail,
+ filename: *const c_char,
+ argv: *const *const c_char,
+ pchild_pid: *mut pid_t,
+ pstdin_fd: *mut c_int,
+ pstdout_fd: *mut c_int,
+ pstderr_fd: *mut c_int,
+ ) -> c_int;
+ pub fn minijail_run_pid_pipes_no_preload(
+ j: *mut minijail,
+ filename: *const c_char,
+ argv: *const *const c_char,
+ pchild_pid: *mut pid_t,
+ pstdin_fd: *mut c_int,
+ pstdout_fd: *mut c_int,
+ pstderr_fd: *mut c_int,
+ ) -> c_int;
+ pub fn minijail_kill(j: *mut minijail) -> c_int;
+ pub fn minijail_wait(j: *mut minijail) -> c_int;
+ pub fn minijail_destroy(j: *mut minijail);
+} // extern "C"
diff --git a/io_jail/src/test_filter.policy b/io_jail/src/test_filter.policy
new file mode 100644
index 0000000..9f4c943
--- /dev/null
+++ b/io_jail/src/test_filter.policy
@@ -0,0 +1,7 @@
+close: 1
+exit: 1
+futex: 1
+getpid: 1
+lseek: 1
+read: 1
+write: 1
diff --git a/kernel_cmdline/Cargo.toml b/kernel_cmdline/Cargo.toml
new file mode 100644
index 0000000..0000610
--- /dev/null
+++ b/kernel_cmdline/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "kernel_cmdline"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+libc = "*"
+
+[lib]
+path = "src/kernel_cmdline.rs"
diff --git a/kernel_cmdline/src/kernel_cmdline.rs b/kernel_cmdline/src/kernel_cmdline.rs
new file mode 100644
index 0000000..7284004
--- /dev/null
+++ b/kernel_cmdline/src/kernel_cmdline.rs
@@ -0,0 +1,230 @@
+// Copyright 2017 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.
+
+//! Helper for creating valid kernel command line strings.
+
+use std::fmt::{self, Display};
+use std::result;
+
+/// The error type for command line building operations.
+#[derive(PartialEq, Debug)]
+pub enum Error {
+ /// Operation would have resulted in a non-printable ASCII character.
+ InvalidAscii,
+ /// Key/Value Operation would have had a space in it.
+ HasSpace,
+ /// Key/Value Operation would have had an equals sign in it.
+ HasEquals,
+ /// Operation would have made the command line too large.
+ TooLarge,
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ let description = match self {
+ InvalidAscii => "string contains non-printable ASCII character",
+ HasSpace => "string contains a space",
+ HasEquals => "string contains an equals sign",
+ TooLarge => "inserting string would make command line too long",
+ };
+
+ write!(f, "{}", description)
+ }
+}
+
+/// Specialized Result type for command line operations.
+pub type Result<T> = result::Result<T, Error>;
+
+fn valid_char(c: char) -> bool {
+ match c {
+ ' '...'~' => true,
+ _ => false,
+ }
+}
+
+fn valid_str(s: &str) -> Result<()> {
+ if s.chars().all(valid_char) {
+ Ok(())
+ } else {
+ Err(Error::InvalidAscii)
+ }
+}
+
+fn valid_element(s: &str) -> Result<()> {
+ if !s.chars().all(valid_char) {
+ Err(Error::InvalidAscii)
+ } else if s.contains(' ') {
+ Err(Error::HasSpace)
+ } else if s.contains('=') {
+ Err(Error::HasEquals)
+ } else {
+ Ok(())
+ }
+}
+
+/// A builder for a kernel command line string that validates the string as its being built. A
+/// `CString` can be constructed from this directly using `CString::new`.
+pub struct Cmdline {
+ line: String,
+ capacity: usize,
+}
+
+impl Cmdline {
+ /// Constructs an empty Cmdline with the given capacity, which includes the nul terminator.
+ /// Capacity must be greater than 0.
+ pub fn new(capacity: usize) -> Cmdline {
+ assert_ne!(capacity, 0);
+ Cmdline {
+ line: String::new(),
+ capacity,
+ }
+ }
+
+ fn has_capacity(&self, more: usize) -> Result<()> {
+ let needs_space = if self.line.is_empty() { 0 } else { 1 };
+ if self.line.len() + more + needs_space < self.capacity {
+ Ok(())
+ } else {
+ Err(Error::TooLarge)
+ }
+ }
+
+ fn start_push(&mut self) {
+ if !self.line.is_empty() {
+ self.line.push(' ');
+ }
+ }
+
+ fn end_push(&mut self) {
+ // This assert is always true because of the `has_capacity` check that each insert method
+ // uses.
+ assert!(self.line.len() < self.capacity);
+ }
+
+ /// Validates and inserts a key value pair into this command line
+ pub fn insert<T: AsRef<str>>(&mut self, key: T, val: T) -> Result<()> {
+ let k = key.as_ref();
+ let v = val.as_ref();
+
+ valid_element(k)?;
+ valid_element(v)?;
+ self.has_capacity(k.len() + v.len() + 1)?;
+
+ self.start_push();
+ self.line.push_str(k);
+ self.line.push('=');
+ self.line.push_str(v);
+ self.end_push();
+
+ Ok(())
+ }
+
+ /// Validates and inserts a string to the end of the current command line
+ pub fn insert_str<T: AsRef<str>>(&mut self, slug: T) -> Result<()> {
+ let s = slug.as_ref();
+ valid_str(s)?;
+
+ self.has_capacity(s.len())?;
+
+ self.start_push();
+ self.line.push_str(s);
+ self.end_push();
+
+ Ok(())
+ }
+
+ /// Returns the cmdline in progress without nul termination
+ pub fn as_str(&self) -> &str {
+ self.line.as_str()
+ }
+}
+
+impl Into<Vec<u8>> for Cmdline {
+ fn into(self) -> Vec<u8> {
+ self.line.into_bytes()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::ffi::CString;
+
+ #[test]
+ fn insert_hello_world() {
+ let mut cl = Cmdline::new(100);
+ assert_eq!(cl.as_str(), "");
+ assert!(cl.insert("hello", "world").is_ok());
+ assert_eq!(cl.as_str(), "hello=world");
+
+ let s = CString::new(cl).expect("failed to create CString from Cmdline");
+ assert_eq!(s, CString::new("hello=world").unwrap());
+ }
+
+ #[test]
+ fn insert_multi() {
+ let mut cl = Cmdline::new(100);
+ assert!(cl.insert("hello", "world").is_ok());
+ assert!(cl.insert("foo", "bar").is_ok());
+ assert_eq!(cl.as_str(), "hello=world foo=bar");
+ }
+
+ #[test]
+ fn insert_space() {
+ let mut cl = Cmdline::new(100);
+ assert_eq!(cl.insert("a ", "b"), Err(Error::HasSpace));
+ assert_eq!(cl.insert("a", "b "), Err(Error::HasSpace));
+ assert_eq!(cl.insert("a ", "b "), Err(Error::HasSpace));
+ assert_eq!(cl.insert(" a", "b"), Err(Error::HasSpace));
+ assert_eq!(cl.as_str(), "");
+ }
+
+ #[test]
+ fn insert_equals() {
+ let mut cl = Cmdline::new(100);
+ assert_eq!(cl.insert("a=", "b"), Err(Error::HasEquals));
+ assert_eq!(cl.insert("a", "b="), Err(Error::HasEquals));
+ assert_eq!(cl.insert("a=", "b "), Err(Error::HasEquals));
+ assert_eq!(cl.insert("=a", "b"), Err(Error::HasEquals));
+ assert_eq!(cl.insert("a", "=b"), Err(Error::HasEquals));
+ assert_eq!(cl.as_str(), "");
+ }
+
+ #[test]
+ fn insert_emoji() {
+ let mut cl = Cmdline::new(100);
+ assert_eq!(cl.insert("heart", "💖"), Err(Error::InvalidAscii));
+ assert_eq!(cl.insert("💖", "love"), Err(Error::InvalidAscii));
+ assert_eq!(cl.as_str(), "");
+ }
+
+ #[test]
+ fn insert_string() {
+ let mut cl = Cmdline::new(13);
+ assert_eq!(cl.as_str(), "");
+ assert!(cl.insert_str("noapic").is_ok());
+ assert_eq!(cl.as_str(), "noapic");
+ assert!(cl.insert_str("nopci").is_ok());
+ assert_eq!(cl.as_str(), "noapic nopci");
+ }
+
+ #[test]
+ fn insert_too_large() {
+ let mut cl = Cmdline::new(4);
+ assert_eq!(cl.insert("hello", "world"), Err(Error::TooLarge));
+ assert_eq!(cl.insert("a", "world"), Err(Error::TooLarge));
+ assert_eq!(cl.insert("hello", "b"), Err(Error::TooLarge));
+ assert!(cl.insert("a", "b").is_ok());
+ assert_eq!(cl.insert("a", "b"), Err(Error::TooLarge));
+ assert_eq!(cl.insert_str("a"), Err(Error::TooLarge));
+ assert_eq!(cl.as_str(), "a=b");
+
+ let mut cl = Cmdline::new(10);
+ assert!(cl.insert("ab", "ba").is_ok()); // adds 5 length
+ assert_eq!(cl.insert("c", "da"), Err(Error::TooLarge)); // adds 5 (including space) length
+ assert!(cl.insert("c", "d").is_ok()); // adds 4 (including space) length
+ }
+}
diff --git a/kernel_loader/Cargo.toml b/kernel_loader/Cargo.toml
new file mode 100644
index 0000000..b49dbad
--- /dev/null
+++ b/kernel_loader/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "kernel_loader"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+libc = "*"
+sys_util = { path = "../sys_util" }
diff --git a/kernel_loader/src/elf.rs b/kernel_loader/src/elf.rs
new file mode 100644
index 0000000..c591e52
--- /dev/null
+++ b/kernel_loader/src/elf.rs
@@ -0,0 +1,285 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ * automatically generated by bindgen
+ * From chromeos-linux v4.19 include/uapi/linux/elf.h
+ * $ bindgen \
+ * --no-layout-tests --with-derive-default \
+ * --whitelist-type Elf64_Ehdr --whitelist-type Elf64_Phdr --whitelist-var .+ \
+ * include/uapi/linux/elf.h
+ */
+
+pub const __BITS_PER_LONG: u32 = 64;
+pub const __FD_SETSIZE: u32 = 1024;
+pub const EM_NONE: u32 = 0;
+pub const EM_M32: u32 = 1;
+pub const EM_SPARC: u32 = 2;
+pub const EM_386: u32 = 3;
+pub const EM_68K: u32 = 4;
+pub const EM_88K: u32 = 5;
+pub const EM_486: u32 = 6;
+pub const EM_860: u32 = 7;
+pub const EM_MIPS: u32 = 8;
+pub const EM_MIPS_RS3_LE: u32 = 10;
+pub const EM_MIPS_RS4_BE: u32 = 10;
+pub const EM_PARISC: u32 = 15;
+pub const EM_SPARC32PLUS: u32 = 18;
+pub const EM_PPC: u32 = 20;
+pub const EM_PPC64: u32 = 21;
+pub const EM_SPU: u32 = 23;
+pub const EM_ARM: u32 = 40;
+pub const EM_SH: u32 = 42;
+pub const EM_SPARCV9: u32 = 43;
+pub const EM_H8_300: u32 = 46;
+pub const EM_IA_64: u32 = 50;
+pub const EM_X86_64: u32 = 62;
+pub const EM_S390: u32 = 22;
+pub const EM_CRIS: u32 = 76;
+pub const EM_M32R: u32 = 88;
+pub const EM_MN10300: u32 = 89;
+pub const EM_OPENRISC: u32 = 92;
+pub const EM_BLACKFIN: u32 = 106;
+pub const EM_ALTERA_NIOS2: u32 = 113;
+pub const EM_TI_C6000: u32 = 140;
+pub const EM_AARCH64: u32 = 183;
+pub const EM_TILEPRO: u32 = 188;
+pub const EM_MICROBLAZE: u32 = 189;
+pub const EM_TILEGX: u32 = 191;
+pub const EM_BPF: u32 = 247;
+pub const EM_FRV: u32 = 21569;
+pub const EM_ALPHA: u32 = 36902;
+pub const EM_CYGNUS_M32R: u32 = 36929;
+pub const EM_S390_OLD: u32 = 41872;
+pub const EM_CYGNUS_MN10300: u32 = 48879;
+pub const PT_NULL: u32 = 0;
+pub const PT_LOAD: u32 = 1;
+pub const PT_DYNAMIC: u32 = 2;
+pub const PT_INTERP: u32 = 3;
+pub const PT_NOTE: u32 = 4;
+pub const PT_SHLIB: u32 = 5;
+pub const PT_PHDR: u32 = 6;
+pub const PT_TLS: u32 = 7;
+pub const PT_LOOS: u32 = 1610612736;
+pub const PT_HIOS: u32 = 1879048191;
+pub const PT_LOPROC: u32 = 1879048192;
+pub const PT_HIPROC: u32 = 2147483647;
+pub const PT_GNU_EH_FRAME: u32 = 1685382480;
+pub const PT_GNU_STACK: u32 = 1685382481;
+pub const PN_XNUM: u32 = 65535;
+pub const ET_NONE: u32 = 0;
+pub const ET_REL: u32 = 1;
+pub const ET_EXEC: u32 = 2;
+pub const ET_DYN: u32 = 3;
+pub const ET_CORE: u32 = 4;
+pub const ET_LOPROC: u32 = 65280;
+pub const ET_HIPROC: u32 = 65535;
+pub const DT_NULL: u32 = 0;
+pub const DT_NEEDED: u32 = 1;
+pub const DT_PLTRELSZ: u32 = 2;
+pub const DT_PLTGOT: u32 = 3;
+pub const DT_HASH: u32 = 4;
+pub const DT_STRTAB: u32 = 5;
+pub const DT_SYMTAB: u32 = 6;
+pub const DT_RELA: u32 = 7;
+pub const DT_RELASZ: u32 = 8;
+pub const DT_RELAENT: u32 = 9;
+pub const DT_STRSZ: u32 = 10;
+pub const DT_SYMENT: u32 = 11;
+pub const DT_INIT: u32 = 12;
+pub const DT_FINI: u32 = 13;
+pub const DT_SONAME: u32 = 14;
+pub const DT_RPATH: u32 = 15;
+pub const DT_SYMBOLIC: u32 = 16;
+pub const DT_REL: u32 = 17;
+pub const DT_RELSZ: u32 = 18;
+pub const DT_RELENT: u32 = 19;
+pub const DT_PLTREL: u32 = 20;
+pub const DT_DEBUG: u32 = 21;
+pub const DT_TEXTREL: u32 = 22;
+pub const DT_JMPREL: u32 = 23;
+pub const DT_ENCODING: u32 = 32;
+pub const OLD_DT_LOOS: u32 = 1610612736;
+pub const DT_LOOS: u32 = 1610612749;
+pub const DT_HIOS: u32 = 1879044096;
+pub const DT_VALRNGLO: u32 = 1879047424;
+pub const DT_VALRNGHI: u32 = 1879047679;
+pub const DT_ADDRRNGLO: u32 = 1879047680;
+pub const DT_ADDRRNGHI: u32 = 1879047935;
+pub const DT_VERSYM: u32 = 1879048176;
+pub const DT_RELACOUNT: u32 = 1879048185;
+pub const DT_RELCOUNT: u32 = 1879048186;
+pub const DT_FLAGS_1: u32 = 1879048187;
+pub const DT_VERDEF: u32 = 1879048188;
+pub const DT_VERDEFNUM: u32 = 1879048189;
+pub const DT_VERNEED: u32 = 1879048190;
+pub const DT_VERNEEDNUM: u32 = 1879048191;
+pub const OLD_DT_HIOS: u32 = 1879048191;
+pub const DT_LOPROC: u32 = 1879048192;
+pub const DT_HIPROC: u32 = 2147483647;
+pub const STB_LOCAL: u32 = 0;
+pub const STB_GLOBAL: u32 = 1;
+pub const STB_WEAK: u32 = 2;
+pub const STT_NOTYPE: u32 = 0;
+pub const STT_OBJECT: u32 = 1;
+pub const STT_FUNC: u32 = 2;
+pub const STT_SECTION: u32 = 3;
+pub const STT_FILE: u32 = 4;
+pub const STT_COMMON: u32 = 5;
+pub const STT_TLS: u32 = 6;
+pub const EI_NIDENT: u32 = 16;
+pub const PF_R: u32 = 4;
+pub const PF_W: u32 = 2;
+pub const PF_X: u32 = 1;
+pub const SHT_NULL: u32 = 0;
+pub const SHT_PROGBITS: u32 = 1;
+pub const SHT_SYMTAB: u32 = 2;
+pub const SHT_STRTAB: u32 = 3;
+pub const SHT_RELA: u32 = 4;
+pub const SHT_HASH: u32 = 5;
+pub const SHT_DYNAMIC: u32 = 6;
+pub const SHT_NOTE: u32 = 7;
+pub const SHT_NOBITS: u32 = 8;
+pub const SHT_REL: u32 = 9;
+pub const SHT_SHLIB: u32 = 10;
+pub const SHT_DYNSYM: u32 = 11;
+pub const SHT_NUM: u32 = 12;
+pub const SHT_LOPROC: u32 = 1879048192;
+pub const SHT_HIPROC: u32 = 2147483647;
+pub const SHT_LOUSER: u32 = 2147483648;
+pub const SHT_HIUSER: u32 = 4294967295;
+pub const SHF_WRITE: u32 = 1;
+pub const SHF_ALLOC: u32 = 2;
+pub const SHF_EXECINSTR: u32 = 4;
+pub const SHF_RELA_LIVEPATCH: u32 = 1048576;
+pub const SHF_RO_AFTER_INIT: u32 = 2097152;
+pub const SHF_MASKPROC: u32 = 4026531840;
+pub const SHN_UNDEF: u32 = 0;
+pub const SHN_LORESERVE: u32 = 65280;
+pub const SHN_LOPROC: u32 = 65280;
+pub const SHN_HIPROC: u32 = 65311;
+pub const SHN_LIVEPATCH: u32 = 65312;
+pub const SHN_ABS: u32 = 65521;
+pub const SHN_COMMON: u32 = 65522;
+pub const SHN_HIRESERVE: u32 = 65535;
+pub const EI_MAG0: u32 = 0;
+pub const EI_MAG1: u32 = 1;
+pub const EI_MAG2: u32 = 2;
+pub const EI_MAG3: u32 = 3;
+pub const EI_CLASS: u32 = 4;
+pub const EI_DATA: u32 = 5;
+pub const EI_VERSION: u32 = 6;
+pub const EI_OSABI: u32 = 7;
+pub const EI_PAD: u32 = 8;
+pub const ELFMAG0: u32 = 127;
+pub const ELFMAG1: u8 = 69u8;
+pub const ELFMAG2: u8 = 76u8;
+pub const ELFMAG3: u8 = 70u8;
+pub const ELFMAG: &'static [u8; 5usize] = b"\x7FELF\0";
+pub const SELFMAG: u32 = 4;
+pub const ELFCLASSNONE: u32 = 0;
+pub const ELFCLASS32: u32 = 1;
+pub const ELFCLASS64: u32 = 2;
+pub const ELFCLASSNUM: u32 = 3;
+pub const ELFDATANONE: u32 = 0;
+pub const ELFDATA2LSB: u32 = 1;
+pub const ELFDATA2MSB: u32 = 2;
+pub const EV_NONE: u32 = 0;
+pub const EV_CURRENT: u32 = 1;
+pub const EV_NUM: u32 = 2;
+pub const ELFOSABI_NONE: u32 = 0;
+pub const ELFOSABI_LINUX: u32 = 3;
+pub const ELF_OSABI: u32 = 0;
+pub const NT_PRSTATUS: u32 = 1;
+pub const NT_PRFPREG: u32 = 2;
+pub const NT_PRPSINFO: u32 = 3;
+pub const NT_TASKSTRUCT: u32 = 4;
+pub const NT_AUXV: u32 = 6;
+pub const NT_SIGINFO: u32 = 1397311305;
+pub const NT_FILE: u32 = 1179208773;
+pub const NT_PRXFPREG: u32 = 1189489535;
+pub const NT_PPC_VMX: u32 = 256;
+pub const NT_PPC_SPE: u32 = 257;
+pub const NT_PPC_VSX: u32 = 258;
+pub const NT_PPC_TAR: u32 = 259;
+pub const NT_PPC_PPR: u32 = 260;
+pub const NT_PPC_DSCR: u32 = 261;
+pub const NT_PPC_EBB: u32 = 262;
+pub const NT_PPC_PMU: u32 = 263;
+pub const NT_PPC_TM_CGPR: u32 = 264;
+pub const NT_PPC_TM_CFPR: u32 = 265;
+pub const NT_PPC_TM_CVMX: u32 = 266;
+pub const NT_PPC_TM_CVSX: u32 = 267;
+pub const NT_PPC_TM_SPR: u32 = 268;
+pub const NT_PPC_TM_CTAR: u32 = 269;
+pub const NT_PPC_TM_CPPR: u32 = 270;
+pub const NT_PPC_TM_CDSCR: u32 = 271;
+pub const NT_PPC_PKEY: u32 = 272;
+pub const NT_386_TLS: u32 = 512;
+pub const NT_386_IOPERM: u32 = 513;
+pub const NT_X86_XSTATE: u32 = 514;
+pub const NT_S390_HIGH_GPRS: u32 = 768;
+pub const NT_S390_TIMER: u32 = 769;
+pub const NT_S390_TODCMP: u32 = 770;
+pub const NT_S390_TODPREG: u32 = 771;
+pub const NT_S390_CTRS: u32 = 772;
+pub const NT_S390_PREFIX: u32 = 773;
+pub const NT_S390_LAST_BREAK: u32 = 774;
+pub const NT_S390_SYSTEM_CALL: u32 = 775;
+pub const NT_S390_TDB: u32 = 776;
+pub const NT_S390_VXRS_LOW: u32 = 777;
+pub const NT_S390_VXRS_HIGH: u32 = 778;
+pub const NT_S390_GS_CB: u32 = 779;
+pub const NT_S390_GS_BC: u32 = 780;
+pub const NT_S390_RI_CB: u32 = 781;
+pub const NT_ARM_VFP: u32 = 1024;
+pub const NT_ARM_TLS: u32 = 1025;
+pub const NT_ARM_HW_BREAK: u32 = 1026;
+pub const NT_ARM_HW_WATCH: u32 = 1027;
+pub const NT_ARM_SYSTEM_CALL: u32 = 1028;
+pub const NT_ARM_SVE: u32 = 1029;
+pub const NT_ARC_V2: u32 = 1536;
+pub const NT_VMCOREDD: u32 = 1792;
+pub const NT_MIPS_DSP: u32 = 2048;
+pub const NT_MIPS_FP_MODE: u32 = 2049;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+pub type Elf64_Addr = __u64;
+pub type Elf64_Half = __u16;
+pub type Elf64_Off = __u64;
+pub type Elf64_Word = __u32;
+pub type Elf64_Xword = __u64;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct elf64_hdr {
+ pub e_ident: [::std::os::raw::c_uchar; 16usize],
+ pub e_type: Elf64_Half,
+ pub e_machine: Elf64_Half,
+ pub e_version: Elf64_Word,
+ pub e_entry: Elf64_Addr,
+ pub e_phoff: Elf64_Off,
+ pub e_shoff: Elf64_Off,
+ pub e_flags: Elf64_Word,
+ pub e_ehsize: Elf64_Half,
+ pub e_phentsize: Elf64_Half,
+ pub e_phnum: Elf64_Half,
+ pub e_shentsize: Elf64_Half,
+ pub e_shnum: Elf64_Half,
+ pub e_shstrndx: Elf64_Half,
+}
+pub type Elf64_Ehdr = elf64_hdr;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct elf64_phdr {
+ pub p_type: Elf64_Word,
+ pub p_flags: Elf64_Word,
+ pub p_offset: Elf64_Off,
+ pub p_vaddr: Elf64_Addr,
+ pub p_paddr: Elf64_Addr,
+ pub p_filesz: Elf64_Xword,
+ pub p_memsz: Elf64_Xword,
+ pub p_align: Elf64_Xword,
+}
+pub type Elf64_Phdr = elf64_phdr;
diff --git a/kernel_loader/src/lib.rs b/kernel_loader/src/lib.rs
new file mode 100644
index 0000000..7ff6efa
--- /dev/null
+++ b/kernel_loader/src/lib.rs
@@ -0,0 +1,290 @@
+// Copyright 2017 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::CStr;
+use std::fmt::{self, Display};
+use std::io::{Read, Seek, SeekFrom};
+use std::mem;
+use std::os::unix::io::AsRawFd;
+
+use sys_util::{GuestAddress, GuestMemory};
+
+#[allow(dead_code)]
+#[allow(non_camel_case_types)]
+#[allow(non_snake_case)]
+#[allow(non_upper_case_globals)]
+#[allow(clippy::all)]
+mod elf;
+
+#[derive(Debug, PartialEq)]
+pub enum Error {
+ BigEndianElfOnLittle,
+ CommandLineCopy,
+ CommandLineOverflow,
+ InvalidElfMagicNumber,
+ InvalidProgramHeaderSize,
+ InvalidProgramHeaderOffset,
+ InvalidProgramHeaderAddress,
+ ReadElfHeader,
+ ReadKernelImage,
+ ReadProgramHeader,
+ SeekKernelStart,
+ SeekElfStart,
+ SeekProgramHeader,
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ let description = match self {
+ BigEndianElfOnLittle => "trying to load big-endian binary on little-endian machine",
+ CommandLineCopy => "failed writing command line to guest memory",
+ CommandLineOverflow => "command line overflowed guest memory",
+ InvalidElfMagicNumber => "invalid Elf magic number",
+ InvalidProgramHeaderSize => "invalid program header size",
+ InvalidProgramHeaderOffset => "invalid program header offset",
+ InvalidProgramHeaderAddress => "invalid Program Header Address",
+ ReadElfHeader => "unable to read elf header",
+ ReadKernelImage => "unable to read kernel image",
+ ReadProgramHeader => "unable to read program header",
+ SeekKernelStart => "unable to seek to kernel start",
+ SeekElfStart => "unable to seek to elf start",
+ SeekProgramHeader => "unable to seek to program header",
+ };
+
+ write!(f, "kernel loader: {}", description)
+ }
+}
+
+/// Loads a kernel from a vmlinux elf image to a slice
+///
+/// # Arguments
+///
+/// * `guest_mem` - The guest memory region the kernel is written to.
+/// * `kernel_start` - The offset into `guest_mem` at which to load the kernel.
+/// * `kernel_image` - Input vmlinux image.
+pub fn load_kernel<F>(
+ guest_mem: &GuestMemory,
+ kernel_start: GuestAddress,
+ kernel_image: &mut F,
+) -> Result<u64>
+where
+ F: Read + Seek + AsRawFd,
+{
+ let mut ehdr: elf::Elf64_Ehdr = Default::default();
+ kernel_image
+ .seek(SeekFrom::Start(0))
+ .map_err(|_| Error::SeekElfStart)?;
+ unsafe {
+ // read_struct is safe when reading a POD struct. It can be used and dropped without issue.
+ sys_util::read_struct(kernel_image, &mut ehdr).map_err(|_| Error::ReadElfHeader)?;
+ }
+
+ // Sanity checks
+ if ehdr.e_ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8
+ || ehdr.e_ident[elf::EI_MAG1 as usize] != elf::ELFMAG1
+ || ehdr.e_ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
+ || ehdr.e_ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
+ {
+ return Err(Error::InvalidElfMagicNumber);
+ }
+ if ehdr.e_ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
+ return Err(Error::BigEndianElfOnLittle);
+ }
+ if ehdr.e_phentsize as usize != mem::size_of::<elf::Elf64_Phdr>() {
+ return Err(Error::InvalidProgramHeaderSize);
+ }
+ if (ehdr.e_phoff as usize) < mem::size_of::<elf::Elf64_Ehdr>() {
+ // If the program header is backwards, bail.
+ return Err(Error::InvalidProgramHeaderOffset);
+ }
+
+ kernel_image
+ .seek(SeekFrom::Start(ehdr.e_phoff))
+ .map_err(|_| Error::SeekProgramHeader)?;
+ let phdrs: Vec<elf::Elf64_Phdr> = unsafe {
+ // Reading the structs is safe for a slice of POD structs.
+ sys_util::read_struct_slice(kernel_image, ehdr.e_phnum as usize)
+ .map_err(|_| Error::ReadProgramHeader)?
+ };
+
+ let mut kernel_end = 0;
+
+ // Read in each section pointed to by the program headers.
+ for phdr in &phdrs {
+ if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
+ continue;
+ }
+
+ kernel_image
+ .seek(SeekFrom::Start(phdr.p_offset))
+ .map_err(|_| Error::SeekKernelStart)?;
+
+ let mem_offset = kernel_start
+ .checked_add(phdr.p_paddr)
+ .ok_or(Error::InvalidProgramHeaderAddress)?;
+ guest_mem
+ .read_to_memory(mem_offset, kernel_image, phdr.p_filesz as usize)
+ .map_err(|_| Error::ReadKernelImage)?;
+
+ kernel_end = mem_offset.offset() + phdr.p_memsz;
+ }
+
+ Ok(kernel_end)
+}
+
+/// Writes the command line string to the given memory slice.
+///
+/// # Arguments
+///
+/// * `guest_mem` - A u8 slice that will be partially overwritten by the command line.
+/// * `guest_addr` - The address in `guest_mem` at which to load the command line.
+/// * `cmdline` - The kernel command line.
+pub fn load_cmdline(
+ guest_mem: &GuestMemory,
+ guest_addr: GuestAddress,
+ cmdline: &CStr,
+) -> Result<()> {
+ let len = cmdline.to_bytes().len();
+ if len == 0 {
+ return Ok(());
+ }
+
+ let end = guest_addr
+ .checked_add(len as u64 + 1)
+ .ok_or(Error::CommandLineOverflow)?; // Extra for null termination.
+ if end > guest_mem.end_addr() {
+ return Err(Error::CommandLineOverflow)?;
+ }
+
+ guest_mem
+ .write_at_addr(cmdline.to_bytes_with_nul(), guest_addr)
+ .map_err(|_| Error::CommandLineCopy)?;
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::fs::File;
+ use std::io::Write;
+ use sys_util::{GuestAddress, GuestMemory, SharedMemory};
+
+ const MEM_SIZE: u64 = 0x8000;
+
+ fn create_guest_mem() -> GuestMemory {
+ GuestMemory::new(&vec![(GuestAddress(0x0), MEM_SIZE)]).unwrap()
+ }
+
+ #[test]
+ fn cmdline_overflow() {
+ let gm = create_guest_mem();
+ let cmdline_address = GuestAddress(MEM_SIZE - 5);
+ assert_eq!(
+ Err(Error::CommandLineOverflow),
+ load_cmdline(
+ &gm,
+ cmdline_address,
+ CStr::from_bytes_with_nul(b"12345\0").unwrap()
+ )
+ );
+ }
+
+ #[test]
+ fn cmdline_write_end() {
+ let gm = create_guest_mem();
+ let mut cmdline_address = GuestAddress(45);
+ assert_eq!(
+ Ok(()),
+ load_cmdline(
+ &gm,
+ cmdline_address,
+ CStr::from_bytes_with_nul(b"1234\0").unwrap()
+ )
+ );
+ let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
+ assert_eq!(val, '1' as u8);
+ cmdline_address = cmdline_address.unchecked_add(1);
+ let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
+ assert_eq!(val, '2' as u8);
+ cmdline_address = cmdline_address.unchecked_add(1);
+ let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
+ assert_eq!(val, '3' as u8);
+ cmdline_address = cmdline_address.unchecked_add(1);
+ let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
+ assert_eq!(val, '4' as u8);
+ cmdline_address = cmdline_address.unchecked_add(1);
+ let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
+ assert_eq!(val, '\0' as u8);
+ }
+
+ // Elf64 image that prints hello world on x86_64.
+ fn make_elf_bin() -> File {
+ let elf_bytes = include_bytes!("test_elf.bin");
+ let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
+ shm.set_size(elf_bytes.len() as u64)
+ .expect("failed to set shared memory size");
+ shm.write_all(elf_bytes)
+ .expect("failed to write elf to shared memoy");
+ shm.into()
+ }
+
+ fn mutate_elf_bin(mut f: &File, offset: u64, val: u8) {
+ f.seek(SeekFrom::Start(offset))
+ .expect("failed to seek file");
+ f.write(&[val])
+ .expect("failed to write mutated value to file");
+ }
+
+ #[test]
+ fn load_elf() {
+ let gm = create_guest_mem();
+ let kernel_addr = GuestAddress(0x0);
+ let mut image = make_elf_bin();
+ assert_eq!(Ok(16613), load_kernel(&gm, kernel_addr, &mut image));
+ }
+
+ #[test]
+ fn bad_magic() {
+ let gm = create_guest_mem();
+ let kernel_addr = GuestAddress(0x0);
+ let mut bad_image = make_elf_bin();
+ mutate_elf_bin(&bad_image, 0x1, 0x33);
+ assert_eq!(
+ Err(Error::InvalidElfMagicNumber),
+ load_kernel(&gm, kernel_addr, &mut bad_image)
+ );
+ }
+
+ #[test]
+ fn bad_endian() {
+ // Only little endian is supported
+ let gm = create_guest_mem();
+ let kernel_addr = GuestAddress(0x0);
+ let mut bad_image = make_elf_bin();
+ mutate_elf_bin(&bad_image, 0x5, 2);
+ assert_eq!(
+ Err(Error::BigEndianElfOnLittle),
+ load_kernel(&gm, kernel_addr, &mut bad_image)
+ );
+ }
+
+ #[test]
+ fn bad_phoff() {
+ // program header has to be past the end of the elf header
+ let gm = create_guest_mem();
+ let kernel_addr = GuestAddress(0x0);
+ let mut bad_image = make_elf_bin();
+ mutate_elf_bin(&bad_image, 0x20, 0x10);
+ assert_eq!(
+ Err(Error::InvalidProgramHeaderOffset),
+ load_kernel(&gm, kernel_addr, &mut bad_image)
+ );
+ }
+}
diff --git a/kernel_loader/src/test_elf.bin b/kernel_loader/src/test_elf.bin
new file mode 100644
index 0000000..74eac6e
--- /dev/null
+++ b/kernel_loader/src/test_elf.bin
Binary files differ
diff --git a/kokoro/README.md b/kokoro/README.md
new file mode 100644
index 0000000..727b74a
--- /dev/null
+++ b/kokoro/README.md
@@ -0,0 +1,78 @@
+# Kokoro CI for crosvm
+
+For presubmit testing, each change posted for Gerrit on the master branch of crosvm will be tried by
+Kokoro. The configuration is found in [`presubmit.cfg`](presubmit.cfg) and the build script is at
+[`build.sh`](build.sh). A Docker image called `crosvm-base` is used as the testing environment which
+is built with a [`Dockerfile`](../docker/Dockerfile).
+
+[TOC]
+
+## How to use Docker to test crosvm
+
+Assuming a Docker daemon is already running, build the `crosvm-base` image:
+
+```shell
+path/to/crosvm/docker/build_crosvm_base.sh
+```
+
+Here is how to use the image to test a crosvm repository located at `$CROSVM_SRC`:
+
+```shell
+$CROSVM_SRC/docker/wrapped_smoke_test.sh
+```
+
+> **WARNING**:
+> The `--privileged` flag is used in that script so that the container will have `/dev/kvm` access.
+
+## How to update `crosvm-base`
+
+The `crosvm-base` `Dockerfile` downloads, builds, and install specific library versions needed to
+test crosvm. It also defines a run time environment and default command line for performing a test.
+If an update or new library is needed or any other adjustment is required, a new image can be
+generated as follows:
+
+```shell
+path/to/crosvm/docker/build_crosvm_base.sh
+docker save crosvm-base | xz -T 0 -z >crosvm-base.tar.xz
+```
+
+If you have x20 access, move `crosvm-base.tar.xz` to `/teams/chromeos-vm/docker/` and ensure the
+owner is `chromeos-vm-ci-read-write`. This owner is used to allow Kokoro to read the base image in
+during the test run. The updated image will be used for future Kokoro runs until it is replaced.
+
+```shell
+prodaccess
+cp crosvm-base.tar.xz /google/data/rw/teams/chromeos-vm/docker/
+```
+
+The cp command should preserve the right owner but to be sure you can confirm that it is
+`chromeos-vm-ci-read-write` in the web ui: https://x20.corp.google.com/teams/chromeos-vm/docker
+
+> **WARNING**:
+> If the image tarball uploaded to x20 is defective in any way, Kokoro will fail to verify every
+> crosvm change as if the change itself were defective. Please verify the image is good before
+> uploading to x20.
+
+## How to simulate Kokoro before uploading
+
+If you want to test a change before uploading it in a similar environment to Kokoro, use the
+[`kokoro_simulator.sh`](kokoro_simulator.sh) script. It will invoke the `build.sh` script after
+exporting environment variables and a volume that are expected to be present. The crosvm source code
+is symlinked in, and is tested exactly as in the working directory. Any changes to `build.sh` will
+also be tested, but any changes to `presubmit.cfg` will have no effect. If there are any changes to
+`Dockerfile`, they will have no effect unless the `crosvm-base` image is removed (or never existed)
+from the local Docker daemon. To test `Dockerfile` changes use the following formula to purge
+`crosvm-base`.
+
+```shell
+# So that kokoro_simulator.sh doesn't skip `docker save`.
+rm /tmp/kokoro_simulator/crosvm-base.tar.xz
+
+# Stopped containers prevent the removal of images below.
+docker container prune
+
+# So that kokoro_simulator.sh doesn't skip `docker build`.
+docker rmi crosvm-base
+```
+
+心
diff --git a/kokoro/build.sh b/kokoro/build.sh
new file mode 100755
index 0000000..96028ac
--- /dev/null
+++ b/kokoro/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Copyright 2018 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.
+
+set -ex
+
+main() {
+ if [ -z "${KOKORO_ARTIFACTS_DIR}" ]; then
+ echo "This script must be run in kokoro"
+ exit 1
+ fi
+
+ local src_root="${KOKORO_ARTIFACTS_DIR}"/git/crosvm
+ local base_image_tarball="${KOKORO_GFILE_DIR}"/crosvm-base.tar.xz
+ local base_image="crosvm-base"
+
+ if [[ "$(docker images -q ${base_image} 2> /dev/null)" == "" ]]; then
+ docker load -i "${base_image_tarball}"
+ fi
+ "${src_root}"/docker/wrapped_smoke_test.sh
+
+ return 0
+}
+
+main "$@"
diff --git a/kokoro/continuous.cfg b/kokoro/continuous.cfg
new file mode 100644
index 0000000..3dc7682
--- /dev/null
+++ b/kokoro/continuous.cfg
@@ -0,0 +1,5 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "crosvm/kokoro/build.sh"
+
+gfile_resources: "/x20/teams/chromeos-vm/docker/crosvm-base.tar.xz"
diff --git a/kokoro/kokoro_simulator.sh b/kokoro/kokoro_simulator.sh
new file mode 100755
index 0000000..08961de
--- /dev/null
+++ b/kokoro/kokoro_simulator.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+# Copyright 2018 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.
+
+set -ex
+
+main() {
+ cd "$(dirname "$0")"
+
+ local kokoro_simulator_root=/tmp/kokoro_simulator
+ local src_root="${kokoro_simulator_root}"/git/crosvm
+ local base_image_tarball="${kokoro_simulator_root}"/crosvm-base.tar.xz
+ local base_image="crosvm-base"
+
+ mkdir -p "${kokoro_simulator_root}"
+ if [[ ! -e "${base_image_tarball}" ]]; then
+ if [[ "$(docker images -q ${base_image} 2> /dev/null)" == "" ]]; then
+ ../docker/build_crosvm_base.sh
+ fi
+ docker save ${base_image} | xz -T 0 -z >"${base_image_tarball}"
+ fi
+
+ if [[ ! -e "${src_root}" ]]; then
+ mkdir -p "${kokoro_simulator_root}"/git
+ ln -s "$(realpath ../)" "${src_root}"
+ fi
+
+ export KOKORO_ARTIFACTS_DIR="${kokoro_simulator_root}"
+ export KOKORO_GFILE_DIR="${kokoro_simulator_root}"
+
+ ./build.sh
+}
+
+main "$@"
diff --git a/kokoro/presubmit-cr.cfg b/kokoro/presubmit-cr.cfg
new file mode 100644
index 0000000..3dc7682
--- /dev/null
+++ b/kokoro/presubmit-cr.cfg
@@ -0,0 +1,5 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "crosvm/kokoro/build.sh"
+
+gfile_resources: "/x20/teams/chromeos-vm/docker/crosvm-base.tar.xz"
diff --git a/kokoro/presubmit.cfg b/kokoro/presubmit.cfg
new file mode 100644
index 0000000..3dc7682
--- /dev/null
+++ b/kokoro/presubmit.cfg
@@ -0,0 +1,5 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "crosvm/kokoro/build.sh"
+
+gfile_resources: "/x20/teams/chromeos-vm/docker/crosvm-base.tar.xz"
diff --git a/kvm/Cargo.toml b/kvm/Cargo.toml
new file mode 100644
index 0000000..d12aa76
--- /dev/null
+++ b/kvm/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "kvm"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+kvm_sys = { path = "../kvm_sys" }
+libc = "*"
+msg_socket = { path = "../msg_socket" }
+sys_util = { path = "../sys_util" }
diff --git a/kvm/src/cap.rs b/kvm/src/cap.rs
new file mode 100644
index 0000000..b095f82
--- /dev/null
+++ b/kvm/src/cap.rs
@@ -0,0 +1,121 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use kvm_sys::*;
+
+/// A capability the kernel's KVM interface can possibly expose.
+#[derive(Clone, Copy, Debug, PartialEq)]
+#[repr(u32)]
+pub enum Cap {
+ Irqchip = KVM_CAP_IRQCHIP,
+ Hlt = KVM_CAP_HLT,
+ MmuShadowCacheControl = KVM_CAP_MMU_SHADOW_CACHE_CONTROL,
+ UserMemory = KVM_CAP_USER_MEMORY,
+ SetTssAddr = KVM_CAP_SET_TSS_ADDR,
+ Vapic = KVM_CAP_VAPIC,
+ ExtCpuid = KVM_CAP_EXT_CPUID,
+ Clocksource = KVM_CAP_CLOCKSOURCE,
+ NrVcpus = KVM_CAP_NR_VCPUS,
+ NrMemslots = KVM_CAP_NR_MEMSLOTS,
+ Pit = KVM_CAP_PIT,
+ NopIoDelay = KVM_CAP_NOP_IO_DELAY,
+ PvMmu = KVM_CAP_PV_MMU,
+ MpState = KVM_CAP_MP_STATE,
+ CoalescedMmio = KVM_CAP_COALESCED_MMIO,
+ SyncMmu = KVM_CAP_SYNC_MMU,
+ Iommu = KVM_CAP_IOMMU,
+ DestroyMemoryRegionWorks = KVM_CAP_DESTROY_MEMORY_REGION_WORKS,
+ UserNmi = KVM_CAP_USER_NMI,
+ SetGuestDebug = KVM_CAP_SET_GUEST_DEBUG,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ ReinjectControl = KVM_CAP_REINJECT_CONTROL,
+ IrqRouting = KVM_CAP_IRQ_ROUTING,
+ IrqInjectStatus = KVM_CAP_IRQ_INJECT_STATUS,
+ AssignDevIrq = KVM_CAP_ASSIGN_DEV_IRQ,
+ JoinMemoryRegionsWorks = KVM_CAP_JOIN_MEMORY_REGIONS_WORKS,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ Mce = KVM_CAP_MCE,
+ Irqfd = KVM_CAP_IRQFD,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ Pit2 = KVM_CAP_PIT2,
+ SetBootCpuId = KVM_CAP_SET_BOOT_CPU_ID,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ PitState2 = KVM_CAP_PIT_STATE2,
+ Ioeventfd = KVM_CAP_IOEVENTFD,
+ SetIdentityMapAddr = KVM_CAP_SET_IDENTITY_MAP_ADDR,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ XenHvm = KVM_CAP_XEN_HVM,
+ AdjustClock = KVM_CAP_ADJUST_CLOCK,
+ InternalErrorData = KVM_CAP_INTERNAL_ERROR_DATA,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ VcpuEvents = KVM_CAP_VCPU_EVENTS,
+ S390Psw = KVM_CAP_S390_PSW,
+ PpcSegstate = KVM_CAP_PPC_SEGSTATE,
+ Hyperv = KVM_CAP_HYPERV,
+ HypervVapic = KVM_CAP_HYPERV_VAPIC,
+ HypervSpin = KVM_CAP_HYPERV_SPIN,
+ PciSegment = KVM_CAP_PCI_SEGMENT,
+ PpcPairedSingles = KVM_CAP_PPC_PAIRED_SINGLES,
+ IntrShadow = KVM_CAP_INTR_SHADOW,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ Debugregs = KVM_CAP_DEBUGREGS,
+ X86RobustSinglestep = KVM_CAP_X86_ROBUST_SINGLESTEP,
+ PpcOsi = KVM_CAP_PPC_OSI,
+ PpcUnsetIrq = KVM_CAP_PPC_UNSET_IRQ,
+ EnableCap = KVM_CAP_ENABLE_CAP,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ Xsave = KVM_CAP_XSAVE,
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ Xcrs = KVM_CAP_XCRS,
+ PpcGetPvinfo = KVM_CAP_PPC_GET_PVINFO,
+ PpcIrqLevel = KVM_CAP_PPC_IRQ_LEVEL,
+ AsyncPf = KVM_CAP_ASYNC_PF,
+ TscControl = KVM_CAP_TSC_CONTROL,
+ GetTscKhz = KVM_CAP_GET_TSC_KHZ,
+ PpcBookeSregs = KVM_CAP_PPC_BOOKE_SREGS,
+ SpaprTce = KVM_CAP_SPAPR_TCE,
+ PpcSmt = KVM_CAP_PPC_SMT,
+ PpcRma = KVM_CAP_PPC_RMA,
+ MaxVcpus = KVM_CAP_MAX_VCPUS,
+ PpcHior = KVM_CAP_PPC_HIOR,
+ PpcPapr = KVM_CAP_PPC_PAPR,
+ SwTlb = KVM_CAP_SW_TLB,
+ OneReg = KVM_CAP_ONE_REG,
+ S390Gmap = KVM_CAP_S390_GMAP,
+ TscDeadlineTimer = KVM_CAP_TSC_DEADLINE_TIMER,
+ S390Ucontrol = KVM_CAP_S390_UCONTROL,
+ SyncRegs = KVM_CAP_SYNC_REGS,
+ Pci23 = KVM_CAP_PCI_2_3,
+ KvmclockCtrl = KVM_CAP_KVMCLOCK_CTRL,
+ SignalMsi = KVM_CAP_SIGNAL_MSI,
+ PpcGetSmmuInfo = KVM_CAP_PPC_GET_SMMU_INFO,
+ S390Cow = KVM_CAP_S390_COW,
+ PpcAllocHtab = KVM_CAP_PPC_ALLOC_HTAB,
+ ReadonlyMem = KVM_CAP_READONLY_MEM,
+ IrqfdResample = KVM_CAP_IRQFD_RESAMPLE,
+ PpcBookeWatchdog = KVM_CAP_PPC_BOOKE_WATCHDOG,
+ PpcHtabFd = KVM_CAP_PPC_HTAB_FD,
+ S390CssSupport = KVM_CAP_S390_CSS_SUPPORT,
+ PpcEpr = KVM_CAP_PPC_EPR,
+ ArmPsci = KVM_CAP_ARM_PSCI,
+ ArmSetDeviceAddr = KVM_CAP_ARM_SET_DEVICE_ADDR,
+ DeviceCtrl = KVM_CAP_DEVICE_CTRL,
+ IrqMpic = KVM_CAP_IRQ_MPIC,
+ PpcRtas = KVM_CAP_PPC_RTAS,
+ IrqXics = KVM_CAP_IRQ_XICS,
+ ArmEl132bit = KVM_CAP_ARM_EL1_32BIT,
+ SpaprMultitce = KVM_CAP_SPAPR_MULTITCE,
+ ExtEmulCpuid = KVM_CAP_EXT_EMUL_CPUID,
+ HypervTime = KVM_CAP_HYPERV_TIME,
+ IoapicPolarityIgnored = KVM_CAP_IOAPIC_POLARITY_IGNORED,
+ EnableCapVm = KVM_CAP_ENABLE_CAP_VM,
+ S390Irqchip = KVM_CAP_S390_IRQCHIP,
+ IoeventfdNoLength = KVM_CAP_IOEVENTFD_NO_LENGTH,
+ VmAttributes = KVM_CAP_VM_ATTRIBUTES,
+ ArmPsci02 = KVM_CAP_ARM_PSCI_0_2,
+ PpcFixupHcall = KVM_CAP_PPC_FIXUP_HCALL,
+ PpcEnableHcall = KVM_CAP_PPC_ENABLE_HCALL,
+ CheckExtensionVm = KVM_CAP_CHECK_EXTENSION_VM,
+ S390UserSigp = KVM_CAP_S390_USER_SIGP,
+}
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
new file mode 100644
index 0000000..60ec2d1
--- /dev/null
+++ b/kvm/src/lib.rs
@@ -0,0 +1,2177 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! A safe wrapper around the kernel's KVM interface.
+
+mod cap;
+
+use std::cmp::{min, Ordering};
+use std::collections::{BinaryHeap, HashMap};
+use std::fs::File;
+use std::mem::size_of;
+use std::os::raw::*;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::ptr::copy_nonoverlapping;
+
+use libc::sigset_t;
+use libc::{open, EINVAL, ENOENT, ENOSPC, O_CLOEXEC, O_RDWR};
+
+use kvm_sys::*;
+
+use msg_socket::MsgOnSocket;
+#[allow(unused_imports)]
+use sys_util::{
+ ioctl, ioctl_with_mut_ptr, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val,
+ pagesize, signal, warn, Error, EventFd, GuestAddress, GuestMemory, MemoryMapping,
+ MemoryMappingArena, Result,
+};
+
+pub use crate::cap::*;
+
+fn errno_result<T>() -> Result<T> {
+ Err(Error::last())
+}
+
+// Returns a `Vec<T>` with a size in ytes at least as large as `size_in_bytes`.
+fn vec_with_size_in_bytes<T: Default>(size_in_bytes: usize) -> Vec<T> {
+ let rounded_size = (size_in_bytes + size_of::<T>() - 1) / size_of::<T>();
+ let mut v = Vec::with_capacity(rounded_size);
+ for _ in 0..rounded_size {
+ v.push(T::default())
+ }
+ v
+}
+
+// The kvm API has many structs that resemble the following `Foo` structure:
+//
+// ```
+// #[repr(C)]
+// struct Foo {
+// some_data: u32
+// entries: __IncompleteArrayField<__u32>,
+// }
+// ```
+//
+// In order to allocate such a structure, `size_of::<Foo>()` would be too small because it would not
+// include any space for `entries`. To make the allocation large enough while still being aligned
+// for `Foo`, a `Vec<Foo>` is created. Only the first element of `Vec<Foo>` would actually be used
+// as a `Foo`. The remaining memory in the `Vec<Foo>` is for `entries`, which must be contiguous
+// with `Foo`. This function is used to make the `Vec<Foo>` with enough space for `count` entries.
+fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
+ let element_space = count * size_of::<F>();
+ let vec_size_bytes = size_of::<T>() + element_space;
+ vec_with_size_in_bytes(vec_size_bytes)
+}
+
+unsafe fn set_user_memory_region<F: AsRawFd>(
+ fd: &F,
+ slot: u32,
+ read_only: bool,
+ log_dirty_pages: bool,
+ guest_addr: u64,
+ memory_size: u64,
+ userspace_addr: *mut u8,
+) -> Result<()> {
+ let mut flags = if read_only { KVM_MEM_READONLY } else { 0 };
+ if log_dirty_pages {
+ flags |= KVM_MEM_LOG_DIRTY_PAGES;
+ }
+ let region = kvm_userspace_memory_region {
+ slot,
+ flags,
+ guest_phys_addr: guest_addr,
+ memory_size,
+ userspace_addr: userspace_addr as u64,
+ };
+
+ let ret = ioctl_with_ref(fd, KVM_SET_USER_MEMORY_REGION(), ®ion);
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+}
+
+/// Helper function to determine the size in bytes of a dirty log bitmap for the given memory region
+/// size.
+///
+/// # Arguments
+///
+/// * `size` - Number of bytes in the memory region being queried.
+pub fn dirty_log_bitmap_size(size: usize) -> usize {
+ let page_size = pagesize();
+ (((size + page_size - 1) / page_size) + 7) / 8
+}
+
+/// A wrapper around opening and using `/dev/kvm`.
+///
+/// Useful for querying extensions and basic values from the KVM backend. A `Kvm` is required to
+/// create a `Vm` object.
+pub struct Kvm {
+ kvm: File,
+}
+
+impl Kvm {
+ /// Opens `/dev/kvm/` and returns a Kvm object on success.
+ pub fn new() -> Result<Kvm> {
+ // Open calls are safe because we give a constant nul-terminated string and verify the
+ // result.
+ let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, O_RDWR | O_CLOEXEC) };
+ if ret < 0 {
+ return errno_result();
+ }
+ // Safe because we verify that ret is valid and we own the fd.
+ Ok(Kvm {
+ kvm: unsafe { File::from_raw_fd(ret) },
+ })
+ }
+
+ fn check_extension_int(&self, c: Cap) -> i32 {
+ // Safe because we know that our file is a KVM fd and that the extension is one of the ones
+ // defined by kernel.
+ unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) }
+ }
+
+ /// Checks if a particular `Cap` is available.
+ pub fn check_extension(&self, c: Cap) -> bool {
+ self.check_extension_int(c) == 1
+ }
+
+ /// Gets the size of the mmap required to use vcpu's `kvm_run` structure.
+ pub fn get_vcpu_mmap_size(&self) -> Result<usize> {
+ // Safe because we know that our file is a KVM fd and we verify the return result.
+ let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE() as c_ulong) };
+ if res > 0 {
+ Ok(res as usize)
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Gets the recommended maximum number of VCPUs per VM.
+ pub fn get_nr_vcpus(&self) -> u32 {
+ match self.check_extension_int(Cap::NrVcpus) {
+ 0 => 4, // according to api.txt
+ x if x > 0 => x as u32,
+ _ => {
+ warn!("kernel returned invalid number of VCPUs");
+ 4
+ }
+ }
+ }
+
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn get_cpuid(&self, kind: u64) -> Result<CpuId> {
+ const MAX_KVM_CPUID_ENTRIES: usize = 256;
+ let mut cpuid = CpuId::new(MAX_KVM_CPUID_ENTRIES);
+
+ let ret = unsafe {
+ // 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, which is set to the allocated
+ // size(MAX_KVM_CPUID_ENTRIES) above.
+ ioctl_with_mut_ptr(self, kind, cpuid.as_mut_ptr())
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ Ok(cpuid)
+ }
+
+ /// X86 specific call to get the system supported CPUID values
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_supported_cpuid(&self) -> Result<CpuId> {
+ self.get_cpuid(KVM_GET_SUPPORTED_CPUID())
+ }
+
+ /// X86 specific call to get the system emulated CPUID values
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_emulated_cpuid(&self) -> Result<CpuId> {
+ self.get_cpuid(KVM_GET_EMULATED_CPUID())
+ }
+
+ /// X86 specific call to get list of supported MSRS
+ ///
+ /// See the documentation for KVM_GET_MSR_INDEX_LIST.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_msr_index_list(&self) -> Result<Vec<u32>> {
+ const MAX_KVM_MSR_ENTRIES: usize = 256;
+
+ let mut msr_list = vec_with_array_field::<kvm_msr_list, u32>(MAX_KVM_MSR_ENTRIES);
+ msr_list[0].nmsrs = MAX_KVM_MSR_ENTRIES as u32;
+
+ let ret = unsafe {
+ // 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 nmsrs, which is set to the allocated
+ // size (MAX_KVM_MSR_ENTRIES) above.
+ ioctl_with_mut_ref(self, KVM_GET_MSR_INDEX_LIST(), &mut msr_list[0])
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ let mut nmsrs = msr_list[0].nmsrs;
+
+ // Mapping the unsized array to a slice is unsafe because the length isn't known. Using
+ // the length we originally allocated with eliminates the possibility of overflow.
+ let indices: &[u32] = unsafe {
+ if nmsrs > MAX_KVM_MSR_ENTRIES as u32 {
+ nmsrs = MAX_KVM_MSR_ENTRIES as u32;
+ }
+ msr_list[0].indices.as_slice(nmsrs as usize)
+ };
+
+ Ok(indices.to_vec())
+ }
+}
+
+impl AsRawFd for Kvm {
+ fn as_raw_fd(&self) -> RawFd {
+ self.kvm.as_raw_fd()
+ }
+}
+
+/// An address either in programmable I/O space or in memory mapped I/O space.
+#[derive(Copy, Clone, Debug, MsgOnSocket)]
+pub enum IoeventAddress {
+ Pio(u64),
+ Mmio(u64),
+}
+
+/// Used in `Vm::register_ioevent` to indicate a size and optionally value to match.
+pub enum Datamatch {
+ AnyLength,
+ U8(Option<u8>),
+ U16(Option<u16>),
+ U32(Option<u32>),
+ U64(Option<u64>),
+}
+
+/// A source of IRQs in an `IrqRoute`.
+pub enum IrqSource {
+ Irqchip { chip: u32, pin: u32 },
+ Msi { address: u64, data: u32 },
+}
+
+/// A single route for an IRQ.
+pub struct IrqRoute {
+ pub gsi: u32,
+ pub source: IrqSource,
+}
+
+/// Interrupt controller IDs
+pub enum PicId {
+ Primary = 0,
+ Secondary = 1,
+}
+
+/// Number of pins on the IOAPIC.
+pub const NUM_IOAPIC_PINS: usize = 24;
+
+// Used to invert the order when stored in a max-heap.
+#[derive(Copy, Clone, Eq, PartialEq)]
+struct MemSlot(u32);
+
+impl Ord for MemSlot {
+ fn cmp(&self, other: &MemSlot) -> 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 MemSlot {
+ fn partial_cmp(&self, other: &MemSlot) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+/// A wrapper around creating and using a VM.
+pub struct Vm {
+ vm: File,
+ guest_mem: GuestMemory,
+ device_memory: HashMap<u32, MemoryMapping>,
+ mmap_arenas: HashMap<u32, MemoryMappingArena>,
+ mem_slot_gaps: BinaryHeap<MemSlot>,
+}
+
+impl Vm {
+ /// Constructs a new `Vm` using the given `Kvm` instance.
+ pub fn new(kvm: &Kvm, guest_mem: GuestMemory) -> Result<Vm> {
+ // Safe because we know kvm is a real kvm fd as this module is the only one that can make
+ // Kvm objects.
+ 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) };
+ guest_mem.with_regions(|index, guest_addr, size, host_addr, _| {
+ unsafe {
+ // Safe because the guest regions are guaranteed not to overlap.
+ set_user_memory_region(
+ &vm_file,
+ index as u32,
+ false,
+ false,
+ guest_addr.offset() as u64,
+ size as u64,
+ host_addr as *mut u8,
+ )
+ }
+ })?;
+
+ Ok(Vm {
+ vm: vm_file,
+ guest_mem,
+ device_memory: HashMap::new(),
+ mmap_arenas: HashMap::new(),
+ mem_slot_gaps: BinaryHeap::new(),
+ })
+ } else {
+ errno_result()
+ }
+ }
+
+ // Helper method for `set_user_memory_region` that tracks available slots.
+ unsafe fn set_user_memory_region(
+ &mut self,
+ read_only: bool,
+ log_dirty_pages: bool,
+ guest_addr: u64,
+ memory_size: u64,
+ userspace_addr: *mut u8,
+ ) -> Result<u32> {
+ let slot = match self.mem_slot_gaps.pop() {
+ Some(gap) => gap.0,
+ None => {
+ (self.device_memory.len()
+ + self.guest_mem.num_regions() as usize
+ + self.mmap_arenas.len()) as u32
+ }
+ };
+
+ let res = set_user_memory_region(
+ &self.vm,
+ slot,
+ read_only,
+ log_dirty_pages,
+ guest_addr,
+ memory_size,
+ userspace_addr,
+ );
+ match res {
+ Ok(_) => Ok(slot),
+ Err(e) => {
+ self.mem_slot_gaps.push(MemSlot(slot));
+ Err(e)
+ }
+ }
+ }
+
+ // Helper method for `set_user_memory_region` that tracks available slots.
+ unsafe fn remove_user_memory_region(&mut self, slot: u32) -> Result<()> {
+ set_user_memory_region(&self.vm, slot, false, false, 0, 0, std::ptr::null_mut())?;
+ self.mem_slot_gaps.push(MemSlot(slot));
+ Ok(())
+ }
+
+ /// Checks if a particular `Cap` is available.
+ ///
+ /// This is distinct from the `Kvm` version of this method because the some extensions depend on
+ /// the particular `Vm` existence. This method is encouraged by the kernel because it more
+ /// accurately reflects the usable capabilities.
+ pub fn check_extension(&self, c: Cap) -> bool {
+ // Safe because we know that our file is a KVM fd and that the extension is one of the ones
+ // defined by kernel.
+ unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) == 1 }
+ }
+
+ /// Inserts the given `MemoryMapping` into the VM's address space at `guest_addr`.
+ ///
+ /// The slot that was assigned the device memory mapping is returned on success. The slot can be
+ /// given to `Vm::remove_device_memory` to remove the memory from the VM's address space and
+ /// take back ownership of `mem`.
+ ///
+ /// Note that memory inserted into the VM's address space must not overlap with any other memory
+ /// slot's region.
+ ///
+ /// If `read_only` is true, the guest will be able to read the memory as normal, but attempts to
+ /// write will trigger a mmio VM exit, leaving the memory untouched.
+ ///
+ /// If `log_dirty_pages` is true, the slot number can be used to retrieve the pages written to
+ /// by the guest with `get_dirty_log`.
+ pub fn add_device_memory(
+ &mut self,
+ guest_addr: GuestAddress,
+ mem: MemoryMapping,
+ read_only: bool,
+ log_dirty_pages: bool,
+ ) -> Result<u32> {
+ if guest_addr < self.guest_mem.end_addr() {
+ return Err(Error::new(ENOSPC));
+ }
+
+ // Safe because we check that the given guest address is valid and has no overlaps. We also
+ // know that the pointer and size are correct because the MemoryMapping interface ensures
+ // this. We take ownership of the memory mapping so that it won't be unmapped until the slot
+ // is removed.
+ let slot = unsafe {
+ self.set_user_memory_region(
+ read_only,
+ log_dirty_pages,
+ guest_addr.offset() as u64,
+ mem.size() as u64,
+ mem.as_ptr(),
+ )?
+ };
+ self.device_memory.insert(slot, mem);
+
+ Ok(slot)
+ }
+
+ /// Removes device memory that was previously added at the given slot.
+ ///
+ /// Ownership of the host memory mapping associated with the given slot is returned on success.
+ pub fn remove_device_memory(&mut self, slot: u32) -> Result<MemoryMapping> {
+ if self.device_memory.contains_key(&slot) {
+ // Safe because the slot is checked against the list of device memory slots.
+ unsafe {
+ self.remove_user_memory_region(slot)?;
+ }
+ // Safe to unwrap since map is checked to contain key
+ Ok(self.device_memory.remove(&slot).unwrap())
+ } else {
+ Err(Error::new(ENOENT))
+ }
+ }
+
+ /// Inserts the given `MemoryMappingArena` into the VM's address space at `guest_addr`.
+ ///
+ /// The slot that was assigned the device memory mapping is returned on success. The slot can be
+ /// given to `Vm::remove_mmap_arena` to remove the memory from the VM's address space and
+ /// take back ownership of `mmap_arena`.
+ ///
+ /// Note that memory inserted into the VM's address space must not overlap with any other memory
+ /// slot's region.
+ ///
+ /// If `read_only` is true, the guest will be able to read the memory as normal, but attempts to
+ /// write will trigger a mmio VM exit, leaving the memory untouched.
+ ///
+ /// If `log_dirty_pages` is true, the slot number can be used to retrieve the pages written to
+ /// by the guest with `get_dirty_log`.
+ pub fn add_mmap_arena(
+ &mut self,
+ guest_addr: GuestAddress,
+ mmap_arena: MemoryMappingArena,
+ read_only: bool,
+ log_dirty_pages: bool,
+ ) -> Result<u32> {
+ if guest_addr < self.guest_mem.end_addr() {
+ return Err(Error::new(ENOSPC));
+ }
+
+ // Safe because we check that the given guest address is valid and has no overlaps. We also
+ // know that the pointer and size are correct because the MemoryMapping interface ensures
+ // this. We take ownership of the memory mapping so that it won't be unmapped until the slot
+ // is removed.
+ let slot = unsafe {
+ self.set_user_memory_region(
+ read_only,
+ log_dirty_pages,
+ guest_addr.offset() as u64,
+ mmap_arena.size() as u64,
+ mmap_arena.as_ptr(),
+ )?
+ };
+ self.mmap_arenas.insert(slot, mmap_arena);
+
+ Ok(slot)
+ }
+
+ /// Removes memory map arena that was previously added at the given slot.
+ ///
+ /// Ownership of the host memory mapping associated with the given slot is returned on success.
+ pub fn remove_mmap_arena(&mut self, slot: u32) -> Result<MemoryMappingArena> {
+ if self.mmap_arenas.contains_key(&slot) {
+ // Safe because the slot is checked against the list of device memory slots.
+ unsafe {
+ self.remove_user_memory_region(slot)?;
+ }
+ // Safe to unwrap since map is checked to contain key
+ Ok(self.mmap_arenas.remove(&slot).unwrap())
+ } else {
+ Err(Error::new(ENOENT))
+ }
+ }
+
+ /// Get a mutable reference to the memory map arena added at the given slot.
+ pub fn get_mmap_arena(&mut self, slot: u32) -> Option<&mut MemoryMappingArena> {
+ self.mmap_arenas.get_mut(&slot)
+ }
+
+ /// Gets the bitmap of dirty pages since the last call to `get_dirty_log` for the memory at
+ /// `slot`.
+ ///
+ /// The size of `dirty_log` must be at least as many bits as there are pages in the memory
+ /// region `slot` represents. For example, if the size of `slot` is 16 pages, `dirty_log` must
+ /// be 2 bytes or greater.
+ pub fn get_dirty_log(&self, slot: u32, dirty_log: &mut [u8]) -> Result<()> {
+ match self.device_memory.get(&slot) {
+ Some(mmap) => {
+ // Ensures that there are as many bytes in dirty_log as there are pages in the mmap.
+ if dirty_log_bitmap_size(mmap.size()) > dirty_log.len() {
+ return Err(Error::new(EINVAL));
+ }
+ let mut dirty_log_kvm = kvm_dirty_log {
+ slot,
+ ..Default::default()
+ };
+ dirty_log_kvm.__bindgen_anon_1.dirty_bitmap = dirty_log.as_ptr() as *mut c_void;
+ // Safe because the `dirty_bitmap` pointer assigned above is guaranteed to be valid
+ // (because it's from a slice) and we checked that it will be large enough to hold
+ // the entire log.
+ let ret = unsafe { ioctl_with_ref(self, KVM_GET_DIRTY_LOG(), &dirty_log_kvm) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+ _ => Err(Error::new(ENOENT)),
+ }
+ }
+
+ /// Gets a reference to the guest memory owned by this VM.
+ ///
+ /// Note that `GuestMemory` does not include any device memory that may have been added after
+ /// this VM was constructed.
+ pub fn get_memory(&self) -> &GuestMemory {
+ &self.guest_mem
+ }
+
+ /// Sets the address of the three-page region in the VM's address space.
+ ///
+ /// See the documentation on the KVM_SET_TSS_ADDR ioctl.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_tss_addr(&self, addr: GuestAddress) -> Result<()> {
+ // Safe because we know that our file is a VM fd and we verify the return result.
+ let ret = unsafe { ioctl_with_val(self, KVM_SET_TSS_ADDR(), addr.offset() as u64) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Sets the address of a one-page region in the VM's address space.
+ ///
+ /// See the documentation on the KVM_SET_IDENTITY_MAP_ADDR ioctl.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_identity_map_addr(&self, addr: GuestAddress) -> Result<()> {
+ // Safe because we know that our file is a VM fd and we verify the return result.
+ let ret =
+ unsafe { ioctl_with_ref(self, KVM_SET_IDENTITY_MAP_ADDR(), &(addr.offset() as u64)) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Retrieves the current timestamp of kvmclock as seen by the current guest.
+ ///
+ /// See the documentation on the KVM_GET_CLOCK ioctl.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_clock(&self) -> Result<kvm_clock_data> {
+ // Safe because we know that our file is a VM fd, we know the kernel will only write
+ // correct amount of memory to our pointer, and we verify the return result.
+ let mut clock_data = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_CLOCK(), &mut clock_data) };
+ if ret == 0 {
+ Ok(clock_data)
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Sets the current timestamp of kvmclock to the specified value.
+ ///
+ /// See the documentation on the KVM_SET_CLOCK ioctl.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_clock(&self, clock_data: &kvm_clock_data) -> Result<()> {
+ // Safe because we know that our file is a VM fd, we know the kernel will only read
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_SET_CLOCK(), clock_data) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Crates an in kernel interrupt controller.
+ ///
+ /// See the documentation on the KVM_CREATE_IRQCHIP ioctl.
+ #[cfg(any(
+ target_arch = "x86",
+ target_arch = "x86_64",
+ target_arch = "arm",
+ target_arch = "aarch64"
+ ))]
+ pub fn create_irq_chip(&self) -> Result<()> {
+ // Safe because we know that our file is a VM fd and we verify the return result.
+ let ret = unsafe { ioctl(self, KVM_CREATE_IRQCHIP()) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Retrieves the state of given interrupt controller by issuing KVM_GET_IRQCHIP ioctl.
+ ///
+ /// Note that this call can only succeed after a call to `Vm::create_irq_chip`.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_pic_state(&self, id: PicId) -> Result<kvm_pic_state> {
+ let mut irqchip_state = kvm_irqchip::default();
+ irqchip_state.chip_id = id as u32;
+ let ret = unsafe {
+ // Safe because we know our file is a VM fd, we know the kernel will only write
+ // correct amount of memory to our pointer, and we verify the return result.
+ ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), &mut irqchip_state)
+ };
+ if ret == 0 {
+ Ok(unsafe {
+ // Safe as we know that we are retrieving data related to the
+ // PIC (primary or secondary) and not IOAPIC.
+ irqchip_state.chip.pic
+ })
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Sets the state of given interrupt controller by issuing KVM_SET_IRQCHIP ioctl.
+ ///
+ /// Note that this call can only succeed after a call to `Vm::create_irq_chip`.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_pic_state(&self, id: PicId, state: &kvm_pic_state) -> Result<()> {
+ let mut irqchip_state = kvm_irqchip::default();
+ irqchip_state.chip_id = id as u32;
+ irqchip_state.chip.pic = *state;
+ // Safe because we know that our file is a VM fd, we know the kernel will only read
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_SET_IRQCHIP(), &irqchip_state) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Retrieves the state of IOAPIC by issuing KVM_GET_IRQCHIP ioctl.
+ ///
+ /// Note that this call can only succeed after a call to `Vm::create_irq_chip`.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_ioapic_state(&self) -> Result<kvm_ioapic_state> {
+ let mut irqchip_state = kvm_irqchip::default();
+ irqchip_state.chip_id = 2;
+ let ret = unsafe {
+ // Safe because we know our file is a VM fd, we know the kernel will only write
+ // correct amount of memory to our pointer, and we verify the return result.
+ ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), &mut irqchip_state)
+ };
+ if ret == 0 {
+ Ok(unsafe {
+ // Safe as we know that we are retrieving data related to the
+ // IOAPIC and not PIC.
+ irqchip_state.chip.ioapic
+ })
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Sets the state of IOAPIC by issuing KVM_SET_IRQCHIP ioctl.
+ ///
+ /// Note that this call can only succeed after a call to `Vm::create_irq_chip`.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_ioapic_state(&self, state: &kvm_ioapic_state) -> Result<()> {
+ let mut irqchip_state = kvm_irqchip::default();
+ irqchip_state.chip_id = 2;
+ irqchip_state.chip.ioapic = *state;
+ // Safe because we know that our file is a VM fd, we know the kernel will only read
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_SET_IRQCHIP(), &irqchip_state) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Sets the level on the given irq to 1 if `active` is true, and 0 otherwise.
+ #[cfg(any(
+ target_arch = "x86",
+ target_arch = "x86_64",
+ target_arch = "arm",
+ target_arch = "aarch64"
+ ))]
+ pub fn set_irq_line(&self, irq: u32, active: bool) -> Result<()> {
+ let mut irq_level = kvm_irq_level::default();
+ irq_level.__bindgen_anon_1.irq = irq;
+ irq_level.level = if active { 1 } else { 0 };
+
+ // Safe because we know that our file is a VM fd, we know the kernel will only read the
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_IRQ_LINE(), &irq_level) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Creates a PIT as per the KVM_CREATE_PIT2 ioctl.
+ ///
+ /// Note that this call can only succeed after a call to `Vm::create_irq_chip`.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn create_pit(&self) -> Result<()> {
+ let pit_config = kvm_pit_config::default();
+ // Safe because we know that our file is a VM fd, we know the kernel will only read the
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_PIT2(), &pit_config) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Retrieves the state of PIT by issuing KVM_GET_PIT2 ioctl.
+ ///
+ /// Note that this call can only succeed after a call to `Vm::create_pit`.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_pit_state(&self) -> Result<kvm_pit_state2> {
+ // Safe because we know that our file is a VM fd, we know the kernel will only write
+ // correct amount of memory to our pointer, and we verify the return result.
+ let mut pit_state = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_PIT2(), &mut pit_state) };
+ if ret == 0 {
+ Ok(pit_state)
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Sets the state of PIT by issuing KVM_SET_PIT2 ioctl.
+ ///
+ /// Note that this call can only succeed after a call to `Vm::create_pit`.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_pit_state(&self, pit_state: &kvm_pit_state2) -> Result<()> {
+ // Safe because we know that our file is a VM fd, we know the kernel will only read
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_SET_PIT2(), pit_state) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Registers an event to be signaled whenever a certain address is written to.
+ ///
+ /// The `datamatch` parameter can be used to limit signaling `evt` to only the cases where the
+ /// value being written is equal to `datamatch`. Note that the size of `datamatch` is important
+ /// and must match the expected size of the guest's write.
+ ///
+ /// In all cases where `evt` is signaled, the ordinary vmexit to userspace that would be
+ /// triggered is prevented.
+ pub fn register_ioevent(
+ &self,
+ evt: &EventFd,
+ addr: IoeventAddress,
+ datamatch: Datamatch,
+ ) -> Result<()> {
+ self.ioeventfd(evt, addr, datamatch, false)
+ }
+
+ /// Unregisters an event previously registered with `register_ioevent`.
+ ///
+ /// The `evt`, `addr`, and `datamatch` set must be the same as the ones passed into
+ /// `register_ioevent`.
+ pub fn unregister_ioevent(
+ &self,
+ evt: &EventFd,
+ addr: IoeventAddress,
+ datamatch: Datamatch,
+ ) -> Result<()> {
+ self.ioeventfd(evt, addr, datamatch, true)
+ }
+
+ fn ioeventfd(
+ &self,
+ evt: &EventFd,
+ addr: IoeventAddress,
+ datamatch: Datamatch,
+ deassign: bool,
+ ) -> Result<()> {
+ let (do_datamatch, datamatch_value, datamatch_len) = match datamatch {
+ Datamatch::AnyLength => (false, 0, 0),
+ Datamatch::U8(v) => match v {
+ Some(u) => (true, u as u64, 1),
+ None => (false, 0, 1),
+ },
+ Datamatch::U16(v) => match v {
+ Some(u) => (true, u as u64, 2),
+ None => (false, 0, 2),
+ },
+ Datamatch::U32(v) => match v {
+ Some(u) => (true, u as u64, 4),
+ None => (false, 0, 4),
+ },
+ Datamatch::U64(v) => match v {
+ Some(u) => (true, u as u64, 8),
+ None => (false, 0, 8),
+ },
+ };
+ let mut flags = 0;
+ if deassign {
+ flags |= 1 << kvm_ioeventfd_flag_nr_deassign;
+ }
+ if do_datamatch {
+ flags |= 1 << kvm_ioeventfd_flag_nr_datamatch
+ }
+ if let IoeventAddress::Pio(_) = addr {
+ flags |= 1 << kvm_ioeventfd_flag_nr_pio;
+ }
+ let ioeventfd = kvm_ioeventfd {
+ datamatch: datamatch_value,
+ len: datamatch_len,
+ addr: match addr {
+ IoeventAddress::Pio(p) => p as u64,
+ IoeventAddress::Mmio(m) => m,
+ },
+ fd: evt.as_raw_fd(),
+ flags,
+ ..Default::default()
+ };
+ // Safe because we know that our file is a VM fd, we know the kernel will only read the
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Registers an event that will, when signalled, trigger the `gsi` irq.
+ #[cfg(any(
+ target_arch = "x86",
+ target_arch = "x86_64",
+ target_arch = "arm",
+ target_arch = "aarch64"
+ ))]
+ pub fn register_irqfd(&self, evt: &EventFd, gsi: u32) -> Result<()> {
+ let irqfd = kvm_irqfd {
+ fd: evt.as_raw_fd() as u32,
+ gsi,
+ ..Default::default()
+ };
+ // Safe because we know that our file is a VM fd, we know the kernel will only read the
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Registers an event that will, when signalled, trigger the `gsi` irq, and `resample_evt` will
+ /// get triggered when the irqchip is resampled.
+ #[cfg(any(
+ target_arch = "x86",
+ target_arch = "x86_64",
+ target_arch = "arm",
+ target_arch = "aarch64"
+ ))]
+ pub fn register_irqfd_resample(
+ &self,
+ evt: &EventFd,
+ resample_evt: &EventFd,
+ gsi: u32,
+ ) -> 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,
+ gsi,
+ ..Default::default()
+ };
+ // Safe because we know that our file is a VM fd, we know the kernel will only read the
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Unregisters an event that was previously registered with
+ /// `register_irqfd`/`register_irqfd_resample`.
+ ///
+ /// The `evt` and `gsi` pair must be the same as the ones passed into
+ /// `register_irqfd`/`register_irqfd_resample`.
+ #[cfg(any(
+ target_arch = "x86",
+ target_arch = "x86_64",
+ target_arch = "arm",
+ target_arch = "aarch64"
+ ))]
+ pub fn unregister_irqfd(&self, evt: &EventFd, gsi: u32) -> Result<()> {
+ let irqfd = kvm_irqfd {
+ fd: evt.as_raw_fd() as u32,
+ gsi,
+ flags: KVM_IRQFD_FLAG_DEASSIGN,
+ ..Default::default()
+ };
+ // Safe because we know that our file is a VM fd, we know the kernel will only read the
+ // correct amount of memory from our pointer, and we verify the return result.
+ let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Sets the GSI routing table, replacing any table set with previous calls to
+ /// `set_gsi_routing`.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_gsi_routing(&self, routes: &[IrqRoute]) -> Result<()> {
+ let mut irq_routing =
+ vec_with_array_field::<kvm_irq_routing, kvm_irq_routing_entry>(routes.len());
+ irq_routing[0].nr = routes.len() as u32;
+
+ // Safe because we ensured there is enough space in irq_routing to hold the number of
+ // route entries.
+ let irq_routes = unsafe { irq_routing[0].entries.as_mut_slice(routes.len()) };
+ for (route, irq_route) in routes.iter().zip(irq_routes.iter_mut()) {
+ irq_route.gsi = route.gsi;
+ match route.source {
+ IrqSource::Irqchip { chip, pin } => {
+ irq_route.type_ = KVM_IRQ_ROUTING_IRQCHIP;
+ irq_route.u.irqchip = kvm_irq_routing_irqchip { irqchip: chip, pin }
+ }
+ IrqSource::Msi { address, data } => {
+ irq_route.type_ = KVM_IRQ_ROUTING_MSI;
+ irq_route.u.msi = kvm_irq_routing_msi {
+ address_lo: address as u32,
+ address_hi: (address >> 32) as u32,
+ data,
+ ..Default::default()
+ }
+ }
+ }
+ }
+
+ let ret = unsafe { ioctl_with_ref(self, KVM_SET_GSI_ROUTING(), &irq_routing[0]) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Does KVM_CREATE_DEVICE for a generic device.
+ pub fn create_device(&self, device: &mut kvm_create_device) -> Result<()> {
+ let ret = unsafe { sys_util::ioctl_with_ref(self, KVM_CREATE_DEVICE(), device) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ errno_result()
+ }
+ }
+
+ /// This queries the kernel for the preferred target CPU type.
+ #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+ pub fn arm_preferred_target(&self, kvi: &mut kvm_vcpu_init) -> Result<()> {
+ // The ioctl is safe because we allocated the struct and we know the
+ // kernel will write exactly the size of the struct.
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_ARM_PREFERRED_TARGET(), kvi) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Enable the specified capability.
+ /// See documentation for KVM_ENABLE_CAP.
+ pub fn kvm_enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> {
+ // safe becuase we allocated the struct and we know the kernel will read
+ // exactly the size of the struct
+ let ret = unsafe { ioctl_with_ref(self, KVM_ENABLE_CAP(), cap) };
+ if ret < 0 {
+ errno_result()
+ } else {
+ Ok(())
+ }
+ }
+
+ /// (x86-only): Enable support for split-irqchip.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn enable_split_irqchip(&self) -> Result<()> {
+ let mut cap: kvm_enable_cap = Default::default();
+ cap.cap = KVM_CAP_SPLIT_IRQCHIP;
+ cap.args[0] = NUM_IOAPIC_PINS as u64;
+ self.kvm_enable_cap(&cap)
+ }
+
+ /// Request that the kernel inject the specified MSI message.
+ /// Returns Ok(true) on delivery, Ok(false) if the guest blocked delivery, or an error.
+ /// See kernel documentation for KVM_SIGNAL_MSI.
+ pub fn signal_msi(&self, msi: &kvm_msi) -> Result<bool> {
+ // safe becuase we allocated the struct and we know the kernel will read
+ // exactly the size of the struct
+ let ret = unsafe { ioctl_with_ref(self, KVM_SIGNAL_MSI(), msi) };
+ if ret < 0 {
+ errno_result()
+ } else {
+ Ok(ret > 0)
+ }
+ }
+}
+
+impl AsRawFd for Vm {
+ fn as_raw_fd(&self) -> RawFd {
+ self.vm.as_raw_fd()
+ }
+}
+
+/// A reason why a VCPU exited. One of these returns every time `Vcpu::run` is called.
+#[derive(Debug)]
+pub enum VcpuExit {
+ /// An out port instruction was run on the given port with the given data.
+ IoOut {
+ port: u16,
+ size: usize,
+ data: [u8; 8],
+ },
+ /// An in port instruction was run on the given port.
+ ///
+ /// The date that the instruction receives should be set with `set_data` before `Vcpu::run` is
+ /// called again.
+ IoIn {
+ port: u16,
+ size: usize,
+ },
+ /// A read instruction was run against the given MMIO address.
+ ///
+ /// The date that the instruction receives should be set with `set_data` before `Vcpu::run` is
+ /// called again.
+ MmioRead {
+ address: u64,
+ size: usize,
+ },
+ /// A write instruction was run against the given MMIO address with the given data.
+ MmioWrite {
+ address: u64,
+ size: usize,
+ data: [u8; 8],
+ },
+ Unknown,
+ Exception,
+ Hypercall,
+ Debug,
+ Hlt,
+ IrqWindowOpen,
+ Shutdown,
+ FailEntry,
+ Intr,
+ SetTpr,
+ TprAccess,
+ S390Sieic,
+ S390Reset,
+ Dcr,
+ Nmi,
+ InternalError,
+ Osi,
+ PaprHcall,
+ S390Ucontrol,
+ Watchdog,
+ S390Tsch,
+ Epr,
+ /// The cpu triggered a system level event which is specified by the type field.
+ /// The first field is the event type and the second field is flags.
+ /// The possible event types are shutdown, reset, or crash. So far there
+ /// are not any flags defined.
+ SystemEvent(u32 /* event_type */, u64 /* flags */),
+}
+
+/// A wrapper around creating and using a VCPU.
+pub struct Vcpu {
+ vcpu: File,
+ run_mmap: MemoryMapping,
+ guest_mem: GuestMemory,
+}
+
+impl Vcpu {
+ /// Constructs a new VCPU for `vm`.
+ ///
+ /// The `id` argument is the CPU number between [0, max vcpus).
+ pub fn new(id: c_ulong, kvm: &Kvm, vm: &Vm) -> Result<Vcpu> {
+ let run_mmap_size = kvm.get_vcpu_mmap_size()?;
+
+ // Safe because we know that vm a VM fd and we verify the return result.
+ let vcpu_fd = unsafe { ioctl_with_val(vm, KVM_CREATE_VCPU(), id) };
+ if vcpu_fd < 0 {
+ return errno_result();
+ }
+
+ // 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 run_mmap =
+ MemoryMapping::from_fd(&vcpu, run_mmap_size).map_err(|_| Error::new(ENOSPC))?;
+
+ let guest_mem = vm.guest_mem.clone();
+
+ Ok(Vcpu {
+ vcpu,
+ run_mmap,
+ guest_mem,
+ })
+ }
+
+ /// Gets a reference to the guest memory owned by this VM of this VCPU.
+ ///
+ /// Note that `GuestMemory` does not include any device memory that may have been added after
+ /// this VM was constructed.
+ pub fn get_memory(&self) -> &GuestMemory {
+ &self.guest_mem
+ }
+
+ /// Sets the data received by an mmio or ioport read/in instruction.
+ ///
+ /// This function should be called after `Vcpu::run` returns an `VcpuExit::IoIn` or
+ /// `Vcpu::MmioRead`.
+ #[allow(clippy::cast_ptr_alignment)]
+ pub fn set_data(&self, data: &[u8]) -> Result<()> {
+ // 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) };
+ match run.exit_reason {
+ KVM_EXIT_IO => {
+ let run_start = run as *mut kvm_run as *mut u8;
+ // Safe because the exit_reason (which comes from the kernel) told us which
+ // union field to use.
+ let io = unsafe { run.__bindgen_anon_1.io };
+ if io.direction as u32 != KVM_EXIT_IO_IN {
+ return Err(Error::new(EINVAL));
+ }
+ let data_size = (io.count as usize) * (io.size as usize);
+ if data_size != data.len() {
+ return Err(Error::new(EINVAL));
+ }
+ // The data_offset is defined by the kernel to be some number of bytes into the
+ // kvm_run structure, which we have fully mmap'd.
+ unsafe {
+ let data_ptr = run_start.offset(io.data_offset as isize);
+ copy_nonoverlapping(data.as_ptr(), data_ptr, data_size);
+ }
+ Ok(())
+ }
+ KVM_EXIT_MMIO => {
+ // Safe because the exit_reason (which comes from the kernel) told us which
+ // union field to use.
+ let mmio = unsafe { &mut run.__bindgen_anon_1.mmio };
+ if mmio.is_write != 0 {
+ return Err(Error::new(EINVAL));
+ }
+ let len = mmio.len as usize;
+ if len != data.len() {
+ return Err(Error::new(EINVAL));
+ }
+ mmio.data[..len].copy_from_slice(data);
+ Ok(())
+ }
+ _ => Err(Error::new(EINVAL)),
+ }
+ }
+
+ /// Runs the VCPU until it exits, returning the reason.
+ ///
+ /// Note that the state of the VCPU and associated VM must be setup first for this to do
+ /// anything useful.
+ #[allow(clippy::cast_ptr_alignment)]
+ // The pointer is page aligned so casting to a different type is well defined, hence the clippy
+ // allow attribute.
+ pub fn run(&self) -> Result<VcpuExit> {
+ // Safe because we know that our file is a VCPU fd and we verify the return result.
+ let ret = unsafe { ioctl(self, KVM_RUN()) };
+ if ret == 0 {
+ // Safe because we know we mapped enough memory to hold the kvm_run struct because the
+ // kernel told us how large it was.
+ let run = unsafe { &*(self.run_mmap.as_ptr() as *const kvm_run) };
+ match run.exit_reason {
+ KVM_EXIT_IO => {
+ // Safe because the exit_reason (which comes from the kernel) told us which
+ // union field to use.
+ let io = unsafe { run.__bindgen_anon_1.io };
+ let port = io.port;
+ let size = (io.count as usize) * (io.size as usize);
+ match io.direction as u32 {
+ KVM_EXIT_IO_IN => Ok(VcpuExit::IoIn { port, size }),
+ KVM_EXIT_IO_OUT => {
+ let mut data = [0; 8];
+ let run_start = run as *const kvm_run as *const u8;
+ // The data_offset is defined by the kernel to be some number of bytes
+ // into the kvm_run structure, which we have fully mmap'd.
+ unsafe {
+ let data_ptr = run_start.offset(io.data_offset as isize);
+ copy_nonoverlapping(
+ data_ptr,
+ data.as_mut_ptr(),
+ min(size, data.len()),
+ );
+ }
+ Ok(VcpuExit::IoOut { port, size, data })
+ }
+ _ => Err(Error::new(EINVAL)),
+ }
+ }
+ KVM_EXIT_MMIO => {
+ // Safe because the exit_reason (which comes from the kernel) told us which
+ // union field to use.
+ let mmio = unsafe { &run.__bindgen_anon_1.mmio };
+ let address = mmio.phys_addr;
+ let size = min(mmio.len as usize, mmio.data.len());
+ if mmio.is_write != 0 {
+ Ok(VcpuExit::MmioWrite {
+ address,
+ size,
+ data: mmio.data,
+ })
+ } else {
+ Ok(VcpuExit::MmioRead { address, size })
+ }
+ }
+ KVM_EXIT_UNKNOWN => Ok(VcpuExit::Unknown),
+ KVM_EXIT_EXCEPTION => Ok(VcpuExit::Exception),
+ KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall),
+ KVM_EXIT_DEBUG => Ok(VcpuExit::Debug),
+ KVM_EXIT_HLT => Ok(VcpuExit::Hlt),
+ KVM_EXIT_IRQ_WINDOW_OPEN => Ok(VcpuExit::IrqWindowOpen),
+ KVM_EXIT_SHUTDOWN => Ok(VcpuExit::Shutdown),
+ KVM_EXIT_FAIL_ENTRY => Ok(VcpuExit::FailEntry),
+ KVM_EXIT_INTR => Ok(VcpuExit::Intr),
+ KVM_EXIT_SET_TPR => Ok(VcpuExit::SetTpr),
+ KVM_EXIT_TPR_ACCESS => Ok(VcpuExit::TprAccess),
+ KVM_EXIT_S390_SIEIC => Ok(VcpuExit::S390Sieic),
+ KVM_EXIT_S390_RESET => Ok(VcpuExit::S390Reset),
+ KVM_EXIT_DCR => Ok(VcpuExit::Dcr),
+ KVM_EXIT_NMI => Ok(VcpuExit::Nmi),
+ KVM_EXIT_INTERNAL_ERROR => Ok(VcpuExit::InternalError),
+ KVM_EXIT_OSI => Ok(VcpuExit::Osi),
+ KVM_EXIT_PAPR_HCALL => Ok(VcpuExit::PaprHcall),
+ KVM_EXIT_S390_UCONTROL => Ok(VcpuExit::S390Ucontrol),
+ KVM_EXIT_WATCHDOG => Ok(VcpuExit::Watchdog),
+ KVM_EXIT_S390_TSCH => Ok(VcpuExit::S390Tsch),
+ KVM_EXIT_EPR => Ok(VcpuExit::Epr),
+ KVM_EXIT_SYSTEM_EVENT => {
+ // Safe because we know the exit reason told us this union
+ // field is valid
+ let event_type = unsafe { run.__bindgen_anon_1.system_event.type_ };
+ let event_flags = unsafe { run.__bindgen_anon_1.system_event.flags };
+ Ok(VcpuExit::SystemEvent(event_type, event_flags))
+ }
+ r => panic!("unknown kvm exit reason: {}", r),
+ }
+ } else {
+ errno_result()
+ }
+ }
+
+ /// Gets the VCPU registers.
+ #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
+ pub fn get_regs(&self) -> Result<kvm_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.
+ let mut regs = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REGS(), &mut regs) };
+ if ret != 0 {
+ return errno_result();
+ }
+ Ok(regs)
+ }
+
+ /// Sets the VCPU registers.
+ #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
+ pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> {
+ // 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.
+ let ret = unsafe { ioctl_with_ref(self, KVM_SET_REGS(), regs) };
+ if ret != 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Gets the VCPU special registers.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_sregs(&self) -> Result<kvm_sregs> {
+ // Safe because we know that our file is a VCPU fd, we know the kernel will only write the
+ // correct amount of memory to our pointer, and we verify the return result.
+ let mut regs = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_SREGS(), &mut regs) };
+ if ret != 0 {
+ return errno_result();
+ }
+ Ok(regs)
+ }
+
+ /// Sets the VCPU special registers.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> {
+ // 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.
+ let ret = unsafe { ioctl_with_ref(self, KVM_SET_SREGS(), sregs) };
+ if ret != 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Gets the VCPU FPU registers.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_fpu(&self) -> Result<kvm_fpu> {
+ // Safe because we know that our file is a VCPU fd, we know the kernel will only write the
+ // correct amount of memory to our pointer, and we verify the return result.
+ let mut regs = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_FPU(), &mut regs) };
+ if ret != 0 {
+ return errno_result();
+ }
+ Ok(regs)
+ }
+
+ /// X86 specific call to setup the FPU
+ ///
+ /// See the documentation for KVM_SET_FPU.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> {
+ let ret = unsafe {
+ // Here we trust the kernel not to read past the end of the kvm_fpu struct.
+ ioctl_with_ref(self, KVM_SET_FPU(), fpu)
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Gets the VCPU debug registers.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_debugregs(&self) -> Result<kvm_debugregs> {
+ // Safe because we know that our file is a VCPU fd, we know the kernel will only write the
+ // correct amount of memory to our pointer, and we verify the return result.
+ let mut regs = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEBUGREGS(), &mut regs) };
+ if ret != 0 {
+ return errno_result();
+ }
+ Ok(regs)
+ }
+
+ /// Sets the VCPU debug registers
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_debugregs(&self, dregs: &kvm_debugregs) -> Result<()> {
+ let ret = unsafe {
+ // Here we trust the kernel not to read past the end of the kvm_fpu struct.
+ ioctl_with_ref(self, KVM_SET_DEBUGREGS(), dregs)
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Gets the VCPU extended control registers
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_xcrs(&self) -> Result<kvm_xcrs> {
+ // Safe because we know that our file is a VCPU fd, we know the kernel will only write the
+ // correct amount of memory to our pointer, and we verify the return result.
+ let mut regs = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_XCRS(), &mut regs) };
+ if ret != 0 {
+ return errno_result();
+ }
+ Ok(regs)
+ }
+
+ /// Sets the VCPU extended control registers
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_xcrs(&self, xcrs: &kvm_xcrs) -> Result<()> {
+ let ret = unsafe {
+ // Here we trust the kernel not to read past the end of the kvm_xcrs struct.
+ ioctl_with_ref(self, KVM_SET_XCRS(), xcrs)
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// X86 specific call to get the MSRS
+ ///
+ /// See the documentation for KVM_SET_MSRS.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_msrs(&self, msr_entries: &mut Vec<kvm_msr_entry>) -> Result<()> {
+ let mut msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(msr_entries.len());
+ unsafe {
+ // Mapping the unsized array to a slice is unsafe because the length isn't known.
+ // Providing the length used to create the struct guarantees the entire slice is valid.
+ let entries: &mut [kvm_msr_entry] = msrs[0].entries.as_mut_slice(msr_entries.len());
+ entries.copy_from_slice(&msr_entries);
+ }
+ msrs[0].nmsrs = msr_entries.len() as u32;
+ let ret = unsafe {
+ // Here we trust the kernel not to read or write past the end of the kvm_msrs struct.
+ ioctl_with_ref(self, KVM_GET_MSRS(), &msrs[0])
+ };
+ if ret < 0 {
+ // KVM_SET_MSRS actually returns the number of msr entries written.
+ return errno_result();
+ }
+ unsafe {
+ let count = ret as usize;
+ assert!(count <= msr_entries.len());
+ let entries: &mut [kvm_msr_entry] = msrs[0].entries.as_mut_slice(count);
+ msr_entries.truncate(count);
+ msr_entries.copy_from_slice(&entries);
+ }
+ Ok(())
+ }
+
+ /// X86 specific call to setup the MSRS
+ ///
+ /// See the documentation for KVM_SET_MSRS.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_msrs(&self, msrs: &kvm_msrs) -> Result<()> {
+ let ret = unsafe {
+ // Here we trust the kernel not to read past the end of the kvm_msrs struct.
+ ioctl_with_ref(self, KVM_SET_MSRS(), msrs)
+ };
+ if ret < 0 {
+ // KVM_SET_MSRS actually returns the number of msr entries written.
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// X86 specific call to setup the CPUID registers
+ ///
+ /// See the documentation for KVM_SET_CPUID2.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> {
+ let ret = unsafe {
+ // Here we trust the kernel not to read past the end of the kvm_msrs struct.
+ ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_ptr())
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// X86 specific call to get the state of the "Local Advanced Programmable Interrupt Controller".
+ ///
+ /// See the documentation for KVM_GET_LAPIC.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_lapic(&self) -> Result<kvm_lapic_state> {
+ let mut klapic: kvm_lapic_state = Default::default();
+
+ let ret = unsafe {
+ // The ioctl is unsafe unless you trust the kernel not to write past the end of the
+ // local_apic struct.
+ ioctl_with_mut_ref(self, KVM_GET_LAPIC(), &mut klapic)
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(klapic)
+ }
+
+ /// X86 specific call to set the state of the "Local Advanced Programmable Interrupt Controller".
+ ///
+ /// See the documentation for KVM_SET_LAPIC.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> {
+ let ret = unsafe {
+ // The ioctl is safe because the kernel will only read from the klapic struct.
+ ioctl_with_ref(self, KVM_SET_LAPIC(), klapic)
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Gets the vcpu's current "multiprocessing state".
+ ///
+ /// See the documentation for KVM_GET_MP_STATE. This call can only succeed after
+ /// a call to `Vm::create_irq_chip`.
+ ///
+ /// Note that KVM defines the call for both x86 and s390 but we do not expect anyone
+ /// to run crosvm on s390.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_mp_state(&self) -> Result<kvm_mp_state> {
+ // Safe because we know that our file is a VCPU fd, we know the kernel will only
+ // write correct amount of memory to our pointer, and we verify the return result.
+ let mut state: kvm_mp_state = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_MP_STATE(), &mut state) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(state)
+ }
+
+ /// Sets the vcpu's current "multiprocessing state".
+ ///
+ /// See the documentation for KVM_SET_MP_STATE. This call can only succeed after
+ /// a call to `Vm::create_irq_chip`.
+ ///
+ /// Note that KVM defines the call for both x86 and s390 but we do not expect anyone
+ /// to run crosvm on s390.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_mp_state(&self, state: &kvm_mp_state) -> Result<()> {
+ let ret = unsafe {
+ // The ioctl is safe because the kernel will only read from the kvm_mp_state struct.
+ ioctl_with_ref(self, KVM_SET_MP_STATE(), state)
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Gets the vcpu's currently pending exceptions, interrupts, NMIs, etc
+ ///
+ /// See the documentation for KVM_GET_VCPU_EVENTS.
+ ///
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn get_vcpu_events(&self) -> Result<kvm_vcpu_events> {
+ // Safe because we know that our file is a VCPU fd, we know the kernel
+ // will only write correct amount of memory to our pointer, and we
+ // verify the return result.
+ let mut events: kvm_vcpu_events = unsafe { std::mem::zeroed() };
+ let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_VCPU_EVENTS(), &mut events) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(events)
+ }
+
+ /// Sets the vcpu's currently pending exceptions, interrupts, NMIs, etc
+ ///
+ /// See the documentation for KVM_SET_VCPU_EVENTS.
+ ///
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn set_vcpu_events(&self, events: &kvm_vcpu_events) -> Result<()> {
+ let ret = unsafe {
+ // The ioctl is safe because the kernel will only read from the
+ // kvm_vcpu_events.
+ ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), events)
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Signals to the host kernel that this VCPU is about to be paused.
+ ///
+ /// See the documentation for KVM_KVMCLOCK_CTRL.
+ pub fn kvmclock_ctrl(&self) -> Result<()> {
+ let ret = unsafe {
+ // The ioctl is safe because it does not read or write memory in this process.
+ ioctl(self, KVM_KVMCLOCK_CTRL())
+ };
+
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Specifies set of signals that are blocked during execution of KVM_RUN.
+ /// Signals that are not blocked will will cause KVM_RUN to return
+ /// with -EINTR.
+ ///
+ /// See the documentation for KVM_SET_SIGNAL_MASK
+ pub fn set_signal_mask(&self, signals: &[c_int]) -> Result<()> {
+ let sigset = signal::create_sigset(signals)?;
+
+ let mut kvm_sigmask = vec_with_array_field::<kvm_signal_mask, sigset_t>(1);
+ // Rust definition of sigset_t takes 128 bytes, but the kernel only
+ // expects 8-bytes structure, so we can't write
+ // kvm_sigmask.len = size_of::<sigset_t>() as u32;
+ kvm_sigmask[0].len = 8;
+ // Ensure the length is not too big.
+ const _ASSERT: usize = size_of::<sigset_t>() - 8 as usize;
+
+ // Safe as we allocated exactly the needed space
+ unsafe {
+ copy_nonoverlapping(
+ &sigset as *const sigset_t as *const u8,
+ kvm_sigmask[0].sigset.as_mut_ptr(),
+ 8,
+ );
+ }
+
+ let ret = unsafe {
+ // The ioctl is safe because the kernel will only read from the
+ // kvm_signal_mask structure.
+ ioctl_with_ref(self, KVM_SET_SIGNAL_MASK(), &kvm_sigmask[0])
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Sets the value of one register on this VCPU. The id of the register is
+ /// encoded as specified in the kernel documentation for KVM_SET_ONE_REG.
+ #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+ pub fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()> {
+ let data_ref = &data as *const u64;
+ let onereg = kvm_one_reg {
+ id: reg_id,
+ addr: data_ref as u64,
+ };
+ // safe becuase we allocated the struct and we know the kernel will read
+ // exactly the size of the struct
+ let ret = unsafe { ioctl_with_ref(self, KVM_SET_ONE_REG(), &onereg) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// This initializes an ARM VCPU to the specified type with the specified features
+ /// and resets the values of all of its registers to defaults.
+ #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+ pub fn arm_vcpu_init(&self, kvi: &kvm_vcpu_init) -> Result<()> {
+ // safe becuase we allocated the struct and we know the kernel will read
+ // exactly the size of the struct
+ let ret = unsafe { ioctl_with_ref(self, KVM_ARM_VCPU_INIT(), kvi) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Use the KVM_INTERRUPT ioctl to inject the specified interrupt vector.
+ ///
+ /// While this ioctl exits on PPC and MIPS as well as x86, the semantics are different and
+ /// ChromeOS doesn't support PPC or MIPS.
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn interrupt(&self, irq: u32) -> Result<()> {
+ let interrupt = kvm_interrupt { irq };
+ // safe becuase we allocated the struct and we know the kernel will read
+ // exactly the size of the struct
+ let ret = unsafe { ioctl_with_ref(self, KVM_INTERRUPT(), &interrupt) };
+ if ret < 0 {
+ errno_result()
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl AsRawFd for Vcpu {
+ fn as_raw_fd(&self) -> RawFd {
+ self.vcpu.as_raw_fd()
+ }
+}
+
+/// Wrapper for kvm_cpuid2 which has a zero length array at the end.
+/// Hides the zero length array behind a bounds check.
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub struct CpuId {
+ kvm_cpuid: Vec<kvm_cpuid2>,
+ allocated_len: usize, // Number of kvm_cpuid_entry2 structs at the end of kvm_cpuid2.
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+impl CpuId {
+ pub fn new(array_len: usize) -> CpuId {
+ let mut kvm_cpuid = vec_with_array_field::<kvm_cpuid2, kvm_cpuid_entry2>(array_len);
+ kvm_cpuid[0].nent = array_len as u32;
+
+ CpuId {
+ kvm_cpuid,
+ allocated_len: array_len,
+ }
+ }
+
+ /// Get the entries slice so they can be modified before passing to the VCPU.
+ pub fn mut_entries_slice(&mut self) -> &mut [kvm_cpuid_entry2] {
+ // Mapping the unsized array to a slice is unsafe because the length isn't known. Using
+ // the length we originally allocated with eliminates the possibility of overflow.
+ if self.kvm_cpuid[0].nent as usize > self.allocated_len {
+ self.kvm_cpuid[0].nent = self.allocated_len as u32;
+ }
+ let nent = self.kvm_cpuid[0].nent as usize;
+ unsafe { self.kvm_cpuid[0].entries.as_mut_slice(nent) }
+ }
+
+ /// Get a pointer so it can be passed to the kernel. Using this pointer is unsafe.
+ pub fn as_ptr(&self) -> *const kvm_cpuid2 {
+ &self.kvm_cpuid[0]
+ }
+
+ /// Get a mutable pointer so it can be passed to the kernel. Using this pointer is unsafe.
+ pub fn as_mut_ptr(&mut self) -> *mut kvm_cpuid2 {
+ &mut self.kvm_cpuid[0]
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn dirty_log_size() {
+ let page_size = pagesize();
+ assert_eq!(dirty_log_bitmap_size(0), 0);
+ assert_eq!(dirty_log_bitmap_size(page_size), 1);
+ assert_eq!(dirty_log_bitmap_size(page_size * 8), 1);
+ assert_eq!(dirty_log_bitmap_size(page_size * 8 + 1), 2);
+ assert_eq!(dirty_log_bitmap_size(page_size * 100), 13);
+ }
+
+ #[test]
+ fn new() {
+ Kvm::new().unwrap();
+ }
+
+ #[test]
+ fn create_vm() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ Vm::new(&kvm, gm).unwrap();
+ }
+
+ #[test]
+ fn check_extension() {
+ let kvm = Kvm::new().unwrap();
+ assert!(kvm.check_extension(Cap::UserMemory));
+ // I assume nobody is testing this on s390
+ assert!(!kvm.check_extension(Cap::S390UserSigp));
+ }
+
+ #[test]
+ fn check_vm_extension() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ assert!(vm.check_extension(Cap::UserMemory));
+ // I assume nobody is testing this on s390
+ assert!(!vm.check_extension(Cap::S390UserSigp));
+ }
+
+ #[test]
+ fn get_supported_cpuid() {
+ let kvm = Kvm::new().unwrap();
+ let mut cpuid = kvm.get_supported_cpuid().unwrap();
+ let cpuid_entries = cpuid.mut_entries_slice();
+ assert!(cpuid_entries.len() > 0);
+ }
+
+ #[test]
+ fn get_emulated_cpuid() {
+ let kvm = Kvm::new().unwrap();
+ kvm.get_emulated_cpuid().unwrap();
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn get_msr_index_list() {
+ let kvm = Kvm::new().unwrap();
+ let msr_list = kvm.get_msr_index_list().unwrap();
+ assert!(msr_list.len() >= 2);
+ }
+
+ #[test]
+ fn add_memory() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut vm = Vm::new(&kvm, gm).unwrap();
+ let mem_size = 0x1000;
+ let mem = MemoryMapping::new(mem_size).unwrap();
+ vm.add_device_memory(GuestAddress(0x1000), mem, false, false)
+ .unwrap();
+ }
+
+ #[test]
+ fn add_memory_ro() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut vm = Vm::new(&kvm, gm).unwrap();
+ let mem_size = 0x1000;
+ let mem = MemoryMapping::new(mem_size).unwrap();
+ vm.add_device_memory(GuestAddress(0x1000), mem, true, false)
+ .unwrap();
+ }
+
+ #[test]
+ fn remove_memory() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut vm = Vm::new(&kvm, gm).unwrap();
+ let mem_size = 0x1000;
+ let mem = MemoryMapping::new(mem_size).unwrap();
+ let mem_ptr = mem.as_ptr();
+ let slot = vm
+ .add_device_memory(GuestAddress(0x1000), mem, false, false)
+ .unwrap();
+ let mem = vm.remove_device_memory(slot).unwrap();
+ assert_eq!(mem.size(), mem_size);
+ assert_eq!(mem.as_ptr(), mem_ptr);
+ }
+
+ #[test]
+ fn remove_invalid_memory() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let mut vm = Vm::new(&kvm, gm).unwrap();
+ assert!(vm.remove_device_memory(0).is_err());
+ }
+
+ #[test]
+ fn overlap_memory() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let mut vm = Vm::new(&kvm, gm).unwrap();
+ let mem_size = 0x2000;
+ let mem = MemoryMapping::new(mem_size).unwrap();
+ assert!(vm
+ .add_device_memory(GuestAddress(0x2000), mem, false, false)
+ .is_err());
+ }
+
+ #[test]
+ fn get_memory() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let obj_addr = GuestAddress(0xf0);
+ vm.get_memory().write_obj_at_addr(67u8, obj_addr).unwrap();
+ let read_val: u8 = vm.get_memory().read_obj_from_addr(obj_addr).unwrap();
+ assert_eq!(read_val, 67u8);
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn clock_handling() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let mut clock_data = vm.get_clock().unwrap();
+ clock_data.clock += 1000;
+ vm.set_clock(&clock_data).unwrap();
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn pic_handling() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ vm.create_irq_chip().unwrap();
+ let pic_state = vm.get_pic_state(PicId::Secondary).unwrap();
+ vm.set_pic_state(PicId::Secondary, &pic_state).unwrap();
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn ioapic_handling() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ vm.create_irq_chip().unwrap();
+ let ioapic_state = vm.get_ioapic_state().unwrap();
+ vm.set_ioapic_state(&ioapic_state).unwrap();
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn pit_handling() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ vm.create_irq_chip().unwrap();
+ vm.create_pit().unwrap();
+ let pit_state = vm.get_pit_state().unwrap();
+ vm.set_pit_state(&pit_state).unwrap();
+ }
+
+ #[test]
+ fn register_ioevent() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let evtfd = EventFd::new().unwrap();
+ vm.register_ioevent(&evtfd, IoeventAddress::Pio(0xf4), Datamatch::AnyLength)
+ .unwrap();
+ vm.register_ioevent(&evtfd, IoeventAddress::Mmio(0x1000), Datamatch::AnyLength)
+ .unwrap();
+ vm.register_ioevent(
+ &evtfd,
+ IoeventAddress::Pio(0xc1),
+ Datamatch::U8(Some(0x7fu8)),
+ )
+ .unwrap();
+ vm.register_ioevent(
+ &evtfd,
+ IoeventAddress::Pio(0xc2),
+ Datamatch::U16(Some(0x1337u16)),
+ )
+ .unwrap();
+ vm.register_ioevent(
+ &evtfd,
+ IoeventAddress::Pio(0xc4),
+ Datamatch::U32(Some(0xdeadbeefu32)),
+ )
+ .unwrap();
+ vm.register_ioevent(
+ &evtfd,
+ IoeventAddress::Pio(0xc8),
+ Datamatch::U64(Some(0xdeadbeefdeadbeefu64)),
+ )
+ .unwrap();
+ }
+
+ #[test]
+ fn unregister_ioevent() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let evtfd = EventFd::new().unwrap();
+ vm.register_ioevent(&evtfd, IoeventAddress::Pio(0xf4), Datamatch::AnyLength)
+ .unwrap();
+ vm.register_ioevent(&evtfd, IoeventAddress::Mmio(0x1000), Datamatch::AnyLength)
+ .unwrap();
+ vm.register_ioevent(
+ &evtfd,
+ IoeventAddress::Mmio(0x1004),
+ Datamatch::U8(Some(0x7fu8)),
+ )
+ .unwrap();
+ vm.unregister_ioevent(&evtfd, IoeventAddress::Pio(0xf4), Datamatch::AnyLength)
+ .unwrap();
+ vm.unregister_ioevent(&evtfd, IoeventAddress::Mmio(0x1000), Datamatch::AnyLength)
+ .unwrap();
+ vm.unregister_ioevent(
+ &evtfd,
+ IoeventAddress::Mmio(0x1004),
+ Datamatch::U8(Some(0x7fu8)),
+ )
+ .unwrap();
+ }
+
+ #[test]
+ fn register_irqfd() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let evtfd1 = EventFd::new().unwrap();
+ let evtfd2 = EventFd::new().unwrap();
+ let evtfd3 = EventFd::new().unwrap();
+ vm.register_irqfd(&evtfd1, 4).unwrap();
+ vm.register_irqfd(&evtfd2, 8).unwrap();
+ vm.register_irqfd(&evtfd3, 4).unwrap();
+ vm.register_irqfd(&evtfd3, 4).unwrap_err();
+ }
+
+ #[test]
+ fn unregister_irqfd() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let evtfd1 = EventFd::new().unwrap();
+ let evtfd2 = EventFd::new().unwrap();
+ let evtfd3 = EventFd::new().unwrap();
+ vm.register_irqfd(&evtfd1, 4).unwrap();
+ vm.register_irqfd(&evtfd2, 8).unwrap();
+ vm.register_irqfd(&evtfd3, 4).unwrap();
+ vm.unregister_irqfd(&evtfd1, 4).unwrap();
+ vm.unregister_irqfd(&evtfd2, 8).unwrap();
+ vm.unregister_irqfd(&evtfd3, 4).unwrap();
+ }
+
+ #[test]
+ fn irqfd_resample() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let evtfd1 = EventFd::new().unwrap();
+ let evtfd2 = EventFd::new().unwrap();
+ 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 { &EventFd::from_raw_fd(-1) }, 4)
+ .unwrap_err();
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn set_gsi_routing() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ vm.create_irq_chip().unwrap();
+ vm.set_gsi_routing(&[]).unwrap();
+ vm.set_gsi_routing(&[IrqRoute {
+ gsi: 1,
+ source: IrqSource::Irqchip {
+ chip: KVM_IRQCHIP_IOAPIC,
+ pin: 3,
+ },
+ }])
+ .unwrap();
+ vm.set_gsi_routing(&[IrqRoute {
+ gsi: 1,
+ source: IrqSource::Msi {
+ address: 0xf000000,
+ data: 0xa0,
+ },
+ }])
+ .unwrap();
+ vm.set_gsi_routing(&[
+ IrqRoute {
+ gsi: 1,
+ source: IrqSource::Irqchip {
+ chip: KVM_IRQCHIP_IOAPIC,
+ pin: 3,
+ },
+ },
+ IrqRoute {
+ gsi: 2,
+ source: IrqSource::Msi {
+ address: 0xf000000,
+ data: 0xa0,
+ },
+ },
+ ])
+ .unwrap();
+ }
+
+ #[test]
+ fn create_vcpu() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ Vcpu::new(0, &kvm, &vm).unwrap();
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn debugregs() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let vcpu = Vcpu::new(0, &kvm, &vm).unwrap();
+ let mut dregs = vcpu.get_debugregs().unwrap();
+ dregs.dr7 = 13;
+ vcpu.set_debugregs(&dregs).unwrap();
+ let dregs2 = vcpu.get_debugregs().unwrap();
+ assert_eq!(dregs.dr7, dregs2.dr7);
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn xcrs() {
+ let kvm = Kvm::new().unwrap();
+ if !kvm.check_extension(Cap::Xcrs) {
+ return;
+ }
+
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let vcpu = Vcpu::new(0, &kvm, &vm).unwrap();
+ let mut xcrs = vcpu.get_xcrs().unwrap();
+ xcrs.xcrs[0].value = 1;
+ vcpu.set_xcrs(&xcrs).unwrap();
+ let xcrs2 = vcpu.get_xcrs().unwrap();
+ assert_eq!(xcrs.xcrs[0].value, xcrs2.xcrs[0].value);
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn get_msrs() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let vcpu = Vcpu::new(0, &kvm, &vm).unwrap();
+ let mut msrs = vec![
+ // This one should succeed
+ kvm_msr_entry {
+ index: 0x0000011e,
+ ..Default::default()
+ },
+ // This one will fail to fetch
+ kvm_msr_entry {
+ index: 0x000003f1,
+ ..Default::default()
+ },
+ ];
+ vcpu.get_msrs(&mut msrs).unwrap();
+ assert_eq!(msrs.len(), 1);
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn mp_state() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ vm.create_irq_chip().unwrap();
+ let vcpu = Vcpu::new(0, &kvm, &vm).unwrap();
+ let state = vcpu.get_mp_state().unwrap();
+ vcpu.set_mp_state(&state).unwrap();
+ }
+
+ #[test]
+ fn set_signal_mask() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ let vcpu = Vcpu::new(0, &kvm, &vm).unwrap();
+ vcpu.set_signal_mask(&[sys_util::SIGRTMIN() + 0]).unwrap();
+ }
+
+ #[test]
+ fn vcpu_mmap_size() {
+ let kvm = Kvm::new().unwrap();
+ let mmap_size = kvm.get_vcpu_mmap_size().unwrap();
+ let page_size = pagesize();
+ assert!(mmap_size >= page_size);
+ assert!(mmap_size % page_size == 0);
+ }
+
+ #[test]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ fn set_identity_map_addr() {
+ let kvm = Kvm::new().unwrap();
+ let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+ let vm = Vm::new(&kvm, gm).unwrap();
+ vm.set_identity_map_addr(GuestAddress(0x20000)).unwrap();
+ }
+}
diff --git a/kvm/tests/dirty_log.rs b/kvm/tests/dirty_log.rs
new file mode 100644
index 0000000..4efb208
--- /dev/null
+++ b/kvm/tests/dirty_log.rs
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+
+use kvm::*;
+use kvm_sys::kvm_regs;
+use sys_util::{GuestAddress, GuestMemory, MemoryMapping, SharedMemory};
+
+#[test]
+fn test_run() {
+ /*
+ 0000 881C mov [si],bl
+ 0002 F4 hlt
+ */
+ let code = [0x88, 0x1c, 0xf4];
+ let mem_size = 0x10000;
+ let load_addr = GuestAddress(0x1000);
+ let guest_mem = GuestMemory::new(&[]).unwrap();
+ let mut mem = SharedMemory::new(None).expect("failed to create shared memory");
+ mem.set_size(mem_size)
+ .expect("failed to set shared memory size");
+ let mmap =
+ MemoryMapping::from_fd(&mem, mem_size as usize).expect("failed to create memory mapping");
+
+ mmap.write_slice(&code[..], load_addr.offset() as usize)
+ .expect("Writing code to memory failed.");
+
+ let kvm = Kvm::new().expect("new kvm failed");
+ let mut vm = Vm::new(&kvm, guest_mem).expect("new vm failed");
+ let vcpu = Vcpu::new(0, &kvm, &vm).expect("new vcpu failed");
+ let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed");
+ vcpu_sregs.cs.base = 0;
+ vcpu_sregs.cs.selector = 0;
+ vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
+
+ let mut vcpu_regs: kvm_regs = unsafe { std::mem::zeroed() };
+ vcpu_regs.rip = load_addr.offset() as u64;
+ vcpu_regs.rflags = 2;
+ // Write 0x12 to the beginning of the 9th page.
+ vcpu_regs.rsi = 0x8000;
+ vcpu_regs.rbx = 0x12;
+ vcpu.set_regs(&vcpu_regs).expect("set regs failed");
+ let slot = vm
+ .add_device_memory(
+ GuestAddress(0),
+ MemoryMapping::from_fd(&mem, mem_size as usize)
+ .expect("failed to create memory mapping"),
+ false,
+ true,
+ )
+ .expect("failed to register memory");
+
+ loop {
+ match vcpu.run().expect("run failed") {
+ VcpuExit::Hlt => break,
+ r => panic!("unexpected exit reason: {:?}", r),
+ }
+ }
+
+ let mut dirty_log = [0x0, 0x0];
+ vm.get_dirty_log(slot, &mut dirty_log[..])
+ .expect("failed to get dirty log");
+ // Tests the 9th page was written to.
+ assert_eq!(dirty_log[1], 0x1);
+ assert_eq!(
+ mmap.read_obj::<u64>(vcpu_regs.rsi as usize).unwrap(),
+ vcpu_regs.rbx
+ );
+}
diff --git a/kvm/tests/read_only_memory.rs b/kvm/tests/read_only_memory.rs
new file mode 100644
index 0000000..c1dd854
--- /dev/null
+++ b/kvm/tests/read_only_memory.rs
@@ -0,0 +1,102 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+
+use kvm::*;
+use kvm_sys::kvm_regs;
+use sys_util::{GuestAddress, GuestMemory, MemoryMapping, SharedMemory};
+
+#[test]
+fn test_run() {
+ /*
+ 0000 268A07 mov al,[es:bx]
+ 0003 0401 add al,0x1
+ 0005 268807 mov [es:bx],al
+ 0008 F4 hlt
+ */
+ let code = [0x26, 0x8a, 0x07, 0x04, 0x01, 0x26, 0x88, 0x07, 0xf4];
+ let mem_size = 0x2000;
+ let load_addr = GuestAddress(0x1000);
+ let guest_mem = GuestMemory::new(&[]).unwrap();
+ let mut mem = SharedMemory::new(None).expect("failed to create shared memory");
+ mem.set_size(mem_size)
+ .expect("failed to set shared memory size");
+ let mmap =
+ MemoryMapping::from_fd(&mem, mem_size as usize).expect("failed to create memory mapping");
+
+ mmap.write_slice(&code[..], load_addr.offset() as usize)
+ .expect("Writing code to memory failed.");
+
+ let kvm = Kvm::new().expect("new kvm failed");
+ let mut vm = Vm::new(&kvm, guest_mem).expect("new vm failed");
+ let vcpu = Vcpu::new(0, &kvm, &vm).expect("new vcpu failed");
+ let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed");
+ vcpu_sregs.cs.base = 0;
+ vcpu_sregs.cs.selector = 0;
+ vcpu_sregs.es.base = 0x3000;
+ vcpu_sregs.es.selector = 0;
+ vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
+
+ let mut vcpu_regs: kvm_regs = unsafe { std::mem::zeroed() };
+ vcpu_regs.rip = load_addr.offset() as u64;
+ vcpu_regs.rflags = 2;
+ vcpu_regs.rax = 0x66;
+ vcpu_regs.rbx = 0;
+ vcpu.set_regs(&vcpu_regs).expect("set regs failed");
+ vm.add_device_memory(
+ GuestAddress(0),
+ MemoryMapping::from_fd(&mem, mem_size as usize).expect("failed to create memory mapping"),
+ false,
+ false,
+ )
+ .expect("failed to register memory");
+
+ // Give some read only memory for the test code to read from and force a vcpu exit when it reads
+ // from it.
+ let mut mem_ro = SharedMemory::new(None).expect("failed to create shared memory");
+ mem_ro
+ .set_size(0x1000)
+ .expect("failed to set shared memory size");
+ let mmap_ro = MemoryMapping::from_fd(&mem_ro, 0x1000).expect("failed to create memory mapping");
+ mmap_ro
+ .write_obj(vcpu_regs.rax as u8, 0)
+ .expect("failed writing data to ro memory");
+ vm.add_device_memory(
+ GuestAddress(vcpu_sregs.es.base),
+ MemoryMapping::from_fd(&mem_ro, 0x1000).expect("failed to create memory mapping"),
+ true,
+ false,
+ )
+ .expect("failed to register memory");
+
+ // Ensure we get exactly 1 exit from attempting to write to read only memory.
+ let mut exits = 0;
+
+ loop {
+ match vcpu.run().expect("run failed") {
+ VcpuExit::Hlt => break,
+ VcpuExit::MmioWrite {
+ address,
+ size: 1,
+ data,
+ } => {
+ assert_eq!(address, vcpu_sregs.es.base);
+ assert_eq!(data[0] as u64, vcpu_regs.rax + 1);
+ exits += 1;
+ }
+ r => panic!("unexpected exit reason: {:?}", r),
+ }
+ }
+
+ // Check that exactly 1 attempt to write to read only memory was made, and that the memory is
+ // unchanged after that attempt.
+ assert_eq!(exits, 1);
+ assert_eq!(
+ mmap_ro
+ .read_obj::<u8>(0)
+ .expect("failed to read data from ro memory"),
+ vcpu_regs.rax as u8
+ );
+}
diff --git a/kvm/tests/real_run_adder.rs b/kvm/tests/real_run_adder.rs
new file mode 100644
index 0000000..a419ad9
--- /dev/null
+++ b/kvm/tests/real_run_adder.rs
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+
+use kvm::*;
+use kvm_sys::kvm_regs;
+use sys_util::{GuestAddress, GuestMemory};
+
+#[test]
+fn test_run() {
+ // This example based on https://lwn.net/Articles/658511/
+ let code = [
+ 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
+ 0x00, 0xd8, /* add %bl, %al */
+ 0x04, '0' as u8, /* add $'0', %al */
+ 0xee, /* out %al, (%dx) */
+ 0xb0, '\n' as u8, /* mov $'\n', %al */
+ 0xee, /* out %al, (%dx) */
+ 0x2e, 0xc6, 0x06, 0xf1, 0x10, 0x13, /* movb $0x13, %cs:0xf1 */
+ 0xf4, /* hlt */
+ ];
+
+ let mem_size = 0x1000;
+ let load_addr = GuestAddress(0x1000);
+ let mem = GuestMemory::new(&vec![(load_addr, mem_size)]).unwrap();
+
+ let kvm = Kvm::new().expect("new kvm failed");
+ let vm = Vm::new(&kvm, mem).expect("new vm failed");
+ let vcpu = Vcpu::new(0, &kvm, &vm).expect("new vcpu failed");
+
+ vm.get_memory()
+ .write_at_addr(&code, load_addr)
+ .expect("Writing code to memory failed.");
+
+ let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed");
+ assert_ne!(vcpu_sregs.cs.base, 0);
+ assert_ne!(vcpu_sregs.cs.selector, 0);
+ vcpu_sregs.cs.base = 0;
+ vcpu_sregs.cs.selector = 0;
+ vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
+
+ let mut vcpu_regs: kvm_regs = unsafe { std::mem::zeroed() };
+ vcpu_regs.rip = 0x1000;
+ vcpu_regs.rax = 2;
+ vcpu_regs.rbx = 7;
+ vcpu_regs.rflags = 2;
+ vcpu.set_regs(&vcpu_regs).expect("set regs failed");
+
+ let mut out = String::new();
+ loop {
+ match vcpu.run().expect("run failed") {
+ VcpuExit::IoOut {
+ port: 0x3f8,
+ size,
+ data,
+ } => {
+ assert_eq!(size, 1);
+ out.push(data[0] as char);
+ }
+ VcpuExit::Hlt => break,
+ r => panic!("unexpected exit reason: {:?}", r),
+ }
+ }
+
+ assert_eq!(out, "9\n");
+ let result: u8 = vm
+ .get_memory()
+ .read_obj_from_addr(load_addr.checked_add(0xf1).unwrap())
+ .expect("Error reading the result.");
+ assert_eq!(result, 0x13);
+}
diff --git a/kvm_sys/Cargo.toml b/kvm_sys/Cargo.toml
new file mode 100644
index 0000000..3f62ead
--- /dev/null
+++ b/kvm_sys/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "kvm_sys"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+libc = "*"
+sys_util = { path = "../sys_util" }
diff --git a/kvm_sys/src/aarch64/bindings.rs b/kvm_sys/src/aarch64/bindings.rs
new file mode 100644
index 0000000..26b10b4
--- /dev/null
+++ b/kvm_sys/src/aarch64/bindings.rs
@@ -0,0 +1,6392 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData)
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ 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_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_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_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_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 type __s8 = ::std::os::raw::c_schar;
+pub type __u8 = ::std::os::raw::c_uchar;
+pub type __s16 = ::std::os::raw::c_short;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __s32 = ::std::os::raw::c_int;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __s64 = ::std::os::raw::c_longlong;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __kernel_fd_set {
+ pub fds_bits: [::std::os::raw::c_ulong; 16usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fd_set() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fd_set>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fd_set>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__kernel_fd_set>())).fds_bits as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__kernel_fd_set),
+ "::",
+ stringify!(fds_bits)
+ )
+ );
+}
+pub type __kernel_sighandler_t =
+ ::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
+pub type __kernel_key_t = ::std::os::raw::c_int;
+pub type __kernel_mqd_t = ::std::os::raw::c_int;
+pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
+pub type __kernel_long_t = ::std::os::raw::c_long;
+pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
+pub type __kernel_ino_t = __kernel_ulong_t;
+pub type __kernel_mode_t = ::std::os::raw::c_uint;
+pub type __kernel_pid_t = ::std::os::raw::c_int;
+pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
+pub type __kernel_uid_t = ::std::os::raw::c_uint;
+pub type __kernel_gid_t = ::std::os::raw::c_uint;
+pub type __kernel_suseconds_t = __kernel_long_t;
+pub type __kernel_daddr_t = ::std::os::raw::c_int;
+pub type __kernel_uid32_t = ::std::os::raw::c_uint;
+pub type __kernel_gid32_t = ::std::os::raw::c_uint;
+pub type __kernel_old_dev_t = ::std::os::raw::c_uint;
+pub type __kernel_size_t = __kernel_ulong_t;
+pub type __kernel_ssize_t = __kernel_long_t;
+pub type __kernel_ptrdiff_t = __kernel_long_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __kernel_fsid_t {
+ pub val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fsid_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fsid_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fsid_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__kernel_fsid_t>())).val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__kernel_fsid_t),
+ "::",
+ stringify!(val)
+ )
+ );
+}
+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_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;
+pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
+pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
+pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
+pub type __le16 = __u16;
+pub type __be16 = __u16;
+pub type __le32 = __u32;
+pub type __be32 = __u32;
+pub type __le64 = __u64;
+pub type __be64 = __u64;
+pub type __sum16 = __u16;
+pub type __wsum = __u32;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct user_pt_regs {
+ pub regs: [__u64; 31usize],
+ pub sp: __u64,
+ pub pc: __u64,
+ pub pstate: __u64,
+}
+#[test]
+fn bindgen_test_layout_user_pt_regs() {
+ assert_eq!(
+ ::std::mem::size_of::<user_pt_regs>(),
+ 272usize,
+ concat!("Size of: ", stringify!(user_pt_regs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<user_pt_regs>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(user_pt_regs))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_pt_regs>())).regs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_pt_regs),
+ "::",
+ stringify!(regs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_pt_regs>())).sp as *const _ as usize },
+ 248usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_pt_regs),
+ "::",
+ stringify!(sp)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_pt_regs>())).pc as *const _ as usize },
+ 256usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_pt_regs),
+ "::",
+ stringify!(pc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_pt_regs>())).pstate as *const _ as usize },
+ 264usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_pt_regs),
+ "::",
+ stringify!(pstate)
+ )
+ );
+}
+#[repr(C)]
+#[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,
+}
+#[test]
+fn bindgen_test_layout_user_fpsimd_state() {
+ assert_eq!(
+ ::std::mem::size_of::<user_fpsimd_state>(),
+ 528usize,
+ concat!("Size of: ", stringify!(user_fpsimd_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_fpsimd_state>())).vregs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_fpsimd_state),
+ "::",
+ stringify!(vregs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_fpsimd_state>())).fpsr as *const _ as usize },
+ 512usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_fpsimd_state),
+ "::",
+ stringify!(fpsr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_fpsimd_state>())).fpcr as *const _ as usize },
+ 516usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_fpsimd_state),
+ "::",
+ stringify!(fpcr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct user_hwdebug_state {
+ pub dbg_info: __u32,
+ pub pad: __u32,
+ pub dbg_regs: [user_hwdebug_state__bindgen_ty_1; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct user_hwdebug_state__bindgen_ty_1 {
+ pub addr: __u64,
+ pub ctrl: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_user_hwdebug_state__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<user_hwdebug_state__bindgen_ty_1>(),
+ 16usize,
+ concat!("Size of: ", stringify!(user_hwdebug_state__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<user_hwdebug_state__bindgen_ty_1>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(user_hwdebug_state__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<user_hwdebug_state__bindgen_ty_1>())).addr as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_hwdebug_state__bindgen_ty_1),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<user_hwdebug_state__bindgen_ty_1>())).ctrl as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_hwdebug_state__bindgen_ty_1),
+ "::",
+ stringify!(ctrl)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<user_hwdebug_state__bindgen_ty_1>())).pad as *const _ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_hwdebug_state__bindgen_ty_1),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[test]
+fn bindgen_test_layout_user_hwdebug_state() {
+ assert_eq!(
+ ::std::mem::size_of::<user_hwdebug_state>(),
+ 264usize,
+ concat!("Size of: ", stringify!(user_hwdebug_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<user_hwdebug_state>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(user_hwdebug_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_hwdebug_state>())).dbg_info as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_hwdebug_state),
+ "::",
+ stringify!(dbg_info)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_hwdebug_state>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_hwdebug_state),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<user_hwdebug_state>())).dbg_regs as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(user_hwdebug_state),
+ "::",
+ stringify!(dbg_regs)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_regs {
+ pub regs: user_pt_regs,
+ pub sp_el1: __u64,
+ pub elr_el1: __u64,
+ pub spsr: [__u64; 5usize],
+ pub __bindgen_padding_0: u64,
+ pub fp_regs: user_fpsimd_state,
+}
+#[test]
+fn bindgen_test_layout_kvm_regs() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_regs>(),
+ 864usize,
+ concat!("Size of: ", stringify!(kvm_regs))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).regs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(regs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).sp_el1 as *const _ as usize },
+ 272usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(sp_el1)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).elr_el1 as *const _ as usize },
+ 280usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(elr_el1)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).spsr as *const _ as usize },
+ 288usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(spsr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).fp_regs as *const _ as usize },
+ 336usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(fp_regs)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vcpu_init {
+ pub target: __u32,
+ pub features: [__u32; 7usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_vcpu_init() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_vcpu_init>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_vcpu_init))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_vcpu_init>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_vcpu_init))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vcpu_init>())).target as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_init),
+ "::",
+ stringify!(target)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vcpu_init>())).features as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_init),
+ "::",
+ stringify!(features)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sregs {}
+#[test]
+fn bindgen_test_layout_kvm_sregs() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_sregs>(),
+ 0usize,
+ concat!("Size of: ", stringify!(kvm_sregs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_sregs>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_sregs))
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_fpu {}
+#[test]
+fn bindgen_test_layout_kvm_fpu() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_fpu>(),
+ 0usize,
+ concat!("Size of: ", stringify!(kvm_fpu))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_fpu>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_fpu))
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_guest_debug_arch {
+ pub dbg_bcr: [__u64; 16usize],
+ pub dbg_bvr: [__u64; 16usize],
+ pub dbg_wcr: [__u64; 16usize],
+ pub dbg_wvr: [__u64; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_guest_debug_arch() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_guest_debug_arch>(),
+ 512usize,
+ concat!("Size of: ", stringify!(kvm_guest_debug_arch))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_guest_debug_arch>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_guest_debug_arch))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug_arch>())).dbg_bcr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug_arch),
+ "::",
+ stringify!(dbg_bcr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug_arch>())).dbg_bvr as *const _ as usize },
+ 128usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug_arch),
+ "::",
+ stringify!(dbg_bvr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug_arch>())).dbg_wcr as *const _ as usize },
+ 256usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug_arch),
+ "::",
+ stringify!(dbg_wcr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug_arch>())).dbg_wvr as *const _ as usize },
+ 384usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug_arch),
+ "::",
+ stringify!(dbg_wvr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_debug_exit_arch {
+ pub hsr: __u32,
+ pub far: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_debug_exit_arch() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_debug_exit_arch>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_debug_exit_arch))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_debug_exit_arch>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_debug_exit_arch))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_exit_arch>())).hsr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_exit_arch),
+ "::",
+ stringify!(hsr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_exit_arch>())).far as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_exit_arch),
+ "::",
+ stringify!(far)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sync_regs {}
+#[test]
+fn bindgen_test_layout_kvm_sync_regs() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_sync_regs>(),
+ 0usize,
+ concat!("Size of: ", stringify!(kvm_sync_regs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_sync_regs>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_sync_regs))
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_arch_memory_slot {}
+#[test]
+fn bindgen_test_layout_kvm_arch_memory_slot() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_arch_memory_slot>(),
+ 0usize,
+ concat!("Size of: ", stringify!(kvm_arch_memory_slot))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_arch_memory_slot>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_arch_memory_slot))
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_user_trace_setup {
+ pub buf_size: __u32,
+ pub buf_nr: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_user_trace_setup() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_user_trace_setup>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_user_trace_setup))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_user_trace_setup>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_user_trace_setup))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_user_trace_setup>())).buf_size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_user_trace_setup),
+ "::",
+ stringify!(buf_size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_user_trace_setup>())).buf_nr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_user_trace_setup),
+ "::",
+ stringify!(buf_nr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_breakpoint {
+ pub enabled: __u32,
+ pub padding: __u32,
+ pub address: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_breakpoint() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_breakpoint>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_breakpoint))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_breakpoint>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_breakpoint))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_breakpoint>())).enabled as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_breakpoint),
+ "::",
+ stringify!(enabled)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_breakpoint>())).padding as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_breakpoint),
+ "::",
+ stringify!(padding)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_breakpoint>())).address as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_breakpoint),
+ "::",
+ stringify!(address)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_debug_guest {
+ pub enabled: __u32,
+ pub pad: __u32,
+ pub breakpoints: [kvm_breakpoint; 4usize],
+ pub singlestep: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_debug_guest() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_debug_guest>(),
+ 80usize,
+ concat!("Size of: ", stringify!(kvm_debug_guest))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_debug_guest>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_debug_guest))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_guest>())).enabled as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_guest),
+ "::",
+ stringify!(enabled)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_guest>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_guest),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_guest>())).breakpoints as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_guest),
+ "::",
+ stringify!(breakpoints)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_guest>())).singlestep as *const _ as usize },
+ 72usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_guest),
+ "::",
+ stringify!(singlestep)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_memory_region {
+ pub slot: __u32,
+ pub flags: __u32,
+ pub guest_phys_addr: __u64,
+ pub memory_size: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_memory_region() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_memory_region>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_memory_region))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_memory_region>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_memory_region))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_region>())).slot as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_region),
+ "::",
+ stringify!(slot)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_region>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_region),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_memory_region>())).guest_phys_addr as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_region),
+ "::",
+ stringify!(guest_phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_region>())).memory_size as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_region),
+ "::",
+ stringify!(memory_size)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_userspace_memory_region {
+ pub slot: __u32,
+ pub flags: __u32,
+ pub guest_phys_addr: __u64,
+ pub memory_size: __u64,
+ pub userspace_addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_userspace_memory_region() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_userspace_memory_region>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_userspace_memory_region))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_userspace_memory_region>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_userspace_memory_region))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).slot as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(slot)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).flags as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).guest_phys_addr as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(guest_phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).memory_size as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(memory_size)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).userspace_addr as *const _
+ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(userspace_addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_irq_level {
+ pub __bindgen_anon_1: kvm_irq_level__bindgen_ty_1,
+ pub level: __u32,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_irq_level__bindgen_ty_1 {
+ pub irq: __u32,
+ pub status: __s32,
+ _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_level__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_level__bindgen_ty_1>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_irq_level__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_level__bindgen_ty_1>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irq_level__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_level__bindgen_ty_1>())).irq as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_level__bindgen_ty_1),
+ "::",
+ stringify!(irq)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_level__bindgen_ty_1>())).status as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_level__bindgen_ty_1),
+ "::",
+ stringify!(status)
+ )
+ );
+}
+impl Default for kvm_irq_level__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_level() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_level>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_irq_level))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_level>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irq_level))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_level>())).level as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_level),
+ "::",
+ stringify!(level)
+ )
+ );
+}
+impl Default for kvm_irq_level {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_irqchip {
+ pub chip_id: __u32,
+ pub pad: __u32,
+ pub chip: kvm_irqchip__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_irqchip__bindgen_ty_1 {
+ pub dummy: [::std::os::raw::c_char; 512usize],
+ _bindgen_union_align: [u8; 512usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_irqchip__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irqchip__bindgen_ty_1>(),
+ 512usize,
+ concat!("Size of: ", stringify!(kvm_irqchip__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irqchip__bindgen_ty_1>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_irqchip__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip__bindgen_ty_1>())).dummy as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip__bindgen_ty_1),
+ "::",
+ stringify!(dummy)
+ )
+ );
+}
+impl Default for kvm_irqchip__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_irqchip() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irqchip>(),
+ 520usize,
+ concat!("Size of: ", stringify!(kvm_irqchip))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irqchip>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irqchip))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip>())).chip_id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip),
+ "::",
+ stringify!(chip_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip>())).chip as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip),
+ "::",
+ stringify!(chip)
+ )
+ );
+}
+impl Default for kvm_irqchip {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_pit_config {
+ pub flags: __u32,
+ pub pad: [__u32; 15usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_pit_config() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_pit_config>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_pit_config))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_pit_config>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_pit_config))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_config>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_config),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_config>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_config),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_skeys {
+ pub start_gfn: __u64,
+ pub count: __u64,
+ pub skeydata_addr: __u64,
+ pub flags: __u32,
+ pub reserved: [__u32; 9usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_skeys() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_skeys>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_s390_skeys))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_skeys>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_skeys))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).start_gfn as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(start_gfn)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).count as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(count)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).skeydata_addr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(skeydata_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).flags as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).reserved as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_run {
+ pub request_interrupt_window: __u8,
+ pub padding1: [__u8; 7usize],
+ pub exit_reason: __u32,
+ pub ready_for_interrupt_injection: __u8,
+ pub if_flag: __u8,
+ pub flags: __u16,
+ pub cr8: __u64,
+ pub apic_base: __u64,
+ pub __bindgen_anon_1: kvm_run__bindgen_ty_1,
+ pub kvm_valid_regs: __u64,
+ pub kvm_dirty_regs: __u64,
+ 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,
+ pub fail_entry: kvm_run__bindgen_ty_1__bindgen_ty_2,
+ pub ex: kvm_run__bindgen_ty_1__bindgen_ty_3,
+ pub io: kvm_run__bindgen_ty_1__bindgen_ty_4,
+ pub debug: kvm_run__bindgen_ty_1__bindgen_ty_5,
+ pub mmio: kvm_run__bindgen_ty_1__bindgen_ty_6,
+ pub hypercall: kvm_run__bindgen_ty_1__bindgen_ty_7,
+ pub tpr_access: kvm_run__bindgen_ty_1__bindgen_ty_8,
+ pub s390_sieic: kvm_run__bindgen_ty_1__bindgen_ty_9,
+ pub s390_reset_flags: __u64,
+ pub s390_ucontrol: kvm_run__bindgen_ty_1__bindgen_ty_10,
+ pub dcr: kvm_run__bindgen_ty_1__bindgen_ty_11,
+ pub internal: kvm_run__bindgen_ty_1__bindgen_ty_12,
+ pub osi: kvm_run__bindgen_ty_1__bindgen_ty_13,
+ pub papr_hcall: kvm_run__bindgen_ty_1__bindgen_ty_14,
+ pub s390_tsch: kvm_run__bindgen_ty_1__bindgen_ty_15,
+ pub epr: kvm_run__bindgen_ty_1__bindgen_ty_16,
+ pub system_event: kvm_run__bindgen_ty_1__bindgen_ty_17,
+ pub s390_stsi: kvm_run__bindgen_ty_1__bindgen_ty_18,
+ pub eoi: kvm_run__bindgen_ty_1__bindgen_ty_19,
+ pub padding: [::std::os::raw::c_char; 256usize],
+ _bindgen_union_align: [u64; 32usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_1>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_1>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_1>())).hardware_exit_reason
+ as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_1),
+ "::",
+ stringify!(hardware_exit_reason)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_2 {
+ pub hardware_entry_failure_reason: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_2() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_2>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_2))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_2>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_2)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_2>()))
+ .hardware_entry_failure_reason as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_2),
+ "::",
+ stringify!(hardware_entry_failure_reason)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_3 {
+ pub exception: __u32,
+ pub error_code: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_3() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_3>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_3))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_3>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_3)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_3>())).exception as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_3),
+ "::",
+ stringify!(exception)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_3>())).error_code as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_3),
+ "::",
+ stringify!(error_code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_4 {
+ pub direction: __u8,
+ pub size: __u8,
+ pub port: __u16,
+ pub count: __u32,
+ pub data_offset: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_4() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_4>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_4>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).direction as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(direction)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).size as *const _
+ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(size)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).port as *const _
+ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(port)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).count as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(count)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).data_offset as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(data_offset)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_5 {
+ pub arch: kvm_debug_exit_arch,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_5() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_5>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_5))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_5>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_5)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_5>())).arch as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_5),
+ "::",
+ stringify!(arch)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_6 {
+ pub phys_addr: __u64,
+ pub data: [__u8; 8usize],
+ pub len: __u32,
+ pub is_write: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_6() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_6>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_6>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_6>())).phys_addr as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6),
+ "::",
+ stringify!(phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_6>())).data as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6),
+ "::",
+ stringify!(data)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_6>())).len as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_6>())).is_write as *const _
+ as usize
+ },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6),
+ "::",
+ stringify!(is_write)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_7 {
+ pub nr: __u64,
+ pub args: [__u64; 6usize],
+ pub ret: __u64,
+ pub longmode: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_7() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_7>(),
+ 72usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_7>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).nr as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(nr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).args as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(args)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).ret as *const _ as usize
+ },
+ 56usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(ret)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).longmode as *const _
+ as usize
+ },
+ 64usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(longmode)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).pad as *const _ as usize
+ },
+ 68usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_8 {
+ pub rip: __u64,
+ pub is_write: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_8() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_8>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_8>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_8>())).rip as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8),
+ "::",
+ stringify!(rip)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_8>())).is_write as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8),
+ "::",
+ stringify!(is_write)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_8>())).pad as *const _ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_9 {
+ pub icptcode: __u8,
+ pub ipa: __u16,
+ pub ipb: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_9() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_9>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_9>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_9>())).icptcode as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9),
+ "::",
+ stringify!(icptcode)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_9>())).ipa as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9),
+ "::",
+ stringify!(ipa)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_9>())).ipb as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9),
+ "::",
+ stringify!(ipb)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_10 {
+ pub trans_exc_code: __u64,
+ pub pgm_code: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_10() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_10>(),
+ 16usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_10)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_10>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_10)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_10>())).trans_exc_code
+ as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_10),
+ "::",
+ stringify!(trans_exc_code)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_10>())).pgm_code as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_10),
+ "::",
+ stringify!(pgm_code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_11 {
+ pub dcrn: __u32,
+ pub data: __u32,
+ pub is_write: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_11() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_11>(),
+ 12usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_11>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_11>())).dcrn as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11),
+ "::",
+ stringify!(dcrn)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_11>())).data as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11),
+ "::",
+ stringify!(data)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_11>())).is_write as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11),
+ "::",
+ stringify!(is_write)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_12 {
+ pub suberror: __u32,
+ pub ndata: __u32,
+ pub data: [__u64; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_12() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_12>(),
+ 136usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_12>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_12>())).suberror as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12),
+ "::",
+ stringify!(suberror)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_12>())).ndata as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12),
+ "::",
+ stringify!(ndata)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_12>())).data as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12),
+ "::",
+ stringify!(data)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_13 {
+ pub gprs: [__u64; 32usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_13() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_13>(),
+ 256usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_13)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_13>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_13)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_13>())).gprs as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_13),
+ "::",
+ stringify!(gprs)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_14 {
+ pub nr: __u64,
+ pub ret: __u64,
+ pub args: [__u64; 9usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_14() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_14>(),
+ 88usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_14>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_14>())).nr as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14),
+ "::",
+ stringify!(nr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_14>())).ret as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14),
+ "::",
+ stringify!(ret)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_14>())).args as *const _
+ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14),
+ "::",
+ stringify!(args)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_15 {
+ pub subchannel_id: __u16,
+ pub subchannel_nr: __u16,
+ pub io_int_parm: __u32,
+ pub io_int_word: __u32,
+ pub ipb: __u32,
+ pub dequeued: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_15() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_15>(),
+ 20usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_15>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).subchannel_id
+ as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(subchannel_id)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).subchannel_nr
+ as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(subchannel_nr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).io_int_parm as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(io_int_parm)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).io_int_word as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(io_int_word)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).ipb as *const _
+ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(ipb)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).dequeued as *const _
+ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(dequeued)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_16 {
+ pub epr: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_16() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_16>(),
+ 4usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_16)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_16>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_16)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_16>())).epr as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_16),
+ "::",
+ stringify!(epr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_17 {
+ pub type_: __u32,
+ pub flags: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_17() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_17>(),
+ 16usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_17)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_17>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_17)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_17>())).type_ as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_17),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_17>())).flags as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_17),
+ "::",
+ stringify!(flags)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_18 {
+ pub addr: __u64,
+ pub ar: __u8,
+ pub reserved: __u8,
+ pub fc: __u8,
+ pub sel1: __u8,
+ pub sel2: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_18() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_18>(),
+ 16usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_18>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).addr as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).ar as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(ar)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).reserved as *const _
+ as usize
+ },
+ 9usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(reserved)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).fc as *const _ as usize
+ },
+ 10usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(fc)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).sel1 as *const _
+ as usize
+ },
+ 11usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(sel1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).sel2 as *const _
+ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(sel2)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_19 {
+ pub vector: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_19() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_19>(),
+ 1usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_19)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_19>(),
+ 1usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_19)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_19>())).vector as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_19),
+ "::",
+ stringify!(vector)
+ )
+ );
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1>(),
+ 256usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_run__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).hw as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(hw)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).fail_entry as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(fail_entry)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).ex as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(ex)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).io as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(io)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).debug as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(debug)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).mmio as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(mmio)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).hypercall as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(hypercall)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).tpr_access as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(tpr_access)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_sieic as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_sieic)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_reset_flags as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_reset_flags)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_ucontrol as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_ucontrol)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).dcr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(dcr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).internal as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(internal)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).osi as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(osi)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).papr_hcall as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(papr_hcall)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_tsch as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_tsch)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).epr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(epr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).system_event as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(system_event)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_stsi as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_stsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).eoi as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(eoi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).padding as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+impl Default for kvm_run__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+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],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_2() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_2>(),
+ 2048usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_2))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_2>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_run__bindgen_ty_2))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_2>())).regs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_2),
+ "::",
+ stringify!(regs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_2>())).padding as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_2),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+impl Default for kvm_run__bindgen_ty_2 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_run() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run>(),
+ 2352usize,
+ concat!("Size of: ", stringify!(kvm_run))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_run))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run>())).request_interrupt_window as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(request_interrupt_window)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).padding1 as *const _ as usize },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(padding1)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).exit_reason as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(exit_reason)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run>())).ready_for_interrupt_injection as *const _ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(ready_for_interrupt_injection)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).if_flag as *const _ as usize },
+ 13usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(if_flag)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).flags as *const _ as usize },
+ 14usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).cr8 as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(cr8)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).apic_base as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(apic_base)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).kvm_valid_regs as *const _ as usize },
+ 288usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(kvm_valid_regs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).kvm_dirty_regs as *const _ as usize },
+ 296usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(kvm_dirty_regs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).s as *const _ as usize },
+ 304usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(s)
+ )
+ );
+}
+impl Default for kvm_run {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_coalesced_mmio_zone {
+ pub addr: __u64,
+ pub size: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_coalesced_mmio_zone() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_coalesced_mmio_zone>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_coalesced_mmio_zone))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_coalesced_mmio_zone>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_coalesced_mmio_zone))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_zone>())).addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_zone),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_zone>())).size as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_zone),
+ "::",
+ stringify!(size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_zone>())).pad as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_zone),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[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],
+}
+#[test]
+fn bindgen_test_layout_kvm_coalesced_mmio() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_coalesced_mmio>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_coalesced_mmio))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_coalesced_mmio>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_coalesced_mmio))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio>())).phys_addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio),
+ "::",
+ stringify!(phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio>())).len as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio),
+ "::",
+ stringify!(len)
+ )
+ );
+ 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!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio),
+ "::",
+ stringify!(data)
+ )
+ );
+}
+#[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() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_coalesced_mmio_ring>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_coalesced_mmio_ring))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_coalesced_mmio_ring>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_coalesced_mmio_ring))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_ring>())).first as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_ring),
+ "::",
+ stringify!(first)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_ring>())).last as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_ring),
+ "::",
+ stringify!(last)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_coalesced_mmio_ring>())).coalesced_mmio as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_ring),
+ "::",
+ stringify!(coalesced_mmio)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_translation {
+ pub linear_address: __u64,
+ pub physical_address: __u64,
+ pub valid: __u8,
+ pub writeable: __u8,
+ pub usermode: __u8,
+ pub pad: [__u8; 5usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_translation() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_translation>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_translation))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_translation>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_translation))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).linear_address as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(linear_address)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_translation>())).physical_address as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(physical_address)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).valid as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(valid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).writeable as *const _ as usize },
+ 17usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(writeable)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).usermode as *const _ as usize },
+ 18usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(usermode)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).pad as *const _ as usize },
+ 19usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_mem_op {
+ pub gaddr: __u64,
+ pub flags: __u64,
+ pub size: __u32,
+ pub op: __u32,
+ pub buf: __u64,
+ pub ar: __u8,
+ pub reserved: [__u8; 31usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_mem_op() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_mem_op>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_s390_mem_op))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_mem_op>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_mem_op))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).gaddr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(gaddr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).size as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).op as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(op)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).buf as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(buf)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).ar as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(ar)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).reserved as *const _ as usize },
+ 33usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_interrupt {
+ pub irq: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_interrupt() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_interrupt>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_interrupt))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_interrupt>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_interrupt))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_interrupt>())).irq as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_interrupt),
+ "::",
+ stringify!(irq)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_dirty_log {
+ pub slot: __u32,
+ pub padding1: __u32,
+ pub __bindgen_anon_1: kvm_dirty_log__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_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_dirty_log__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_dirty_log__bindgen_ty_1>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_dirty_log__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_dirty_log__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_dirty_log__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_dirty_log__bindgen_ty_1>())).dirty_bitmap as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_log__bindgen_ty_1),
+ "::",
+ stringify!(dirty_bitmap)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_dirty_log__bindgen_ty_1>())).padding2 as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_log__bindgen_ty_1),
+ "::",
+ stringify!(padding2)
+ )
+ );
+}
+impl Default for kvm_dirty_log__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_dirty_log() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_dirty_log>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_dirty_log))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_dirty_log>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_dirty_log))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dirty_log>())).slot as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_log),
+ "::",
+ stringify!(slot)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dirty_log>())).padding1 as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_log),
+ "::",
+ stringify!(padding1)
+ )
+ );
+}
+impl Default for kvm_dirty_log {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_signal_mask {
+ pub len: __u32,
+ pub sigset: __IncompleteArrayField<__u8>,
+}
+#[test]
+fn bindgen_test_layout_kvm_signal_mask() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_signal_mask>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_signal_mask))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_signal_mask>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_signal_mask))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_signal_mask>())).len as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_signal_mask),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_signal_mask>())).sigset as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_signal_mask),
+ "::",
+ stringify!(sigset)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_tpr_access_ctl {
+ pub enabled: __u32,
+ pub flags: __u32,
+ pub reserved: [__u32; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_tpr_access_ctl() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_tpr_access_ctl>(),
+ 40usize,
+ concat!("Size of: ", stringify!(kvm_tpr_access_ctl))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_tpr_access_ctl>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_tpr_access_ctl))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_tpr_access_ctl>())).enabled as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_tpr_access_ctl),
+ "::",
+ stringify!(enabled)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_tpr_access_ctl>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_tpr_access_ctl),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_tpr_access_ctl>())).reserved as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_tpr_access_ctl),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vapic_addr {
+ pub vapic_addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_vapic_addr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_vapic_addr>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_vapic_addr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_vapic_addr>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_vapic_addr))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vapic_addr>())).vapic_addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vapic_addr),
+ "::",
+ stringify!(vapic_addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_mp_state {
+ pub mp_state: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_mp_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_mp_state>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_mp_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_mp_state>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_mp_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_mp_state>())).mp_state as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_mp_state),
+ "::",
+ stringify!(mp_state)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_psw {
+ pub mask: __u64,
+ pub addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_psw() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_psw>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_s390_psw))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_psw>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_psw))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_psw>())).mask as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_psw),
+ "::",
+ stringify!(mask)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_psw>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_psw),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_interrupt {
+ pub type_: __u32,
+ pub parm: __u32,
+ pub parm64: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_interrupt() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_interrupt>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_s390_interrupt))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_interrupt>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_interrupt))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_interrupt>())).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_interrupt),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_interrupt>())).parm as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_interrupt),
+ "::",
+ stringify!(parm)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_interrupt>())).parm64 as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_interrupt),
+ "::",
+ stringify!(parm64)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_io_info {
+ pub subchannel_id: __u16,
+ pub subchannel_nr: __u16,
+ pub io_int_parm: __u32,
+ pub io_int_word: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_io_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_io_info>(),
+ 12usize,
+ concat!("Size of: ", stringify!(kvm_s390_io_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_io_info>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_s390_io_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_io_info>())).subchannel_id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_io_info),
+ "::",
+ stringify!(subchannel_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_io_info>())).subchannel_nr as *const _ as usize },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_io_info),
+ "::",
+ stringify!(subchannel_nr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_io_info>())).io_int_parm as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_io_info),
+ "::",
+ stringify!(io_int_parm)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_io_info>())).io_int_word as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_io_info),
+ "::",
+ stringify!(io_int_word)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_ext_info {
+ pub ext_params: __u32,
+ pub pad: __u32,
+ pub ext_params2: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_ext_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_ext_info>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_s390_ext_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_ext_info>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_ext_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ext_info>())).ext_params as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ext_info),
+ "::",
+ stringify!(ext_params)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ext_info>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ext_info),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ext_info>())).ext_params2 as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ext_info),
+ "::",
+ stringify!(ext_params2)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_pgm_info {
+ pub trans_exc_code: __u64,
+ pub mon_code: __u64,
+ pub per_address: __u64,
+ pub data_exc_code: __u32,
+ pub code: __u16,
+ pub mon_class_nr: __u16,
+ pub per_code: __u8,
+ pub per_atmid: __u8,
+ pub exc_access_id: __u8,
+ pub per_access_id: __u8,
+ pub op_access_id: __u8,
+ pub pad: [__u8; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_pgm_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_pgm_info>(),
+ 40usize,
+ concat!("Size of: ", stringify!(kvm_s390_pgm_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_pgm_info>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_pgm_info))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_pgm_info>())).trans_exc_code as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(trans_exc_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).mon_code as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(mon_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).per_address as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(per_address)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).data_exc_code as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(data_exc_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).code as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).mon_class_nr as *const _ as usize },
+ 30usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(mon_class_nr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).per_code as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(per_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).per_atmid as *const _ as usize },
+ 33usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(per_atmid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).exc_access_id as *const _ as usize },
+ 34usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(exc_access_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).per_access_id as *const _ as usize },
+ 35usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(per_access_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).op_access_id as *const _ as usize },
+ 36usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(op_access_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).pad as *const _ as usize },
+ 37usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_prefix_info {
+ pub address: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_prefix_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_prefix_info>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_s390_prefix_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_prefix_info>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_s390_prefix_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_prefix_info>())).address as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_prefix_info),
+ "::",
+ stringify!(address)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_extcall_info {
+ pub code: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_extcall_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_extcall_info>(),
+ 2usize,
+ concat!("Size of: ", stringify!(kvm_s390_extcall_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_extcall_info>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(kvm_s390_extcall_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_extcall_info>())).code as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_extcall_info),
+ "::",
+ stringify!(code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_emerg_info {
+ pub code: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_emerg_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_emerg_info>(),
+ 2usize,
+ concat!("Size of: ", stringify!(kvm_s390_emerg_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_emerg_info>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(kvm_s390_emerg_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_emerg_info>())).code as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_emerg_info),
+ "::",
+ stringify!(code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_stop_info {
+ pub flags: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_stop_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_stop_info>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_s390_stop_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_stop_info>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_s390_stop_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_stop_info>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_stop_info),
+ "::",
+ stringify!(flags)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_mchk_info {
+ pub cr14: __u64,
+ pub mcic: __u64,
+ pub failing_storage_address: __u64,
+ pub ext_damage_code: __u32,
+ pub pad: __u32,
+ pub fixed_logout: [__u8; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_mchk_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_mchk_info>(),
+ 48usize,
+ concat!("Size of: ", stringify!(kvm_s390_mchk_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_mchk_info>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_mchk_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mchk_info>())).cr14 as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(cr14)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mchk_info>())).mcic as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(mcic)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_mchk_info>())).failing_storage_address as *const _
+ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(failing_storage_address)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_mchk_info>())).ext_damage_code as *const _ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(ext_damage_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mchk_info>())).pad as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mchk_info>())).fixed_logout as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(fixed_logout)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_s390_irq {
+ pub type_: __u64,
+ pub u: kvm_s390_irq__bindgen_ty_1,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_s390_irq__bindgen_ty_1 {
+ pub io: kvm_s390_io_info,
+ pub ext: kvm_s390_ext_info,
+ pub pgm: kvm_s390_pgm_info,
+ pub emerg: kvm_s390_emerg_info,
+ pub extcall: kvm_s390_extcall_info,
+ pub prefix: kvm_s390_prefix_info,
+ pub stop: kvm_s390_stop_info,
+ pub mchk: kvm_s390_mchk_info,
+ pub reserved: [::std::os::raw::c_char; 64usize],
+ _bindgen_union_align: [u64; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_irq__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_irq__bindgen_ty_1>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_s390_irq__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_irq__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_irq__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).io as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(io)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).ext as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(ext)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).pgm as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(pgm)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).emerg as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(emerg)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).extcall as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(extcall)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).prefix as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(prefix)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).stop as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(stop)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).mchk as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(mchk)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).reserved as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Default for kvm_s390_irq__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_irq() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_irq>(),
+ 72usize,
+ concat!("Size of: ", stringify!(kvm_s390_irq))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_irq>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_irq))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq>())).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq>())).u as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq),
+ "::",
+ stringify!(u)
+ )
+ );
+}
+impl Default for kvm_s390_irq {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_irq_state {
+ pub buf: __u64,
+ pub flags: __u32,
+ pub len: __u32,
+ pub reserved: [__u32; 4usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_irq_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_irq_state>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_s390_irq_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_irq_state>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_irq_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq_state>())).buf as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq_state),
+ "::",
+ stringify!(buf)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq_state>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq_state),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq_state>())).len as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq_state),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq_state>())).reserved as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq_state),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_guest_debug {
+ pub control: __u32,
+ pub pad: __u32,
+ pub arch: kvm_guest_debug_arch,
+}
+#[test]
+fn bindgen_test_layout_kvm_guest_debug() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_guest_debug>(),
+ 520usize,
+ concat!("Size of: ", stringify!(kvm_guest_debug))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_guest_debug>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_guest_debug))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug>())).control as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug),
+ "::",
+ stringify!(control)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug>())).arch as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug),
+ "::",
+ stringify!(arch)
+ )
+ );
+}
+pub const kvm_ioeventfd_flag_nr_datamatch: _bindgen_ty_1 = 0;
+pub const kvm_ioeventfd_flag_nr_pio: _bindgen_ty_1 = 1;
+pub const kvm_ioeventfd_flag_nr_deassign: _bindgen_ty_1 = 2;
+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;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_ioeventfd {
+ pub datamatch: __u64,
+ pub addr: __u64,
+ pub len: __u32,
+ pub fd: __s32,
+ pub flags: __u32,
+ pub pad: [__u8; 36usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_ioeventfd() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ioeventfd>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_ioeventfd))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ioeventfd>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_ioeventfd))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).datamatch as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(datamatch)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).len as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).fd as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(fd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).flags as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).pad as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+impl Default for kvm_ioeventfd {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_enable_cap {
+ pub cap: __u32,
+ pub flags: __u32,
+ pub args: [__u64; 4usize],
+ pub pad: [__u8; 64usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_enable_cap() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_enable_cap>(),
+ 104usize,
+ concat!("Size of: ", stringify!(kvm_enable_cap))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_enable_cap>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_enable_cap))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_enable_cap>())).cap as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_enable_cap),
+ "::",
+ stringify!(cap)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_enable_cap>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_enable_cap),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_enable_cap>())).args as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_enable_cap),
+ "::",
+ stringify!(args)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_enable_cap>())).pad as *const _ as usize },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_enable_cap),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+impl Default for kvm_enable_cap {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_ppc_pvinfo {
+ pub flags: __u32,
+ pub hcall: [__u32; 4usize],
+ pub pad: [__u8; 108usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_pvinfo() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ppc_pvinfo>(),
+ 128usize,
+ concat!("Size of: ", stringify!(kvm_ppc_pvinfo))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ppc_pvinfo>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_ppc_pvinfo))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_pvinfo>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_pvinfo),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_pvinfo>())).hcall as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_pvinfo),
+ "::",
+ stringify!(hcall)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_pvinfo>())).pad as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_pvinfo),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+impl Default for kvm_ppc_pvinfo {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ppc_one_page_size {
+ pub page_shift: __u32,
+ pub pte_enc: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_one_page_size() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ppc_one_page_size>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_ppc_one_page_size))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ppc_one_page_size>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_ppc_one_page_size))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ppc_one_page_size>())).page_shift as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_page_size),
+ "::",
+ stringify!(page_shift)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_one_page_size>())).pte_enc as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_page_size),
+ "::",
+ stringify!(pte_enc)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ppc_one_seg_page_size {
+ pub page_shift: __u32,
+ pub slb_enc: __u32,
+ pub enc: [kvm_ppc_one_page_size; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_one_seg_page_size() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ppc_one_seg_page_size>(),
+ 72usize,
+ concat!("Size of: ", stringify!(kvm_ppc_one_seg_page_size))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ppc_one_seg_page_size>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_ppc_one_seg_page_size))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ppc_one_seg_page_size>())).page_shift as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_seg_page_size),
+ "::",
+ stringify!(page_shift)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ppc_one_seg_page_size>())).slb_enc as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_seg_page_size),
+ "::",
+ stringify!(slb_enc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_one_seg_page_size>())).enc as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_seg_page_size),
+ "::",
+ stringify!(enc)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ppc_smmu_info {
+ pub flags: __u64,
+ pub slb_size: __u32,
+ pub pad: __u32,
+ pub sps: [kvm_ppc_one_seg_page_size; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_smmu_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ppc_smmu_info>(),
+ 592usize,
+ concat!("Size of: ", stringify!(kvm_ppc_smmu_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ppc_smmu_info>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_ppc_smmu_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_smmu_info),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).slb_size as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_smmu_info),
+ "::",
+ stringify!(slb_size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).pad as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_smmu_info),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).sps as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_smmu_info),
+ "::",
+ stringify!(sps)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irq_routing_irqchip {
+ pub irqchip: __u32,
+ pub pin: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_irqchip() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_irqchip>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_irqchip))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_irqchip>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing_irqchip))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_irqchip>())).irqchip as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_irqchip),
+ "::",
+ stringify!(irqchip)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_irqchip>())).pin as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_irqchip),
+ "::",
+ stringify!(pin)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irq_routing_msi {
+ pub address_lo: __u32,
+ pub address_hi: __u32,
+ pub data: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_msi() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_msi>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_msi))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_msi>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing_msi))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_msi>())).address_lo as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_msi),
+ "::",
+ stringify!(address_lo)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_msi>())).address_hi as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_msi),
+ "::",
+ stringify!(address_hi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_msi>())).data as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_msi),
+ "::",
+ 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)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irq_routing_s390_adapter {
+ pub ind_addr: __u64,
+ pub summary_addr: __u64,
+ pub ind_offset: __u64,
+ pub summary_offset: __u32,
+ pub adapter_id: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_s390_adapter() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_s390_adapter>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_s390_adapter))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_s390_adapter>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing_s390_adapter))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).ind_addr as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(ind_addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).summary_addr as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(summary_addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).ind_offset as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(ind_offset)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).summary_offset as *const _
+ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(summary_offset)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).adapter_id as *const _ as usize
+ },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(adapter_id)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_irq_routing_entry {
+ pub gsi: __u32,
+ pub type_: __u32,
+ pub flags: __u32,
+ pub pad: __u32,
+ pub u: kvm_irq_routing_entry__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_irq_routing_entry__bindgen_ty_1 {
+ pub irqchip: kvm_irq_routing_irqchip,
+ pub msi: kvm_irq_routing_msi,
+ pub adapter: kvm_irq_routing_s390_adapter,
+ pub pad: [__u32; 8usize],
+ _bindgen_union_align: [u64; 4usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_entry__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_entry__bindgen_ty_1>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_entry__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_entry__bindgen_ty_1>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).irqchip as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+ "::",
+ stringify!(irqchip)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).msi as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+ "::",
+ stringify!(msi)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).adapter as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+ "::",
+ stringify!(adapter)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).pad as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+impl Default for kvm_irq_routing_entry__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_entry() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_entry>(),
+ 48usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_entry))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_entry>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing_entry))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).gsi as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(gsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).type_ as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).pad as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).u as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(u)
+ )
+ );
+}
+impl Default for kvm_irq_routing_entry {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+pub struct kvm_irq_routing {
+ 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() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing>())).nr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing),
+ "::",
+ stringify!(nr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing>())).entries as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing),
+ "::",
+ stringify!(entries)
+ )
+ );
+}
+impl Default for kvm_irq_routing {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irqfd {
+ pub fd: __u32,
+ pub gsi: __u32,
+ pub flags: __u32,
+ pub resamplefd: __u32,
+ pub pad: [__u8; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_irqfd() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irqfd>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_irqfd))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irqfd>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irqfd))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).fd as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(fd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).gsi as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(gsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).resamplefd as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(resamplefd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).pad as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_clock_data {
+ pub clock: __u64,
+ pub flags: __u32,
+ pub pad: [__u32; 9usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_clock_data() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_clock_data>(),
+ 48usize,
+ concat!("Size of: ", stringify!(kvm_clock_data))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_clock_data>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_clock_data))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_clock_data>())).clock as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_clock_data),
+ "::",
+ stringify!(clock)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_clock_data>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_clock_data),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_clock_data>())).pad as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_clock_data),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_config_tlb {
+ pub params: __u64,
+ pub array: __u64,
+ pub mmu_type: __u32,
+ pub array_len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_config_tlb() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_config_tlb>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_config_tlb))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_config_tlb>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_config_tlb))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_config_tlb>())).params as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_config_tlb),
+ "::",
+ stringify!(params)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_config_tlb>())).array as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_config_tlb),
+ "::",
+ stringify!(array)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_config_tlb>())).mmu_type as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_config_tlb),
+ "::",
+ stringify!(mmu_type)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_config_tlb>())).array_len as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_config_tlb),
+ "::",
+ stringify!(array_len)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_dirty_tlb {
+ pub bitmap: __u64,
+ pub num_dirty: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_dirty_tlb() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_dirty_tlb>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_dirty_tlb))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_dirty_tlb>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_dirty_tlb))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dirty_tlb>())).bitmap as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_tlb),
+ "::",
+ stringify!(bitmap)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dirty_tlb>())).num_dirty as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_tlb),
+ "::",
+ stringify!(num_dirty)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_reg_list {
+ pub n: __u64,
+ pub reg: __IncompleteArrayField<__u64>,
+}
+#[test]
+fn bindgen_test_layout_kvm_reg_list() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_reg_list>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_reg_list))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_reg_list>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_reg_list))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_reg_list>())).n as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_reg_list),
+ "::",
+ stringify!(n)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_reg_list>())).reg as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_reg_list),
+ "::",
+ stringify!(reg)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_one_reg {
+ pub id: __u64,
+ pub addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_one_reg() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_one_reg>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_one_reg))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_one_reg>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_one_reg))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_one_reg>())).id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_one_reg),
+ "::",
+ stringify!(id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_one_reg>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_one_reg),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_msi {
+ pub address_lo: __u32,
+ pub address_hi: __u32,
+ pub data: __u32,
+ pub flags: __u32,
+ pub pad: [__u8; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_msi() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_msi>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_msi))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_msi>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_msi))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).address_lo as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(address_lo)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).address_hi as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(address_hi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).data as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(data)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).flags as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).pad as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_arm_device_addr {
+ pub id: __u64,
+ pub addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_arm_device_addr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_arm_device_addr>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_arm_device_addr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_arm_device_addr>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_arm_device_addr))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_arm_device_addr>())).id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_arm_device_addr),
+ "::",
+ stringify!(id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_arm_device_addr>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_arm_device_addr),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_create_device {
+ pub type_: __u32,
+ pub fd: __u32,
+ pub flags: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_create_device() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_create_device>(),
+ 12usize,
+ concat!("Size of: ", stringify!(kvm_create_device))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_create_device>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_create_device))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_create_device>())).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_create_device),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_create_device>())).fd as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_create_device),
+ "::",
+ stringify!(fd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_create_device>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_create_device),
+ "::",
+ stringify!(flags)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_device_attr {
+ pub flags: __u32,
+ pub group: __u32,
+ pub attr: __u64,
+ pub addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_device_attr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_device_attr>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_device_attr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_device_attr>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_device_attr))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_device_attr>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_device_attr),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_device_attr>())).group as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_device_attr),
+ "::",
+ stringify!(group)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_device_attr>())).attr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_device_attr),
+ "::",
+ stringify!(attr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_device_attr>())).addr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_device_attr),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+pub const kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20: kvm_device_type = 1;
+pub const kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_42: kvm_device_type = 2;
+pub const kvm_device_type_KVM_DEV_TYPE_XICS: kvm_device_type = 3;
+pub const kvm_device_type_KVM_DEV_TYPE_VFIO: kvm_device_type = 4;
+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;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_ucas_mapping {
+ pub user_addr: __u64,
+ pub vcpu_addr: __u64,
+ pub length: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_ucas_mapping() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_ucas_mapping>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_s390_ucas_mapping))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_ucas_mapping>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_ucas_mapping))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ucas_mapping>())).user_addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ucas_mapping),
+ "::",
+ stringify!(user_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ucas_mapping>())).vcpu_addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ucas_mapping),
+ "::",
+ stringify!(vcpu_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ucas_mapping>())).length as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ucas_mapping),
+ "::",
+ stringify!(length)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_assigned_pci_dev {
+ pub assigned_dev_id: __u32,
+ pub busnr: __u32,
+ pub devfn: __u32,
+ pub flags: __u32,
+ pub segnr: __u32,
+ pub __bindgen_anon_1: kvm_assigned_pci_dev__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_assigned_pci_dev__bindgen_ty_1 {
+ pub reserved: [__u32; 11usize],
+ _bindgen_union_align: [u32; 11usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_pci_dev__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_pci_dev__bindgen_ty_1>(),
+ 44usize,
+ concat!("Size of: ", stringify!(kvm_assigned_pci_dev__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_pci_dev__bindgen_ty_1>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_assigned_pci_dev__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_pci_dev__bindgen_ty_1>())).reserved as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev__bindgen_ty_1),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Default for kvm_assigned_pci_dev__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_pci_dev() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_pci_dev>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_assigned_pci_dev))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_pci_dev>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_pci_dev))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).assigned_dev_id as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(assigned_dev_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).busnr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(busnr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).devfn as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(devfn)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).flags as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).segnr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(segnr)
+ )
+ );
+}
+impl Default for kvm_assigned_pci_dev {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_assigned_irq {
+ pub assigned_dev_id: __u32,
+ pub host_irq: __u32,
+ pub guest_irq: __u32,
+ pub flags: __u32,
+ pub __bindgen_anon_1: kvm_assigned_irq__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_assigned_irq__bindgen_ty_1 {
+ pub reserved: [__u32; 12usize],
+ _bindgen_union_align: [u32; 12usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_irq__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_irq__bindgen_ty_1>(),
+ 48usize,
+ concat!("Size of: ", stringify!(kvm_assigned_irq__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_irq__bindgen_ty_1>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_irq__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_irq__bindgen_ty_1>())).reserved as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq__bindgen_ty_1),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Default for kvm_assigned_irq__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_irq() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_irq>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_assigned_irq))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_irq>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_irq))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_irq>())).assigned_dev_id as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq),
+ "::",
+ stringify!(assigned_dev_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_irq>())).host_irq as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq),
+ "::",
+ stringify!(host_irq)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_irq>())).guest_irq as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq),
+ "::",
+ stringify!(guest_irq)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_irq>())).flags as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq),
+ "::",
+ stringify!(flags)
+ )
+ );
+}
+impl Default for kvm_assigned_irq {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_assigned_msix_nr {
+ pub assigned_dev_id: __u32,
+ pub entry_nr: __u16,
+ pub padding: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_msix_nr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_msix_nr>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_assigned_msix_nr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_msix_nr>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_msix_nr))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_msix_nr>())).assigned_dev_id as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_nr),
+ "::",
+ stringify!(assigned_dev_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_nr>())).entry_nr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_nr),
+ "::",
+ stringify!(entry_nr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_nr>())).padding as *const _ as usize },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_nr),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_assigned_msix_entry {
+ pub assigned_dev_id: __u32,
+ pub gsi: __u32,
+ pub entry: __u16,
+ pub padding: [__u16; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_msix_entry() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_msix_entry>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_assigned_msix_entry))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_msix_entry>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_msix_entry))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_msix_entry>())).assigned_dev_id as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_entry),
+ "::",
+ stringify!(assigned_dev_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_entry>())).gsi as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_entry),
+ "::",
+ stringify!(gsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_entry>())).entry as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_entry),
+ "::",
+ stringify!(entry)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_entry>())).padding as *const _ as usize },
+ 10usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_entry),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+pub type __uint128_t = [u64; 2];
diff --git a/kvm_sys/src/lib.rs b/kvm_sys/src/lib.rs
new file mode 100644
index 0000000..b9748a3
--- /dev/null
+++ b/kvm_sys/src/lib.rs
@@ -0,0 +1,161 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+
+use sys_util::{ioctl_io_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr};
+
+// Somehow this one gets missed by bindgen
+pub const KVM_EXIT_IO_OUT: ::std::os::raw::c_uint = 1;
+
+// Each of the below modules defines ioctls specific to their platform.
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub mod x86 {
+ // generated with bindgen /usr/include/linux/kvm.h --no-unstable-rust --constified-enum '*' --with-derive-default
+ #[allow(clippy::all)]
+ pub mod bindings;
+ pub use crate::bindings::*;
+ use sys_util::{ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr};
+
+ ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing);
+ ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x02, kvm_msr_list);
+ ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2);
+ ioctl_iowr_nr!(KVM_GET_EMULATED_CPUID, KVMIO, 0x09, kvm_cpuid2);
+ ioctl_iow_nr!(KVM_SET_MEMORY_ALIAS, KVMIO, 0x43, kvm_memory_alias);
+ ioctl_iow_nr!(KVM_XEN_HVM_CONFIG, KVMIO, 0x7a, kvm_xen_hvm_config);
+ ioctl_ior_nr!(KVM_GET_PIT2, KVMIO, 0x9f, kvm_pit_state2);
+ ioctl_iow_nr!(KVM_SET_PIT2, KVMIO, 0xa0, kvm_pit_state2);
+ ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs);
+ ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs);
+ ioctl_iow_nr!(KVM_SET_CPUID, KVMIO, 0x8a, kvm_cpuid);
+ ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state);
+ ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state);
+ ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2);
+ ioctl_iowr_nr!(KVM_GET_CPUID2, KVMIO, 0x91, kvm_cpuid2);
+ ioctl_iow_nr!(KVM_X86_SETUP_MCE, KVMIO, 0x9c, __u64);
+ ioctl_ior_nr!(KVM_X86_GET_MCE_CAP_SUPPORTED, KVMIO, 0x9d, __u64);
+ ioctl_iow_nr!(KVM_X86_SET_MCE, KVMIO, 0x9e, kvm_x86_mce);
+ ioctl_ior_nr!(KVM_GET_VCPU_EVENTS, KVMIO, 0x9f, kvm_vcpu_events);
+ ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events);
+ ioctl_ior_nr!(KVM_GET_DEBUGREGS, KVMIO, 0xa1, kvm_debugregs);
+ ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs);
+ ioctl_ior_nr!(KVM_GET_XSAVE, KVMIO, 0xa4, kvm_xsave);
+ ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave);
+ ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs);
+ ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs);
+}
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+pub mod aarch64 {
+ // generated with bindgen <arm sysroot>/usr/include/linux/kvm.h --no-unstable-rust --constified-enum '*' --with-derive-default -- -I<arm sysroot>/usr/include
+ pub mod bindings;
+ pub use bindings::*;
+ use sys_util::{ioctl_ior_nr, ioctl_iow_nr};
+
+ ioctl_iow_nr!(KVM_ARM_SET_DEVICE_ADDR, KVMIO, 0xab, kvm_arm_device_addr);
+ ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init);
+ ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init);
+}
+
+// These ioctls are commonly defined on all/multiple platforms.
+ioctl_io_nr!(KVM_GET_API_VERSION, KVMIO, 0x00);
+ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01);
+ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03);
+ioctl_io_nr!(KVM_GET_VCPU_MMAP_SIZE, KVMIO, 0x04);
+ioctl_iow_nr!(KVM_SET_MEMORY_REGION, KVMIO, 0x40, kvm_memory_region);
+ioctl_io_nr!(KVM_CREATE_VCPU, KVMIO, 0x41);
+ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log);
+ioctl_io_nr!(KVM_SET_NR_MMU_PAGES, KVMIO, 0x44);
+ioctl_io_nr!(KVM_GET_NR_MMU_PAGES, KVMIO, 0x45);
+ioctl_iow_nr!(
+ KVM_SET_USER_MEMORY_REGION,
+ KVMIO,
+ 0x46,
+ kvm_userspace_memory_region
+);
+ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47);
+ioctl_iow_nr!(KVM_SET_IDENTITY_MAP_ADDR, KVMIO, 0x48, __u64);
+ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60);
+ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level);
+ioctl_iowr_nr!(KVM_GET_IRQCHIP, KVMIO, 0x62, kvm_irqchip);
+ioctl_ior_nr!(KVM_SET_IRQCHIP, KVMIO, 0x63, kvm_irqchip);
+ioctl_io_nr!(KVM_CREATE_PIT, KVMIO, 0x64);
+ioctl_iowr_nr!(KVM_IRQ_LINE_STATUS, KVMIO, 0x67, kvm_irq_level);
+ioctl_iow_nr!(
+ KVM_REGISTER_COALESCED_MMIO,
+ KVMIO,
+ 0x67,
+ kvm_coalesced_mmio_zone
+);
+ioctl_iow_nr!(
+ KVM_UNREGISTER_COALESCED_MMIO,
+ KVMIO,
+ 0x68,
+ kvm_coalesced_mmio_zone
+);
+ioctl_ior_nr!(KVM_ASSIGN_PCI_DEVICE, KVMIO, 0x69, kvm_assigned_pci_dev);
+ioctl_iow_nr!(KVM_ASSIGN_DEV_IRQ, KVMIO, 0x70, kvm_assigned_irq);
+ioctl_io_nr!(KVM_REINJECT_CONTROL, KVMIO, 0x71);
+ioctl_iow_nr!(KVM_DEASSIGN_PCI_DEVICE, KVMIO, 0x72, kvm_assigned_pci_dev);
+ioctl_iow_nr!(KVM_ASSIGN_SET_MSIX_NR, KVMIO, 0x73, kvm_assigned_msix_nr);
+ioctl_iow_nr!(
+ KVM_ASSIGN_SET_MSIX_ENTRY,
+ KVMIO,
+ 0x74,
+ kvm_assigned_msix_entry
+);
+ioctl_iow_nr!(KVM_DEASSIGN_DEV_IRQ, KVMIO, 0x75, kvm_assigned_irq);
+ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd);
+ioctl_iow_nr!(KVM_CREATE_PIT2, KVMIO, 0x77, kvm_pit_config);
+ioctl_io_nr!(KVM_SET_BOOT_CPU_ID, KVMIO, 0x78);
+ioctl_iow_nr!(KVM_IOEVENTFD, KVMIO, 0x79, kvm_ioeventfd);
+ioctl_iow_nr!(KVM_SET_CLOCK, KVMIO, 0x7b, kvm_clock_data);
+ioctl_ior_nr!(KVM_GET_CLOCK, KVMIO, 0x7c, kvm_clock_data);
+ioctl_io_nr!(KVM_SET_TSC_KHZ, KVMIO, 0xa2);
+ioctl_io_nr!(KVM_GET_TSC_KHZ, KVMIO, 0xa3);
+ioctl_iow_nr!(KVM_ASSIGN_SET_INTX_MASK, KVMIO, 0xa4, kvm_assigned_pci_dev);
+ioctl_iow_nr!(KVM_SIGNAL_MSI, KVMIO, 0xa5, kvm_msi);
+ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device);
+ioctl_iow_nr!(KVM_SET_DEVICE_ATTR, KVMIO, 0xe1, kvm_device_attr);
+ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr);
+ioctl_iow_nr!(KVM_HAS_DEVICE_ATTR, KVMIO, 0xe3, kvm_device_attr);
+ioctl_io_nr!(KVM_RUN, KVMIO, 0x80);
+// The following two ioctls are commonly defined but specifically excluded
+// from arm platforms.
+#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
+ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs);
+#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
+ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs);
+ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs);
+ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs);
+ioctl_iowr_nr!(KVM_TRANSLATE, KVMIO, 0x85, kvm_translation);
+ioctl_iow_nr!(KVM_INTERRUPT, KVMIO, 0x86, kvm_interrupt);
+ioctl_iow_nr!(KVM_SET_SIGNAL_MASK, KVMIO, 0x8b, kvm_signal_mask);
+ioctl_ior_nr!(KVM_GET_FPU, KVMIO, 0x8c, kvm_fpu);
+ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu);
+ioctl_iowr_nr!(KVM_TPR_ACCESS_REPORTING, KVMIO, 0x92, kvm_tpr_access_ctl);
+ioctl_iow_nr!(KVM_SET_VAPIC_ADDR, KVMIO, 0x93, kvm_vapic_addr);
+ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state);
+ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state);
+ioctl_io_nr!(KVM_NMI, KVMIO, 0x9a);
+ioctl_iow_nr!(KVM_SET_GUEST_DEBUG, KVMIO, 0x9b, kvm_guest_debug);
+ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap);
+ioctl_iow_nr!(KVM_DIRTY_TLB, KVMIO, 0xaa, kvm_dirty_tlb);
+ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg);
+ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg);
+ioctl_io_nr!(KVM_KVMCLOCK_CTRL, KVMIO, 0xad);
+ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list);
+ioctl_io_nr!(KVM_SMI, KVMIO, 0xb7);
+
+// Along with the common ioctls, we reexport the ioctls of the current
+// platform.
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub use crate::x86::*;
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+pub use aarch64::*;
diff --git a/kvm_sys/src/x86/bindings.rs b/kvm_sys/src/x86/bindings.rs
new file mode 100644
index 0000000..06b8680
--- /dev/null
+++ b/kvm_sys/src/x86/bindings.rs
@@ -0,0 +1,8967 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
+pub struct __BindgenBitfieldUnit<Storage, Align>
+where
+ Storage: AsRef<[u8]> + AsMut<[u8]>,
+{
+ storage: Storage,
+ align: [Align; 0],
+}
+
+impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
+where
+ Storage: AsRef<[u8]> + AsMut<[u8]>,
+{
+ #[inline]
+ 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 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 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;
+ }
+ }
+
+ 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);
+ }
+ }
+}
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData)
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ 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_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_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_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 type __s8 = ::std::os::raw::c_schar;
+pub type __u8 = ::std::os::raw::c_uchar;
+pub type __s16 = ::std::os::raw::c_short;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __s32 = ::std::os::raw::c_int;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __s64 = ::std::os::raw::c_longlong;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __kernel_fd_set {
+ pub fds_bits: [::std::os::raw::c_ulong; 16usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fd_set() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fd_set>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fd_set>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__kernel_fd_set>())).fds_bits as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__kernel_fd_set),
+ "::",
+ stringify!(fds_bits)
+ )
+ );
+}
+pub type __kernel_sighandler_t =
+ ::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
+pub type __kernel_key_t = ::std::os::raw::c_int;
+pub type __kernel_mqd_t = ::std::os::raw::c_int;
+pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
+pub type __kernel_long_t = ::std::os::raw::c_long;
+pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
+pub type __kernel_ino_t = __kernel_ulong_t;
+pub type __kernel_mode_t = ::std::os::raw::c_uint;
+pub type __kernel_pid_t = ::std::os::raw::c_int;
+pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
+pub type __kernel_uid_t = ::std::os::raw::c_uint;
+pub type __kernel_gid_t = ::std::os::raw::c_uint;
+pub type __kernel_suseconds_t = __kernel_long_t;
+pub type __kernel_daddr_t = ::std::os::raw::c_int;
+pub type __kernel_uid32_t = ::std::os::raw::c_uint;
+pub type __kernel_gid32_t = ::std::os::raw::c_uint;
+pub type __kernel_size_t = __kernel_ulong_t;
+pub type __kernel_ssize_t = __kernel_long_t;
+pub type __kernel_ptrdiff_t = __kernel_long_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __kernel_fsid_t {
+ pub val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fsid_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fsid_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fsid_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__kernel_fsid_t>())).val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__kernel_fsid_t),
+ "::",
+ stringify!(val)
+ )
+ );
+}
+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_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;
+pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
+pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
+pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
+pub type __le16 = __u16;
+pub type __be16 = __u16;
+pub type __le32 = __u32;
+pub type __be32 = __u32;
+pub type __le64 = __u64;
+pub type __be64 = __u64;
+pub type __sum16 = __u16;
+pub type __wsum = __u32;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_memory_alias {
+ pub slot: __u32,
+ pub flags: __u32,
+ pub guest_phys_addr: __u64,
+ pub memory_size: __u64,
+ pub target_phys_addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_memory_alias() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_memory_alias>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_memory_alias))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_memory_alias>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_memory_alias))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_alias>())).slot as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_alias),
+ "::",
+ stringify!(slot)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_alias>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_alias),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_memory_alias>())).guest_phys_addr as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_alias),
+ "::",
+ stringify!(guest_phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_alias>())).memory_size as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_alias),
+ "::",
+ stringify!(memory_size)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_memory_alias>())).target_phys_addr as *const _ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_alias),
+ "::",
+ stringify!(target_phys_addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_pic_state {
+ pub last_irr: __u8,
+ pub irr: __u8,
+ pub imr: __u8,
+ pub isr: __u8,
+ pub priority_add: __u8,
+ pub irq_base: __u8,
+ pub read_reg_select: __u8,
+ pub poll: __u8,
+ pub special_mask: __u8,
+ pub init_state: __u8,
+ pub auto_eoi: __u8,
+ pub rotate_on_auto_eoi: __u8,
+ pub special_fully_nested_mode: __u8,
+ pub init4: __u8,
+ pub elcr: __u8,
+ pub elcr_mask: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_pic_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_pic_state>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_pic_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_pic_state>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_pic_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).last_irr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(last_irr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).irr as *const _ as usize },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(irr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).imr as *const _ as usize },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(imr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).isr as *const _ as usize },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(isr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).priority_add as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(priority_add)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).irq_base as *const _ as usize },
+ 5usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(irq_base)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).read_reg_select as *const _ as usize },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(read_reg_select)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).poll as *const _ as usize },
+ 7usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(poll)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).special_mask as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(special_mask)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).init_state as *const _ as usize },
+ 9usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(init_state)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).auto_eoi as *const _ as usize },
+ 10usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(auto_eoi)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pic_state>())).rotate_on_auto_eoi as *const _ as usize
+ },
+ 11usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(rotate_on_auto_eoi)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pic_state>())).special_fully_nested_mode as *const _ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(special_fully_nested_mode)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).init4 as *const _ as usize },
+ 13usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(init4)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).elcr as *const _ as usize },
+ 14usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(elcr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pic_state>())).elcr_mask as *const _ as usize },
+ 15usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pic_state),
+ "::",
+ stringify!(elcr_mask)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_ioapic_state {
+ pub base_address: __u64,
+ pub ioregsel: __u32,
+ pub id: __u32,
+ pub irr: __u32,
+ pub pad: __u32,
+ pub redirtbl: [kvm_ioapic_state__bindgen_ty_1; 24usize],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_ioapic_state__bindgen_ty_1 {
+ pub bits: __u64,
+ pub fields: kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1,
+ _bindgen_union_align: u64,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1 {
+ pub vector: __u8,
+ pub _bitfield_1: __BindgenBitfieldUnit<[u8; 2usize], u8>,
+ pub reserved: [__u8; 4usize],
+ pub dest_id: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1>(),
+ 8usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1>(),
+ 1usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1>())).vector
+ as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1),
+ "::",
+ stringify!(vector)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1>())).reserved
+ as *const _ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1),
+ "::",
+ stringify!(reserved)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1>())).dest_id
+ as *const _ as usize
+ },
+ 7usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1),
+ "::",
+ stringify!(dest_id)
+ )
+ );
+}
+impl kvm_ioapic_state__bindgen_ty_1__bindgen_ty_1 {
+ #[inline]
+ pub fn delivery_mode(&self) -> __u8 {
+ unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 3u8) as u8) }
+ }
+ #[inline]
+ pub fn set_delivery_mode(&mut self, val: __u8) {
+ unsafe {
+ let val: u8 = ::std::mem::transmute(val);
+ self._bitfield_1.set(0usize, 3u8, val as u64)
+ }
+ }
+ #[inline]
+ pub fn dest_mode(&self) -> __u8 {
+ unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u8) }
+ }
+ #[inline]
+ pub fn set_dest_mode(&mut self, val: __u8) {
+ unsafe {
+ let val: u8 = ::std::mem::transmute(val);
+ self._bitfield_1.set(3usize, 1u8, val as u64)
+ }
+ }
+ #[inline]
+ pub fn delivery_status(&self) -> __u8 {
+ unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u8) }
+ }
+ #[inline]
+ pub fn set_delivery_status(&mut self, val: __u8) {
+ unsafe {
+ let val: u8 = ::std::mem::transmute(val);
+ self._bitfield_1.set(4usize, 1u8, val as u64)
+ }
+ }
+ #[inline]
+ pub fn polarity(&self) -> __u8 {
+ unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u8) }
+ }
+ #[inline]
+ pub fn set_polarity(&mut self, val: __u8) {
+ unsafe {
+ let val: u8 = ::std::mem::transmute(val);
+ self._bitfield_1.set(5usize, 1u8, val as u64)
+ }
+ }
+ #[inline]
+ pub fn remote_irr(&self) -> __u8 {
+ unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u8) }
+ }
+ #[inline]
+ pub fn set_remote_irr(&mut self, val: __u8) {
+ unsafe {
+ let val: u8 = ::std::mem::transmute(val);
+ self._bitfield_1.set(6usize, 1u8, val as u64)
+ }
+ }
+ #[inline]
+ pub fn trig_mode(&self) -> __u8 {
+ unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u8) }
+ }
+ #[inline]
+ pub fn set_trig_mode(&mut self, val: __u8) {
+ unsafe {
+ let val: u8 = ::std::mem::transmute(val);
+ self._bitfield_1.set(7usize, 1u8, val as u64)
+ }
+ }
+ #[inline]
+ pub fn mask(&self) -> __u8 {
+ unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u8) }
+ }
+ #[inline]
+ pub fn set_mask(&mut self, val: __u8) {
+ unsafe {
+ let val: u8 = ::std::mem::transmute(val);
+ self._bitfield_1.set(8usize, 1u8, val as u64)
+ }
+ }
+ #[inline]
+ pub fn reserve(&self) -> __u8 {
+ unsafe { ::std::mem::transmute(self._bitfield_1.get(9usize, 7u8) as u8) }
+ }
+ #[inline]
+ pub fn set_reserve(&mut self, val: __u8) {
+ unsafe {
+ let val: u8 = ::std::mem::transmute(val);
+ self._bitfield_1.set(9usize, 7u8, val as u64)
+ }
+ }
+ #[inline]
+ pub fn new_bitfield_1(
+ delivery_mode: __u8,
+ dest_mode: __u8,
+ delivery_status: __u8,
+ polarity: __u8,
+ remote_irr: __u8,
+ trig_mode: __u8,
+ mask: __u8,
+ reserve: __u8,
+ ) -> __BindgenBitfieldUnit<[u8; 2usize], u8> {
+ let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 2usize], u8> =
+ Default::default();
+ __bindgen_bitfield_unit.set(0usize, 3u8, {
+ let delivery_mode: u8 = unsafe { ::std::mem::transmute(delivery_mode) };
+ delivery_mode as u64
+ });
+ __bindgen_bitfield_unit.set(3usize, 1u8, {
+ let dest_mode: u8 = unsafe { ::std::mem::transmute(dest_mode) };
+ dest_mode as u64
+ });
+ __bindgen_bitfield_unit.set(4usize, 1u8, {
+ let delivery_status: u8 = unsafe { ::std::mem::transmute(delivery_status) };
+ delivery_status as u64
+ });
+ __bindgen_bitfield_unit.set(5usize, 1u8, {
+ let polarity: u8 = unsafe { ::std::mem::transmute(polarity) };
+ polarity as u64
+ });
+ __bindgen_bitfield_unit.set(6usize, 1u8, {
+ let remote_irr: u8 = unsafe { ::std::mem::transmute(remote_irr) };
+ remote_irr as u64
+ });
+ __bindgen_bitfield_unit.set(7usize, 1u8, {
+ let trig_mode: u8 = unsafe { ::std::mem::transmute(trig_mode) };
+ trig_mode as u64
+ });
+ __bindgen_bitfield_unit.set(8usize, 1u8, {
+ let mask: u8 = unsafe { ::std::mem::transmute(mask) };
+ mask as u64
+ });
+ __bindgen_bitfield_unit.set(9usize, 7u8, {
+ let reserve: u8 = unsafe { ::std::mem::transmute(reserve) };
+ reserve as u64
+ });
+ __bindgen_bitfield_unit
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_ioapic_state__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ioapic_state__bindgen_ty_1>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_ioapic_state__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ioapic_state__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_ioapic_state__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ioapic_state__bindgen_ty_1>())).bits as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state__bindgen_ty_1),
+ "::",
+ stringify!(bits)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ioapic_state__bindgen_ty_1>())).fields as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state__bindgen_ty_1),
+ "::",
+ stringify!(fields)
+ )
+ );
+}
+impl Default for kvm_ioapic_state__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_ioapic_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ioapic_state>(),
+ 216usize,
+ concat!("Size of: ", stringify!(kvm_ioapic_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ioapic_state>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_ioapic_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioapic_state>())).base_address as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state),
+ "::",
+ stringify!(base_address)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioapic_state>())).ioregsel as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state),
+ "::",
+ stringify!(ioregsel)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioapic_state>())).id as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state),
+ "::",
+ stringify!(id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioapic_state>())).irr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state),
+ "::",
+ stringify!(irr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioapic_state>())).pad as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioapic_state>())).redirtbl as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioapic_state),
+ "::",
+ stringify!(redirtbl)
+ )
+ );
+}
+impl Default for kvm_ioapic_state {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_regs {
+ pub rax: __u64,
+ pub rbx: __u64,
+ pub rcx: __u64,
+ pub rdx: __u64,
+ pub rsi: __u64,
+ pub rdi: __u64,
+ pub rsp: __u64,
+ pub rbp: __u64,
+ pub r8: __u64,
+ pub r9: __u64,
+ pub r10: __u64,
+ pub r11: __u64,
+ pub r12: __u64,
+ pub r13: __u64,
+ pub r14: __u64,
+ pub r15: __u64,
+ pub rip: __u64,
+ pub rflags: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_regs() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_regs>(),
+ 144usize,
+ concat!("Size of: ", stringify!(kvm_regs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_regs>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_regs))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rax as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rax)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rbx as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rbx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rcx as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rcx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rdx as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rdx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rsi as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rdi as *const _ as usize },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rdi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rsp as *const _ as usize },
+ 48usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rsp)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rbp as *const _ as usize },
+ 56usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rbp)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).r8 as *const _ as usize },
+ 64usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(r8)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).r9 as *const _ as usize },
+ 72usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(r9)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).r10 as *const _ as usize },
+ 80usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(r10)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).r11 as *const _ as usize },
+ 88usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(r11)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).r12 as *const _ as usize },
+ 96usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(r12)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).r13 as *const _ as usize },
+ 104usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(r13)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).r14 as *const _ as usize },
+ 112usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(r14)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).r15 as *const _ as usize },
+ 120usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(r15)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rip as *const _ as usize },
+ 128usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rip)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_regs>())).rflags as *const _ as usize },
+ 136usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_regs),
+ "::",
+ stringify!(rflags)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_lapic_state {
+ pub regs: [::std::os::raw::c_char; 1024usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_lapic_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_lapic_state>(),
+ 1024usize,
+ concat!("Size of: ", stringify!(kvm_lapic_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_lapic_state>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_lapic_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_lapic_state>())).regs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_lapic_state),
+ "::",
+ stringify!(regs)
+ )
+ );
+}
+impl Default for kvm_lapic_state {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_segment {
+ pub base: __u64,
+ pub limit: __u32,
+ pub selector: __u16,
+ pub type_: __u8,
+ pub present: __u8,
+ pub dpl: __u8,
+ pub db: __u8,
+ pub s: __u8,
+ pub l: __u8,
+ pub g: __u8,
+ pub avl: __u8,
+ pub unusable: __u8,
+ pub padding: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_segment() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_segment>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_segment))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_segment>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_segment))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).base as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(base)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).limit as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(limit)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).selector as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(selector)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).type_ as *const _ as usize },
+ 14usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).present as *const _ as usize },
+ 15usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(present)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).dpl as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(dpl)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).db as *const _ as usize },
+ 17usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(db)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).s as *const _ as usize },
+ 18usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(s)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).l as *const _ as usize },
+ 19usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(l)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).g as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(g)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).avl as *const _ as usize },
+ 21usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(avl)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).unusable as *const _ as usize },
+ 22usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(unusable)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_segment>())).padding as *const _ as usize },
+ 23usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_segment),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_dtable {
+ pub base: __u64,
+ pub limit: __u16,
+ pub padding: [__u16; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_dtable() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_dtable>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_dtable))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_dtable>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_dtable))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dtable>())).base as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dtable),
+ "::",
+ stringify!(base)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dtable>())).limit as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dtable),
+ "::",
+ stringify!(limit)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dtable>())).padding as *const _ as usize },
+ 10usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dtable),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sregs {
+ pub cs: kvm_segment,
+ pub ds: kvm_segment,
+ pub es: kvm_segment,
+ pub fs: kvm_segment,
+ pub gs: kvm_segment,
+ pub ss: kvm_segment,
+ pub tr: kvm_segment,
+ pub ldt: kvm_segment,
+ pub gdt: kvm_dtable,
+ pub idt: kvm_dtable,
+ pub cr0: __u64,
+ pub cr2: __u64,
+ pub cr3: __u64,
+ pub cr4: __u64,
+ pub cr8: __u64,
+ pub efer: __u64,
+ pub apic_base: __u64,
+ pub interrupt_bitmap: [__u64; 4usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_sregs() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_sregs>(),
+ 312usize,
+ concat!("Size of: ", stringify!(kvm_sregs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_sregs>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_sregs))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).cs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(cs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).ds as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(ds)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).es as *const _ as usize },
+ 48usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(es)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).fs as *const _ as usize },
+ 72usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(fs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).gs as *const _ as usize },
+ 96usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(gs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).ss as *const _ as usize },
+ 120usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(ss)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).tr as *const _ as usize },
+ 144usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(tr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).ldt as *const _ as usize },
+ 168usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(ldt)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).gdt as *const _ as usize },
+ 192usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(gdt)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).idt as *const _ as usize },
+ 208usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(idt)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).cr0 as *const _ as usize },
+ 224usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(cr0)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).cr2 as *const _ as usize },
+ 232usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(cr2)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).cr3 as *const _ as usize },
+ 240usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(cr3)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).cr4 as *const _ as usize },
+ 248usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(cr4)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).cr8 as *const _ as usize },
+ 256usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(cr8)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).efer as *const _ as usize },
+ 264usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(efer)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).apic_base as *const _ as usize },
+ 272usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(apic_base)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_sregs>())).interrupt_bitmap as *const _ as usize },
+ 280usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_sregs),
+ "::",
+ stringify!(interrupt_bitmap)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_fpu {
+ pub fpr: [[__u8; 16usize]; 8usize],
+ pub fcw: __u16,
+ pub fsw: __u16,
+ pub ftwx: __u8,
+ pub pad1: __u8,
+ pub last_opcode: __u16,
+ pub last_ip: __u64,
+ pub last_dp: __u64,
+ pub xmm: [[__u8; 16usize]; 16usize],
+ pub mxcsr: __u32,
+ pub pad2: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_fpu() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_fpu>(),
+ 416usize,
+ concat!("Size of: ", stringify!(kvm_fpu))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_fpu>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_fpu))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).fpr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(fpr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).fcw as *const _ as usize },
+ 128usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(fcw)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).fsw as *const _ as usize },
+ 130usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(fsw)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).ftwx as *const _ as usize },
+ 132usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(ftwx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).pad1 as *const _ as usize },
+ 133usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(pad1)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).last_opcode as *const _ as usize },
+ 134usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(last_opcode)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).last_ip as *const _ as usize },
+ 136usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(last_ip)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).last_dp as *const _ as usize },
+ 144usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(last_dp)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).xmm as *const _ as usize },
+ 152usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(xmm)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).mxcsr as *const _ as usize },
+ 408usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(mxcsr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_fpu>())).pad2 as *const _ as usize },
+ 412usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_fpu),
+ "::",
+ stringify!(pad2)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_msr_entry {
+ pub index: __u32,
+ pub reserved: __u32,
+ pub data: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_msr_entry() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_msr_entry>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_msr_entry))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_msr_entry>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_msr_entry))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msr_entry>())).index as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msr_entry),
+ "::",
+ stringify!(index)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msr_entry>())).reserved as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msr_entry),
+ "::",
+ stringify!(reserved)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msr_entry>())).data as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msr_entry),
+ "::",
+ stringify!(data)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_msrs {
+ 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() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_msrs>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_msrs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_msrs>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_msrs))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msrs>())).nmsrs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msrs),
+ "::",
+ stringify!(nmsrs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msrs>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msrs),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msrs>())).entries as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msrs),
+ "::",
+ stringify!(entries)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_msr_list {
+ pub nmsrs: __u32,
+ pub indices: __IncompleteArrayField<__u32>,
+}
+#[test]
+fn bindgen_test_layout_kvm_msr_list() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_msr_list>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_msr_list))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_msr_list>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_msr_list))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msr_list>())).nmsrs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msr_list),
+ "::",
+ stringify!(nmsrs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msr_list>())).indices as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msr_list),
+ "::",
+ stringify!(indices)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_cpuid_entry {
+ pub function: __u32,
+ pub eax: __u32,
+ pub ebx: __u32,
+ pub ecx: __u32,
+ pub edx: __u32,
+ pub padding: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_cpuid_entry() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_cpuid_entry>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_cpuid_entry))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_cpuid_entry>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_cpuid_entry))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry>())).function as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry),
+ "::",
+ stringify!(function)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry>())).eax as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry),
+ "::",
+ stringify!(eax)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry>())).ebx as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry),
+ "::",
+ stringify!(ebx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry>())).ecx as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry),
+ "::",
+ stringify!(ecx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry>())).edx as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry),
+ "::",
+ stringify!(edx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry>())).padding as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_cpuid {
+ pub nent: __u32,
+ pub padding: __u32,
+ pub entries: __IncompleteArrayField<kvm_cpuid_entry>,
+}
+#[test]
+fn bindgen_test_layout_kvm_cpuid() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_cpuid>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_cpuid))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_cpuid>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_cpuid))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid>())).nent as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid),
+ "::",
+ stringify!(nent)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid>())).padding as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid),
+ "::",
+ stringify!(padding)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid>())).entries as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid),
+ "::",
+ stringify!(entries)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_cpuid_entry2 {
+ pub function: __u32,
+ pub index: __u32,
+ pub flags: __u32,
+ pub eax: __u32,
+ pub ebx: __u32,
+ pub ecx: __u32,
+ pub edx: __u32,
+ pub padding: [__u32; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_cpuid_entry2() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_cpuid_entry2>(),
+ 40usize,
+ concat!("Size of: ", stringify!(kvm_cpuid_entry2))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_cpuid_entry2>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_cpuid_entry2))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry2>())).function as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry2),
+ "::",
+ stringify!(function)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry2>())).index as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry2),
+ "::",
+ stringify!(index)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry2>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry2),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry2>())).eax as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry2),
+ "::",
+ stringify!(eax)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry2>())).ebx as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry2),
+ "::",
+ stringify!(ebx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry2>())).ecx as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry2),
+ "::",
+ stringify!(ecx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry2>())).edx as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry2),
+ "::",
+ stringify!(edx)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid_entry2>())).padding as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid_entry2),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_cpuid2 {
+ pub nent: __u32,
+ pub padding: __u32,
+ pub entries: __IncompleteArrayField<kvm_cpuid_entry2>,
+}
+#[test]
+fn bindgen_test_layout_kvm_cpuid2() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_cpuid2>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_cpuid2))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_cpuid2>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_cpuid2))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid2>())).nent as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid2),
+ "::",
+ stringify!(nent)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid2>())).padding as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid2),
+ "::",
+ stringify!(padding)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_cpuid2>())).entries as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_cpuid2),
+ "::",
+ stringify!(entries)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_pit_channel_state {
+ pub count: __u32,
+ pub latched_count: __u16,
+ pub count_latched: __u8,
+ pub status_latched: __u8,
+ pub status: __u8,
+ pub read_state: __u8,
+ pub write_state: __u8,
+ pub write_latch: __u8,
+ pub rw_mode: __u8,
+ pub mode: __u8,
+ pub bcd: __u8,
+ pub gate: __u8,
+ pub count_load_time: __s64,
+}
+#[test]
+fn bindgen_test_layout_kvm_pit_channel_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_pit_channel_state>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_pit_channel_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_pit_channel_state>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_pit_channel_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_channel_state>())).count as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(count)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pit_channel_state>())).latched_count as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(latched_count)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pit_channel_state>())).count_latched as *const _ as usize
+ },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(count_latched)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pit_channel_state>())).status_latched as *const _ as usize
+ },
+ 7usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(status_latched)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_channel_state>())).status as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(status)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pit_channel_state>())).read_state as *const _ as usize
+ },
+ 9usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(read_state)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pit_channel_state>())).write_state as *const _ as usize
+ },
+ 10usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(write_state)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pit_channel_state>())).write_latch as *const _ as usize
+ },
+ 11usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(write_latch)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_channel_state>())).rw_mode as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(rw_mode)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_channel_state>())).mode as *const _ as usize },
+ 13usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(mode)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_channel_state>())).bcd as *const _ as usize },
+ 14usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(bcd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_channel_state>())).gate as *const _ as usize },
+ 15usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(gate)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_pit_channel_state>())).count_load_time as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_channel_state),
+ "::",
+ stringify!(count_load_time)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_debug_exit_arch {
+ pub exception: __u32,
+ pub pad: __u32,
+ pub pc: __u64,
+ pub dr6: __u64,
+ pub dr7: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_debug_exit_arch() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_debug_exit_arch>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_debug_exit_arch))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_debug_exit_arch>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_debug_exit_arch))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_exit_arch>())).exception as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_exit_arch),
+ "::",
+ stringify!(exception)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_exit_arch>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_exit_arch),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_exit_arch>())).pc as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_exit_arch),
+ "::",
+ stringify!(pc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_exit_arch>())).dr6 as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_exit_arch),
+ "::",
+ stringify!(dr6)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_exit_arch>())).dr7 as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_exit_arch),
+ "::",
+ stringify!(dr7)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_guest_debug_arch {
+ pub debugreg: [__u64; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_guest_debug_arch() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_guest_debug_arch>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_guest_debug_arch))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_guest_debug_arch>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_guest_debug_arch))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug_arch>())).debugreg as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug_arch),
+ "::",
+ stringify!(debugreg)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_pit_state {
+ pub channels: [kvm_pit_channel_state; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_pit_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_pit_state>(),
+ 72usize,
+ concat!("Size of: ", stringify!(kvm_pit_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_pit_state>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_pit_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_state>())).channels as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_state),
+ "::",
+ stringify!(channels)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_pit_state2 {
+ pub channels: [kvm_pit_channel_state; 3usize],
+ pub flags: __u32,
+ pub reserved: [__u32; 9usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_pit_state2() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_pit_state2>(),
+ 112usize,
+ concat!("Size of: ", stringify!(kvm_pit_state2))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_pit_state2>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_pit_state2))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_state2>())).channels as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_state2),
+ "::",
+ stringify!(channels)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_state2>())).flags as *const _ as usize },
+ 72usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_state2),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_state2>())).reserved as *const _ as usize },
+ 76usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_state2),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_reinject_control {
+ pub pit_reinject: __u8,
+ pub reserved: [__u8; 31usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_reinject_control() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_reinject_control>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_reinject_control))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_reinject_control>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_reinject_control))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_reinject_control>())).pit_reinject as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_reinject_control),
+ "::",
+ stringify!(pit_reinject)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_reinject_control>())).reserved as *const _ as usize },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_reinject_control),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vcpu_events {
+ pub exception: kvm_vcpu_events__bindgen_ty_1,
+ pub interrupt: kvm_vcpu_events__bindgen_ty_2,
+ pub nmi: kvm_vcpu_events__bindgen_ty_3,
+ pub sipi_vector: __u32,
+ pub flags: __u32,
+ pub smi: kvm_vcpu_events__bindgen_ty_4,
+ pub reserved: [__u32; 9usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vcpu_events__bindgen_ty_1 {
+ pub injected: __u8,
+ pub nr: __u8,
+ pub has_error_code: __u8,
+ pub pad: __u8,
+ pub error_code: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_vcpu_events__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_vcpu_events__bindgen_ty_1>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_vcpu_events__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_vcpu_events__bindgen_ty_1>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_vcpu_events__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).injected as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_1),
+ "::",
+ stringify!(injected)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).nr as *const _ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_1),
+ "::",
+ stringify!(nr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).has_error_code as *const _
+ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_1),
+ "::",
+ stringify!(has_error_code)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_1>())).pad as *const _ as usize
+ },
+ 3usize,
+ 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>())).error_code as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_1),
+ "::",
+ stringify!(error_code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vcpu_events__bindgen_ty_2 {
+ pub injected: __u8,
+ pub nr: __u8,
+ pub soft: __u8,
+ pub shadow: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_vcpu_events__bindgen_ty_2() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_vcpu_events__bindgen_ty_2>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_vcpu_events__bindgen_ty_2))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_vcpu_events__bindgen_ty_2>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_vcpu_events__bindgen_ty_2))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_2>())).injected as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_2),
+ "::",
+ stringify!(injected)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_2>())).nr as *const _ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_2),
+ "::",
+ stringify!(nr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_2>())).soft as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_2),
+ "::",
+ stringify!(soft)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_2>())).shadow as *const _ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_2),
+ "::",
+ stringify!(shadow)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vcpu_events__bindgen_ty_3 {
+ pub injected: __u8,
+ pub pending: __u8,
+ pub masked: __u8,
+ pub pad: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_vcpu_events__bindgen_ty_3() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_vcpu_events__bindgen_ty_3>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_vcpu_events__bindgen_ty_3))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_vcpu_events__bindgen_ty_3>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_vcpu_events__bindgen_ty_3))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_3>())).injected as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_3),
+ "::",
+ stringify!(injected)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_3>())).pending as *const _ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_3),
+ "::",
+ stringify!(pending)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_3>())).masked as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_3),
+ "::",
+ stringify!(masked)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_3>())).pad as *const _ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_3),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vcpu_events__bindgen_ty_4 {
+ pub smm: __u8,
+ pub pending: __u8,
+ pub smm_inside_nmi: __u8,
+ pub latched_init: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_vcpu_events__bindgen_ty_4() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_vcpu_events__bindgen_ty_4>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_vcpu_events__bindgen_ty_4))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_vcpu_events__bindgen_ty_4>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_vcpu_events__bindgen_ty_4))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_4>())).smm as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_4),
+ "::",
+ stringify!(smm)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_4>())).pending as *const _ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_4),
+ "::",
+ stringify!(pending)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_4>())).smm_inside_nmi as *const _
+ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_4),
+ "::",
+ stringify!(smm_inside_nmi)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_vcpu_events__bindgen_ty_4>())).latched_init as *const _
+ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events__bindgen_ty_4),
+ "::",
+ stringify!(latched_init)
+ )
+ );
+}
+#[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>(),
+ 4usize,
+ 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>())).interrupt as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events),
+ "::",
+ stringify!(interrupt)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vcpu_events>())).nmi as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events),
+ "::",
+ stringify!(nmi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vcpu_events>())).sipi_vector as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events),
+ "::",
+ stringify!(sipi_vector)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vcpu_events>())).flags as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vcpu_events>())).smi as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events),
+ "::",
+ stringify!(smi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vcpu_events>())).reserved as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vcpu_events),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_debugregs {
+ pub db: [__u64; 4usize],
+ pub dr6: __u64,
+ pub dr7: __u64,
+ pub flags: __u64,
+ pub reserved: [__u64; 9usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_debugregs() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_debugregs>(),
+ 128usize,
+ concat!("Size of: ", stringify!(kvm_debugregs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_debugregs>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_debugregs))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debugregs>())).db as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debugregs),
+ "::",
+ stringify!(db)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debugregs>())).dr6 as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debugregs),
+ "::",
+ stringify!(dr6)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debugregs>())).dr7 as *const _ as usize },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debugregs),
+ "::",
+ stringify!(dr7)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debugregs>())).flags as *const _ as usize },
+ 48usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debugregs),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debugregs>())).reserved as *const _ as usize },
+ 56usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debugregs),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_xsave {
+ pub region: [__u32; 1024usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_xsave() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_xsave>(),
+ 4096usize,
+ concat!("Size of: ", stringify!(kvm_xsave))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_xsave>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_xsave))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xsave>())).region as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xsave),
+ "::",
+ stringify!(region)
+ )
+ );
+}
+impl Default for kvm_xsave {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_xcr {
+ pub xcr: __u32,
+ pub reserved: __u32,
+ pub value: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_xcr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_xcr>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_xcr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_xcr>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_xcr))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xcr>())).xcr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xcr),
+ "::",
+ stringify!(xcr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xcr>())).reserved as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xcr),
+ "::",
+ stringify!(reserved)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xcr>())).value as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xcr),
+ "::",
+ stringify!(value)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_xcrs {
+ pub nr_xcrs: __u32,
+ pub flags: __u32,
+ pub xcrs: [kvm_xcr; 16usize],
+ pub padding: [__u64; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_xcrs() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_xcrs>(),
+ 392usize,
+ concat!("Size of: ", stringify!(kvm_xcrs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_xcrs>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_xcrs))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xcrs>())).nr_xcrs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xcrs),
+ "::",
+ stringify!(nr_xcrs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xcrs>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xcrs),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xcrs>())).xcrs as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xcrs),
+ "::",
+ stringify!(xcrs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xcrs>())).padding as *const _ as usize },
+ 264usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xcrs),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_sync_regs {}
+#[test]
+fn bindgen_test_layout_kvm_sync_regs() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_sync_regs>(),
+ 0usize,
+ concat!("Size of: ", stringify!(kvm_sync_regs))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_sync_regs>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_sync_regs))
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_user_trace_setup {
+ pub buf_size: __u32,
+ pub buf_nr: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_user_trace_setup() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_user_trace_setup>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_user_trace_setup))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_user_trace_setup>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_user_trace_setup))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_user_trace_setup>())).buf_size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_user_trace_setup),
+ "::",
+ stringify!(buf_size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_user_trace_setup>())).buf_nr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_user_trace_setup),
+ "::",
+ stringify!(buf_nr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_breakpoint {
+ pub enabled: __u32,
+ pub padding: __u32,
+ pub address: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_breakpoint() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_breakpoint>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_breakpoint))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_breakpoint>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_breakpoint))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_breakpoint>())).enabled as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_breakpoint),
+ "::",
+ stringify!(enabled)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_breakpoint>())).padding as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_breakpoint),
+ "::",
+ stringify!(padding)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_breakpoint>())).address as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_breakpoint),
+ "::",
+ stringify!(address)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_debug_guest {
+ pub enabled: __u32,
+ pub pad: __u32,
+ pub breakpoints: [kvm_breakpoint; 4usize],
+ pub singlestep: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_debug_guest() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_debug_guest>(),
+ 80usize,
+ concat!("Size of: ", stringify!(kvm_debug_guest))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_debug_guest>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_debug_guest))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_guest>())).enabled as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_guest),
+ "::",
+ stringify!(enabled)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_guest>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_guest),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_guest>())).breakpoints as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_guest),
+ "::",
+ stringify!(breakpoints)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_debug_guest>())).singlestep as *const _ as usize },
+ 72usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_debug_guest),
+ "::",
+ stringify!(singlestep)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_memory_region {
+ pub slot: __u32,
+ pub flags: __u32,
+ pub guest_phys_addr: __u64,
+ pub memory_size: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_memory_region() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_memory_region>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_memory_region))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_memory_region>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_memory_region))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_region>())).slot as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_region),
+ "::",
+ stringify!(slot)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_region>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_region),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_memory_region>())).guest_phys_addr as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_region),
+ "::",
+ stringify!(guest_phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_memory_region>())).memory_size as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_memory_region),
+ "::",
+ stringify!(memory_size)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_userspace_memory_region {
+ pub slot: __u32,
+ pub flags: __u32,
+ pub guest_phys_addr: __u64,
+ pub memory_size: __u64,
+ pub userspace_addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_userspace_memory_region() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_userspace_memory_region>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_userspace_memory_region))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_userspace_memory_region>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_userspace_memory_region))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).slot as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(slot)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).flags as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).guest_phys_addr as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(guest_phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).memory_size as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(memory_size)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_userspace_memory_region>())).userspace_addr as *const _
+ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_userspace_memory_region),
+ "::",
+ stringify!(userspace_addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_irq_level {
+ pub __bindgen_anon_1: kvm_irq_level__bindgen_ty_1,
+ pub level: __u32,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_irq_level__bindgen_ty_1 {
+ pub irq: __u32,
+ pub status: __s32,
+ _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_level__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_level__bindgen_ty_1>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_irq_level__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_level__bindgen_ty_1>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irq_level__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_level__bindgen_ty_1>())).irq as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_level__bindgen_ty_1),
+ "::",
+ stringify!(irq)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_level__bindgen_ty_1>())).status as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_level__bindgen_ty_1),
+ "::",
+ stringify!(status)
+ )
+ );
+}
+impl Default for kvm_irq_level__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_level() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_level>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_irq_level))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_level>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irq_level))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_level>())).level as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_level),
+ "::",
+ stringify!(level)
+ )
+ );
+}
+impl Default for kvm_irq_level {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_irqchip {
+ pub chip_id: __u32,
+ pub pad: __u32,
+ pub chip: kvm_irqchip__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_irqchip__bindgen_ty_1 {
+ pub dummy: [::std::os::raw::c_char; 512usize],
+ pub pic: kvm_pic_state,
+ pub ioapic: kvm_ioapic_state,
+ _bindgen_union_align: [u64; 64usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_irqchip__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irqchip__bindgen_ty_1>(),
+ 512usize,
+ concat!("Size of: ", stringify!(kvm_irqchip__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irqchip__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_irqchip__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip__bindgen_ty_1>())).dummy as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip__bindgen_ty_1),
+ "::",
+ stringify!(dummy)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip__bindgen_ty_1>())).pic as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip__bindgen_ty_1),
+ "::",
+ stringify!(pic)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irqchip__bindgen_ty_1>())).ioapic as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip__bindgen_ty_1),
+ "::",
+ stringify!(ioapic)
+ )
+ );
+}
+impl Default for kvm_irqchip__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_irqchip() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irqchip>(),
+ 520usize,
+ concat!("Size of: ", stringify!(kvm_irqchip))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irqchip>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_irqchip))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip>())).chip_id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip),
+ "::",
+ stringify!(chip_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqchip>())).chip as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqchip),
+ "::",
+ stringify!(chip)
+ )
+ );
+}
+impl Default for kvm_irqchip {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_pit_config {
+ pub flags: __u32,
+ pub pad: [__u32; 15usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_pit_config() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_pit_config>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_pit_config))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_pit_config>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_pit_config))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_config>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_config),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_pit_config>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_pit_config),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_skeys {
+ pub start_gfn: __u64,
+ pub count: __u64,
+ pub skeydata_addr: __u64,
+ pub flags: __u32,
+ pub reserved: [__u32; 9usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_skeys() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_skeys>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_s390_skeys))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_skeys>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_skeys))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).start_gfn as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(start_gfn)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).count as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(count)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).skeydata_addr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(skeydata_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).flags as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_skeys>())).reserved as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_skeys),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_run {
+ pub request_interrupt_window: __u8,
+ pub padding1: [__u8; 7usize],
+ pub exit_reason: __u32,
+ pub ready_for_interrupt_injection: __u8,
+ pub if_flag: __u8,
+ pub flags: __u16,
+ pub cr8: __u64,
+ pub apic_base: __u64,
+ pub __bindgen_anon_1: kvm_run__bindgen_ty_1,
+ pub kvm_valid_regs: __u64,
+ pub kvm_dirty_regs: __u64,
+ 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,
+ pub fail_entry: kvm_run__bindgen_ty_1__bindgen_ty_2,
+ pub ex: kvm_run__bindgen_ty_1__bindgen_ty_3,
+ pub io: kvm_run__bindgen_ty_1__bindgen_ty_4,
+ pub debug: kvm_run__bindgen_ty_1__bindgen_ty_5,
+ pub mmio: kvm_run__bindgen_ty_1__bindgen_ty_6,
+ pub hypercall: kvm_run__bindgen_ty_1__bindgen_ty_7,
+ pub tpr_access: kvm_run__bindgen_ty_1__bindgen_ty_8,
+ pub s390_sieic: kvm_run__bindgen_ty_1__bindgen_ty_9,
+ pub s390_reset_flags: __u64,
+ pub s390_ucontrol: kvm_run__bindgen_ty_1__bindgen_ty_10,
+ pub dcr: kvm_run__bindgen_ty_1__bindgen_ty_11,
+ pub internal: kvm_run__bindgen_ty_1__bindgen_ty_12,
+ pub osi: kvm_run__bindgen_ty_1__bindgen_ty_13,
+ pub papr_hcall: kvm_run__bindgen_ty_1__bindgen_ty_14,
+ pub s390_tsch: kvm_run__bindgen_ty_1__bindgen_ty_15,
+ pub epr: kvm_run__bindgen_ty_1__bindgen_ty_16,
+ pub system_event: kvm_run__bindgen_ty_1__bindgen_ty_17,
+ pub s390_stsi: kvm_run__bindgen_ty_1__bindgen_ty_18,
+ pub eoi: kvm_run__bindgen_ty_1__bindgen_ty_19,
+ pub padding: [::std::os::raw::c_char; 256usize],
+ _bindgen_union_align: [u64; 32usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_1>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_1>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_1>())).hardware_exit_reason
+ as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_1),
+ "::",
+ stringify!(hardware_exit_reason)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_2 {
+ pub hardware_entry_failure_reason: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_2() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_2>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_2))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_2>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_2)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_2>()))
+ .hardware_entry_failure_reason as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_2),
+ "::",
+ stringify!(hardware_entry_failure_reason)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_3 {
+ pub exception: __u32,
+ pub error_code: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_3() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_3>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_3))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_3>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_3)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_3>())).exception as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_3),
+ "::",
+ stringify!(exception)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_3>())).error_code as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_3),
+ "::",
+ stringify!(error_code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_4 {
+ pub direction: __u8,
+ pub size: __u8,
+ pub port: __u16,
+ pub count: __u32,
+ pub data_offset: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_4() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_4>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_4>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).direction as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(direction)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).size as *const _
+ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(size)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).port as *const _
+ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(port)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).count as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(count)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_4>())).data_offset as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_4),
+ "::",
+ stringify!(data_offset)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_5 {
+ pub arch: kvm_debug_exit_arch,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_5() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_5>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_5))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_5>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_5)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_5>())).arch as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_5),
+ "::",
+ stringify!(arch)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_6 {
+ pub phys_addr: __u64,
+ pub data: [__u8; 8usize],
+ pub len: __u32,
+ pub is_write: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_6() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_6>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_6>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_6>())).phys_addr as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6),
+ "::",
+ stringify!(phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_6>())).data as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6),
+ "::",
+ stringify!(data)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_6>())).len as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_6>())).is_write as *const _
+ as usize
+ },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_6),
+ "::",
+ stringify!(is_write)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_7 {
+ pub nr: __u64,
+ pub args: [__u64; 6usize],
+ pub ret: __u64,
+ pub longmode: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_7() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_7>(),
+ 72usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_7>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).nr as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(nr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).args as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(args)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).ret as *const _ as usize
+ },
+ 56usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(ret)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).longmode as *const _
+ as usize
+ },
+ 64usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(longmode)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_7>())).pad as *const _ as usize
+ },
+ 68usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_7),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_8 {
+ pub rip: __u64,
+ pub is_write: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_8() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_8>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_8>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_8>())).rip as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8),
+ "::",
+ stringify!(rip)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_8>())).is_write as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8),
+ "::",
+ stringify!(is_write)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_8>())).pad as *const _ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_8),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_9 {
+ pub icptcode: __u8,
+ pub ipa: __u16,
+ pub ipb: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_9() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_9>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_9>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_9>())).icptcode as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9),
+ "::",
+ stringify!(icptcode)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_9>())).ipa as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9),
+ "::",
+ stringify!(ipa)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_9>())).ipb as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_9),
+ "::",
+ stringify!(ipb)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_10 {
+ pub trans_exc_code: __u64,
+ pub pgm_code: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_10() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_10>(),
+ 16usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_10)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_10>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_10)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_10>())).trans_exc_code
+ as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_10),
+ "::",
+ stringify!(trans_exc_code)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_10>())).pgm_code as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_10),
+ "::",
+ stringify!(pgm_code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_11 {
+ pub dcrn: __u32,
+ pub data: __u32,
+ pub is_write: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_11() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_11>(),
+ 12usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_11>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_11>())).dcrn as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11),
+ "::",
+ stringify!(dcrn)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_11>())).data as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11),
+ "::",
+ stringify!(data)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_11>())).is_write as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_11),
+ "::",
+ stringify!(is_write)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_12 {
+ pub suberror: __u32,
+ pub ndata: __u32,
+ pub data: [__u64; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_12() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_12>(),
+ 136usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_12>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_12>())).suberror as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12),
+ "::",
+ stringify!(suberror)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_12>())).ndata as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12),
+ "::",
+ stringify!(ndata)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_12>())).data as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_12),
+ "::",
+ stringify!(data)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_13 {
+ pub gprs: [__u64; 32usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_13() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_13>(),
+ 256usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_13)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_13>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_13)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_13>())).gprs as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_13),
+ "::",
+ stringify!(gprs)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_14 {
+ pub nr: __u64,
+ pub ret: __u64,
+ pub args: [__u64; 9usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_14() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_14>(),
+ 88usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_14>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_14>())).nr as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14),
+ "::",
+ stringify!(nr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_14>())).ret as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14),
+ "::",
+ stringify!(ret)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_14>())).args as *const _
+ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_14),
+ "::",
+ stringify!(args)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_15 {
+ pub subchannel_id: __u16,
+ pub subchannel_nr: __u16,
+ pub io_int_parm: __u32,
+ pub io_int_word: __u32,
+ pub ipb: __u32,
+ pub dequeued: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_15() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_15>(),
+ 20usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_15>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).subchannel_id
+ as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(subchannel_id)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).subchannel_nr
+ as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(subchannel_nr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).io_int_parm as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(io_int_parm)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).io_int_word as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(io_int_word)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).ipb as *const _
+ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(ipb)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_15>())).dequeued as *const _
+ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_15),
+ "::",
+ stringify!(dequeued)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_16 {
+ pub epr: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_16() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_16>(),
+ 4usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_16)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_16>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_16)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_16>())).epr as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_16),
+ "::",
+ stringify!(epr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_17 {
+ pub type_: __u32,
+ pub flags: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_17() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_17>(),
+ 16usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_17)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_17>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_17)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_17>())).type_ as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_17),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_17>())).flags as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_17),
+ "::",
+ stringify!(flags)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_18 {
+ pub addr: __u64,
+ pub ar: __u8,
+ pub reserved: __u8,
+ pub fc: __u8,
+ pub sel1: __u8,
+ pub sel2: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_18() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_18>(),
+ 16usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_18>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).addr as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).ar as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(ar)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).reserved as *const _
+ as usize
+ },
+ 9usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(reserved)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).fc as *const _ as usize
+ },
+ 10usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(fc)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).sel1 as *const _
+ as usize
+ },
+ 11usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(sel1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_18>())).sel2 as *const _
+ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_18),
+ "::",
+ stringify!(sel2)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_run__bindgen_ty_1__bindgen_ty_19 {
+ pub vector: __u8,
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1__bindgen_ty_19() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1__bindgen_ty_19>(),
+ 1usize,
+ concat!(
+ "Size of: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_19)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1__bindgen_ty_19>(),
+ 1usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_19)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1__bindgen_ty_19>())).vector as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1__bindgen_ty_19),
+ "::",
+ stringify!(vector)
+ )
+ );
+}
+
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_1>(),
+ 256usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_run__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).hw as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(hw)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).fail_entry as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(fail_entry)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).ex as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(ex)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).io as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(io)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).debug as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(debug)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).mmio as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(mmio)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).hypercall as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(hypercall)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).tpr_access as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(tpr_access)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_sieic as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_sieic)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_reset_flags as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_reset_flags)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_ucontrol as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_ucontrol)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).dcr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(dcr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).internal as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(internal)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).osi as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(osi)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).papr_hcall as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(papr_hcall)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_tsch as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_tsch)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).epr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(epr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).system_event as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(system_event)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).s390_stsi as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(s390_stsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).eoi as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(eoi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_1>())).padding as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_1),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+impl Default for kvm_run__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+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],
+}
+#[test]
+fn bindgen_test_layout_kvm_run__bindgen_ty_2() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run__bindgen_ty_2>(),
+ 2048usize,
+ concat!("Size of: ", stringify!(kvm_run__bindgen_ty_2))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run__bindgen_ty_2>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(kvm_run__bindgen_ty_2))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_2>())).regs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_2),
+ "::",
+ stringify!(regs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run__bindgen_ty_2>())).padding as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run__bindgen_ty_2),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+impl Default for kvm_run__bindgen_ty_2 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_run() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_run>(),
+ 2352usize,
+ concat!("Size of: ", stringify!(kvm_run))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_run>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_run))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run>())).request_interrupt_window as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(request_interrupt_window)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).padding1 as *const _ as usize },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(padding1)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).exit_reason as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(exit_reason)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_run>())).ready_for_interrupt_injection as *const _ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(ready_for_interrupt_injection)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).if_flag as *const _ as usize },
+ 13usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(if_flag)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).flags as *const _ as usize },
+ 14usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).cr8 as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(cr8)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).apic_base as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(apic_base)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).kvm_valid_regs as *const _ as usize },
+ 288usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(kvm_valid_regs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).kvm_dirty_regs as *const _ as usize },
+ 296usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(kvm_dirty_regs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_run>())).s as *const _ as usize },
+ 304usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_run),
+ "::",
+ stringify!(s)
+ )
+ );
+}
+impl Default for kvm_run {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_coalesced_mmio_zone {
+ pub addr: __u64,
+ pub size: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_coalesced_mmio_zone() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_coalesced_mmio_zone>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_coalesced_mmio_zone))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_coalesced_mmio_zone>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_coalesced_mmio_zone))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_zone>())).addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_zone),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_zone>())).size as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_zone),
+ "::",
+ stringify!(size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_zone>())).pad as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_zone),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[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],
+}
+#[test]
+fn bindgen_test_layout_kvm_coalesced_mmio() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_coalesced_mmio>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_coalesced_mmio))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_coalesced_mmio>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_coalesced_mmio))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio>())).phys_addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio),
+ "::",
+ stringify!(phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio>())).len as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio),
+ "::",
+ stringify!(len)
+ )
+ );
+ 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!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio),
+ "::",
+ stringify!(data)
+ )
+ );
+}
+#[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() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_coalesced_mmio_ring>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_coalesced_mmio_ring))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_coalesced_mmio_ring>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_coalesced_mmio_ring))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_ring>())).first as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_ring),
+ "::",
+ stringify!(first)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_coalesced_mmio_ring>())).last as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_ring),
+ "::",
+ stringify!(last)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_coalesced_mmio_ring>())).coalesced_mmio as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_coalesced_mmio_ring),
+ "::",
+ stringify!(coalesced_mmio)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_translation {
+ pub linear_address: __u64,
+ pub physical_address: __u64,
+ pub valid: __u8,
+ pub writeable: __u8,
+ pub usermode: __u8,
+ pub pad: [__u8; 5usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_translation() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_translation>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_translation))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_translation>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_translation))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).linear_address as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(linear_address)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_translation>())).physical_address as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(physical_address)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).valid as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(valid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).writeable as *const _ as usize },
+ 17usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(writeable)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).usermode as *const _ as usize },
+ 18usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(usermode)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_translation>())).pad as *const _ as usize },
+ 19usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_translation),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_mem_op {
+ pub gaddr: __u64,
+ pub flags: __u64,
+ pub size: __u32,
+ pub op: __u32,
+ pub buf: __u64,
+ pub ar: __u8,
+ pub reserved: [__u8; 31usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_mem_op() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_mem_op>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_s390_mem_op))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_mem_op>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_mem_op))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).gaddr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(gaddr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).size as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).op as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(op)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).buf as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(buf)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).ar as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(ar)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mem_op>())).reserved as *const _ as usize },
+ 33usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mem_op),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_interrupt {
+ pub irq: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_interrupt() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_interrupt>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_interrupt))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_interrupt>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_interrupt))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_interrupt>())).irq as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_interrupt),
+ "::",
+ stringify!(irq)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_dirty_log {
+ pub slot: __u32,
+ pub padding1: __u32,
+ pub __bindgen_anon_1: kvm_dirty_log__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_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_dirty_log__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_dirty_log__bindgen_ty_1>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_dirty_log__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_dirty_log__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_dirty_log__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_dirty_log__bindgen_ty_1>())).dirty_bitmap as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_log__bindgen_ty_1),
+ "::",
+ stringify!(dirty_bitmap)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_dirty_log__bindgen_ty_1>())).padding2 as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_log__bindgen_ty_1),
+ "::",
+ stringify!(padding2)
+ )
+ );
+}
+impl Default for kvm_dirty_log__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_dirty_log() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_dirty_log>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_dirty_log))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_dirty_log>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_dirty_log))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dirty_log>())).slot as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_log),
+ "::",
+ stringify!(slot)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dirty_log>())).padding1 as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_log),
+ "::",
+ stringify!(padding1)
+ )
+ );
+}
+impl Default for kvm_dirty_log {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_signal_mask {
+ pub len: __u32,
+ pub sigset: __IncompleteArrayField<__u8>,
+}
+#[test]
+fn bindgen_test_layout_kvm_signal_mask() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_signal_mask>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_signal_mask))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_signal_mask>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_signal_mask))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_signal_mask>())).len as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_signal_mask),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_signal_mask>())).sigset as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_signal_mask),
+ "::",
+ stringify!(sigset)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_tpr_access_ctl {
+ pub enabled: __u32,
+ pub flags: __u32,
+ pub reserved: [__u32; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_tpr_access_ctl() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_tpr_access_ctl>(),
+ 40usize,
+ concat!("Size of: ", stringify!(kvm_tpr_access_ctl))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_tpr_access_ctl>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_tpr_access_ctl))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_tpr_access_ctl>())).enabled as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_tpr_access_ctl),
+ "::",
+ stringify!(enabled)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_tpr_access_ctl>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_tpr_access_ctl),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_tpr_access_ctl>())).reserved as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_tpr_access_ctl),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_vapic_addr {
+ pub vapic_addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_vapic_addr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_vapic_addr>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_vapic_addr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_vapic_addr>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_vapic_addr))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_vapic_addr>())).vapic_addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_vapic_addr),
+ "::",
+ stringify!(vapic_addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_mp_state {
+ pub mp_state: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_mp_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_mp_state>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_mp_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_mp_state>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_mp_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_mp_state>())).mp_state as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_mp_state),
+ "::",
+ stringify!(mp_state)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_psw {
+ pub mask: __u64,
+ pub addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_psw() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_psw>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_s390_psw))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_psw>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_psw))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_psw>())).mask as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_psw),
+ "::",
+ stringify!(mask)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_psw>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_psw),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_interrupt {
+ pub type_: __u32,
+ pub parm: __u32,
+ pub parm64: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_interrupt() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_interrupt>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_s390_interrupt))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_interrupt>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_interrupt))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_interrupt>())).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_interrupt),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_interrupt>())).parm as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_interrupt),
+ "::",
+ stringify!(parm)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_interrupt>())).parm64 as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_interrupt),
+ "::",
+ stringify!(parm64)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_io_info {
+ pub subchannel_id: __u16,
+ pub subchannel_nr: __u16,
+ pub io_int_parm: __u32,
+ pub io_int_word: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_io_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_io_info>(),
+ 12usize,
+ concat!("Size of: ", stringify!(kvm_s390_io_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_io_info>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_s390_io_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_io_info>())).subchannel_id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_io_info),
+ "::",
+ stringify!(subchannel_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_io_info>())).subchannel_nr as *const _ as usize },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_io_info),
+ "::",
+ stringify!(subchannel_nr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_io_info>())).io_int_parm as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_io_info),
+ "::",
+ stringify!(io_int_parm)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_io_info>())).io_int_word as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_io_info),
+ "::",
+ stringify!(io_int_word)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_ext_info {
+ pub ext_params: __u32,
+ pub pad: __u32,
+ pub ext_params2: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_ext_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_ext_info>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_s390_ext_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_ext_info>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_ext_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ext_info>())).ext_params as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ext_info),
+ "::",
+ stringify!(ext_params)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ext_info>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ext_info),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ext_info>())).ext_params2 as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ext_info),
+ "::",
+ stringify!(ext_params2)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_pgm_info {
+ pub trans_exc_code: __u64,
+ pub mon_code: __u64,
+ pub per_address: __u64,
+ pub data_exc_code: __u32,
+ pub code: __u16,
+ pub mon_class_nr: __u16,
+ pub per_code: __u8,
+ pub per_atmid: __u8,
+ pub exc_access_id: __u8,
+ pub per_access_id: __u8,
+ pub op_access_id: __u8,
+ pub pad: [__u8; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_pgm_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_pgm_info>(),
+ 40usize,
+ concat!("Size of: ", stringify!(kvm_s390_pgm_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_pgm_info>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_pgm_info))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_pgm_info>())).trans_exc_code as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(trans_exc_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).mon_code as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(mon_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).per_address as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(per_address)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).data_exc_code as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(data_exc_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).code as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).mon_class_nr as *const _ as usize },
+ 30usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(mon_class_nr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).per_code as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(per_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).per_atmid as *const _ as usize },
+ 33usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(per_atmid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).exc_access_id as *const _ as usize },
+ 34usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(exc_access_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).per_access_id as *const _ as usize },
+ 35usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(per_access_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).op_access_id as *const _ as usize },
+ 36usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(op_access_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_pgm_info>())).pad as *const _ as usize },
+ 37usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_pgm_info),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_prefix_info {
+ pub address: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_prefix_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_prefix_info>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_s390_prefix_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_prefix_info>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_s390_prefix_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_prefix_info>())).address as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_prefix_info),
+ "::",
+ stringify!(address)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_extcall_info {
+ pub code: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_extcall_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_extcall_info>(),
+ 2usize,
+ concat!("Size of: ", stringify!(kvm_s390_extcall_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_extcall_info>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(kvm_s390_extcall_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_extcall_info>())).code as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_extcall_info),
+ "::",
+ stringify!(code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_emerg_info {
+ pub code: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_emerg_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_emerg_info>(),
+ 2usize,
+ concat!("Size of: ", stringify!(kvm_s390_emerg_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_emerg_info>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(kvm_s390_emerg_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_emerg_info>())).code as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_emerg_info),
+ "::",
+ stringify!(code)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_stop_info {
+ pub flags: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_stop_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_stop_info>(),
+ 4usize,
+ concat!("Size of: ", stringify!(kvm_s390_stop_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_stop_info>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_s390_stop_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_stop_info>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_stop_info),
+ "::",
+ stringify!(flags)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_mchk_info {
+ pub cr14: __u64,
+ pub mcic: __u64,
+ pub failing_storage_address: __u64,
+ pub ext_damage_code: __u32,
+ pub pad: __u32,
+ pub fixed_logout: [__u8; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_mchk_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_mchk_info>(),
+ 48usize,
+ concat!("Size of: ", stringify!(kvm_s390_mchk_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_mchk_info>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_mchk_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mchk_info>())).cr14 as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(cr14)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mchk_info>())).mcic as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(mcic)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_mchk_info>())).failing_storage_address as *const _
+ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(failing_storage_address)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_mchk_info>())).ext_damage_code as *const _ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(ext_damage_code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mchk_info>())).pad as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_mchk_info>())).fixed_logout as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_mchk_info),
+ "::",
+ stringify!(fixed_logout)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_s390_irq {
+ pub type_: __u64,
+ pub u: kvm_s390_irq__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_s390_irq__bindgen_ty_1 {
+ pub io: kvm_s390_io_info,
+ pub ext: kvm_s390_ext_info,
+ pub pgm: kvm_s390_pgm_info,
+ pub emerg: kvm_s390_emerg_info,
+ pub extcall: kvm_s390_extcall_info,
+ pub prefix: kvm_s390_prefix_info,
+ pub stop: kvm_s390_stop_info,
+ pub mchk: kvm_s390_mchk_info,
+ pub reserved: [::std::os::raw::c_char; 64usize],
+ _bindgen_union_align: [u64; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_irq__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_irq__bindgen_ty_1>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_s390_irq__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_irq__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_irq__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).io as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(io)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).ext as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(ext)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).pgm as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(pgm)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).emerg as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(emerg)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).extcall as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(extcall)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).prefix as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(prefix)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).stop as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(stop)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).mchk as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(mchk)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_s390_irq__bindgen_ty_1>())).reserved as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq__bindgen_ty_1),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Default for kvm_s390_irq__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_irq() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_irq>(),
+ 72usize,
+ concat!("Size of: ", stringify!(kvm_s390_irq))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_irq>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_irq))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq>())).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq>())).u as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq),
+ "::",
+ stringify!(u)
+ )
+ );
+}
+impl Default for kvm_s390_irq {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_irq_state {
+ pub buf: __u64,
+ pub flags: __u32,
+ pub len: __u32,
+ pub reserved: [__u32; 4usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_irq_state() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_irq_state>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_s390_irq_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_irq_state>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_irq_state))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq_state>())).buf as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq_state),
+ "::",
+ stringify!(buf)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq_state>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq_state),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq_state>())).len as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq_state),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_irq_state>())).reserved as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_irq_state),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_guest_debug {
+ pub control: __u32,
+ pub pad: __u32,
+ pub arch: kvm_guest_debug_arch,
+}
+#[test]
+fn bindgen_test_layout_kvm_guest_debug() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_guest_debug>(),
+ 72usize,
+ concat!("Size of: ", stringify!(kvm_guest_debug))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_guest_debug>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_guest_debug))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug>())).control as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug),
+ "::",
+ stringify!(control)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug>())).pad as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_guest_debug>())).arch as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_guest_debug),
+ "::",
+ stringify!(arch)
+ )
+ );
+}
+pub const kvm_ioeventfd_flag_nr_datamatch: _bindgen_ty_1 = 0;
+pub const kvm_ioeventfd_flag_nr_pio: _bindgen_ty_1 = 1;
+pub const kvm_ioeventfd_flag_nr_deassign: _bindgen_ty_1 = 2;
+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;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_ioeventfd {
+ pub datamatch: __u64,
+ pub addr: __u64,
+ pub len: __u32,
+ pub fd: __s32,
+ pub flags: __u32,
+ pub pad: [__u8; 36usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_ioeventfd() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ioeventfd>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_ioeventfd))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ioeventfd>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_ioeventfd))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).datamatch as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(datamatch)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).len as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).fd as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(fd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).flags as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ioeventfd>())).pad as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ioeventfd),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+impl Default for kvm_ioeventfd {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_enable_cap {
+ pub cap: __u32,
+ pub flags: __u32,
+ pub args: [__u64; 4usize],
+ pub pad: [__u8; 64usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_enable_cap() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_enable_cap>(),
+ 104usize,
+ concat!("Size of: ", stringify!(kvm_enable_cap))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_enable_cap>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_enable_cap))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_enable_cap>())).cap as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_enable_cap),
+ "::",
+ stringify!(cap)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_enable_cap>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_enable_cap),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_enable_cap>())).args as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_enable_cap),
+ "::",
+ stringify!(args)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_enable_cap>())).pad as *const _ as usize },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_enable_cap),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+impl Default for kvm_enable_cap {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_ppc_pvinfo {
+ pub flags: __u32,
+ pub hcall: [__u32; 4usize],
+ pub pad: [__u8; 108usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_pvinfo() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ppc_pvinfo>(),
+ 128usize,
+ concat!("Size of: ", stringify!(kvm_ppc_pvinfo))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ppc_pvinfo>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_ppc_pvinfo))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_pvinfo>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_pvinfo),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_pvinfo>())).hcall as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_pvinfo),
+ "::",
+ stringify!(hcall)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_pvinfo>())).pad as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_pvinfo),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+impl Default for kvm_ppc_pvinfo {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ppc_one_page_size {
+ pub page_shift: __u32,
+ pub pte_enc: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_one_page_size() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ppc_one_page_size>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_ppc_one_page_size))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ppc_one_page_size>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_ppc_one_page_size))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ppc_one_page_size>())).page_shift as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_page_size),
+ "::",
+ stringify!(page_shift)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_one_page_size>())).pte_enc as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_page_size),
+ "::",
+ stringify!(pte_enc)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ppc_one_seg_page_size {
+ pub page_shift: __u32,
+ pub slb_enc: __u32,
+ pub enc: [kvm_ppc_one_page_size; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_one_seg_page_size() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ppc_one_seg_page_size>(),
+ 72usize,
+ concat!("Size of: ", stringify!(kvm_ppc_one_seg_page_size))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ppc_one_seg_page_size>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_ppc_one_seg_page_size))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ppc_one_seg_page_size>())).page_shift as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_seg_page_size),
+ "::",
+ stringify!(page_shift)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_ppc_one_seg_page_size>())).slb_enc as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_seg_page_size),
+ "::",
+ stringify!(slb_enc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_one_seg_page_size>())).enc as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_one_seg_page_size),
+ "::",
+ stringify!(enc)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_ppc_smmu_info {
+ pub flags: __u64,
+ pub slb_size: __u32,
+ pub pad: __u32,
+ pub sps: [kvm_ppc_one_seg_page_size; 8usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_ppc_smmu_info() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_ppc_smmu_info>(),
+ 592usize,
+ concat!("Size of: ", stringify!(kvm_ppc_smmu_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_ppc_smmu_info>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_ppc_smmu_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_smmu_info),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).slb_size as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_smmu_info),
+ "::",
+ stringify!(slb_size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).pad as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_smmu_info),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_ppc_smmu_info>())).sps as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_ppc_smmu_info),
+ "::",
+ stringify!(sps)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irq_routing_irqchip {
+ pub irqchip: __u32,
+ pub pin: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_irqchip() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_irqchip>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_irqchip))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_irqchip>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing_irqchip))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_irqchip>())).irqchip as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_irqchip),
+ "::",
+ stringify!(irqchip)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_irqchip>())).pin as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_irqchip),
+ "::",
+ stringify!(pin)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irq_routing_msi {
+ pub address_lo: __u32,
+ pub address_hi: __u32,
+ pub data: __u32,
+ pub pad: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_msi() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_msi>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_msi))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_msi>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing_msi))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_msi>())).address_lo as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_msi),
+ "::",
+ stringify!(address_lo)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_msi>())).address_hi as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_msi),
+ "::",
+ stringify!(address_hi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_msi>())).data as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_msi),
+ "::",
+ 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)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irq_routing_s390_adapter {
+ pub ind_addr: __u64,
+ pub summary_addr: __u64,
+ pub ind_offset: __u64,
+ pub summary_offset: __u32,
+ pub adapter_id: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_s390_adapter() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_s390_adapter>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_s390_adapter))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_s390_adapter>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing_s390_adapter))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).ind_addr as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(ind_addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).summary_addr as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(summary_addr)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).ind_offset as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(ind_offset)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).summary_offset as *const _
+ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(summary_offset)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_s390_adapter>())).adapter_id as *const _ as usize
+ },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_s390_adapter),
+ "::",
+ stringify!(adapter_id)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_irq_routing_entry {
+ pub gsi: __u32,
+ pub type_: __u32,
+ pub flags: __u32,
+ pub pad: __u32,
+ pub u: kvm_irq_routing_entry__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_irq_routing_entry__bindgen_ty_1 {
+ pub irqchip: kvm_irq_routing_irqchip,
+ pub msi: kvm_irq_routing_msi,
+ pub adapter: kvm_irq_routing_s390_adapter,
+ pub pad: [__u32; 8usize],
+ _bindgen_union_align: [u64; 4usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_entry__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_entry__bindgen_ty_1>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_entry__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_entry__bindgen_ty_1>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).irqchip as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+ "::",
+ stringify!(irqchip)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).msi as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+ "::",
+ stringify!(msi)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).adapter as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+ "::",
+ stringify!(adapter)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_irq_routing_entry__bindgen_ty_1>())).pad as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry__bindgen_ty_1),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+impl Default for kvm_irq_routing_entry__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_irq_routing_entry() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing_entry>(),
+ 48usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing_entry))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing_entry>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing_entry))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).gsi as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(gsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).type_ as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).pad as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(pad)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing_entry>())).u as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing_entry),
+ "::",
+ stringify!(u)
+ )
+ );
+}
+impl Default for kvm_irq_routing_entry {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+pub struct kvm_irq_routing {
+ 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() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irq_routing>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_irq_routing))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irq_routing>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_irq_routing))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing>())).nr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing),
+ "::",
+ stringify!(nr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing>())).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irq_routing>())).entries as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irq_routing),
+ "::",
+ stringify!(entries)
+ )
+ );
+}
+impl Default for kvm_irq_routing {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_x86_mce {
+ pub status: __u64,
+ pub addr: __u64,
+ pub misc: __u64,
+ pub mcg_status: __u64,
+ pub bank: __u8,
+ pub pad1: [__u8; 7usize],
+ pub pad2: [__u64; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_x86_mce() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_x86_mce>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_x86_mce))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_x86_mce>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_x86_mce))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_x86_mce>())).status as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_x86_mce),
+ "::",
+ stringify!(status)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_x86_mce>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_x86_mce),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_x86_mce>())).misc as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_x86_mce),
+ "::",
+ stringify!(misc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_x86_mce>())).mcg_status as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_x86_mce),
+ "::",
+ stringify!(mcg_status)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_x86_mce>())).bank as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_x86_mce),
+ "::",
+ stringify!(bank)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_x86_mce>())).pad1 as *const _ as usize },
+ 33usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_x86_mce),
+ "::",
+ stringify!(pad1)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_x86_mce>())).pad2 as *const _ as usize },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_x86_mce),
+ "::",
+ stringify!(pad2)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_xen_hvm_config {
+ pub flags: __u32,
+ pub msr: __u32,
+ pub blob_addr_32: __u64,
+ pub blob_addr_64: __u64,
+ pub blob_size_32: __u8,
+ pub blob_size_64: __u8,
+ pub pad2: [__u8; 30usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_xen_hvm_config() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_xen_hvm_config>(),
+ 56usize,
+ concat!("Size of: ", stringify!(kvm_xen_hvm_config))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_xen_hvm_config>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_xen_hvm_config))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xen_hvm_config>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xen_hvm_config),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xen_hvm_config>())).msr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xen_hvm_config),
+ "::",
+ stringify!(msr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xen_hvm_config>())).blob_addr_32 as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xen_hvm_config),
+ "::",
+ stringify!(blob_addr_32)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xen_hvm_config>())).blob_addr_64 as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xen_hvm_config),
+ "::",
+ stringify!(blob_addr_64)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xen_hvm_config>())).blob_size_32 as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xen_hvm_config),
+ "::",
+ stringify!(blob_size_32)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xen_hvm_config>())).blob_size_64 as *const _ as usize },
+ 25usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xen_hvm_config),
+ "::",
+ stringify!(blob_size_64)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_xen_hvm_config>())).pad2 as *const _ as usize },
+ 26usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_xen_hvm_config),
+ "::",
+ stringify!(pad2)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_irqfd {
+ pub fd: __u32,
+ pub gsi: __u32,
+ pub flags: __u32,
+ pub resamplefd: __u32,
+ pub pad: [__u8; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_irqfd() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_irqfd>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_irqfd))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_irqfd>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_irqfd))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).fd as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(fd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).gsi as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(gsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).resamplefd as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(resamplefd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_irqfd>())).pad as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_irqfd),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_clock_data {
+ pub clock: __u64,
+ pub flags: __u32,
+ pub pad: [__u32; 9usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_clock_data() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_clock_data>(),
+ 48usize,
+ concat!("Size of: ", stringify!(kvm_clock_data))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_clock_data>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_clock_data))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_clock_data>())).clock as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_clock_data),
+ "::",
+ stringify!(clock)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_clock_data>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_clock_data),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_clock_data>())).pad as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_clock_data),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_config_tlb {
+ pub params: __u64,
+ pub array: __u64,
+ pub mmu_type: __u32,
+ pub array_len: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_config_tlb() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_config_tlb>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_config_tlb))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_config_tlb>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_config_tlb))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_config_tlb>())).params as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_config_tlb),
+ "::",
+ stringify!(params)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_config_tlb>())).array as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_config_tlb),
+ "::",
+ stringify!(array)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_config_tlb>())).mmu_type as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_config_tlb),
+ "::",
+ stringify!(mmu_type)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_config_tlb>())).array_len as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_config_tlb),
+ "::",
+ stringify!(array_len)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_dirty_tlb {
+ pub bitmap: __u64,
+ pub num_dirty: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_dirty_tlb() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_dirty_tlb>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_dirty_tlb))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_dirty_tlb>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_dirty_tlb))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dirty_tlb>())).bitmap as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_tlb),
+ "::",
+ stringify!(bitmap)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_dirty_tlb>())).num_dirty as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_dirty_tlb),
+ "::",
+ stringify!(num_dirty)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct kvm_reg_list {
+ pub n: __u64,
+ pub reg: __IncompleteArrayField<__u64>,
+}
+#[test]
+fn bindgen_test_layout_kvm_reg_list() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_reg_list>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_reg_list))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_reg_list>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_reg_list))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_reg_list>())).n as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_reg_list),
+ "::",
+ stringify!(n)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_reg_list>())).reg as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_reg_list),
+ "::",
+ stringify!(reg)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_one_reg {
+ pub id: __u64,
+ pub addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_one_reg() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_one_reg>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_one_reg))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_one_reg>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_one_reg))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_one_reg>())).id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_one_reg),
+ "::",
+ stringify!(id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_one_reg>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_one_reg),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_msi {
+ pub address_lo: __u32,
+ pub address_hi: __u32,
+ pub data: __u32,
+ pub flags: __u32,
+ pub pad: [__u8; 16usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_msi() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_msi>(),
+ 32usize,
+ concat!("Size of: ", stringify!(kvm_msi))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_msi>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_msi))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).address_lo as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(address_lo)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).address_hi as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(address_hi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).data as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(data)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).flags as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_msi>())).pad as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_msi),
+ "::",
+ stringify!(pad)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_arm_device_addr {
+ pub id: __u64,
+ pub addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_arm_device_addr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_arm_device_addr>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_arm_device_addr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_arm_device_addr>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_arm_device_addr))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_arm_device_addr>())).id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_arm_device_addr),
+ "::",
+ stringify!(id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_arm_device_addr>())).addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_arm_device_addr),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_create_device {
+ pub type_: __u32,
+ pub fd: __u32,
+ pub flags: __u32,
+}
+#[test]
+fn bindgen_test_layout_kvm_create_device() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_create_device>(),
+ 12usize,
+ concat!("Size of: ", stringify!(kvm_create_device))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_create_device>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_create_device))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_create_device>())).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_create_device),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_create_device>())).fd as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_create_device),
+ "::",
+ stringify!(fd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_create_device>())).flags as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_create_device),
+ "::",
+ stringify!(flags)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_device_attr {
+ pub flags: __u32,
+ pub group: __u32,
+ pub attr: __u64,
+ pub addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_device_attr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_device_attr>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_device_attr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_device_attr>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_device_attr))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_device_attr>())).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_device_attr),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_device_attr>())).group as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_device_attr),
+ "::",
+ stringify!(group)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_device_attr>())).attr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_device_attr),
+ "::",
+ stringify!(attr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_device_attr>())).addr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_device_attr),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+pub const kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20: kvm_device_type = 1;
+pub const kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_42: kvm_device_type = 2;
+pub const kvm_device_type_KVM_DEV_TYPE_XICS: kvm_device_type = 3;
+pub const kvm_device_type_KVM_DEV_TYPE_VFIO: kvm_device_type = 4;
+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;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_s390_ucas_mapping {
+ pub user_addr: __u64,
+ pub vcpu_addr: __u64,
+ pub length: __u64,
+}
+#[test]
+fn bindgen_test_layout_kvm_s390_ucas_mapping() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_s390_ucas_mapping>(),
+ 24usize,
+ concat!("Size of: ", stringify!(kvm_s390_ucas_mapping))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_s390_ucas_mapping>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(kvm_s390_ucas_mapping))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ucas_mapping>())).user_addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ucas_mapping),
+ "::",
+ stringify!(user_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ucas_mapping>())).vcpu_addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ucas_mapping),
+ "::",
+ stringify!(vcpu_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_s390_ucas_mapping>())).length as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_s390_ucas_mapping),
+ "::",
+ stringify!(length)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_assigned_pci_dev {
+ pub assigned_dev_id: __u32,
+ pub busnr: __u32,
+ pub devfn: __u32,
+ pub flags: __u32,
+ pub segnr: __u32,
+ pub __bindgen_anon_1: kvm_assigned_pci_dev__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_assigned_pci_dev__bindgen_ty_1 {
+ pub reserved: [__u32; 11usize],
+ _bindgen_union_align: [u32; 11usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_pci_dev__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_pci_dev__bindgen_ty_1>(),
+ 44usize,
+ concat!("Size of: ", stringify!(kvm_assigned_pci_dev__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_pci_dev__bindgen_ty_1>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(kvm_assigned_pci_dev__bindgen_ty_1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_pci_dev__bindgen_ty_1>())).reserved as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev__bindgen_ty_1),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Default for kvm_assigned_pci_dev__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_pci_dev() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_pci_dev>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_assigned_pci_dev))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_pci_dev>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_pci_dev))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).assigned_dev_id as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(assigned_dev_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).busnr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(busnr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).devfn as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(devfn)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).flags as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_pci_dev>())).segnr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_pci_dev),
+ "::",
+ stringify!(segnr)
+ )
+ );
+}
+impl Default for kvm_assigned_pci_dev {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct kvm_assigned_irq {
+ pub assigned_dev_id: __u32,
+ pub host_irq: __u32,
+ pub guest_irq: __u32,
+ pub flags: __u32,
+ pub __bindgen_anon_1: kvm_assigned_irq__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union kvm_assigned_irq__bindgen_ty_1 {
+ pub reserved: [__u32; 12usize],
+ _bindgen_union_align: [u32; 12usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_irq__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_irq__bindgen_ty_1>(),
+ 48usize,
+ concat!("Size of: ", stringify!(kvm_assigned_irq__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_irq__bindgen_ty_1>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_irq__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_irq__bindgen_ty_1>())).reserved as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq__bindgen_ty_1),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Default for kvm_assigned_irq__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_irq() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_irq>(),
+ 64usize,
+ concat!("Size of: ", stringify!(kvm_assigned_irq))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_irq>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_irq))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_irq>())).assigned_dev_id as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq),
+ "::",
+ stringify!(assigned_dev_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_irq>())).host_irq as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq),
+ "::",
+ stringify!(host_irq)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_irq>())).guest_irq as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq),
+ "::",
+ stringify!(guest_irq)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_irq>())).flags as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_irq),
+ "::",
+ stringify!(flags)
+ )
+ );
+}
+impl Default for kvm_assigned_irq {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_assigned_msix_nr {
+ pub assigned_dev_id: __u32,
+ pub entry_nr: __u16,
+ pub padding: __u16,
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_msix_nr() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_msix_nr>(),
+ 8usize,
+ concat!("Size of: ", stringify!(kvm_assigned_msix_nr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_msix_nr>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_msix_nr))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_msix_nr>())).assigned_dev_id as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_nr),
+ "::",
+ stringify!(assigned_dev_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_nr>())).entry_nr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_nr),
+ "::",
+ stringify!(entry_nr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_nr>())).padding as *const _ as usize },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_nr),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct kvm_assigned_msix_entry {
+ pub assigned_dev_id: __u32,
+ pub gsi: __u32,
+ pub entry: __u16,
+ pub padding: [__u16; 3usize],
+}
+#[test]
+fn bindgen_test_layout_kvm_assigned_msix_entry() {
+ assert_eq!(
+ ::std::mem::size_of::<kvm_assigned_msix_entry>(),
+ 16usize,
+ concat!("Size of: ", stringify!(kvm_assigned_msix_entry))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<kvm_assigned_msix_entry>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(kvm_assigned_msix_entry))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<kvm_assigned_msix_entry>())).assigned_dev_id as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_entry),
+ "::",
+ stringify!(assigned_dev_id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_entry>())).gsi as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_entry),
+ "::",
+ stringify!(gsi)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_entry>())).entry as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_entry),
+ "::",
+ stringify!(entry)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<kvm_assigned_msix_entry>())).padding as *const _ as usize },
+ 10usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(kvm_assigned_msix_entry),
+ "::",
+ stringify!(padding)
+ )
+ );
+}
diff --git a/kvm_sys/tests/sanity.rs b/kvm_sys/tests/sanity.rs
new file mode 100644
index 0000000..7669b44
--- /dev/null
+++ b/kvm_sys/tests/sanity.rs
@@ -0,0 +1,36 @@
+// Copyright 2017 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 libc::{c_char, ioctl, open, O_RDWR};
+
+use kvm_sys::*;
+
+const KVM_PATH: &'static str = "/dev/kvm\0";
+
+#[test]
+fn get_version() {
+ let sys_fd = unsafe { open(KVM_PATH.as_ptr() as *const c_char, O_RDWR) };
+ assert!(sys_fd >= 0);
+
+ let ret = unsafe { ioctl(sys_fd, KVM_GET_API_VERSION(), 0) };
+ assert_eq!(ret as u32, KVM_API_VERSION);
+}
+
+#[test]
+fn create_vm_fd() {
+ let sys_fd = unsafe { open(KVM_PATH.as_ptr() as *const c_char, O_RDWR) };
+ assert!(sys_fd >= 0);
+
+ let vm_fd = unsafe { ioctl(sys_fd, KVM_CREATE_VM(), 0) };
+ assert!(vm_fd >= 0);
+}
+
+#[test]
+fn check_vm_extension() {
+ let sys_fd = unsafe { open(KVM_PATH.as_ptr() as *const c_char, O_RDWR) };
+ assert!(sys_fd >= 0);
+
+ let has_user_memory = unsafe { ioctl(sys_fd, KVM_CHECK_EXTENSION(), KVM_CAP_USER_MEMORY) };
+ assert_eq!(has_user_memory, 1);
+}
diff --git a/msg_socket/Cargo.toml b/msg_socket/Cargo.toml
new file mode 100644
index 0000000..dcfccfc
--- /dev/null
+++ b/msg_socket/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "msg_socket"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+data_model = { path = "../data_model" }
+msg_on_socket_derive = { path = "msg_on_socket_derive" }
+sys_util = { path = "../sys_util" }
diff --git a/msg_socket/msg_on_socket_derive/Cargo.toml b/msg_socket/msg_on_socket_derive/Cargo.toml
new file mode 100644
index 0000000..78e88de
--- /dev/null
+++ b/msg_socket/msg_on_socket_derive/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "msg_on_socket_derive"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+proc-macro2 = "=0.4"
+quote = "=0.6"
+syn = "=0.15"
+
+[lib]
+proc-macro = true
+path = "msg_on_socket_derive.rs"
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
new file mode 100644
index 0000000..3779357
--- /dev/null
+++ b/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs
@@ -0,0 +1,747 @@
+// Copyright 2018 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.
+
+#![recursion_limit = "256"]
+extern crate proc_macro;
+
+use std::vec::Vec;
+
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Fields, Ident};
+
+/// The function that derives the recursive implementation for struct or enum.
+#[proc_macro_derive(MsgOnSocket)]
+pub fn msg_on_socket_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ let impl_for_input = socket_msg_impl(input);
+ impl_for_input.into()
+}
+
+fn socket_msg_impl(input: DeriveInput) -> TokenStream {
+ if !input.generics.params.is_empty() {
+ return quote! {
+ compile_error!("derive(SocketMsg) does not support generic parameters");
+ };
+ }
+ match input.data {
+ Data::Struct(ds) => {
+ if is_named_struct(&ds) {
+ impl_for_named_struct(input.ident, ds)
+ } else {
+ impl_for_tuple_struct(input.ident, ds)
+ }
+ }
+ Data::Enum(de) => impl_for_enum(input.ident, de),
+ _ => quote! {
+ compile_error!("derive(SocketMsg) only support struct and enum");
+ },
+ }
+}
+
+fn is_named_struct(ds: &DataStruct) -> bool {
+ match &ds.fields {
+ Fields::Named(_f) => true,
+ _ => false,
+ }
+}
+
+/************************** Named Struct Impls ********************************************/
+fn impl_for_named_struct(name: Ident, ds: DataStruct) -> TokenStream {
+ let fields = get_struct_fields(ds);
+ let fields_types = get_types_from_fields_vec(&fields);
+ let buffer_sizes_impls = define_buffer_size_for_struct(&fields_types);
+
+ let read_buffer = define_read_buffer_for_struct(&name, &fields);
+ let write_buffer = define_write_buffer_for_struct(&name, &fields);
+ quote! {
+ impl msg_socket::MsgOnSocket for #name {
+ #buffer_sizes_impls
+ #read_buffer
+ #write_buffer
+ }
+ }
+}
+
+fn get_types_from_fields_vec(v: &[(Ident, syn::Type)]) -> Vec<syn::Type> {
+ let mut fields_types = Vec::new();
+ for (_i, t) in v {
+ fields_types.push(t.clone());
+ }
+ fields_types
+}
+
+// Flatten struct fields.
+// "myfield : Type" -> \(ident\("myfield"\), Token\(Type\)\)
+fn get_struct_fields(ds: DataStruct) -> Vec<(Ident, syn::Type)> {
+ let fields = match ds.fields {
+ Fields::Named(fields_named) => fields_named.named,
+ _ => {
+ panic!("Struct must have named fields");
+ }
+ };
+ let mut vec = Vec::new();
+ for field in fields {
+ let ident = match field.ident {
+ Some(ident) => ident,
+ None => panic!("Unknown Error."),
+ };
+ let ty = field.ty;
+ vec.push((ident, ty));
+ }
+ vec
+}
+
+fn define_buffer_size_for_struct(field_types: &[syn::Type]) -> TokenStream {
+ let (msg_size, max_fd_count) = get_fields_buffer_size_sum(field_types);
+ quote! {
+ fn msg_size() -> usize {
+ #msg_size
+ }
+ fn max_fd_count() -> usize {
+ #max_fd_count
+ }
+ }
+}
+
+fn define_read_buffer_for_struct(_name: &Ident, fields: &[(Ident, syn::Type)]) -> TokenStream {
+ let mut read_fields = Vec::new();
+ let mut init_fields = Vec::new();
+ for f in fields {
+ let read_field = read_from_buffer_and_move_offset(&f.0, &f.1);
+ read_fields.push(read_field);
+ let name = f.0.clone();
+ init_fields.push(quote!(#name));
+ }
+ quote! {
+ unsafe fn read_from_buffer(
+ buffer: &[u8],
+ fds: &[std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<(Self, usize)> {
+ let mut __offset = 0usize;
+ let mut __fd_offset = 0usize;
+ #(#read_fields)*
+ Ok((
+ Self {
+ #(#init_fields),*
+ },
+ __fd_offset
+ ))
+ }
+ }
+}
+
+fn define_write_buffer_for_struct(_name: &Ident, fields: &[(Ident, syn::Type)]) -> TokenStream {
+ let mut write_fields = Vec::new();
+ for f in fields {
+ let write_field = write_to_buffer_and_move_offset(&f.0, &f.1);
+ write_fields.push(write_field);
+ }
+ quote! {
+ fn write_to_buffer(
+ &self,
+ buffer: &mut [u8],
+ fds: &mut [std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<usize> {
+ let mut __offset = 0usize;
+ let mut __fd_offset = 0usize;
+ #(#write_fields)*
+ Ok(__fd_offset)
+ }
+ }
+}
+
+/************************** Enum Impls ********************************************/
+fn impl_for_enum(name: Ident, de: DataEnum) -> TokenStream {
+ let variants = get_enum_variant_types(&de);
+ let buffer_sizes_impls = define_buffer_size_for_enum(&variants);
+
+ let read_buffer = define_read_buffer_for_enum(&name, &de);
+ let write_buffer = define_write_buffer_for_enum(&name, &de);
+ quote! {
+ impl msg_socket::MsgOnSocket for #name {
+ #buffer_sizes_impls
+ #read_buffer
+ #write_buffer
+ }
+ }
+}
+
+fn define_buffer_size_for_enum(variants: &[(Ident, Vec<syn::Type>)]) -> TokenStream {
+ let mut variant_buffer_sizes = Vec::new();
+ let mut variant_fd_sizes = Vec::new();
+ for v in variants {
+ let (msg_size_impl, fd_count_impl) = get_fields_buffer_size_sum(&v.1);
+ variant_buffer_sizes.push(msg_size_impl);
+ variant_fd_sizes.push(fd_count_impl);
+ }
+ quote! {
+ fn msg_size() -> usize {
+ // First byte is used for variant.
+ [#(#variant_buffer_sizes,)*].iter().max().unwrap().clone() as usize + 1
+ }
+ fn max_fd_count() -> usize {
+ [#(#variant_fd_sizes,)*].iter().max().unwrap().clone() as usize
+ }
+ }
+}
+
+// Flatten enum variants. Return value = \[variant_name, \[types_of_this_variant\]\]
+fn get_enum_variant_types(de: &DataEnum) -> Vec<(Ident, Vec<syn::Type>)> {
+ let mut variants = Vec::new();
+ let de = de.clone();
+ for v in de.variants {
+ let name = v.ident;
+ match v.fields {
+ Fields::Unnamed(fields) => {
+ let mut vec = Vec::new();
+ for field in fields.unnamed {
+ let ty = field.ty;
+ vec.push(ty);
+ }
+ variants.push((name, vec));
+ }
+ Fields::Unit => {
+ variants.push((name, Vec::new()));
+ continue;
+ }
+ Fields::Named(fields) => {
+ let mut vec = Vec::new();
+ for field in fields.named {
+ let ty = field.ty;
+ vec.push(ty);
+ }
+ variants.push((name, vec));
+ }
+ };
+ }
+ variants
+}
+
+fn define_read_buffer_for_enum(name: &Ident, de: &DataEnum) -> TokenStream {
+ let mut match_variants = Vec::new();
+ let de = de.clone();
+ let mut i = 0u8;
+ for v in de.variants {
+ let variant_name = v.ident;
+ match v.fields {
+ Fields::Named(fields) => {
+ let mut tmp_names = Vec::new();
+ let mut read_tmps = Vec::new();
+ for f in fields.named {
+ tmp_names.push(f.ident.clone());
+ let read_tmp = read_from_buffer_and_move_offset(&f.ident.unwrap(), &f.ty);
+ read_tmps.push(read_tmp);
+ }
+ let v = quote! {
+ #i => {
+ let mut __offset = 1usize;
+ let mut __fd_offset = 0usize;
+ #(#read_tmps)*
+ Ok((#name::#variant_name { #(#tmp_names),* }, __fd_offset))
+ }
+ };
+ match_variants.push(v);
+ }
+ Fields::Unnamed(fields) => {
+ let mut tmp_names = Vec::new();
+ let mut read_tmps = Vec::new();
+ let mut j = 0usize;
+ for f in fields.unnamed {
+ let tmp_name = format!("enum_variant_tmp{}", j);
+ let tmp_name = Ident::new(&tmp_name, Span::call_site());
+ tmp_names.push(tmp_name.clone());
+ let read_tmp = read_from_buffer_and_move_offset(&tmp_name, &f.ty);
+ read_tmps.push(read_tmp);
+ j += 1;
+ }
+
+ let v = quote! {
+ #i => {
+ let mut __offset = 1usize;
+ let mut __fd_offset = 0usize;
+ #(#read_tmps)*
+ Ok((#name::#variant_name( #(#tmp_names),*), __fd_offset))
+ }
+ };
+ match_variants.push(v);
+ }
+ Fields::Unit => {
+ let v = quote! {
+ #i => Ok((#name::#variant_name, 0)),
+ };
+ match_variants.push(v);
+ }
+ }
+ i += 1;
+ }
+ quote! {
+ unsafe fn read_from_buffer(
+ buffer: &[u8],
+ fds: &[std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<(Self, usize)> {
+ let v = buffer[0];
+ match v {
+ #(#match_variants)*
+ _ => Err(msg_socket::MsgError::InvalidType),
+ }
+ }
+ }
+}
+
+fn define_write_buffer_for_enum(name: &Ident, de: &DataEnum) -> TokenStream {
+ let mut match_variants = Vec::new();
+ let mut i = 0u8;
+ let de = de.clone();
+ for v in de.variants {
+ let variant_name = v.ident;
+ match v.fields {
+ Fields::Named(fields) => {
+ let mut tmp_names = Vec::new();
+ let mut write_tmps = Vec::new();
+ for f in fields.named {
+ tmp_names.push(f.ident.clone().unwrap());
+ let write_tmp = enum_write_to_buffer_and_move_offset(&f.ident.unwrap(), &f.ty);
+ write_tmps.push(write_tmp);
+ }
+
+ let v = quote! {
+ #name::#variant_name { #(#tmp_names),* } => {
+ buffer[0] = #i;
+ let mut __offset = 1usize;
+ let mut __fd_offset = 0usize;
+ #(#write_tmps)*
+ Ok(__fd_offset)
+ }
+ };
+ match_variants.push(v);
+ }
+ Fields::Unnamed(fields) => {
+ let mut tmp_names = Vec::new();
+ let mut write_tmps = Vec::new();
+ let mut j = 0usize;
+ for f in fields.unnamed {
+ let tmp_name = format!("enum_variant_tmp{}", j);
+ let tmp_name = Ident::new(&tmp_name, Span::call_site());
+ tmp_names.push(tmp_name.clone());
+ let write_tmp = enum_write_to_buffer_and_move_offset(&tmp_name, &f.ty);
+ write_tmps.push(write_tmp);
+ j += 1;
+ }
+
+ let v = quote! {
+ #name::#variant_name(#(#tmp_names),*) => {
+ buffer[0] = #i;
+ let mut __offset = 1usize;
+ let mut __fd_offset = 0usize;
+ #(#write_tmps)*
+ Ok(__fd_offset)
+ }
+ };
+ match_variants.push(v);
+ }
+ Fields::Unit => {
+ let v = quote! {
+ #name::#variant_name => {
+ buffer[0] = #i;
+ Ok(0)
+ }
+ };
+ match_variants.push(v);
+ }
+ }
+ i += 1;
+ }
+
+ quote! {
+ fn write_to_buffer(
+ &self,
+ buffer: &mut [u8],
+ fds: &mut [std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<usize> {
+ match self {
+ #(#match_variants)*
+ }
+ }
+ }
+}
+
+fn enum_write_to_buffer_and_move_offset(name: &Ident, ty: &syn::Type) -> TokenStream {
+ quote! {
+ let o = #name.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <#ty>::msg_size();
+ __fd_offset += o;
+ }
+}
+
+/************************** Tuple Impls ********************************************/
+fn impl_for_tuple_struct(name: Ident, ds: DataStruct) -> TokenStream {
+ let types = get_tuple_types(ds);
+
+ let buffer_sizes_impls = define_buffer_size_for_struct(&types);
+
+ let read_buffer = define_read_buffer_for_tuples(&name, &types);
+ let write_buffer = define_write_buffer_for_tuples(&name, &types);
+ quote! {
+ impl msg_socket::MsgOnSocket for #name {
+ #buffer_sizes_impls
+ #read_buffer
+ #write_buffer
+ }
+ }
+}
+
+fn get_tuple_types(ds: DataStruct) -> Vec<syn::Type> {
+ let mut types = Vec::new();
+ let fields = match ds.fields {
+ Fields::Unnamed(fields_unnamed) => fields_unnamed.unnamed,
+ _ => {
+ panic!("Tuple struct must have unnamed fields.");
+ }
+ };
+ for field in fields {
+ let ty = field.ty;
+ types.push(ty);
+ }
+ types
+}
+
+fn define_read_buffer_for_tuples(name: &Ident, fields: &[syn::Type]) -> TokenStream {
+ let mut read_fields = Vec::new();
+ let mut init_fields = Vec::new();
+ for i in 0..fields.len() {
+ let tmp_name = format!("tuple_tmp{}", i);
+ let tmp_name = Ident::new(&tmp_name, Span::call_site());
+ let read_field = read_from_buffer_and_move_offset(&tmp_name, &fields[i]);
+ read_fields.push(read_field);
+ init_fields.push(quote!(#tmp_name));
+ }
+
+ quote! {
+ unsafe fn read_from_buffer(
+ buffer: &[u8],
+ fds: &[std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<(Self, usize)> {
+ let mut __offset = 0usize;
+ let mut __fd_offset = 0usize;
+ #(#read_fields)*
+ Ok((
+ #name (
+ #(#init_fields),*
+ ),
+ __fd_offset
+ ))
+ }
+ }
+}
+
+fn define_write_buffer_for_tuples(name: &Ident, fields: &[syn::Type]) -> TokenStream {
+ let mut write_fields = Vec::new();
+ let mut tmp_names = Vec::new();
+ for i in 0..fields.len() {
+ let tmp_name = format!("tuple_tmp{}", i);
+ let tmp_name = Ident::new(&tmp_name, Span::call_site());
+ let write_field = enum_write_to_buffer_and_move_offset(&tmp_name, &fields[i]);
+ write_fields.push(write_field);
+ tmp_names.push(tmp_name);
+ }
+ quote! {
+ fn write_to_buffer(
+ &self,
+ buffer: &mut [u8],
+ fds: &mut [std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<usize> {
+ let mut __offset = 0usize;
+ let mut __fd_offset = 0usize;
+ let #name( #(#tmp_names),* ) = self;
+ #(#write_fields)*
+ Ok(__fd_offset)
+ }
+ }
+}
+/************************** Helpers ********************************************/
+fn get_fields_buffer_size_sum(field_types: &[syn::Type]) -> (TokenStream, TokenStream) {
+ if field_types.len() > 0 {
+ (
+ quote! {
+ #( <#field_types>::msg_size() as usize )+*
+ },
+ quote! {
+ #( <#field_types>::max_fd_count() as usize )+*
+ },
+ )
+ } else {
+ (quote!(0), quote!(0))
+ }
+}
+
+fn read_from_buffer_and_move_offset(name: &Ident, ty: &syn::Type) -> TokenStream {
+ quote! {
+ let t = <#ty>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <#ty>::msg_size();
+ __fd_offset += t.1;
+ let #name = t.0;
+ }
+}
+
+fn write_to_buffer_and_move_offset(name: &Ident, ty: &syn::Type) -> TokenStream {
+ quote! {
+ let o = self.#name.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <#ty>::msg_size();
+ __fd_offset += o;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::socket_msg_impl;
+ use quote::quote;
+ use syn::{parse_quote, DeriveInput};
+
+ #[test]
+ fn end_to_end_struct_test() {
+ let input: DeriveInput = parse_quote! {
+ struct MyMsg {
+ a: u8,
+ b: RawFd,
+ c: u32,
+ }
+ };
+
+ let expected = quote! {
+ impl msg_socket::MsgOnSocket for MyMsg {
+ fn msg_size() -> usize {
+ <u8>::msg_size() as usize
+ + <RawFd>::msg_size() as usize
+ + <u32>::msg_size() as usize
+ }
+ fn max_fd_count() -> usize {
+ <u8>::max_fd_count() as usize
+ + <RawFd>::max_fd_count() as usize
+ + <u32>::max_fd_count() as usize
+ }
+ unsafe fn read_from_buffer(
+ buffer: &[u8],
+ fds: &[std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<(Self, usize)> {
+ let mut __offset = 0usize;
+ let mut __fd_offset = 0usize;
+ let t = <u8>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <u8>::msg_size();
+ __fd_offset += t.1;
+ let a = t.0;
+ let t = <RawFd>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <RawFd>::msg_size();
+ __fd_offset += t.1;
+ let b = t.0;
+ let t = <u32>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <u32>::msg_size();
+ __fd_offset += t.1;
+ let c = t.0;
+ Ok((Self { a, b, c }, __fd_offset))
+ }
+ fn write_to_buffer(
+ &self,
+ buffer: &mut [u8],
+ fds: &mut [std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<usize> {
+ let mut __offset = 0usize;
+ let mut __fd_offset = 0usize;
+ let o = self.a
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <u8>::msg_size();
+ __fd_offset += o;
+ let o = self.b
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <RawFd>::msg_size();
+ __fd_offset += o;
+ let o = self.c
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <u32>::msg_size();
+ __fd_offset += o;
+ Ok(__fd_offset)
+ }
+ }
+ };
+
+ assert_eq!(socket_msg_impl(input).to_string(), expected.to_string());
+ }
+
+ #[test]
+ fn end_to_end_tuple_struct_test() {
+ let input: DeriveInput = parse_quote! {
+ struct MyMsg(u8, u32, File);
+ };
+
+ let expected = quote! {
+ impl msg_socket::MsgOnSocket for MyMsg {
+ fn msg_size() -> usize {
+ <u8>::msg_size() as usize
+ + <u32>::msg_size() as usize
+ + <File>::msg_size() as usize
+ }
+ fn max_fd_count() -> usize {
+ <u8>::max_fd_count() as usize
+ + <u32>::max_fd_count() as usize
+ + <File>::max_fd_count() as usize
+ }
+ unsafe fn read_from_buffer(
+ buffer: &[u8],
+ fds: &[std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<(Self, usize)> {
+ let mut __offset = 0usize;
+ let mut __fd_offset = 0usize;
+ let t = <u8>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <u8>::msg_size();
+ __fd_offset += t.1;
+ let tuple_tmp0 = t.0;
+ let t = <u32>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <u32>::msg_size();
+ __fd_offset += t.1;
+ let tuple_tmp1 = t.0;
+ let t = <File>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <File>::msg_size();
+ __fd_offset += t.1;
+ let tuple_tmp2 = t.0;
+ Ok((MyMsg(tuple_tmp0, tuple_tmp1, tuple_tmp2), __fd_offset))
+ }
+ fn write_to_buffer(
+ &self,
+ buffer: &mut [u8],
+ fds: &mut [std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<usize> {
+ let mut __offset = 0usize;
+ let mut __fd_offset = 0usize;
+ let MyMsg(tuple_tmp0, tuple_tmp1, tuple_tmp2) = self;
+ let o = tuple_tmp0
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <u8>::msg_size();
+ __fd_offset += o;
+ let o = tuple_tmp1
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <u32>::msg_size();
+ __fd_offset += o;
+ let o = tuple_tmp2
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <File>::msg_size();
+ __fd_offset += o;
+ Ok(__fd_offset)
+ }
+ }
+ };
+
+ assert_eq!(socket_msg_impl(input).to_string(), expected.to_string());
+ }
+
+ #[test]
+ fn end_to_end_enum_test() {
+ let input: DeriveInput = parse_quote! {
+ enum MyMsg {
+ A(u8),
+ B,
+ C {
+ f0: u8,
+ f1: RawFd,
+ },
+ }
+ };
+
+ let expected = quote! {
+ impl msg_socket::MsgOnSocket for MyMsg {
+ fn msg_size() -> usize {
+ [
+ <u8>::msg_size() as usize,
+ 0,
+ <u8>::msg_size() as usize + <RawFd>::msg_size() as usize,
+ ].iter()
+ .max().unwrap().clone() as usize+ 1
+ }
+ fn max_fd_count() -> usize {
+ [
+ <u8>::max_fd_count() as usize,
+ 0,
+ <u8>::max_fd_count() as usize + <RawFd>::max_fd_count() as usize,
+ ].iter()
+ .max().unwrap().clone() as usize
+ }
+ unsafe fn read_from_buffer(
+ buffer: &[u8],
+ fds: &[std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<(Self, usize)> {
+ let v = buffer[0];
+ match v {
+ 0u8 => {
+ let mut __offset = 1usize;
+ let mut __fd_offset = 0usize;
+ let t =
+ <u8>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <u8>::msg_size();
+ __fd_offset += t.1;
+ let enum_variant_tmp0 = t.0;
+ Ok((MyMsg::A(enum_variant_tmp0), __fd_offset))
+ }
+ 1u8 => Ok((MyMsg::B, 0)),
+ 2u8 => {
+ let mut __offset = 1usize;
+ let mut __fd_offset = 0usize;
+ let t =
+ <u8>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
+ __offset += <u8>::msg_size();
+ __fd_offset += t.1;
+ let f0 = t.0;
+ let t = <RawFd>::read_from_buffer(
+ &buffer[__offset..],
+ &fds[__fd_offset..]
+ )?;
+ __offset += <RawFd>::msg_size();
+ __fd_offset += t.1;
+ let f1 = t.0;
+ Ok((MyMsg::C { f0, f1 }, __fd_offset))
+ }
+ _ => Err(msg_socket::MsgError::InvalidType),
+ }
+ }
+ fn write_to_buffer(
+ &self,
+ buffer: &mut [u8],
+ fds: &mut [std::os::unix::io::RawFd],
+ ) -> msg_socket::MsgResult<usize> {
+ match self {
+ MyMsg::A(enum_variant_tmp0) => {
+ buffer[0] = 0u8;
+ let mut __offset = 1usize;
+ let mut __fd_offset = 0usize;
+ let o = enum_variant_tmp0
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <u8>::msg_size();
+ __fd_offset += o;
+ Ok(__fd_offset)
+ }
+ MyMsg::B => {
+ buffer[0] = 1u8;
+ Ok(0)
+ }
+ MyMsg::C { f0, f1 } => {
+ buffer[0] = 2u8;
+ let mut __offset = 1usize;
+ let mut __fd_offset = 0usize;
+ let o = f0
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <u8>::msg_size();
+ __fd_offset += o;
+ let o = f1
+ .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
+ __offset += <RawFd>::msg_size();
+ __fd_offset += o;
+ Ok(__fd_offset)
+ }
+ }
+ }
+ }
+
+ };
+
+ assert_eq!(socket_msg_impl(input).to_string(), expected.to_string());
+ }
+}
diff --git a/msg_socket/src/lib.rs b/msg_socket/src/lib.rs
new file mode 100644
index 0000000..bead3da
--- /dev/null
+++ b/msg_socket/src/lib.rs
@@ -0,0 +1,191 @@
+// Copyright 2018 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.
+
+mod msg_on_socket;
+
+use std::io::Result;
+use std::marker::PhantomData;
+use std::ops::Deref;
+use std::os::unix::io::{AsRawFd, RawFd};
+
+use sys_util::{handle_eintr, net::UnixSeqpacket, Error as SysError, ScmSocket};
+
+pub use crate::msg_on_socket::*;
+pub use msg_on_socket_derive::*;
+
+/// Create a pair of socket. Request is send in one direction while response is in the other
+/// direction.
+pub fn pair<Request: MsgOnSocket, Response: MsgOnSocket>(
+) -> Result<(MsgSocket<Request, Response>, MsgSocket<Response, Request>)> {
+ let (sock1, sock2) = UnixSeqpacket::pair()?;
+ let requester = MsgSocket {
+ sock: sock1,
+ _i: PhantomData,
+ _o: PhantomData,
+ };
+ let responder = MsgSocket {
+ sock: sock2,
+ _i: PhantomData,
+ _o: PhantomData,
+ };
+ Ok((requester, responder))
+}
+
+/// Bidirection sock that support both send and recv.
+pub struct MsgSocket<I: MsgOnSocket, O: MsgOnSocket> {
+ sock: UnixSeqpacket,
+ _i: PhantomData<I>,
+ _o: PhantomData<O>,
+}
+
+impl<I: MsgOnSocket, O: MsgOnSocket> MsgSocket<I, O> {
+ // Create a new MsgSocket.
+ pub fn new(s: UnixSeqpacket) -> MsgSocket<I, O> {
+ MsgSocket {
+ sock: s,
+ _i: PhantomData,
+ _o: PhantomData,
+ }
+ }
+}
+
+impl<I: MsgOnSocket, O: MsgOnSocket> Deref for MsgSocket<I, O> {
+ type Target = UnixSeqpacket;
+ fn deref(&self) -> &Self::Target {
+ &self.sock
+ }
+}
+
+/// One direction socket that only supports sending.
+pub struct Sender<M: MsgOnSocket> {
+ sock: UnixSeqpacket,
+ _m: PhantomData<M>,
+}
+
+impl<M: MsgOnSocket> Sender<M> {
+ /// Create a new sender sock.
+ pub fn new(s: UnixSeqpacket) -> Sender<M> {
+ Sender {
+ sock: s,
+ _m: PhantomData,
+ }
+ }
+}
+
+/// One direction socket that only supports receiving.
+pub struct Receiver<M: MsgOnSocket> {
+ sock: UnixSeqpacket,
+ _m: PhantomData<M>,
+}
+
+impl<M: MsgOnSocket> Receiver<M> {
+ /// Create a new receiver sock.
+ pub fn new(s: UnixSeqpacket) -> Receiver<M> {
+ Receiver {
+ sock: s,
+ _m: PhantomData,
+ }
+ }
+}
+
+impl<I: MsgOnSocket, O: MsgOnSocket> AsRef<UnixSeqpacket> for MsgSocket<I, O> {
+ fn as_ref(&self) -> &UnixSeqpacket {
+ &self.sock
+ }
+}
+
+impl<I: MsgOnSocket, O: MsgOnSocket> AsRawFd for MsgSocket<I, O> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.sock.as_raw_fd()
+ }
+}
+
+impl<M: MsgOnSocket> AsRef<UnixSeqpacket> for Sender<M> {
+ fn as_ref(&self) -> &UnixSeqpacket {
+ &self.sock
+ }
+}
+
+impl<M: MsgOnSocket> AsRawFd for Sender<M> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.sock.as_raw_fd()
+ }
+}
+
+impl<M: MsgOnSocket> AsRef<UnixSeqpacket> for Receiver<M> {
+ fn as_ref(&self) -> &UnixSeqpacket {
+ &self.sock
+ }
+}
+
+impl<M: MsgOnSocket> AsRawFd for Receiver<M> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.sock.as_raw_fd()
+ }
+}
+
+/// Types that could send a message.
+pub trait MsgSender<M: MsgOnSocket>: AsRef<UnixSeqpacket> {
+ fn send(&self, msg: &M) -> MsgResult<()> {
+ let msg_size = M::msg_size();
+ let fd_size = M::max_fd_count();
+ let mut msg_buffer: Vec<u8> = vec![0; msg_size];
+ let mut fd_buffer: Vec<RawFd> = vec![0; fd_size];
+
+ let fd_size = msg.write_to_buffer(&mut msg_buffer, &mut fd_buffer)?;
+ let sock: &UnixSeqpacket = self.as_ref();
+ if fd_size == 0 {
+ handle_eintr!(sock.send(&msg_buffer))
+ .map_err(|e| MsgError::Send(SysError::new(e.raw_os_error().unwrap_or(0))))?;
+ } else {
+ sock.send_with_fds(&msg_buffer[..], &fd_buffer[0..fd_size])
+ .map_err(MsgError::Send)?;
+ }
+ Ok(())
+ }
+}
+
+/// Types that could receive a message.
+pub trait MsgReceiver<M: MsgOnSocket>: AsRef<UnixSeqpacket> {
+ fn recv(&self) -> MsgResult<M> {
+ let msg_size = M::msg_size();
+ let fd_size = M::max_fd_count();
+ let mut msg_buffer: Vec<u8> = vec![0; msg_size];
+ let mut fd_buffer: Vec<RawFd> = vec![0; fd_size];
+
+ let sock: &UnixSeqpacket = self.as_ref();
+
+ let (recv_msg_size, recv_fd_size) = {
+ if fd_size == 0 {
+ let size = sock
+ .recv(&mut msg_buffer)
+ .map_err(|e| MsgError::Recv(SysError::new(e.raw_os_error().unwrap_or(0))))?;
+ (size, 0)
+ } else {
+ sock.recv_with_fds(&mut msg_buffer, &mut fd_buffer)
+ .map_err(MsgError::Recv)?
+ }
+ };
+ if msg_size != recv_msg_size {
+ return Err(MsgError::BadRecvSize {
+ expected: msg_size,
+ actual: recv_msg_size,
+ });
+ }
+ // Safe because fd buffer is read from socket.
+ let (v, read_fd_size) = unsafe {
+ M::read_from_buffer(&msg_buffer[0..recv_msg_size], &fd_buffer[0..recv_fd_size])?
+ };
+ if recv_fd_size != read_fd_size {
+ return Err(MsgError::NotExpectFd);
+ }
+ Ok(v)
+ }
+}
+
+impl<I: MsgOnSocket, O: MsgOnSocket> MsgSender<I> for MsgSocket<I, O> {}
+impl<I: MsgOnSocket, O: MsgOnSocket> MsgReceiver<O> for MsgSocket<I, O> {}
+
+impl<M: MsgOnSocket> MsgSender<M> for Sender<M> {}
+impl<M: MsgOnSocket> MsgReceiver<M> for Receiver<M> {}
diff --git a/msg_socket/src/msg_on_socket.rs b/msg_socket/src/msg_on_socket.rs
new file mode 100644
index 0000000..2924dc6
--- /dev/null
+++ b/msg_socket/src/msg_on_socket.rs
@@ -0,0 +1,385 @@
+// Copyright 2018 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::fmt::{self, Display};
+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};
+use std::result;
+
+use data_model::*;
+use sys_util::{Error as SysError, EventFd};
+
+#[derive(Debug, PartialEq)]
+/// An error during transaction or serialization/deserialization.
+pub enum MsgError {
+ /// Error while sending a request or response.
+ Send(SysError),
+ /// Error while receiving a request or response.
+ Recv(SysError),
+ /// The type of a received request or response is unknown.
+ InvalidType,
+ /// There was not the expected amount of data when receiving a message. The inner
+ /// value is how much data is expected and how much data was actually received.
+ BadRecvSize { expected: usize, actual: usize },
+ /// There was no associated file descriptor received for a request that expected it.
+ ExpectFd,
+ /// There was some associated file descriptor received but not used when deserialize.
+ NotExpectFd,
+ /// 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,
+ /// 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,
+}
+
+pub type MsgResult<T> = result::Result<T, MsgError>;
+
+impl Display for MsgError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::MsgError::*;
+
+ match self {
+ Send(e) => write!(f, "failed to send request or response: {}", e),
+ Recv(e) => write!(f, "failed to receive request or response: {}", e),
+ InvalidType => write!(f, "invalid type"),
+ BadRecvSize { expected, actual } => write!(
+ f,
+ "wrong amount of data received; expected {} bytes; got {} bytes",
+ expected, actual
+ ),
+ ExpectFd => write!(f, "missing associated file descriptor for request"),
+ NotExpectFd => write!(f, "unexpected file descriptor is unused"),
+ WrongFdBufferSize => write!(f, "fd buffer size too small"),
+ WrongMsgBufferSize => write!(f, "msg buffer size too small"),
+ }
+ }
+}
+
+/// A msg that could be serialized to and deserialize from array in little endian.
+///
+/// For structs, we always have fixed size of bytes and fixed count of fds.
+/// For enums, the size needed might be different for each variant.
+///
+/// e.g.
+/// ```
+/// use std::os::unix::io::RawFd;
+/// enum Message {
+/// VariantA(u8),
+/// VariantB(u32, RawFd),
+/// 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 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.
+/// However, for fd_buffer, we could not do the same thing. Otherwise, we are essentially sending
+/// fd 0 through the socket.
+/// 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 {
+ /// Size of message in bytes.
+ fn msg_size() -> usize;
+ /// Max possible fd count in this type.
+ fn max_fd_count() -> usize {
+ 0
+ }
+ /// Returns (self, fd read count).
+ /// This function is safe only when:
+ /// 0. fds contains valid fds, received from socket, serialized by Self::write_to_buffer.
+ /// 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)>;
+ /// Serialize self to buffers.
+ fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize>;
+}
+
+impl MsgOnSocket for SysError {
+ fn msg_size() -> usize {
+ u32::msg_size()
+ }
+ unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> 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> {
+ let v = self.errno() as u32;
+ v.write_to_buffer(buffer, fds)
+ }
+}
+
+impl MsgOnSocket for RawFd {
+ fn msg_size() -> usize {
+ 0
+ }
+ fn max_fd_count() -> usize {
+ 1
+ }
+ unsafe fn read_from_buffer(_buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+ if fds.is_empty() {
+ return Err(MsgError::ExpectFd);
+ }
+ Ok((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;
+ Ok(1)
+ }
+}
+
+impl<T: MsgOnSocket> MsgOnSocket for Option<T> {
+ fn msg_size() -> usize {
+ T::msg_size() + 1
+ }
+
+ fn max_fd_count() -> usize {
+ T::max_fd_count()
+ }
+
+ unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+ match buffer[0] {
+ 0 => Ok((None, 0)),
+ 1 => {
+ let (inner, len) = T::read_from_buffer(&buffer[1..], fds)?;
+ Ok((Some(inner), len))
+ }
+ _ => Err(MsgError::InvalidType),
+ }
+ }
+
+ fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+ match self {
+ None => {
+ buffer[0] = 0;
+ Ok(0)
+ }
+ Some(inner) => {
+ buffer[0] = 1;
+ inner.write_to_buffer(&mut buffer[1..], fds)
+ }
+ }
+ }
+}
+
+impl MsgOnSocket for () {
+ fn msg_size() -> usize {
+ 0
+ }
+
+ fn max_fd_count() -> usize {
+ 0
+ }
+
+ unsafe fn read_from_buffer(_buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+ Ok(((), 0))
+ }
+
+ fn write_to_buffer(&self, _buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
+ Ok(0)
+ }
+}
+
+macro_rules! rawfd_impl {
+ ($type:ident) => {
+ impl MsgOnSocket for $type {
+ fn msg_size() -> usize {
+ 0
+ }
+ fn max_fd_count() -> 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].clone()), 1))
+ }
+ fn write_to_buffer(&self, _buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+ if fds.len() < 1 {
+ return Err(MsgError::WrongFdBufferSize);
+ }
+ fds[0] = self.as_raw_fd();
+ Ok(1)
+ }
+ }
+ };
+}
+
+rawfd_impl!(EventFd);
+rawfd_impl!(File);
+rawfd_impl!(UnixStream);
+rawfd_impl!(TcpStream);
+rawfd_impl!(TcpListener);
+rawfd_impl!(UdpSocket);
+rawfd_impl!(UnixListener);
+rawfd_impl!(UnixDatagram);
+
+// Converts a slice into an array of fixed size inferred from by the return value. Panics if the
+// slice is too small, but will tolerate slices that are too large.
+fn slice_to_array<T, O>(s: &[T]) -> O
+where
+ T: Copy,
+ O: Default + AsMut<[T]>,
+{
+ let mut o = O::default();
+ let o_slice = o.as_mut();
+ let len = o_slice.len();
+ o_slice.copy_from_slice(&s[..len]);
+ o
+}
+
+// usize could be different sizes on different targets. We always use u64.
+impl MsgOnSocket for usize {
+ fn msg_size() -> usize {
+ std::mem::size_of::<u64>()
+ }
+ unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+ if buffer.len() < std::mem::size_of::<u64>() {
+ return Err(MsgError::WrongMsgBufferSize);
+ }
+ let t = u64::from_le_bytes(slice_to_array(buffer));
+ Ok((t as usize, 0))
+ }
+
+ fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
+ if buffer.len() < std::mem::size_of::<u64>() {
+ return Err(MsgError::WrongMsgBufferSize);
+ }
+ let t: Le64 = (*self as u64).into();
+ buffer[0..Self::msg_size()].copy_from_slice(t.as_slice());
+ Ok(0)
+ }
+}
+
+// Encode bool as a u8 of value 0 or 1
+impl MsgOnSocket for bool {
+ fn msg_size() -> usize {
+ std::mem::size_of::<u8>()
+ }
+ unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+ if buffer.len() < std::mem::size_of::<u8>() {
+ return Err(MsgError::WrongMsgBufferSize);
+ }
+ let t: u8 = buffer[0];
+ match t {
+ 0 => Ok((false, 0)),
+ 1 => Ok((true, 0)),
+ _ => Err(MsgError::InvalidType),
+ }
+ }
+ fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
+ if buffer.len() < std::mem::size_of::<u8>() {
+ return Err(MsgError::WrongMsgBufferSize);
+ }
+ buffer[0] = *self as u8;
+ Ok(0)
+ }
+}
+
+macro_rules! le_impl {
+ ($type:ident, $native_type:ident) => {
+ impl MsgOnSocket for $type {
+ fn msg_size() -> usize {
+ std::mem::size_of::<$native_type>()
+ }
+ unsafe fn read_from_buffer(buffer: &[u8], _fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+ if buffer.len() < std::mem::size_of::<$native_type>() {
+ return Err(MsgError::WrongMsgBufferSize);
+ }
+ let t = $native_type::from_le_bytes(slice_to_array(buffer));
+ Ok((t.into(), 0))
+ }
+
+ fn write_to_buffer(&self, buffer: &mut [u8], _fds: &mut [RawFd]) -> MsgResult<usize> {
+ if buffer.len() < std::mem::size_of::<$native_type>() {
+ return Err(MsgError::WrongMsgBufferSize);
+ }
+ let t: $native_type = self.clone().into();
+ buffer[0..Self::msg_size()].copy_from_slice(&t.to_le_bytes());
+ Ok(0)
+ }
+ }
+ };
+}
+
+le_impl!(u8, u8);
+le_impl!(u16, u16);
+le_impl!(u32, u32);
+le_impl!(u64, u64);
+
+le_impl!(Le16, u16);
+le_impl!(Le32, u32);
+le_impl!(Le64, u64);
+
+macro_rules! array_impls {
+ ($N:expr, $t: ident $($ts:ident)*)
+ => {
+ impl<T: MsgOnSocket + Clone> MsgOnSocket for [T; $N] {
+ fn msg_size() -> usize {
+ T::msg_size() * $N
+ }
+ fn max_fd_count() -> usize {
+ T::max_fd_count() * $N
+ }
+ unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+ if buffer.len() < Self::msg_size() {
+ return Err(MsgError::WrongMsgBufferSize);
+ }
+ let mut offset = 0usize;
+ let mut fd_offset = 0usize;
+ let ($t, fd_size) =
+ T::read_from_buffer(&buffer[offset..], &fds[fd_offset..])?;
+ offset += T::msg_size();
+ fd_offset += fd_size;
+ $(
+ let ($ts, fd_size) =
+ T::read_from_buffer(&buffer[offset..], &fds[fd_offset..])?;
+ offset += T::msg_size();
+ fd_offset += fd_size;
+ )*
+ assert_eq!(offset, Self::msg_size());
+ Ok(([$t, $($ts),*], fd_offset))
+ }
+
+ fn write_to_buffer(
+ &self,
+ buffer: &mut [u8],
+ fds: &mut [RawFd],
+ ) -> MsgResult<usize> {
+ if buffer.len() < Self::msg_size() {
+ return Err(MsgError::WrongMsgBufferSize);
+ }
+ let mut offset = 0usize;
+ let mut fd_offset = 0usize;
+ for idx in 0..$N {
+ let fd_size = self[idx].clone().write_to_buffer(&mut buffer[offset..],
+ &mut fds[fd_offset..])?;
+ offset += T::msg_size();
+ fd_offset += fd_size;
+ }
+
+ Ok(fd_offset)
+ }
+ }
+ array_impls!(($N - 1), $($ts)*);
+ };
+ {$N:expr, } => {};
+}
+
+array_impls! {
+ 32, tmp1 tmp2 tmp3 tmp4 tmp5 tmp6 tmp7 tmp8 tmp9 tmp10 tmp11 tmp12 tmp13 tmp14 tmp15 tmp16
+ tmp17 tmp18 tmp19 tmp20 tmp21 tmp22 tmp23 tmp24 tmp25 tmp26 tmp27 tmp28 tmp29 tmp30 tmp31
+ tmp32
+}
+
+// TODO(jkwang) Define MsgOnSocket for tuple?
diff --git a/msg_socket/tests/enum.rs b/msg_socket/tests/enum.rs
new file mode 100644
index 0000000..0e590f0
--- /dev/null
+++ b/msg_socket/tests/enum.rs
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use sys_util::EventFd;
+
+use msg_socket::*;
+
+#[derive(MsgOnSocket)]
+struct DummyRequest {}
+
+#[derive(MsgOnSocket)]
+enum Response {
+ A(u8),
+ B,
+ C(u32, EventFd),
+ D([u8; 4]),
+ E { f0: u8, f1: u32 },
+}
+
+#[test]
+fn sock_send_recv_enum() {
+ let (req, res) = pair::<DummyRequest, Response>().unwrap();
+ let e0 = EventFd::new().unwrap();
+ let e1 = e0.try_clone().unwrap();
+ res.send(&Response::C(0xf0f0, e0)).unwrap();
+ let r = req.recv().unwrap();
+ match r {
+ Response::C(v, efd) => {
+ assert_eq!(v, 0xf0f0);
+ efd.write(0x0f0f).unwrap();
+ }
+ _ => panic!("wrong type"),
+ };
+ assert_eq!(e1.read().unwrap(), 0x0f0f);
+
+ res.send(&Response::B).unwrap();
+ match req.recv().unwrap() {
+ Response::B => {}
+ _ => panic!("Wrong enum type"),
+ };
+
+ res.send(&Response::A(0x3)).unwrap();
+ match req.recv().unwrap() {
+ Response::A(v) => assert_eq!(v, 0x3),
+ _ => panic!("Wrong enum type"),
+ };
+
+ res.send(&Response::D([0, 1, 2, 3])).unwrap();
+ match req.recv().unwrap() {
+ Response::D(v) => assert_eq!(v, [0, 1, 2, 3]),
+ _ => panic!("Wrong enum type"),
+ };
+
+ res.send(&Response::E {
+ f0: 0x12,
+ f1: 0x0f0f,
+ })
+ .unwrap();
+ match req.recv().unwrap() {
+ Response::E { f0, f1 } => {
+ assert_eq!(f0, 0x12);
+ assert_eq!(f1, 0x0f0f);
+ }
+ _ => panic!("Wrong enum type"),
+ };
+}
diff --git a/msg_socket/tests/struct.rs b/msg_socket/tests/struct.rs
new file mode 100644
index 0000000..5efc369
--- /dev/null
+++ b/msg_socket/tests/struct.rs
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use sys_util::EventFd;
+
+use msg_socket::*;
+
+#[derive(MsgOnSocket)]
+struct Request {
+ field0: u8,
+ field1: EventFd,
+ field2: u32,
+ field3: bool,
+}
+
+#[derive(MsgOnSocket)]
+struct DummyResponse {}
+
+#[test]
+fn sock_send_recv_struct() {
+ let (req, res) = pair::<Request, DummyResponse>().unwrap();
+ let e0 = EventFd::new().unwrap();
+ let e1 = e0.try_clone().unwrap();
+ req.send(&Request {
+ field0: 2,
+ field1: e0,
+ field2: 0xf0f0,
+ field3: true,
+ })
+ .unwrap();
+ let r = res.recv().unwrap();
+ assert_eq!(r.field0, 2);
+ assert_eq!(r.field2, 0xf0f0);
+ assert_eq!(r.field3, true);
+ r.field1.write(0x0f0f).unwrap();
+ assert_eq!(e1.read().unwrap(), 0x0f0f);
+}
diff --git a/msg_socket/tests/tuple.rs b/msg_socket/tests/tuple.rs
new file mode 100644
index 0000000..862e0cb
--- /dev/null
+++ b/msg_socket/tests/tuple.rs
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use msg_socket::*;
+use sys_util::EventFd;
+
+#[derive(MsgOnSocket)]
+struct Message(u8, u16, EventFd);
+
+#[test]
+fn sock_send_recv_tuple() {
+ let (req, res) = pair::<Message, Message>().unwrap();
+ let e0 = EventFd::new().unwrap();
+ let e1 = e0.try_clone().unwrap();
+ req.send(&Message(1, 0x12, e0)).unwrap();
+ let r = res.recv().unwrap();
+ assert_eq!(r.0, 1);
+ assert_eq!(r.1, 0x12);
+ r.2.write(0x0f0f).unwrap();
+ assert_eq!(e1.read().unwrap(), 0x0f0f);
+}
diff --git a/msg_socket/tests/unit.rs b/msg_socket/tests/unit.rs
new file mode 100644
index 0000000..9855752
--- /dev/null
+++ b/msg_socket/tests/unit.rs
@@ -0,0 +1,12 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use msg_socket::*;
+
+#[test]
+fn sock_send_recv_unit() {
+ let (req, res) = pair::<(), ()>().unwrap();
+ req.send(&()).unwrap();
+ let _ = res.recv().unwrap();
+}
diff --git a/net_sys/Cargo.toml b/net_sys/Cargo.toml
new file mode 100644
index 0000000..ec04269
--- /dev/null
+++ b/net_sys/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "net_sys"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+sys_util = { path = "../sys_util" }
diff --git a/net_sys/src/if_tun.rs b/net_sys/src/if_tun.rs
new file mode 100644
index 0000000..0874b77
--- /dev/null
+++ b/net_sys/src/if_tun.rs
@@ -0,0 +1,599 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData)
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ 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 ETH_ALEN: ::std::os::raw::c_uint = 6;
+pub const ETH_HLEN: ::std::os::raw::c_uint = 14;
+pub const ETH_ZLEN: ::std::os::raw::c_uint = 60;
+pub const ETH_DATA_LEN: ::std::os::raw::c_uint = 1500;
+pub const ETH_FRAME_LEN: ::std::os::raw::c_uint = 1514;
+pub const ETH_FCS_LEN: ::std::os::raw::c_uint = 4;
+pub const ETH_P_LOOP: ::std::os::raw::c_uint = 96;
+pub const ETH_P_PUP: ::std::os::raw::c_uint = 512;
+pub const ETH_P_PUPAT: ::std::os::raw::c_uint = 513;
+pub const ETH_P_TSN: ::std::os::raw::c_uint = 8944;
+pub const ETH_P_IP: ::std::os::raw::c_uint = 2048;
+pub const ETH_P_X25: ::std::os::raw::c_uint = 2053;
+pub const ETH_P_ARP: ::std::os::raw::c_uint = 2054;
+pub const ETH_P_BPQ: ::std::os::raw::c_uint = 2303;
+pub const ETH_P_IEEEPUP: ::std::os::raw::c_uint = 2560;
+pub const ETH_P_IEEEPUPAT: ::std::os::raw::c_uint = 2561;
+pub const ETH_P_BATMAN: ::std::os::raw::c_uint = 17157;
+pub const ETH_P_DEC: ::std::os::raw::c_uint = 24576;
+pub const ETH_P_DNA_DL: ::std::os::raw::c_uint = 24577;
+pub const ETH_P_DNA_RC: ::std::os::raw::c_uint = 24578;
+pub const ETH_P_DNA_RT: ::std::os::raw::c_uint = 24579;
+pub const ETH_P_LAT: ::std::os::raw::c_uint = 24580;
+pub const ETH_P_DIAG: ::std::os::raw::c_uint = 24581;
+pub const ETH_P_CUST: ::std::os::raw::c_uint = 24582;
+pub const ETH_P_SCA: ::std::os::raw::c_uint = 24583;
+pub const ETH_P_TEB: ::std::os::raw::c_uint = 25944;
+pub const ETH_P_RARP: ::std::os::raw::c_uint = 32821;
+pub const ETH_P_ATALK: ::std::os::raw::c_uint = 32923;
+pub const ETH_P_AARP: ::std::os::raw::c_uint = 33011;
+pub const ETH_P_8021Q: ::std::os::raw::c_uint = 33024;
+pub const ETH_P_IPX: ::std::os::raw::c_uint = 33079;
+pub const ETH_P_IPV6: ::std::os::raw::c_uint = 34525;
+pub const ETH_P_PAUSE: ::std::os::raw::c_uint = 34824;
+pub const ETH_P_SLOW: ::std::os::raw::c_uint = 34825;
+pub const ETH_P_WCCP: ::std::os::raw::c_uint = 34878;
+pub const ETH_P_MPLS_UC: ::std::os::raw::c_uint = 34887;
+pub const ETH_P_MPLS_MC: ::std::os::raw::c_uint = 34888;
+pub const ETH_P_ATMMPOA: ::std::os::raw::c_uint = 34892;
+pub const ETH_P_PPP_DISC: ::std::os::raw::c_uint = 34915;
+pub const ETH_P_PPP_SES: ::std::os::raw::c_uint = 34916;
+pub const ETH_P_LINK_CTL: ::std::os::raw::c_uint = 34924;
+pub const ETH_P_ATMFATE: ::std::os::raw::c_uint = 34948;
+pub const ETH_P_PAE: ::std::os::raw::c_uint = 34958;
+pub const ETH_P_AOE: ::std::os::raw::c_uint = 34978;
+pub const ETH_P_8021AD: ::std::os::raw::c_uint = 34984;
+pub const ETH_P_802_EX1: ::std::os::raw::c_uint = 34997;
+pub const ETH_P_TIPC: ::std::os::raw::c_uint = 35018;
+pub const ETH_P_8021AH: ::std::os::raw::c_uint = 35047;
+pub const ETH_P_MVRP: ::std::os::raw::c_uint = 35061;
+pub const ETH_P_1588: ::std::os::raw::c_uint = 35063;
+pub const ETH_P_PRP: ::std::os::raw::c_uint = 35067;
+pub const ETH_P_FCOE: ::std::os::raw::c_uint = 35078;
+pub const ETH_P_TDLS: ::std::os::raw::c_uint = 35085;
+pub const ETH_P_FIP: ::std::os::raw::c_uint = 35092;
+pub const ETH_P_80221: ::std::os::raw::c_uint = 35095;
+pub const ETH_P_LOOPBACK: ::std::os::raw::c_uint = 36864;
+pub const ETH_P_QINQ1: ::std::os::raw::c_uint = 37120;
+pub const ETH_P_QINQ2: ::std::os::raw::c_uint = 37376;
+pub const ETH_P_QINQ3: ::std::os::raw::c_uint = 37632;
+pub const ETH_P_EDSA: ::std::os::raw::c_uint = 56026;
+pub const ETH_P_AF_IUCV: ::std::os::raw::c_uint = 64507;
+pub const ETH_P_802_3_MIN: ::std::os::raw::c_uint = 1536;
+pub const ETH_P_802_3: ::std::os::raw::c_uint = 1;
+pub const ETH_P_AX25: ::std::os::raw::c_uint = 2;
+pub const ETH_P_ALL: ::std::os::raw::c_uint = 3;
+pub const ETH_P_802_2: ::std::os::raw::c_uint = 4;
+pub const ETH_P_SNAP: ::std::os::raw::c_uint = 5;
+pub const ETH_P_DDCMP: ::std::os::raw::c_uint = 6;
+pub const ETH_P_WAN_PPP: ::std::os::raw::c_uint = 7;
+pub const ETH_P_PPP_MP: ::std::os::raw::c_uint = 8;
+pub const ETH_P_LOCALTALK: ::std::os::raw::c_uint = 9;
+pub const ETH_P_CAN: ::std::os::raw::c_uint = 12;
+pub const ETH_P_CANFD: ::std::os::raw::c_uint = 13;
+pub const ETH_P_PPPTALK: ::std::os::raw::c_uint = 16;
+pub const ETH_P_TR_802_2: ::std::os::raw::c_uint = 17;
+pub const ETH_P_MOBITEX: ::std::os::raw::c_uint = 21;
+pub const ETH_P_CONTROL: ::std::os::raw::c_uint = 22;
+pub const ETH_P_IRDA: ::std::os::raw::c_uint = 23;
+pub const ETH_P_ECONET: ::std::os::raw::c_uint = 24;
+pub const ETH_P_HDLC: ::std::os::raw::c_uint = 25;
+pub const ETH_P_ARCNET: ::std::os::raw::c_uint = 26;
+pub const ETH_P_DSA: ::std::os::raw::c_uint = 27;
+pub const ETH_P_TRAILER: ::std::os::raw::c_uint = 28;
+pub const ETH_P_PHONET: ::std::os::raw::c_uint = 245;
+pub const ETH_P_IEEE802154: ::std::os::raw::c_uint = 246;
+pub const ETH_P_CAIF: ::std::os::raw::c_uint = 247;
+pub const ETH_P_XDSA: ::std::os::raw::c_uint = 248;
+pub const BPF_LD: ::std::os::raw::c_uint = 0;
+pub const BPF_LDX: ::std::os::raw::c_uint = 1;
+pub const BPF_ST: ::std::os::raw::c_uint = 2;
+pub const BPF_STX: ::std::os::raw::c_uint = 3;
+pub const BPF_ALU: ::std::os::raw::c_uint = 4;
+pub const BPF_JMP: ::std::os::raw::c_uint = 5;
+pub const BPF_RET: ::std::os::raw::c_uint = 6;
+pub const BPF_MISC: ::std::os::raw::c_uint = 7;
+pub const BPF_W: ::std::os::raw::c_uint = 0;
+pub const BPF_H: ::std::os::raw::c_uint = 8;
+pub const BPF_B: ::std::os::raw::c_uint = 16;
+pub const BPF_IMM: ::std::os::raw::c_uint = 0;
+pub const BPF_ABS: ::std::os::raw::c_uint = 32;
+pub const BPF_IND: ::std::os::raw::c_uint = 64;
+pub const BPF_MEM: ::std::os::raw::c_uint = 96;
+pub const BPF_LEN: ::std::os::raw::c_uint = 128;
+pub const BPF_MSH: ::std::os::raw::c_uint = 160;
+pub const BPF_ADD: ::std::os::raw::c_uint = 0;
+pub const BPF_SUB: ::std::os::raw::c_uint = 16;
+pub const BPF_MUL: ::std::os::raw::c_uint = 32;
+pub const BPF_DIV: ::std::os::raw::c_uint = 48;
+pub const BPF_OR: ::std::os::raw::c_uint = 64;
+pub const BPF_AND: ::std::os::raw::c_uint = 80;
+pub const BPF_LSH: ::std::os::raw::c_uint = 96;
+pub const BPF_RSH: ::std::os::raw::c_uint = 112;
+pub const BPF_NEG: ::std::os::raw::c_uint = 128;
+pub const BPF_MOD: ::std::os::raw::c_uint = 144;
+pub const BPF_XOR: ::std::os::raw::c_uint = 160;
+pub const BPF_JA: ::std::os::raw::c_uint = 0;
+pub const BPF_JEQ: ::std::os::raw::c_uint = 16;
+pub const BPF_JGT: ::std::os::raw::c_uint = 32;
+pub const BPF_JGE: ::std::os::raw::c_uint = 48;
+pub const BPF_JSET: ::std::os::raw::c_uint = 64;
+pub const BPF_K: ::std::os::raw::c_uint = 0;
+pub const BPF_X: ::std::os::raw::c_uint = 8;
+pub const BPF_MAXINSNS: ::std::os::raw::c_uint = 4096;
+pub const BPF_MAJOR_VERSION: ::std::os::raw::c_uint = 1;
+pub const BPF_MINOR_VERSION: ::std::os::raw::c_uint = 1;
+pub const BPF_A: ::std::os::raw::c_uint = 16;
+pub const BPF_TAX: ::std::os::raw::c_uint = 0;
+pub const BPF_TXA: ::std::os::raw::c_uint = 128;
+pub const BPF_MEMWORDS: ::std::os::raw::c_uint = 16;
+pub const SKF_AD_OFF: ::std::os::raw::c_int = -4096;
+pub const SKF_AD_PROTOCOL: ::std::os::raw::c_uint = 0;
+pub const SKF_AD_PKTTYPE: ::std::os::raw::c_uint = 4;
+pub const SKF_AD_IFINDEX: ::std::os::raw::c_uint = 8;
+pub const SKF_AD_NLATTR: ::std::os::raw::c_uint = 12;
+pub const SKF_AD_NLATTR_NEST: ::std::os::raw::c_uint = 16;
+pub const SKF_AD_MARK: ::std::os::raw::c_uint = 20;
+pub const SKF_AD_QUEUE: ::std::os::raw::c_uint = 24;
+pub const SKF_AD_HATYPE: ::std::os::raw::c_uint = 28;
+pub const SKF_AD_RXHASH: ::std::os::raw::c_uint = 32;
+pub const SKF_AD_CPU: ::std::os::raw::c_uint = 36;
+pub const SKF_AD_ALU_XOR_X: ::std::os::raw::c_uint = 40;
+pub const SKF_AD_VLAN_TAG: ::std::os::raw::c_uint = 44;
+pub const SKF_AD_VLAN_TAG_PRESENT: ::std::os::raw::c_uint = 48;
+pub const SKF_AD_PAY_OFFSET: ::std::os::raw::c_uint = 52;
+pub const SKF_AD_RANDOM: ::std::os::raw::c_uint = 56;
+pub const SKF_AD_VLAN_TPID: ::std::os::raw::c_uint = 60;
+pub const SKF_AD_MAX: ::std::os::raw::c_uint = 64;
+pub const SKF_NET_OFF: ::std::os::raw::c_int = -1048576;
+pub const SKF_LL_OFF: ::std::os::raw::c_int = -2097152;
+pub const BPF_NET_OFF: ::std::os::raw::c_int = -1048576;
+pub const BPF_LL_OFF: ::std::os::raw::c_int = -2097152;
+pub const TUN_READQ_SIZE: ::std::os::raw::c_uint = 500;
+pub const TUN_TYPE_MASK: ::std::os::raw::c_uint = 15;
+pub const IFF_TUN: ::std::os::raw::c_uint = 1;
+pub const IFF_TAP: ::std::os::raw::c_uint = 2;
+pub const IFF_NO_PI: ::std::os::raw::c_uint = 4096;
+pub const IFF_ONE_QUEUE: ::std::os::raw::c_uint = 8192;
+pub const IFF_VNET_HDR: ::std::os::raw::c_uint = 16384;
+pub const IFF_TUN_EXCL: ::std::os::raw::c_uint = 32768;
+pub const IFF_MULTI_QUEUE: ::std::os::raw::c_uint = 256;
+pub const IFF_ATTACH_QUEUE: ::std::os::raw::c_uint = 512;
+pub const IFF_DETACH_QUEUE: ::std::os::raw::c_uint = 1024;
+pub const IFF_PERSIST: ::std::os::raw::c_uint = 2048;
+pub const IFF_NOFILTER: ::std::os::raw::c_uint = 4096;
+pub const TUN_TX_TIMESTAMP: ::std::os::raw::c_uint = 1;
+pub const TUN_F_CSUM: ::std::os::raw::c_uint = 1;
+pub const TUN_F_TSO4: ::std::os::raw::c_uint = 2;
+pub const TUN_F_TSO6: ::std::os::raw::c_uint = 4;
+pub const TUN_F_TSO_ECN: ::std::os::raw::c_uint = 8;
+pub const TUN_F_UFO: ::std::os::raw::c_uint = 16;
+pub const TUN_PKT_STRIP: ::std::os::raw::c_uint = 1;
+pub const TUN_FLT_ALLMULTI: ::std::os::raw::c_uint = 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;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __s32 = ::std::os::raw::c_int;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __s64 = ::std::os::raw::c_longlong;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct __kernel_fd_set {
+ pub fds_bits: [::std::os::raw::c_ulong; 16usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fd_set() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fd_set>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fd_set>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fd_set)).fds_bits as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fd_set),
+ "::",
+ stringify!(fds_bits)
+ )
+ );
+}
+impl Clone for __kernel_fd_set {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub type __kernel_sighandler_t =
+ ::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
+pub type __kernel_key_t = ::std::os::raw::c_int;
+pub type __kernel_mqd_t = ::std::os::raw::c_int;
+pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
+pub type __kernel_long_t = ::std::os::raw::c_long;
+pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
+pub type __kernel_ino_t = __kernel_ulong_t;
+pub type __kernel_mode_t = ::std::os::raw::c_uint;
+pub type __kernel_pid_t = ::std::os::raw::c_int;
+pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
+pub type __kernel_uid_t = ::std::os::raw::c_uint;
+pub type __kernel_gid_t = ::std::os::raw::c_uint;
+pub type __kernel_suseconds_t = __kernel_long_t;
+pub type __kernel_daddr_t = ::std::os::raw::c_int;
+pub type __kernel_uid32_t = ::std::os::raw::c_uint;
+pub type __kernel_gid32_t = ::std::os::raw::c_uint;
+pub type __kernel_size_t = __kernel_ulong_t;
+pub type __kernel_ssize_t = __kernel_long_t;
+pub type __kernel_ptrdiff_t = __kernel_long_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct __kernel_fsid_t {
+ pub val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fsid_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fsid_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fsid_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fsid_t)).val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fsid_t),
+ "::",
+ stringify!(val)
+ )
+ );
+}
+impl Clone for __kernel_fsid_t {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+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_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;
+pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
+pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
+pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
+pub type __le16 = __u16;
+pub type __be16 = __u16;
+pub type __le32 = __u32;
+pub type __be32 = __u32;
+pub type __le64 = __u64;
+pub type __be64 = __u64;
+pub type __sum16 = __u16;
+pub type __wsum = __u32;
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy)]
+pub struct ethhdr {
+ pub h_dest: [::std::os::raw::c_uchar; 6usize],
+ pub h_source: [::std::os::raw::c_uchar; 6usize],
+ pub h_proto: __be16,
+}
+#[test]
+fn bindgen_test_layout_ethhdr() {
+ assert_eq!(
+ ::std::mem::size_of::<ethhdr>(),
+ 14usize,
+ concat!("Size of: ", stringify!(ethhdr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<ethhdr>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(ethhdr))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ethhdr)).h_dest as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ethhdr),
+ "::",
+ stringify!(h_dest)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ethhdr)).h_source as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ethhdr),
+ "::",
+ stringify!(h_source)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ethhdr)).h_proto as *const _ as usize },
+ 12usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ethhdr),
+ "::",
+ stringify!(h_proto)
+ )
+ );
+}
+impl Clone for ethhdr {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct sock_filter {
+ pub code: __u16,
+ pub jt: __u8,
+ pub jf: __u8,
+ pub k: __u32,
+}
+#[test]
+fn bindgen_test_layout_sock_filter() {
+ assert_eq!(
+ ::std::mem::size_of::<sock_filter>(),
+ 8usize,
+ concat!("Size of: ", stringify!(sock_filter))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<sock_filter>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(sock_filter))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sock_filter)).code as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sock_filter),
+ "::",
+ stringify!(code)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sock_filter)).jt as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sock_filter),
+ "::",
+ stringify!(jt)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sock_filter)).jf as *const _ as usize },
+ 3usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sock_filter),
+ "::",
+ stringify!(jf)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sock_filter)).k as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sock_filter),
+ "::",
+ stringify!(k)
+ )
+ );
+}
+impl Clone for sock_filter {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct sock_fprog {
+ pub len: ::std::os::raw::c_ushort,
+ pub filter: *mut sock_filter,
+}
+#[test]
+fn bindgen_test_layout_sock_fprog() {
+ assert_eq!(
+ ::std::mem::size_of::<sock_fprog>(),
+ 16usize,
+ concat!("Size of: ", stringify!(sock_fprog))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<sock_fprog>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(sock_fprog))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sock_fprog)).len as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sock_fprog),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sock_fprog)).filter as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sock_fprog),
+ "::",
+ stringify!(filter)
+ )
+ );
+}
+impl Clone for sock_fprog {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl Default for sock_fprog {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct tun_pi {
+ pub flags: __u16,
+ pub proto: __be16,
+}
+#[test]
+fn bindgen_test_layout_tun_pi() {
+ assert_eq!(
+ ::std::mem::size_of::<tun_pi>(),
+ 4usize,
+ concat!("Size of: ", stringify!(tun_pi))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<tun_pi>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(tun_pi))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const tun_pi)).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(tun_pi),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const tun_pi)).proto as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(tun_pi),
+ "::",
+ stringify!(proto)
+ )
+ );
+}
+impl Clone for tun_pi {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct tun_filter {
+ pub flags: __u16,
+ pub count: __u16,
+ pub addr: __IncompleteArrayField<[__u8; 6usize]>,
+}
+#[test]
+fn bindgen_test_layout_tun_filter() {
+ assert_eq!(
+ ::std::mem::size_of::<tun_filter>(),
+ 4usize,
+ concat!("Size of: ", stringify!(tun_filter))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<tun_filter>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(tun_filter))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const tun_filter)).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(tun_filter),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const tun_filter)).count as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(tun_filter),
+ "::",
+ stringify!(count)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const tun_filter)).addr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(tun_filter),
+ "::",
+ stringify!(addr)
+ )
+ );
+}
+impl Clone for tun_filter {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
diff --git a/net_sys/src/iff.rs b/net_sys/src/iff.rs
new file mode 100644
index 0000000..928a2cc
--- /dev/null
+++ b/net_sys/src/iff.rs
@@ -0,0 +1,1254 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData, [])
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self::new()
+ }
+}
+pub const __UAPI_DEF_IF_IFCONF: u32 = 1;
+pub const __UAPI_DEF_IF_IFMAP: u32 = 1;
+pub const __UAPI_DEF_IF_IFNAMSIZ: u32 = 1;
+pub const __UAPI_DEF_IF_IFREQ: u32 = 1;
+pub const __UAPI_DEF_IF_NET_DEVICE_FLAGS: u32 = 1;
+pub const __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO: u32 = 1;
+pub const __UAPI_DEF_IN_ADDR: u32 = 1;
+pub const __UAPI_DEF_IN_IPPROTO: u32 = 1;
+pub const __UAPI_DEF_IN_PKTINFO: u32 = 1;
+pub const __UAPI_DEF_IP_MREQ: u32 = 1;
+pub const __UAPI_DEF_SOCKADDR_IN: u32 = 1;
+pub const __UAPI_DEF_IN_CLASS: u32 = 1;
+pub const __UAPI_DEF_IN6_ADDR: u32 = 1;
+pub const __UAPI_DEF_IN6_ADDR_ALT: u32 = 1;
+pub const __UAPI_DEF_SOCKADDR_IN6: u32 = 1;
+pub const __UAPI_DEF_IPV6_MREQ: u32 = 1;
+pub const __UAPI_DEF_IPPROTO_V6: u32 = 1;
+pub const __UAPI_DEF_IPV6_OPTIONS: u32 = 1;
+pub const __UAPI_DEF_IN6_PKTINFO: u32 = 1;
+pub const __UAPI_DEF_IP6_MTUINFO: u32 = 1;
+pub const __UAPI_DEF_SOCKADDR_IPX: u32 = 1;
+pub const __UAPI_DEF_IPX_ROUTE_DEFINITION: u32 = 1;
+pub const __UAPI_DEF_IPX_INTERFACE_DEFINITION: u32 = 1;
+pub const __UAPI_DEF_IPX_CONFIG_DATA: u32 = 1;
+pub const __UAPI_DEF_IPX_ROUTE_DEF: u32 = 1;
+pub const __UAPI_DEF_XATTR: u32 = 1;
+pub const __BITS_PER_LONG: u32 = 64;
+pub const __FD_SETSIZE: u32 = 1024;
+pub const _K_SS_MAXSIZE: u32 = 128;
+pub const _SYS_SOCKET_H: u32 = 1;
+pub const _FEATURES_H: u32 = 1;
+pub const _DEFAULT_SOURCE: u32 = 1;
+pub const __USE_ISOC11: u32 = 1;
+pub const __USE_ISOC99: u32 = 1;
+pub const __USE_ISOC95: u32 = 1;
+pub const __USE_POSIX_IMPLICITLY: u32 = 1;
+pub const _POSIX_SOURCE: u32 = 1;
+pub const _POSIX_C_SOURCE: u32 = 200809;
+pub const __USE_POSIX: u32 = 1;
+pub const __USE_POSIX2: u32 = 1;
+pub const __USE_POSIX199309: u32 = 1;
+pub const __USE_POSIX199506: u32 = 1;
+pub const __USE_XOPEN2K: u32 = 1;
+pub const __USE_XOPEN2K8: u32 = 1;
+pub const _ATFILE_SOURCE: u32 = 1;
+pub const __USE_MISC: u32 = 1;
+pub const __USE_ATFILE: u32 = 1;
+pub const __USE_FORTIFY_LEVEL: u32 = 0;
+pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0;
+pub const _STDC_PREDEF_H: u32 = 1;
+pub const __STDC_IEC_559__: u32 = 1;
+pub const __STDC_IEC_559_COMPLEX__: u32 = 1;
+pub const __STDC_ISO_10646__: u32 = 201706;
+pub const __STDC_NO_THREADS__: u32 = 1;
+pub const __GNU_LIBRARY__: u32 = 6;
+pub const __GLIBC__: u32 = 2;
+pub const __GLIBC_MINOR__: u32 = 27;
+pub const _SYS_CDEFS_H: u32 = 1;
+pub const __glibc_c99_flexarr_available: u32 = 1;
+pub const __WORDSIZE: u32 = 64;
+pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1;
+pub const __SYSCALL_WORDSIZE: u32 = 64;
+pub const __HAVE_GENERIC_SELECTION: u32 = 1;
+pub const __iovec_defined: u32 = 1;
+pub const _SYS_TYPES_H: u32 = 1;
+pub const _BITS_TYPES_H: u32 = 1;
+pub const _BITS_TYPESIZES_H: u32 = 1;
+pub const __OFF_T_MATCHES_OFF64_T: u32 = 1;
+pub const __INO_T_MATCHES_INO64_T: u32 = 1;
+pub const __RLIM_T_MATCHES_RLIM64_T: u32 = 1;
+pub const __clock_t_defined: u32 = 1;
+pub const __clockid_t_defined: u32 = 1;
+pub const __time_t_defined: u32 = 1;
+pub const __timer_t_defined: u32 = 1;
+pub const _BITS_STDINT_INTN_H: u32 = 1;
+pub const __BIT_TYPES_DEFINED__: u32 = 1;
+pub const _ENDIAN_H: u32 = 1;
+pub const __LITTLE_ENDIAN: u32 = 1234;
+pub const __BIG_ENDIAN: u32 = 4321;
+pub const __PDP_ENDIAN: u32 = 3412;
+pub const __BYTE_ORDER: u32 = 1234;
+pub const __FLOAT_WORD_ORDER: u32 = 1234;
+pub const LITTLE_ENDIAN: u32 = 1234;
+pub const BIG_ENDIAN: u32 = 4321;
+pub const PDP_ENDIAN: u32 = 3412;
+pub const BYTE_ORDER: u32 = 1234;
+pub const _BITS_BYTESWAP_H: u32 = 1;
+pub const _BITS_UINTN_IDENTITY_H: u32 = 1;
+pub const _SYS_SELECT_H: u32 = 1;
+pub const __FD_ZERO_STOS: &'static [u8; 6usize] = b"stosq\0";
+pub const __sigset_t_defined: u32 = 1;
+pub const __timeval_defined: u32 = 1;
+pub const _STRUCT_TIMESPEC: u32 = 1;
+pub const FD_SETSIZE: u32 = 1024;
+pub const _BITS_PTHREADTYPES_COMMON_H: u32 = 1;
+pub const _THREAD_SHARED_TYPES_H: u32 = 1;
+pub const _BITS_PTHREADTYPES_ARCH_H: u32 = 1;
+pub const __SIZEOF_PTHREAD_MUTEX_T: u32 = 40;
+pub const __SIZEOF_PTHREAD_ATTR_T: u32 = 56;
+pub const __SIZEOF_PTHREAD_RWLOCK_T: u32 = 56;
+pub const __SIZEOF_PTHREAD_BARRIER_T: u32 = 32;
+pub const __SIZEOF_PTHREAD_MUTEXATTR_T: u32 = 4;
+pub const __SIZEOF_PTHREAD_COND_T: u32 = 48;
+pub const __SIZEOF_PTHREAD_CONDATTR_T: u32 = 4;
+pub const __SIZEOF_PTHREAD_RWLOCKATTR_T: u32 = 8;
+pub const __SIZEOF_PTHREAD_BARRIERATTR_T: u32 = 4;
+pub const __PTHREAD_MUTEX_LOCK_ELISION: u32 = 1;
+pub const __PTHREAD_MUTEX_NUSERS_AFTER_KIND: u32 = 0;
+pub const __PTHREAD_MUTEX_USE_UNION: u32 = 0;
+pub const __PTHREAD_RWLOCK_INT_FLAGS_SHARED: u32 = 1;
+pub const __PTHREAD_MUTEX_HAVE_PREV: u32 = 1;
+pub const __have_pthread_attr_t: u32 = 1;
+pub const PF_UNSPEC: u32 = 0;
+pub const PF_LOCAL: u32 = 1;
+pub const PF_UNIX: u32 = 1;
+pub const PF_FILE: u32 = 1;
+pub const PF_INET: u32 = 2;
+pub const PF_AX25: u32 = 3;
+pub const PF_IPX: u32 = 4;
+pub const PF_APPLETALK: u32 = 5;
+pub const PF_NETROM: u32 = 6;
+pub const PF_BRIDGE: u32 = 7;
+pub const PF_ATMPVC: u32 = 8;
+pub const PF_X25: u32 = 9;
+pub const PF_INET6: u32 = 10;
+pub const PF_ROSE: u32 = 11;
+pub const PF_DECnet: u32 = 12;
+pub const PF_NETBEUI: u32 = 13;
+pub const PF_SECURITY: u32 = 14;
+pub const PF_KEY: u32 = 15;
+pub const PF_NETLINK: u32 = 16;
+pub const PF_ROUTE: u32 = 16;
+pub const PF_PACKET: u32 = 17;
+pub const PF_ASH: u32 = 18;
+pub const PF_ECONET: u32 = 19;
+pub const PF_ATMSVC: u32 = 20;
+pub const PF_RDS: u32 = 21;
+pub const PF_SNA: u32 = 22;
+pub const PF_IRDA: u32 = 23;
+pub const PF_PPPOX: u32 = 24;
+pub const PF_WANPIPE: u32 = 25;
+pub const PF_LLC: u32 = 26;
+pub const PF_IB: u32 = 27;
+pub const PF_MPLS: u32 = 28;
+pub const PF_CAN: u32 = 29;
+pub const PF_TIPC: u32 = 30;
+pub const PF_BLUETOOTH: u32 = 31;
+pub const PF_IUCV: u32 = 32;
+pub const PF_RXRPC: u32 = 33;
+pub const PF_ISDN: u32 = 34;
+pub const PF_PHONET: u32 = 35;
+pub const PF_IEEE802154: u32 = 36;
+pub const PF_CAIF: u32 = 37;
+pub const PF_ALG: u32 = 38;
+pub const PF_NFC: u32 = 39;
+pub const PF_VSOCK: u32 = 40;
+pub const PF_KCM: u32 = 41;
+pub const PF_QIPCRTR: u32 = 42;
+pub const PF_SMC: u32 = 43;
+pub const PF_MAX: u32 = 44;
+pub const AF_UNSPEC: u32 = 0;
+pub const AF_LOCAL: u32 = 1;
+pub const AF_UNIX: u32 = 1;
+pub const AF_FILE: u32 = 1;
+pub const AF_INET: u32 = 2;
+pub const AF_AX25: u32 = 3;
+pub const AF_IPX: u32 = 4;
+pub const AF_APPLETALK: u32 = 5;
+pub const AF_NETROM: u32 = 6;
+pub const AF_BRIDGE: u32 = 7;
+pub const AF_ATMPVC: u32 = 8;
+pub const AF_X25: u32 = 9;
+pub const AF_INET6: u32 = 10;
+pub const AF_ROSE: u32 = 11;
+pub const AF_DECnet: u32 = 12;
+pub const AF_NETBEUI: u32 = 13;
+pub const AF_SECURITY: u32 = 14;
+pub const AF_KEY: u32 = 15;
+pub const AF_NETLINK: u32 = 16;
+pub const AF_ROUTE: u32 = 16;
+pub const AF_PACKET: u32 = 17;
+pub const AF_ASH: u32 = 18;
+pub const AF_ECONET: u32 = 19;
+pub const AF_ATMSVC: u32 = 20;
+pub const AF_RDS: u32 = 21;
+pub const AF_SNA: u32 = 22;
+pub const AF_IRDA: u32 = 23;
+pub const AF_PPPOX: u32 = 24;
+pub const AF_WANPIPE: u32 = 25;
+pub const AF_LLC: u32 = 26;
+pub const AF_IB: u32 = 27;
+pub const AF_MPLS: u32 = 28;
+pub const AF_CAN: u32 = 29;
+pub const AF_TIPC: u32 = 30;
+pub const AF_BLUETOOTH: u32 = 31;
+pub const AF_IUCV: u32 = 32;
+pub const AF_RXRPC: u32 = 33;
+pub const AF_ISDN: u32 = 34;
+pub const AF_PHONET: u32 = 35;
+pub const AF_IEEE802154: u32 = 36;
+pub const AF_CAIF: u32 = 37;
+pub const AF_ALG: u32 = 38;
+pub const AF_NFC: u32 = 39;
+pub const AF_VSOCK: u32 = 40;
+pub const AF_KCM: u32 = 41;
+pub const AF_QIPCRTR: u32 = 42;
+pub const AF_SMC: u32 = 43;
+pub const AF_MAX: u32 = 44;
+pub const SOL_RAW: u32 = 255;
+pub const SOL_DECNET: u32 = 261;
+pub const SOL_X25: u32 = 262;
+pub const SOL_PACKET: u32 = 263;
+pub const SOL_ATM: u32 = 264;
+pub const SOL_AAL: u32 = 265;
+pub const SOL_IRDA: u32 = 266;
+pub const SOL_NETBEUI: u32 = 267;
+pub const SOL_LLC: u32 = 268;
+pub const SOL_DCCP: u32 = 269;
+pub const SOL_NETLINK: u32 = 270;
+pub const SOL_TIPC: u32 = 271;
+pub const SOL_RXRPC: u32 = 272;
+pub const SOL_PPPOL2TP: u32 = 273;
+pub const SOL_BLUETOOTH: u32 = 274;
+pub const SOL_PNPIPE: u32 = 275;
+pub const SOL_RDS: u32 = 276;
+pub const SOL_IUCV: u32 = 277;
+pub const SOL_CAIF: u32 = 278;
+pub const SOL_ALG: u32 = 279;
+pub const SOL_NFC: u32 = 280;
+pub const SOL_KCM: u32 = 281;
+pub const SOL_TLS: u32 = 282;
+pub const SOMAXCONN: u32 = 128;
+pub const _BITS_SOCKADDR_H: u32 = 1;
+pub const _SS_SIZE: u32 = 128;
+pub const FIOSETOWN: u32 = 35073;
+pub const SIOCSPGRP: u32 = 35074;
+pub const FIOGETOWN: u32 = 35075;
+pub const SIOCGPGRP: u32 = 35076;
+pub const SIOCATMARK: u32 = 35077;
+pub const SIOCGSTAMP: u32 = 35078;
+pub const SIOCGSTAMPNS: u32 = 35079;
+pub const SOL_SOCKET: u32 = 1;
+pub const SO_DEBUG: u32 = 1;
+pub const SO_REUSEADDR: u32 = 2;
+pub const SO_TYPE: u32 = 3;
+pub const SO_ERROR: u32 = 4;
+pub const SO_DONTROUTE: u32 = 5;
+pub const SO_BROADCAST: u32 = 6;
+pub const SO_SNDBUF: u32 = 7;
+pub const SO_RCVBUF: u32 = 8;
+pub const SO_SNDBUFFORCE: u32 = 32;
+pub const SO_RCVBUFFORCE: u32 = 33;
+pub const SO_KEEPALIVE: u32 = 9;
+pub const SO_OOBINLINE: u32 = 10;
+pub const SO_NO_CHECK: u32 = 11;
+pub const SO_PRIORITY: u32 = 12;
+pub const SO_LINGER: u32 = 13;
+pub const SO_BSDCOMPAT: u32 = 14;
+pub const SO_REUSEPORT: u32 = 15;
+pub const SO_PASSCRED: u32 = 16;
+pub const SO_PEERCRED: u32 = 17;
+pub const SO_RCVLOWAT: u32 = 18;
+pub const SO_SNDLOWAT: u32 = 19;
+pub const SO_RCVTIMEO: u32 = 20;
+pub const SO_SNDTIMEO: u32 = 21;
+pub const SO_SECURITY_AUTHENTICATION: u32 = 22;
+pub const SO_SECURITY_ENCRYPTION_TRANSPORT: u32 = 23;
+pub const SO_SECURITY_ENCRYPTION_NETWORK: u32 = 24;
+pub const SO_BINDTODEVICE: u32 = 25;
+pub const SO_ATTACH_FILTER: u32 = 26;
+pub const SO_DETACH_FILTER: u32 = 27;
+pub const SO_GET_FILTER: u32 = 26;
+pub const SO_PEERNAME: u32 = 28;
+pub const SO_TIMESTAMP: u32 = 29;
+pub const SCM_TIMESTAMP: u32 = 29;
+pub const SO_ACCEPTCONN: u32 = 30;
+pub const SO_PEERSEC: u32 = 31;
+pub const SO_PASSSEC: u32 = 34;
+pub const SO_TIMESTAMPNS: u32 = 35;
+pub const SCM_TIMESTAMPNS: u32 = 35;
+pub const SO_MARK: u32 = 36;
+pub const SO_TIMESTAMPING: u32 = 37;
+pub const SCM_TIMESTAMPING: u32 = 37;
+pub const SO_PROTOCOL: u32 = 38;
+pub const SO_DOMAIN: u32 = 39;
+pub const SO_RXQ_OVFL: u32 = 40;
+pub const SO_WIFI_STATUS: u32 = 41;
+pub const SCM_WIFI_STATUS: u32 = 41;
+pub const SO_PEEK_OFF: u32 = 42;
+pub const SO_NOFCS: u32 = 43;
+pub const SO_LOCK_FILTER: u32 = 44;
+pub const SO_SELECT_ERR_QUEUE: u32 = 45;
+pub const SO_BUSY_POLL: u32 = 46;
+pub const SO_MAX_PACING_RATE: u32 = 47;
+pub const SO_BPF_EXTENSIONS: u32 = 48;
+pub const SO_INCOMING_CPU: u32 = 49;
+pub const SO_ATTACH_BPF: u32 = 50;
+pub const SO_DETACH_BPF: u32 = 27;
+pub const SO_ATTACH_REUSEPORT_CBPF: u32 = 51;
+pub const SO_ATTACH_REUSEPORT_EBPF: u32 = 52;
+pub const SO_CNX_ADVICE: u32 = 53;
+pub const SCM_TIMESTAMPING_OPT_STATS: u32 = 54;
+pub const SO_MEMINFO: u32 = 55;
+pub const SO_INCOMING_NAPI_ID: u32 = 56;
+pub const SO_COOKIE: u32 = 57;
+pub const SCM_TIMESTAMPING_PKTINFO: u32 = 58;
+pub const SO_PEERGROUPS: u32 = 59;
+pub const SO_ZEROCOPY: u32 = 60;
+pub const __osockaddr_defined: u32 = 1;
+pub const IFNAMSIZ: u32 = 16;
+pub const IFALIASZ: u32 = 256;
+pub const GENERIC_HDLC_VERSION: u32 = 4;
+pub const CLOCK_DEFAULT: u32 = 0;
+pub const CLOCK_EXT: u32 = 1;
+pub const CLOCK_INT: u32 = 2;
+pub const CLOCK_TXINT: u32 = 3;
+pub const CLOCK_TXFROMRX: u32 = 4;
+pub const ENCODING_DEFAULT: u32 = 0;
+pub const ENCODING_NRZ: u32 = 1;
+pub const ENCODING_NRZI: u32 = 2;
+pub const ENCODING_FM_MARK: u32 = 3;
+pub const ENCODING_FM_SPACE: u32 = 4;
+pub const ENCODING_MANCHESTER: u32 = 5;
+pub const PARITY_DEFAULT: u32 = 0;
+pub const PARITY_NONE: u32 = 1;
+pub const PARITY_CRC16_PR0: u32 = 2;
+pub const PARITY_CRC16_PR1: u32 = 3;
+pub const PARITY_CRC16_PR0_CCITT: u32 = 4;
+pub const PARITY_CRC16_PR1_CCITT: u32 = 5;
+pub const PARITY_CRC32_PR0_CCITT: u32 = 6;
+pub const PARITY_CRC32_PR1_CCITT: u32 = 7;
+pub const LMI_DEFAULT: u32 = 0;
+pub const LMI_NONE: u32 = 1;
+pub const LMI_ANSI: u32 = 2;
+pub const LMI_CCITT: u32 = 3;
+pub const LMI_CISCO: u32 = 4;
+pub const IF_GET_IFACE: u32 = 1;
+pub const IF_GET_PROTO: u32 = 2;
+pub const IF_IFACE_V35: u32 = 4096;
+pub const IF_IFACE_V24: u32 = 4097;
+pub const IF_IFACE_X21: u32 = 4098;
+pub const IF_IFACE_T1: u32 = 4099;
+pub const IF_IFACE_E1: u32 = 4100;
+pub const IF_IFACE_SYNC_SERIAL: u32 = 4101;
+pub const IF_IFACE_X21D: u32 = 4102;
+pub const IF_PROTO_HDLC: u32 = 8192;
+pub const IF_PROTO_PPP: u32 = 8193;
+pub const IF_PROTO_CISCO: u32 = 8194;
+pub const IF_PROTO_FR: u32 = 8195;
+pub const IF_PROTO_FR_ADD_PVC: u32 = 8196;
+pub const IF_PROTO_FR_DEL_PVC: u32 = 8197;
+pub const IF_PROTO_X25: u32 = 8198;
+pub const IF_PROTO_HDLC_ETH: u32 = 8199;
+pub const IF_PROTO_FR_ADD_ETH_PVC: u32 = 8200;
+pub const IF_PROTO_FR_DEL_ETH_PVC: u32 = 8201;
+pub const IF_PROTO_FR_PVC: u32 = 8202;
+pub const IF_PROTO_FR_ETH_PVC: u32 = 8203;
+pub const IF_PROTO_RAW: u32 = 8204;
+pub const IFHWADDRLEN: u32 = 6;
+pub type __s8 = ::std::os::raw::c_schar;
+pub type __u8 = ::std::os::raw::c_uchar;
+pub type __s16 = ::std::os::raw::c_short;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __s32 = ::std::os::raw::c_int;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __s64 = ::std::os::raw::c_longlong;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __kernel_fd_set {
+ pub fds_bits: [::std::os::raw::c_ulong; 16usize],
+}
+pub type __kernel_sighandler_t =
+ ::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
+pub type __kernel_key_t = ::std::os::raw::c_int;
+pub type __kernel_mqd_t = ::std::os::raw::c_int;
+pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
+pub type __kernel_long_t = ::std::os::raw::c_long;
+pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
+pub type __kernel_ino_t = __kernel_ulong_t;
+pub type __kernel_mode_t = ::std::os::raw::c_uint;
+pub type __kernel_pid_t = ::std::os::raw::c_int;
+pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
+pub type __kernel_uid_t = ::std::os::raw::c_uint;
+pub type __kernel_gid_t = ::std::os::raw::c_uint;
+pub type __kernel_suseconds_t = __kernel_long_t;
+pub type __kernel_daddr_t = ::std::os::raw::c_int;
+pub type __kernel_uid32_t = ::std::os::raw::c_uint;
+pub type __kernel_gid32_t = ::std::os::raw::c_uint;
+pub type __kernel_size_t = __kernel_ulong_t;
+pub type __kernel_ssize_t = __kernel_long_t;
+pub type __kernel_ptrdiff_t = __kernel_long_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __kernel_fsid_t {
+ pub val: [::std::os::raw::c_int; 2usize],
+}
+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_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;
+pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
+pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
+pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
+pub type __le16 = __u16;
+pub type __be16 = __u16;
+pub type __le32 = __u32;
+pub type __be32 = __u32;
+pub type __le64 = __u64;
+pub type __be64 = __u64;
+pub type __sum16 = __u16;
+pub type __wsum = __u32;
+pub type __kernel_sa_family_t = ::std::os::raw::c_ushort;
+#[repr(C)]
+#[repr(align(8))]
+#[derive(Copy, Clone)]
+pub struct __kernel_sockaddr_storage {
+ pub ss_family: __kernel_sa_family_t,
+ pub __data: [::std::os::raw::c_char; 126usize],
+}
+impl Default for __kernel_sockaddr_storage {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct iovec {
+ pub iov_base: *mut ::std::os::raw::c_void,
+ pub iov_len: usize,
+}
+impl Default for iovec {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+pub type __u_char = ::std::os::raw::c_uchar;
+pub type __u_short = ::std::os::raw::c_ushort;
+pub type __u_int = ::std::os::raw::c_uint;
+pub type __u_long = ::std::os::raw::c_ulong;
+pub type __int8_t = ::std::os::raw::c_schar;
+pub type __uint8_t = ::std::os::raw::c_uchar;
+pub type __int16_t = ::std::os::raw::c_short;
+pub type __uint16_t = ::std::os::raw::c_ushort;
+pub type __int32_t = ::std::os::raw::c_int;
+pub type __uint32_t = ::std::os::raw::c_uint;
+pub type __int64_t = ::std::os::raw::c_long;
+pub type __uint64_t = ::std::os::raw::c_ulong;
+pub type __quad_t = ::std::os::raw::c_long;
+pub type __u_quad_t = ::std::os::raw::c_ulong;
+pub type __intmax_t = ::std::os::raw::c_long;
+pub type __uintmax_t = ::std::os::raw::c_ulong;
+pub type __dev_t = ::std::os::raw::c_ulong;
+pub type __uid_t = ::std::os::raw::c_uint;
+pub type __gid_t = ::std::os::raw::c_uint;
+pub type __ino_t = ::std::os::raw::c_ulong;
+pub type __ino64_t = ::std::os::raw::c_ulong;
+pub type __mode_t = ::std::os::raw::c_uint;
+pub type __nlink_t = ::std::os::raw::c_ulong;
+pub type __off_t = ::std::os::raw::c_long;
+pub type __off64_t = ::std::os::raw::c_long;
+pub type __pid_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __fsid_t {
+ pub __val: [::std::os::raw::c_int; 2usize],
+}
+pub type __clock_t = ::std::os::raw::c_long;
+pub type __rlim_t = ::std::os::raw::c_ulong;
+pub type __rlim64_t = ::std::os::raw::c_ulong;
+pub type __id_t = ::std::os::raw::c_uint;
+pub type __time_t = ::std::os::raw::c_long;
+pub type __useconds_t = ::std::os::raw::c_uint;
+pub type __suseconds_t = ::std::os::raw::c_long;
+pub type __daddr_t = ::std::os::raw::c_int;
+pub type __key_t = ::std::os::raw::c_int;
+pub type __clockid_t = ::std::os::raw::c_int;
+pub type __timer_t = *mut ::std::os::raw::c_void;
+pub type __blksize_t = ::std::os::raw::c_long;
+pub type __blkcnt_t = ::std::os::raw::c_long;
+pub type __blkcnt64_t = ::std::os::raw::c_long;
+pub type __fsblkcnt_t = ::std::os::raw::c_ulong;
+pub type __fsblkcnt64_t = ::std::os::raw::c_ulong;
+pub type __fsfilcnt_t = ::std::os::raw::c_ulong;
+pub type __fsfilcnt64_t = ::std::os::raw::c_ulong;
+pub type __fsword_t = ::std::os::raw::c_long;
+pub type __ssize_t = ::std::os::raw::c_long;
+pub type __syscall_slong_t = ::std::os::raw::c_long;
+pub type __syscall_ulong_t = ::std::os::raw::c_ulong;
+pub type __loff_t = __off64_t;
+pub type __caddr_t = *mut ::std::os::raw::c_char;
+pub type __intptr_t = ::std::os::raw::c_long;
+pub type __socklen_t = ::std::os::raw::c_uint;
+pub type __sig_atomic_t = ::std::os::raw::c_int;
+pub type u_char = __u_char;
+pub type u_short = __u_short;
+pub type u_int = __u_int;
+pub type u_long = __u_long;
+pub type quad_t = __quad_t;
+pub type u_quad_t = __u_quad_t;
+pub type fsid_t = __fsid_t;
+pub type loff_t = __loff_t;
+pub type ino_t = __ino_t;
+pub type dev_t = __dev_t;
+pub type gid_t = __gid_t;
+pub type mode_t = __mode_t;
+pub type nlink_t = __nlink_t;
+pub type uid_t = __uid_t;
+pub type off_t = __off_t;
+pub type pid_t = __pid_t;
+pub type id_t = __id_t;
+pub type daddr_t = __daddr_t;
+pub type caddr_t = __caddr_t;
+pub type key_t = __key_t;
+pub type clock_t = __clock_t;
+pub type clockid_t = __clockid_t;
+pub type time_t = __time_t;
+pub type timer_t = __timer_t;
+pub type ulong = ::std::os::raw::c_ulong;
+pub type ushort = ::std::os::raw::c_ushort;
+pub type uint = ::std::os::raw::c_uint;
+pub type u_int8_t = ::std::os::raw::c_uchar;
+pub type u_int16_t = ::std::os::raw::c_ushort;
+pub type u_int32_t = ::std::os::raw::c_uint;
+pub type u_int64_t = ::std::os::raw::c_ulong;
+pub type register_t = ::std::os::raw::c_long;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __sigset_t {
+ pub __val: [::std::os::raw::c_ulong; 16usize],
+}
+pub type sigset_t = __sigset_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct timeval {
+ pub tv_sec: __time_t,
+ pub tv_usec: __suseconds_t,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct timespec {
+ pub tv_sec: __time_t,
+ pub tv_nsec: __syscall_slong_t,
+}
+pub type suseconds_t = __suseconds_t;
+pub type __fd_mask = ::std::os::raw::c_long;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct fd_set {
+ pub __fds_bits: [__fd_mask; 16usize],
+}
+pub type fd_mask = __fd_mask;
+extern "C" {
+ pub fn select(
+ __nfds: ::std::os::raw::c_int,
+ __readfds: *mut fd_set,
+ __writefds: *mut fd_set,
+ __exceptfds: *mut fd_set,
+ __timeout: *mut timeval,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn pselect(
+ __nfds: ::std::os::raw::c_int,
+ __readfds: *mut fd_set,
+ __writefds: *mut fd_set,
+ __exceptfds: *mut fd_set,
+ __timeout: *const timespec,
+ __sigmask: *const __sigset_t,
+ ) -> ::std::os::raw::c_int;
+}
+pub type blksize_t = __blksize_t;
+pub type blkcnt_t = __blkcnt_t;
+pub type fsblkcnt_t = __fsblkcnt_t;
+pub type fsfilcnt_t = __fsfilcnt_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __pthread_rwlock_arch_t {
+ pub __readers: ::std::os::raw::c_uint,
+ pub __writers: ::std::os::raw::c_uint,
+ pub __wrphase_futex: ::std::os::raw::c_uint,
+ pub __writers_futex: ::std::os::raw::c_uint,
+ pub __pad3: ::std::os::raw::c_uint,
+ pub __pad4: ::std::os::raw::c_uint,
+ pub __cur_writer: ::std::os::raw::c_int,
+ pub __shared: ::std::os::raw::c_int,
+ pub __rwelision: ::std::os::raw::c_schar,
+ pub __pad1: [::std::os::raw::c_uchar; 7usize],
+ pub __pad2: ::std::os::raw::c_ulong,
+ pub __flags: ::std::os::raw::c_uint,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __pthread_internal_list {
+ pub __prev: *mut __pthread_internal_list,
+ pub __next: *mut __pthread_internal_list,
+}
+impl Default for __pthread_internal_list {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+pub type __pthread_list_t = __pthread_internal_list;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __pthread_mutex_s {
+ pub __lock: ::std::os::raw::c_int,
+ pub __count: ::std::os::raw::c_uint,
+ pub __owner: ::std::os::raw::c_int,
+ pub __nusers: ::std::os::raw::c_uint,
+ pub __kind: ::std::os::raw::c_int,
+ pub __spins: ::std::os::raw::c_short,
+ pub __elision: ::std::os::raw::c_short,
+ pub __list: __pthread_list_t,
+}
+impl Default for __pthread_mutex_s {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct __pthread_cond_s {
+ pub __bindgen_anon_1: __pthread_cond_s__bindgen_ty_1,
+ pub __bindgen_anon_2: __pthread_cond_s__bindgen_ty_2,
+ pub __g_refs: [::std::os::raw::c_uint; 2usize],
+ pub __g_size: [::std::os::raw::c_uint; 2usize],
+ pub __g1_orig_size: ::std::os::raw::c_uint,
+ pub __wrefs: ::std::os::raw::c_uint,
+ pub __g_signals: [::std::os::raw::c_uint; 2usize],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union __pthread_cond_s__bindgen_ty_1 {
+ pub __wseq: ::std::os::raw::c_ulonglong,
+ pub __wseq32: __pthread_cond_s__bindgen_ty_1__bindgen_ty_1,
+ _bindgen_union_align: u64,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __pthread_cond_s__bindgen_ty_1__bindgen_ty_1 {
+ pub __low: ::std::os::raw::c_uint,
+ pub __high: ::std::os::raw::c_uint,
+}
+impl Default for __pthread_cond_s__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union __pthread_cond_s__bindgen_ty_2 {
+ pub __g1_start: ::std::os::raw::c_ulonglong,
+ pub __g1_start32: __pthread_cond_s__bindgen_ty_2__bindgen_ty_1,
+ _bindgen_union_align: u64,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct __pthread_cond_s__bindgen_ty_2__bindgen_ty_1 {
+ pub __low: ::std::os::raw::c_uint,
+ pub __high: ::std::os::raw::c_uint,
+}
+impl Default for __pthread_cond_s__bindgen_ty_2 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+impl Default for __pthread_cond_s {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+pub type pthread_t = ::std::os::raw::c_ulong;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_mutexattr_t {
+ pub __size: [::std::os::raw::c_char; 4usize],
+ pub __align: ::std::os::raw::c_int,
+ _bindgen_union_align: u32,
+}
+impl Default for pthread_mutexattr_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_condattr_t {
+ pub __size: [::std::os::raw::c_char; 4usize],
+ pub __align: ::std::os::raw::c_int,
+ _bindgen_union_align: u32,
+}
+impl Default for pthread_condattr_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+pub type pthread_key_t = ::std::os::raw::c_uint;
+pub type pthread_once_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_attr_t {
+ pub __size: [::std::os::raw::c_char; 56usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: [u64; 7usize],
+}
+impl Default for pthread_attr_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_mutex_t {
+ pub __data: __pthread_mutex_s,
+ pub __size: [::std::os::raw::c_char; 40usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: [u64; 5usize],
+}
+impl Default for pthread_mutex_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_cond_t {
+ pub __data: __pthread_cond_s,
+ pub __size: [::std::os::raw::c_char; 48usize],
+ pub __align: ::std::os::raw::c_longlong,
+ _bindgen_union_align: [u64; 6usize],
+}
+impl Default for pthread_cond_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_rwlock_t {
+ pub __data: __pthread_rwlock_arch_t,
+ pub __size: [::std::os::raw::c_char; 56usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: [u64; 7usize],
+}
+impl Default for pthread_rwlock_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_rwlockattr_t {
+ pub __size: [::std::os::raw::c_char; 8usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: u64,
+}
+impl Default for pthread_rwlockattr_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+pub type pthread_spinlock_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_barrier_t {
+ pub __size: [::std::os::raw::c_char; 32usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: [u64; 4usize],
+}
+impl Default for pthread_barrier_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_barrierattr_t {
+ pub __size: [::std::os::raw::c_char; 4usize],
+ pub __align: ::std::os::raw::c_int,
+ _bindgen_union_align: u32,
+}
+impl Default for pthread_barrierattr_t {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+pub type socklen_t = __socklen_t;
+pub const __socket_type_SOCK_STREAM: __socket_type = 1;
+pub const __socket_type_SOCK_DGRAM: __socket_type = 2;
+pub const __socket_type_SOCK_RAW: __socket_type = 3;
+pub const __socket_type_SOCK_RDM: __socket_type = 4;
+pub const __socket_type_SOCK_SEQPACKET: __socket_type = 5;
+pub const __socket_type_SOCK_DCCP: __socket_type = 6;
+pub const __socket_type_SOCK_PACKET: __socket_type = 10;
+pub const __socket_type_SOCK_CLOEXEC: __socket_type = 524288;
+pub const __socket_type_SOCK_NONBLOCK: __socket_type = 2048;
+pub type __socket_type = u32;
+pub type sa_family_t = ::std::os::raw::c_ushort;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct sockaddr {
+ pub sa_family: sa_family_t,
+ pub sa_data: [::std::os::raw::c_char; 14usize],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct sockaddr_storage {
+ pub ss_family: sa_family_t,
+ pub __ss_padding: [::std::os::raw::c_char; 118usize],
+ pub __ss_align: ::std::os::raw::c_ulong,
+}
+impl Default for sockaddr_storage {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+pub const MSG_OOB: _bindgen_ty_1 = 1;
+pub const MSG_PEEK: _bindgen_ty_1 = 2;
+pub const MSG_DONTROUTE: _bindgen_ty_1 = 4;
+pub const MSG_CTRUNC: _bindgen_ty_1 = 8;
+pub const MSG_PROXY: _bindgen_ty_1 = 16;
+pub const MSG_TRUNC: _bindgen_ty_1 = 32;
+pub const MSG_DONTWAIT: _bindgen_ty_1 = 64;
+pub const MSG_EOR: _bindgen_ty_1 = 128;
+pub const MSG_WAITALL: _bindgen_ty_1 = 256;
+pub const MSG_FIN: _bindgen_ty_1 = 512;
+pub const MSG_SYN: _bindgen_ty_1 = 1024;
+pub const MSG_CONFIRM: _bindgen_ty_1 = 2048;
+pub const MSG_RST: _bindgen_ty_1 = 4096;
+pub const MSG_ERRQUEUE: _bindgen_ty_1 = 8192;
+pub const MSG_NOSIGNAL: _bindgen_ty_1 = 16384;
+pub const MSG_MORE: _bindgen_ty_1 = 32768;
+pub const MSG_WAITFORONE: _bindgen_ty_1 = 65536;
+pub const MSG_BATCH: _bindgen_ty_1 = 262144;
+pub const MSG_ZEROCOPY: _bindgen_ty_1 = 67108864;
+pub const MSG_FASTOPEN: _bindgen_ty_1 = 536870912;
+pub const MSG_CMSG_CLOEXEC: _bindgen_ty_1 = 1073741824;
+pub type _bindgen_ty_1 = u32;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct msghdr {
+ pub msg_name: *mut ::std::os::raw::c_void,
+ pub msg_namelen: socklen_t,
+ pub msg_iov: *mut iovec,
+ pub msg_iovlen: usize,
+ pub msg_control: *mut ::std::os::raw::c_void,
+ pub msg_controllen: usize,
+ pub msg_flags: ::std::os::raw::c_int,
+}
+impl Default for msghdr {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct cmsghdr {
+ pub cmsg_len: usize,
+ pub cmsg_level: ::std::os::raw::c_int,
+ pub cmsg_type: ::std::os::raw::c_int,
+ pub __cmsg_data: __IncompleteArrayField<::std::os::raw::c_uchar>,
+}
+extern "C" {
+ pub fn __cmsg_nxthdr(__mhdr: *mut msghdr, __cmsg: *mut cmsghdr) -> *mut cmsghdr;
+}
+pub const SCM_RIGHTS: _bindgen_ty_2 = 1;
+pub type _bindgen_ty_2 = u32;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct linger {
+ pub l_onoff: ::std::os::raw::c_int,
+ pub l_linger: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct osockaddr {
+ pub sa_family: ::std::os::raw::c_ushort,
+ pub sa_data: [::std::os::raw::c_uchar; 14usize],
+}
+pub const SHUT_RD: _bindgen_ty_3 = 0;
+pub const SHUT_WR: _bindgen_ty_3 = 1;
+pub const SHUT_RDWR: _bindgen_ty_3 = 2;
+pub type _bindgen_ty_3 = u32;
+extern "C" {
+ pub fn socket(
+ __domain: ::std::os::raw::c_int,
+ __type: ::std::os::raw::c_int,
+ __protocol: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn socketpair(
+ __domain: ::std::os::raw::c_int,
+ __type: ::std::os::raw::c_int,
+ __protocol: ::std::os::raw::c_int,
+ __fds: *mut ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn bind(
+ __fd: ::std::os::raw::c_int,
+ __addr: *const sockaddr,
+ __len: socklen_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn getsockname(
+ __fd: ::std::os::raw::c_int,
+ __addr: *mut sockaddr,
+ __len: *mut socklen_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn connect(
+ __fd: ::std::os::raw::c_int,
+ __addr: *const sockaddr,
+ __len: socklen_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn getpeername(
+ __fd: ::std::os::raw::c_int,
+ __addr: *mut sockaddr,
+ __len: *mut socklen_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn send(
+ __fd: ::std::os::raw::c_int,
+ __buf: *const ::std::os::raw::c_void,
+ __n: usize,
+ __flags: ::std::os::raw::c_int,
+ ) -> isize;
+}
+extern "C" {
+ pub fn recv(
+ __fd: ::std::os::raw::c_int,
+ __buf: *mut ::std::os::raw::c_void,
+ __n: usize,
+ __flags: ::std::os::raw::c_int,
+ ) -> isize;
+}
+extern "C" {
+ pub fn sendto(
+ __fd: ::std::os::raw::c_int,
+ __buf: *const ::std::os::raw::c_void,
+ __n: usize,
+ __flags: ::std::os::raw::c_int,
+ __addr: *const sockaddr,
+ __addr_len: socklen_t,
+ ) -> isize;
+}
+extern "C" {
+ pub fn recvfrom(
+ __fd: ::std::os::raw::c_int,
+ __buf: *mut ::std::os::raw::c_void,
+ __n: usize,
+ __flags: ::std::os::raw::c_int,
+ __addr: *mut sockaddr,
+ __addr_len: *mut socklen_t,
+ ) -> isize;
+}
+extern "C" {
+ pub fn sendmsg(
+ __fd: ::std::os::raw::c_int,
+ __message: *const msghdr,
+ __flags: ::std::os::raw::c_int,
+ ) -> isize;
+}
+extern "C" {
+ pub fn recvmsg(
+ __fd: ::std::os::raw::c_int,
+ __message: *mut msghdr,
+ __flags: ::std::os::raw::c_int,
+ ) -> isize;
+}
+extern "C" {
+ pub fn getsockopt(
+ __fd: ::std::os::raw::c_int,
+ __level: ::std::os::raw::c_int,
+ __optname: ::std::os::raw::c_int,
+ __optval: *mut ::std::os::raw::c_void,
+ __optlen: *mut socklen_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn setsockopt(
+ __fd: ::std::os::raw::c_int,
+ __level: ::std::os::raw::c_int,
+ __optname: ::std::os::raw::c_int,
+ __optval: *const ::std::os::raw::c_void,
+ __optlen: socklen_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn listen(__fd: ::std::os::raw::c_int, __n: ::std::os::raw::c_int)
+ -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn accept(
+ __fd: ::std::os::raw::c_int,
+ __addr: *mut sockaddr,
+ __addr_len: *mut socklen_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn shutdown(
+ __fd: ::std::os::raw::c_int,
+ __how: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn sockatmark(__fd: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn isfdtype(
+ __fd: ::std::os::raw::c_int,
+ __fdtype: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct sync_serial_settings {
+ pub clock_rate: ::std::os::raw::c_uint,
+ pub clock_type: ::std::os::raw::c_uint,
+ pub loopback: ::std::os::raw::c_ushort,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct te1_settings {
+ pub clock_rate: ::std::os::raw::c_uint,
+ pub clock_type: ::std::os::raw::c_uint,
+ pub loopback: ::std::os::raw::c_ushort,
+ pub slot_map: ::std::os::raw::c_uint,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct raw_hdlc_proto {
+ pub encoding: ::std::os::raw::c_ushort,
+ pub parity: ::std::os::raw::c_ushort,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct fr_proto {
+ pub t391: ::std::os::raw::c_uint,
+ pub t392: ::std::os::raw::c_uint,
+ pub n391: ::std::os::raw::c_uint,
+ pub n392: ::std::os::raw::c_uint,
+ pub n393: ::std::os::raw::c_uint,
+ pub lmi: ::std::os::raw::c_ushort,
+ pub dce: ::std::os::raw::c_ushort,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct fr_proto_pvc {
+ pub dlci: ::std::os::raw::c_uint,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct fr_proto_pvc_info {
+ pub dlci: ::std::os::raw::c_uint,
+ pub master: [::std::os::raw::c_char; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct cisco_proto {
+ pub interval: ::std::os::raw::c_uint,
+ pub timeout: ::std::os::raw::c_uint,
+}
+pub const net_device_flags_IFF_UP: net_device_flags = 1;
+pub const net_device_flags_IFF_BROADCAST: net_device_flags = 2;
+pub const net_device_flags_IFF_DEBUG: net_device_flags = 4;
+pub const net_device_flags_IFF_LOOPBACK: net_device_flags = 8;
+pub const net_device_flags_IFF_POINTOPOINT: net_device_flags = 16;
+pub const net_device_flags_IFF_NOTRAILERS: net_device_flags = 32;
+pub const net_device_flags_IFF_RUNNING: net_device_flags = 64;
+pub const net_device_flags_IFF_NOARP: net_device_flags = 128;
+pub const net_device_flags_IFF_PROMISC: net_device_flags = 256;
+pub const net_device_flags_IFF_ALLMULTI: net_device_flags = 512;
+pub const net_device_flags_IFF_MASTER: net_device_flags = 1024;
+pub const net_device_flags_IFF_SLAVE: net_device_flags = 2048;
+pub const net_device_flags_IFF_MULTICAST: net_device_flags = 4096;
+pub const net_device_flags_IFF_PORTSEL: net_device_flags = 8192;
+pub const net_device_flags_IFF_AUTOMEDIA: net_device_flags = 16384;
+pub const net_device_flags_IFF_DYNAMIC: net_device_flags = 32768;
+pub const net_device_flags_IFF_LOWER_UP: net_device_flags = 65536;
+pub const net_device_flags_IFF_DORMANT: net_device_flags = 131072;
+pub const net_device_flags_IFF_ECHO: net_device_flags = 262144;
+pub type net_device_flags = u32;
+pub const IF_OPER_UNKNOWN: _bindgen_ty_4 = 0;
+pub const IF_OPER_NOTPRESENT: _bindgen_ty_4 = 1;
+pub const IF_OPER_DOWN: _bindgen_ty_4 = 2;
+pub const IF_OPER_LOWERLAYERDOWN: _bindgen_ty_4 = 3;
+pub const IF_OPER_TESTING: _bindgen_ty_4 = 4;
+pub const IF_OPER_DORMANT: _bindgen_ty_4 = 5;
+pub const IF_OPER_UP: _bindgen_ty_4 = 6;
+pub type _bindgen_ty_4 = u32;
+pub const IF_LINK_MODE_DEFAULT: _bindgen_ty_5 = 0;
+pub const IF_LINK_MODE_DORMANT: _bindgen_ty_5 = 1;
+pub type _bindgen_ty_5 = u32;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct ifmap {
+ pub mem_start: ::std::os::raw::c_ulong,
+ pub mem_end: ::std::os::raw::c_ulong,
+ pub base_addr: ::std::os::raw::c_ushort,
+ pub irq: ::std::os::raw::c_uchar,
+ pub dma: ::std::os::raw::c_uchar,
+ pub port: ::std::os::raw::c_uchar,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct if_settings {
+ pub type_: ::std::os::raw::c_uint,
+ pub size: ::std::os::raw::c_uint,
+ pub ifs_ifsu: if_settings__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union if_settings__bindgen_ty_1 {
+ pub raw_hdlc: *mut raw_hdlc_proto,
+ pub cisco: *mut cisco_proto,
+ pub fr: *mut fr_proto,
+ pub fr_pvc: *mut fr_proto_pvc,
+ pub fr_pvc_info: *mut fr_proto_pvc_info,
+ pub sync: *mut sync_serial_settings,
+ pub te1: *mut te1_settings,
+ _bindgen_union_align: u64,
+}
+impl Default for if_settings__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+impl Default for if_settings {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct ifreq {
+ pub ifr_ifrn: ifreq__bindgen_ty_1,
+ pub ifr_ifru: ifreq__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union ifreq__bindgen_ty_1 {
+ pub ifrn_name: [::std::os::raw::c_char; 16usize],
+ _bindgen_union_align: [u8; 16usize],
+}
+impl Default for ifreq__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union ifreq__bindgen_ty_2 {
+ pub ifru_addr: sockaddr,
+ pub ifru_dstaddr: sockaddr,
+ pub ifru_broadaddr: sockaddr,
+ pub ifru_netmask: sockaddr,
+ pub ifru_hwaddr: sockaddr,
+ pub ifru_flags: ::std::os::raw::c_short,
+ pub ifru_ivalue: ::std::os::raw::c_int,
+ pub ifru_mtu: ::std::os::raw::c_int,
+ pub ifru_map: ifmap,
+ pub ifru_slave: [::std::os::raw::c_char; 16usize],
+ pub ifru_newname: [::std::os::raw::c_char; 16usize],
+ pub ifru_data: *mut ::std::os::raw::c_void,
+ pub ifru_settings: if_settings,
+ _bindgen_union_align: [u64; 3usize],
+}
+impl Default for ifreq__bindgen_ty_2 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+impl Default for ifreq {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct ifconf {
+ pub ifc_len: ::std::os::raw::c_int,
+ pub ifc_ifcu: ifconf__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union ifconf__bindgen_ty_1 {
+ pub ifcu_buf: *mut ::std::os::raw::c_char,
+ pub ifcu_req: *mut ifreq,
+ _bindgen_union_align: u64,
+}
+impl Default for ifconf__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+impl Default for ifconf {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
diff --git a/net_sys/src/inn.rs b/net_sys/src/inn.rs
new file mode 100644
index 0000000..2214fdd
--- /dev/null
+++ b/net_sys/src/inn.rs
@@ -0,0 +1,843 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+pub const __BITS_PER_LONG: ::std::os::raw::c_uint = 64;
+pub const __FD_SETSIZE: ::std::os::raw::c_uint = 1024;
+pub const __UAPI_DEF_IN_ADDR: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IN_IPPROTO: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IN_PKTINFO: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IP_MREQ: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_SOCKADDR_IN: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IN_CLASS: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IN6_ADDR: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IN6_ADDR_ALT: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_SOCKADDR_IN6: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IPV6_MREQ: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IPPROTO_V6: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IPV6_OPTIONS: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IN6_PKTINFO: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_IP6_MTUINFO: ::std::os::raw::c_uint = 1;
+pub const __UAPI_DEF_XATTR: ::std::os::raw::c_uint = 1;
+pub const _K_SS_MAXSIZE: ::std::os::raw::c_uint = 128;
+pub const IP_TOS: ::std::os::raw::c_uint = 1;
+pub const IP_TTL: ::std::os::raw::c_uint = 2;
+pub const IP_HDRINCL: ::std::os::raw::c_uint = 3;
+pub const IP_OPTIONS: ::std::os::raw::c_uint = 4;
+pub const IP_ROUTER_ALERT: ::std::os::raw::c_uint = 5;
+pub const IP_RECVOPTS: ::std::os::raw::c_uint = 6;
+pub const IP_RETOPTS: ::std::os::raw::c_uint = 7;
+pub const IP_PKTINFO: ::std::os::raw::c_uint = 8;
+pub const IP_PKTOPTIONS: ::std::os::raw::c_uint = 9;
+pub const IP_MTU_DISCOVER: ::std::os::raw::c_uint = 10;
+pub const IP_RECVERR: ::std::os::raw::c_uint = 11;
+pub const IP_RECVTTL: ::std::os::raw::c_uint = 12;
+pub const IP_RECVTOS: ::std::os::raw::c_uint = 13;
+pub const IP_MTU: ::std::os::raw::c_uint = 14;
+pub const IP_FREEBIND: ::std::os::raw::c_uint = 15;
+pub const IP_IPSEC_POLICY: ::std::os::raw::c_uint = 16;
+pub const IP_XFRM_POLICY: ::std::os::raw::c_uint = 17;
+pub const IP_PASSSEC: ::std::os::raw::c_uint = 18;
+pub const IP_TRANSPARENT: ::std::os::raw::c_uint = 19;
+pub const IP_RECVRETOPTS: ::std::os::raw::c_uint = 7;
+pub const IP_ORIGDSTADDR: ::std::os::raw::c_uint = 20;
+pub const IP_RECVORIGDSTADDR: ::std::os::raw::c_uint = 20;
+pub const IP_MINTTL: ::std::os::raw::c_uint = 21;
+pub const IP_NODEFRAG: ::std::os::raw::c_uint = 22;
+pub const IP_CHECKSUM: ::std::os::raw::c_uint = 23;
+pub const IP_BIND_ADDRESS_NO_PORT: ::std::os::raw::c_uint = 24;
+pub const IP_RECVFRAGSIZE: ::std::os::raw::c_uint = 25;
+pub const IP_PMTUDISC_DONT: ::std::os::raw::c_uint = 0;
+pub const IP_PMTUDISC_WANT: ::std::os::raw::c_uint = 1;
+pub const IP_PMTUDISC_DO: ::std::os::raw::c_uint = 2;
+pub const IP_PMTUDISC_PROBE: ::std::os::raw::c_uint = 3;
+pub const IP_PMTUDISC_INTERFACE: ::std::os::raw::c_uint = 4;
+pub const IP_PMTUDISC_OMIT: ::std::os::raw::c_uint = 5;
+pub const IP_MULTICAST_IF: ::std::os::raw::c_uint = 32;
+pub const IP_MULTICAST_TTL: ::std::os::raw::c_uint = 33;
+pub const IP_MULTICAST_LOOP: ::std::os::raw::c_uint = 34;
+pub const IP_ADD_MEMBERSHIP: ::std::os::raw::c_uint = 35;
+pub const IP_DROP_MEMBERSHIP: ::std::os::raw::c_uint = 36;
+pub const IP_UNBLOCK_SOURCE: ::std::os::raw::c_uint = 37;
+pub const IP_BLOCK_SOURCE: ::std::os::raw::c_uint = 38;
+pub const IP_ADD_SOURCE_MEMBERSHIP: ::std::os::raw::c_uint = 39;
+pub const IP_DROP_SOURCE_MEMBERSHIP: ::std::os::raw::c_uint = 40;
+pub const IP_MSFILTER: ::std::os::raw::c_uint = 41;
+pub const MCAST_JOIN_GROUP: ::std::os::raw::c_uint = 42;
+pub const MCAST_BLOCK_SOURCE: ::std::os::raw::c_uint = 43;
+pub const MCAST_UNBLOCK_SOURCE: ::std::os::raw::c_uint = 44;
+pub const MCAST_LEAVE_GROUP: ::std::os::raw::c_uint = 45;
+pub const MCAST_JOIN_SOURCE_GROUP: ::std::os::raw::c_uint = 46;
+pub const MCAST_LEAVE_SOURCE_GROUP: ::std::os::raw::c_uint = 47;
+pub const MCAST_MSFILTER: ::std::os::raw::c_uint = 48;
+pub const IP_MULTICAST_ALL: ::std::os::raw::c_uint = 49;
+pub const IP_UNICAST_IF: ::std::os::raw::c_uint = 50;
+pub const MCAST_EXCLUDE: ::std::os::raw::c_uint = 0;
+pub const MCAST_INCLUDE: ::std::os::raw::c_uint = 1;
+pub const IP_DEFAULT_MULTICAST_TTL: ::std::os::raw::c_uint = 1;
+pub const IP_DEFAULT_MULTICAST_LOOP: ::std::os::raw::c_uint = 1;
+pub const __SOCK_SIZE__: ::std::os::raw::c_uint = 16;
+pub const IN_CLASSA_NET: ::std::os::raw::c_uint = 4278190080;
+pub const IN_CLASSA_NSHIFT: ::std::os::raw::c_uint = 24;
+pub const IN_CLASSA_HOST: ::std::os::raw::c_uint = 16777215;
+pub const IN_CLASSA_MAX: ::std::os::raw::c_uint = 128;
+pub const IN_CLASSB_NET: ::std::os::raw::c_uint = 4294901760;
+pub const IN_CLASSB_NSHIFT: ::std::os::raw::c_uint = 16;
+pub const IN_CLASSB_HOST: ::std::os::raw::c_uint = 65535;
+pub const IN_CLASSB_MAX: ::std::os::raw::c_uint = 65536;
+pub const IN_CLASSC_NET: ::std::os::raw::c_uint = 4294967040;
+pub const IN_CLASSC_NSHIFT: ::std::os::raw::c_uint = 8;
+pub const IN_CLASSC_HOST: ::std::os::raw::c_uint = 255;
+pub const IN_MULTICAST_NET: ::std::os::raw::c_uint = 4026531840;
+pub const IN_LOOPBACKNET: ::std::os::raw::c_uint = 127;
+pub const INADDR_LOOPBACK: ::std::os::raw::c_uint = 2130706433;
+pub const INADDR_UNSPEC_GROUP: ::std::os::raw::c_uint = 3758096384;
+pub const INADDR_ALLHOSTS_GROUP: ::std::os::raw::c_uint = 3758096385;
+pub const INADDR_ALLRTRS_GROUP: ::std::os::raw::c_uint = 3758096386;
+pub const INADDR_MAX_LOCAL_GROUP: ::std::os::raw::c_uint = 3758096639;
+pub const __LITTLE_ENDIAN: ::std::os::raw::c_uint = 1234;
+pub type __s8 = ::std::os::raw::c_schar;
+pub type __u8 = ::std::os::raw::c_uchar;
+pub type __s16 = ::std::os::raw::c_short;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __s32 = ::std::os::raw::c_int;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __s64 = ::std::os::raw::c_longlong;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct __kernel_fd_set {
+ pub fds_bits: [::std::os::raw::c_ulong; 16usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fd_set() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fd_set>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fd_set>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fd_set)).fds_bits as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fd_set),
+ "::",
+ stringify!(fds_bits)
+ )
+ );
+}
+impl Clone for __kernel_fd_set {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub type __kernel_sighandler_t =
+ ::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
+pub type __kernel_key_t = ::std::os::raw::c_int;
+pub type __kernel_mqd_t = ::std::os::raw::c_int;
+pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
+pub type __kernel_long_t = ::std::os::raw::c_long;
+pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
+pub type __kernel_ino_t = __kernel_ulong_t;
+pub type __kernel_mode_t = ::std::os::raw::c_uint;
+pub type __kernel_pid_t = ::std::os::raw::c_int;
+pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
+pub type __kernel_uid_t = ::std::os::raw::c_uint;
+pub type __kernel_gid_t = ::std::os::raw::c_uint;
+pub type __kernel_suseconds_t = __kernel_long_t;
+pub type __kernel_daddr_t = ::std::os::raw::c_int;
+pub type __kernel_uid32_t = ::std::os::raw::c_uint;
+pub type __kernel_gid32_t = ::std::os::raw::c_uint;
+pub type __kernel_size_t = __kernel_ulong_t;
+pub type __kernel_ssize_t = __kernel_long_t;
+pub type __kernel_ptrdiff_t = __kernel_long_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct __kernel_fsid_t {
+ pub val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fsid_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fsid_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fsid_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fsid_t)).val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fsid_t),
+ "::",
+ stringify!(val)
+ )
+ );
+}
+impl Clone for __kernel_fsid_t {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+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_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;
+pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
+pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
+pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
+pub type __le16 = __u16;
+pub type __be16 = __u16;
+pub type __le32 = __u32;
+pub type __be32 = __u32;
+pub type __le64 = __u64;
+pub type __be64 = __u64;
+pub type __sum16 = __u16;
+pub type __wsum = __u32;
+pub type __kernel_sa_family_t = ::std::os::raw::c_ushort;
+#[repr(C)]
+pub struct __kernel_sockaddr_storage {
+ pub ss_family: __kernel_sa_family_t,
+ pub __data: [::std::os::raw::c_char; 126usize],
+ pub __bindgen_align: [u64; 0usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_sockaddr_storage() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_sockaddr_storage>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__kernel_sockaddr_storage))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_sockaddr_storage>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__kernel_sockaddr_storage))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_sockaddr_storage)).ss_family as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_sockaddr_storage),
+ "::",
+ stringify!(ss_family)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_sockaddr_storage)).__data as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_sockaddr_storage),
+ "::",
+ stringify!(__data)
+ )
+ );
+}
+impl Default for __kernel_sockaddr_storage {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+pub const IPPROTO_IP: _bindgen_ty_1 = 0;
+pub const IPPROTO_ICMP: _bindgen_ty_1 = 1;
+pub const IPPROTO_IGMP: _bindgen_ty_1 = 2;
+pub const IPPROTO_IPIP: _bindgen_ty_1 = 4;
+pub const IPPROTO_TCP: _bindgen_ty_1 = 6;
+pub const IPPROTO_EGP: _bindgen_ty_1 = 8;
+pub const IPPROTO_PUP: _bindgen_ty_1 = 12;
+pub const IPPROTO_UDP: _bindgen_ty_1 = 17;
+pub const IPPROTO_IDP: _bindgen_ty_1 = 22;
+pub const IPPROTO_TP: _bindgen_ty_1 = 29;
+pub const IPPROTO_DCCP: _bindgen_ty_1 = 33;
+pub const IPPROTO_IPV6: _bindgen_ty_1 = 41;
+pub const IPPROTO_RSVP: _bindgen_ty_1 = 46;
+pub const IPPROTO_GRE: _bindgen_ty_1 = 47;
+pub const IPPROTO_ESP: _bindgen_ty_1 = 50;
+pub const IPPROTO_AH: _bindgen_ty_1 = 51;
+pub const IPPROTO_MTP: _bindgen_ty_1 = 92;
+pub const IPPROTO_BEETPH: _bindgen_ty_1 = 94;
+pub const IPPROTO_ENCAP: _bindgen_ty_1 = 98;
+pub const IPPROTO_PIM: _bindgen_ty_1 = 103;
+pub const IPPROTO_COMP: _bindgen_ty_1 = 108;
+pub const IPPROTO_SCTP: _bindgen_ty_1 = 132;
+pub const IPPROTO_UDPLITE: _bindgen_ty_1 = 136;
+pub const IPPROTO_MPLS: _bindgen_ty_1 = 137;
+pub const IPPROTO_RAW: _bindgen_ty_1 = 255;
+pub const IPPROTO_MAX: _bindgen_ty_1 = 256;
+pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct in_addr {
+ pub s_addr: __be32,
+}
+#[test]
+fn bindgen_test_layout_in_addr() {
+ assert_eq!(
+ ::std::mem::size_of::<in_addr>(),
+ 4usize,
+ concat!("Size of: ", stringify!(in_addr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<in_addr>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(in_addr))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const in_addr)).s_addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(in_addr),
+ "::",
+ stringify!(s_addr)
+ )
+ );
+}
+impl Clone for in_addr {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct ip_mreq {
+ pub imr_multiaddr: in_addr,
+ pub imr_interface: in_addr,
+}
+#[test]
+fn bindgen_test_layout_ip_mreq() {
+ assert_eq!(
+ ::std::mem::size_of::<ip_mreq>(),
+ 8usize,
+ concat!("Size of: ", stringify!(ip_mreq))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<ip_mreq>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(ip_mreq))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_mreq)).imr_multiaddr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_mreq),
+ "::",
+ stringify!(imr_multiaddr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_mreq)).imr_interface as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_mreq),
+ "::",
+ stringify!(imr_interface)
+ )
+ );
+}
+impl Clone for ip_mreq {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct ip_mreqn {
+ pub imr_multiaddr: in_addr,
+ pub imr_address: in_addr,
+ pub imr_ifindex: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_ip_mreqn() {
+ assert_eq!(
+ ::std::mem::size_of::<ip_mreqn>(),
+ 12usize,
+ concat!("Size of: ", stringify!(ip_mreqn))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<ip_mreqn>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(ip_mreqn))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_mreqn)).imr_multiaddr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_mreqn),
+ "::",
+ stringify!(imr_multiaddr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_mreqn)).imr_address as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_mreqn),
+ "::",
+ stringify!(imr_address)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_mreqn)).imr_ifindex as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_mreqn),
+ "::",
+ stringify!(imr_ifindex)
+ )
+ );
+}
+impl Clone for ip_mreqn {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct ip_mreq_source {
+ pub imr_multiaddr: __be32,
+ pub imr_interface: __be32,
+ pub imr_sourceaddr: __be32,
+}
+#[test]
+fn bindgen_test_layout_ip_mreq_source() {
+ assert_eq!(
+ ::std::mem::size_of::<ip_mreq_source>(),
+ 12usize,
+ concat!("Size of: ", stringify!(ip_mreq_source))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<ip_mreq_source>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(ip_mreq_source))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_mreq_source)).imr_multiaddr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_mreq_source),
+ "::",
+ stringify!(imr_multiaddr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_mreq_source)).imr_interface as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_mreq_source),
+ "::",
+ stringify!(imr_interface)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_mreq_source)).imr_sourceaddr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_mreq_source),
+ "::",
+ stringify!(imr_sourceaddr)
+ )
+ );
+}
+impl Clone for ip_mreq_source {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct ip_msfilter {
+ pub imsf_multiaddr: __be32,
+ pub imsf_interface: __be32,
+ pub imsf_fmode: __u32,
+ pub imsf_numsrc: __u32,
+ pub imsf_slist: [__be32; 1usize],
+}
+#[test]
+fn bindgen_test_layout_ip_msfilter() {
+ assert_eq!(
+ ::std::mem::size_of::<ip_msfilter>(),
+ 20usize,
+ concat!("Size of: ", stringify!(ip_msfilter))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<ip_msfilter>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(ip_msfilter))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_msfilter)).imsf_multiaddr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_msfilter),
+ "::",
+ stringify!(imsf_multiaddr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_msfilter)).imsf_interface as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_msfilter),
+ "::",
+ stringify!(imsf_interface)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_msfilter)).imsf_fmode as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_msfilter),
+ "::",
+ stringify!(imsf_fmode)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_msfilter)).imsf_numsrc as *const _ as usize },
+ 12usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_msfilter),
+ "::",
+ stringify!(imsf_numsrc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ip_msfilter)).imsf_slist as *const _ as usize },
+ 16usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ip_msfilter),
+ "::",
+ stringify!(imsf_slist)
+ )
+ );
+}
+impl Clone for ip_msfilter {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+pub struct group_req {
+ pub gr_interface: __u32,
+ pub gr_group: __kernel_sockaddr_storage,
+}
+#[test]
+fn bindgen_test_layout_group_req() {
+ assert_eq!(
+ ::std::mem::size_of::<group_req>(),
+ 136usize,
+ concat!("Size of: ", stringify!(group_req))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<group_req>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(group_req))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_req)).gr_interface as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_req),
+ "::",
+ stringify!(gr_interface)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_req)).gr_group as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_req),
+ "::",
+ stringify!(gr_group)
+ )
+ );
+}
+impl Default for group_req {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+pub struct group_source_req {
+ pub gsr_interface: __u32,
+ pub gsr_group: __kernel_sockaddr_storage,
+ pub gsr_source: __kernel_sockaddr_storage,
+}
+#[test]
+fn bindgen_test_layout_group_source_req() {
+ assert_eq!(
+ ::std::mem::size_of::<group_source_req>(),
+ 264usize,
+ concat!("Size of: ", stringify!(group_source_req))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<group_source_req>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(group_source_req))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_source_req)).gsr_interface as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_source_req),
+ "::",
+ stringify!(gsr_interface)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_source_req)).gsr_group as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_source_req),
+ "::",
+ stringify!(gsr_group)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_source_req)).gsr_source as *const _ as usize },
+ 136usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_source_req),
+ "::",
+ stringify!(gsr_source)
+ )
+ );
+}
+impl Default for group_source_req {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+pub struct group_filter {
+ pub gf_interface: __u32,
+ pub gf_group: __kernel_sockaddr_storage,
+ pub gf_fmode: __u32,
+ pub gf_numsrc: __u32,
+ pub gf_slist: [__kernel_sockaddr_storage; 1usize],
+}
+#[test]
+fn bindgen_test_layout_group_filter() {
+ assert_eq!(
+ ::std::mem::size_of::<group_filter>(),
+ 272usize,
+ concat!("Size of: ", stringify!(group_filter))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<group_filter>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(group_filter))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_filter)).gf_interface as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_filter),
+ "::",
+ stringify!(gf_interface)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_filter)).gf_group as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_filter),
+ "::",
+ stringify!(gf_group)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_filter)).gf_fmode as *const _ as usize },
+ 136usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_filter),
+ "::",
+ stringify!(gf_fmode)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_filter)).gf_numsrc as *const _ as usize },
+ 140usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_filter),
+ "::",
+ stringify!(gf_numsrc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const group_filter)).gf_slist as *const _ as usize },
+ 144usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(group_filter),
+ "::",
+ stringify!(gf_slist)
+ )
+ );
+}
+impl Default for group_filter {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct in_pktinfo {
+ pub ipi_ifindex: ::std::os::raw::c_int,
+ pub ipi_spec_dst: in_addr,
+ pub ipi_addr: in_addr,
+}
+#[test]
+fn bindgen_test_layout_in_pktinfo() {
+ assert_eq!(
+ ::std::mem::size_of::<in_pktinfo>(),
+ 12usize,
+ concat!("Size of: ", stringify!(in_pktinfo))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<in_pktinfo>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(in_pktinfo))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const in_pktinfo)).ipi_ifindex as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(in_pktinfo),
+ "::",
+ stringify!(ipi_ifindex)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const in_pktinfo)).ipi_spec_dst as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(in_pktinfo),
+ "::",
+ stringify!(ipi_spec_dst)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const in_pktinfo)).ipi_addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(in_pktinfo),
+ "::",
+ stringify!(ipi_addr)
+ )
+ );
+}
+impl Clone for in_pktinfo {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct sockaddr_in {
+ pub sin_family: __kernel_sa_family_t,
+ pub sin_port: __be16,
+ pub sin_addr: in_addr,
+ pub __pad: [::std::os::raw::c_uchar; 8usize],
+}
+#[test]
+fn bindgen_test_layout_sockaddr_in() {
+ assert_eq!(
+ ::std::mem::size_of::<sockaddr_in>(),
+ 16usize,
+ concat!("Size of: ", stringify!(sockaddr_in))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<sockaddr_in>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(sockaddr_in))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sockaddr_in)).sin_family as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sockaddr_in),
+ "::",
+ stringify!(sin_family)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sockaddr_in)).sin_port as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sockaddr_in),
+ "::",
+ stringify!(sin_port)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sockaddr_in)).sin_addr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sockaddr_in),
+ "::",
+ stringify!(sin_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const sockaddr_in)).__pad as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(sockaddr_in),
+ "::",
+ stringify!(__pad)
+ )
+ );
+}
+impl Clone for sockaddr_in {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
diff --git a/net_sys/src/lib.rs b/net_sys/src/lib.rs
new file mode 100644
index 0000000..32013fc
--- /dev/null
+++ b/net_sys/src/lib.rs
@@ -0,0 +1,62 @@
+// Copyright TUNTAP, 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+
+use sys_util::{ioctl_ior_nr, ioctl_iow_nr};
+
+// generated with bindgen /usr/include/linux/if.h --no-unstable-rust
+// --constified-enum '*' --with-derive-default -- -D __UAPI_DEF_IF_IFNAMSIZ -D
+// __UAPI_DEF_IF_NET_DEVICE_FLAGS -D __UAPI_DEF_IF_IFREQ -D __UAPI_DEF_IF_IFMAP
+// Name is "iff" to avoid conflicting with "if" keyword.
+// Generated against Linux 4.11 to include fix "uapi: fix linux/if.h userspace
+// compilation errors".
+// Manual fixup of ifrn_name to be of type c_uchar instead of c_char.
+#[allow(clippy::all)]
+pub mod iff;
+// generated with bindgen /usr/include/linux/if_tun.h --no-unstable-rust
+// --constified-enum '*' --with-derive-default
+pub mod if_tun;
+// generated with bindgen /usr/include/linux/in.h --no-unstable-rust
+// --constified-enum '*' --with-derive-default
+// Name is "inn" to avoid conflicting with "in" keyword.
+pub mod inn;
+// generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust
+// --constified-enum '*' --with-derive-default
+pub mod sockios;
+pub use crate::if_tun::*;
+pub use crate::iff::*;
+pub use crate::inn::*;
+pub use crate::sockios::*;
+
+pub const TUNTAP: ::std::os::raw::c_uint = 84;
+
+pub const ARPHRD_ETHER: sa_family_t = 1;
+
+ioctl_iow_nr!(TUNSETNOCSUM, TUNTAP, 200, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETDEBUG, TUNTAP, 201, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETIFF, TUNTAP, 202, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETPERSIST, TUNTAP, 203, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETOWNER, TUNTAP, 204, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETLINK, TUNTAP, 205, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETGROUP, TUNTAP, 206, ::std::os::raw::c_int);
+ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 207, ::std::os::raw::c_uint);
+ioctl_iow_nr!(TUNSETOFFLOAD, TUNTAP, 208, ::std::os::raw::c_uint);
+ioctl_iow_nr!(TUNSETTXFILTER, TUNTAP, 209, ::std::os::raw::c_uint);
+ioctl_ior_nr!(TUNGETIFF, TUNTAP, 210, ::std::os::raw::c_uint);
+ioctl_ior_nr!(TUNGETSNDBUF, TUNTAP, 211, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETSNDBUF, TUNTAP, 212, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNATTACHFILTER, TUNTAP, 213, sock_fprog);
+ioctl_iow_nr!(TUNDETACHFILTER, TUNTAP, 214, sock_fprog);
+ioctl_ior_nr!(TUNGETVNETHDRSZ, TUNTAP, 215, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETVNETHDRSZ, TUNTAP, 216, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 217, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETIFINDEX, TUNTAP, 218, ::std::os::raw::c_uint);
+ioctl_ior_nr!(TUNGETFILTER, TUNTAP, 219, sock_fprog);
+ioctl_iow_nr!(TUNSETVNETLE, TUNTAP, 220, ::std::os::raw::c_int);
+ioctl_ior_nr!(TUNGETVNETLE, TUNTAP, 221, ::std::os::raw::c_int);
+ioctl_iow_nr!(TUNSETVNETBE, TUNTAP, 222, ::std::os::raw::c_int);
+ioctl_ior_nr!(TUNGETVNETBE, TUNTAP, 223, ::std::os::raw::c_int);
diff --git a/net_sys/src/sockios.rs b/net_sys/src/sockios.rs
new file mode 100644
index 0000000..0c0af76
--- /dev/null
+++ b/net_sys/src/sockios.rs
@@ -0,0 +1,89 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+pub const FIOSETOWN: ::std::os::raw::c_uint = 35073;
+pub const SIOCSPGRP: ::std::os::raw::c_uint = 35074;
+pub const FIOGETOWN: ::std::os::raw::c_uint = 35075;
+pub const SIOCGPGRP: ::std::os::raw::c_uint = 35076;
+pub const SIOCATMARK: ::std::os::raw::c_uint = 35077;
+pub const SIOCGSTAMP: ::std::os::raw::c_uint = 35078;
+pub const SIOCGSTAMPNS: ::std::os::raw::c_uint = 35079;
+pub const SOCK_IOC_TYPE: ::std::os::raw::c_uint = 137;
+pub const SIOCADDRT: ::std::os::raw::c_uint = 35083;
+pub const SIOCDELRT: ::std::os::raw::c_uint = 35084;
+pub const SIOCRTMSG: ::std::os::raw::c_uint = 35085;
+pub const SIOCGIFNAME: ::std::os::raw::c_uint = 35088;
+pub const SIOCSIFLINK: ::std::os::raw::c_uint = 35089;
+pub const SIOCGIFCONF: ::std::os::raw::c_uint = 35090;
+pub const SIOCGIFFLAGS: ::std::os::raw::c_uint = 35091;
+pub const SIOCSIFFLAGS: ::std::os::raw::c_uint = 35092;
+pub const SIOCGIFADDR: ::std::os::raw::c_uint = 35093;
+pub const SIOCSIFADDR: ::std::os::raw::c_uint = 35094;
+pub const SIOCGIFDSTADDR: ::std::os::raw::c_uint = 35095;
+pub const SIOCSIFDSTADDR: ::std::os::raw::c_uint = 35096;
+pub const SIOCGIFBRDADDR: ::std::os::raw::c_uint = 35097;
+pub const SIOCSIFBRDADDR: ::std::os::raw::c_uint = 35098;
+pub const SIOCGIFNETMASK: ::std::os::raw::c_uint = 35099;
+pub const SIOCSIFNETMASK: ::std::os::raw::c_uint = 35100;
+pub const SIOCGIFMETRIC: ::std::os::raw::c_uint = 35101;
+pub const SIOCSIFMETRIC: ::std::os::raw::c_uint = 35102;
+pub const SIOCGIFMEM: ::std::os::raw::c_uint = 35103;
+pub const SIOCSIFMEM: ::std::os::raw::c_uint = 35104;
+pub const SIOCGIFMTU: ::std::os::raw::c_uint = 35105;
+pub const SIOCSIFMTU: ::std::os::raw::c_uint = 35106;
+pub const SIOCSIFNAME: ::std::os::raw::c_uint = 35107;
+pub const SIOCSIFHWADDR: ::std::os::raw::c_uint = 35108;
+pub const SIOCGIFENCAP: ::std::os::raw::c_uint = 35109;
+pub const SIOCSIFENCAP: ::std::os::raw::c_uint = 35110;
+pub const SIOCGIFHWADDR: ::std::os::raw::c_uint = 35111;
+pub const SIOCGIFSLAVE: ::std::os::raw::c_uint = 35113;
+pub const SIOCSIFSLAVE: ::std::os::raw::c_uint = 35120;
+pub const SIOCADDMULTI: ::std::os::raw::c_uint = 35121;
+pub const SIOCDELMULTI: ::std::os::raw::c_uint = 35122;
+pub const SIOCGIFINDEX: ::std::os::raw::c_uint = 35123;
+pub const SIOGIFINDEX: ::std::os::raw::c_uint = 35123;
+pub const SIOCSIFPFLAGS: ::std::os::raw::c_uint = 35124;
+pub const SIOCGIFPFLAGS: ::std::os::raw::c_uint = 35125;
+pub const SIOCDIFADDR: ::std::os::raw::c_uint = 35126;
+pub const SIOCSIFHWBROADCAST: ::std::os::raw::c_uint = 35127;
+pub const SIOCGIFCOUNT: ::std::os::raw::c_uint = 35128;
+pub const SIOCGIFBR: ::std::os::raw::c_uint = 35136;
+pub const SIOCSIFBR: ::std::os::raw::c_uint = 35137;
+pub const SIOCGIFTXQLEN: ::std::os::raw::c_uint = 35138;
+pub const SIOCSIFTXQLEN: ::std::os::raw::c_uint = 35139;
+pub const SIOCETHTOOL: ::std::os::raw::c_uint = 35142;
+pub const SIOCGMIIPHY: ::std::os::raw::c_uint = 35143;
+pub const SIOCGMIIREG: ::std::os::raw::c_uint = 35144;
+pub const SIOCSMIIREG: ::std::os::raw::c_uint = 35145;
+pub const SIOCWANDEV: ::std::os::raw::c_uint = 35146;
+pub const SIOCOUTQNSD: ::std::os::raw::c_uint = 35147;
+pub const SIOCGSKNS: ::std::os::raw::c_uint = 35148;
+pub const SIOCDARP: ::std::os::raw::c_uint = 35155;
+pub const SIOCGARP: ::std::os::raw::c_uint = 35156;
+pub const SIOCSARP: ::std::os::raw::c_uint = 35157;
+pub const SIOCDRARP: ::std::os::raw::c_uint = 35168;
+pub const SIOCGRARP: ::std::os::raw::c_uint = 35169;
+pub const SIOCSRARP: ::std::os::raw::c_uint = 35170;
+pub const SIOCGIFMAP: ::std::os::raw::c_uint = 35184;
+pub const SIOCSIFMAP: ::std::os::raw::c_uint = 35185;
+pub const SIOCADDDLCI: ::std::os::raw::c_uint = 35200;
+pub const SIOCDELDLCI: ::std::os::raw::c_uint = 35201;
+pub const SIOCGIFVLAN: ::std::os::raw::c_uint = 35202;
+pub const SIOCSIFVLAN: ::std::os::raw::c_uint = 35203;
+pub const SIOCBONDENSLAVE: ::std::os::raw::c_uint = 35216;
+pub const SIOCBONDRELEASE: ::std::os::raw::c_uint = 35217;
+pub const SIOCBONDSETHWADDR: ::std::os::raw::c_uint = 35218;
+pub const SIOCBONDSLAVEINFOQUERY: ::std::os::raw::c_uint = 35219;
+pub const SIOCBONDINFOQUERY: ::std::os::raw::c_uint = 35220;
+pub const SIOCBONDCHANGEACTIVE: ::std::os::raw::c_uint = 35221;
+pub const SIOCBRADDBR: ::std::os::raw::c_uint = 35232;
+pub const SIOCBRDELBR: ::std::os::raw::c_uint = 35233;
+pub const SIOCBRADDIF: ::std::os::raw::c_uint = 35234;
+pub const SIOCBRDELIF: ::std::os::raw::c_uint = 35235;
+pub const SIOCSHWTSTAMP: ::std::os::raw::c_uint = 35248;
+pub const SIOCGHWTSTAMP: ::std::os::raw::c_uint = 35249;
+pub const SIOCDEVPRIVATE: ::std::os::raw::c_uint = 35312;
+pub const SIOCPROTOPRIVATE: ::std::os::raw::c_uint = 35296;
diff --git a/net_util/Cargo.toml b/net_util/Cargo.toml
new file mode 100644
index 0000000..bfa3bc8
--- /dev/null
+++ b/net_util/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "net_util"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+libc = "*"
+net_sys = { path = "../net_sys" }
+sys_util = { path = "../sys_util" }
diff --git a/net_util/src/lib.rs b/net_util/src/lib.rs
new file mode 100644
index 0000000..9508dcc
--- /dev/null
+++ b/net_util/src/lib.rs
@@ -0,0 +1,645 @@
+// Copyright 2017 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::fmt::{self, Display};
+use std::fs::File;
+use std::io::{Read, Result as IoResult, Write};
+use std::mem;
+use std::net;
+use std::num::ParseIntError;
+use std::os::raw::*;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::str::FromStr;
+
+use libc::EPERM;
+
+use sys_util::Error as SysError;
+use sys_util::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val};
+
+#[derive(Debug)]
+pub enum Error {
+ /// Failed to create a socket.
+ CreateSocket(SysError),
+ /// Couldn't open /dev/net/tun.
+ OpenTun(SysError),
+ /// Unable to create tap interface.
+ CreateTap(SysError),
+ /// ioctl failed.
+ IoctlError(SysError),
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ CreateSocket(e) => write!(f, "failed to create a socket: {}", e),
+ OpenTun(e) => write!(f, "failed to open /dev/net/tun: {}", e),
+ CreateTap(e) => write!(f, "failed to create tap interface: {}", e),
+ IoctlError(e) => write!(f, "ioctl failed: {}", e),
+ }
+ }
+}
+
+impl Error {
+ pub fn sys_error(&self) -> SysError {
+ match *self {
+ Error::CreateSocket(e) => e,
+ Error::OpenTun(e) => e,
+ Error::CreateTap(e) => e,
+ Error::IoctlError(e) => e,
+ }
+ }
+}
+
+/// Create a sockaddr_in from an IPv4 address, and expose it as
+/// an opaque sockaddr suitable for usage by socket ioctls.
+fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_sys::sockaddr {
+ // IPv4 addresses big-endian (network order), but Ipv4Addr will give us
+ // a view of those bytes directly so we can avoid any endian trickiness.
+ let addr_in = net_sys::sockaddr_in {
+ sin_family: net_sys::AF_INET as u16,
+ sin_port: 0,
+ sin_addr: unsafe { mem::transmute(ip_addr.octets()) },
+ __pad: [0; 8usize],
+ };
+
+ unsafe { mem::transmute(addr_in) }
+}
+
+/// Extract the IPv4 address from a sockaddr. Assumes the sockaddr is a sockaddr_in.
+fn read_ipv4_addr(addr: &net_sys::sockaddr) -> net::Ipv4Addr {
+ debug_assert_eq!(addr.sa_family as u32, net_sys::AF_INET);
+ // This is safe because sockaddr and sockaddr_in are the same size, and we've checked that
+ // this address is AF_INET.
+ let in_addr: net_sys::sockaddr_in = unsafe { mem::transmute(*addr) };
+ net::Ipv4Addr::from(in_addr.sin_addr.s_addr)
+}
+
+fn create_socket() -> Result<net::UdpSocket> {
+ // This is safe since we check the return value.
+ let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
+ if sock < 0 {
+ return Err(Error::CreateSocket(SysError::last()));
+ }
+
+ // This is safe; nothing else will use or hold onto the raw sock fd.
+ Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
+}
+
+#[derive(Debug)]
+pub enum MacAddressError {
+ /// Invalid number of octets.
+ InvalidNumOctets(usize),
+ /// Failed to parse octet.
+ ParseOctet(ParseIntError),
+}
+
+impl Display for MacAddressError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::MacAddressError::*;
+
+ match self {
+ InvalidNumOctets(n) => write!(f, "invalid number of octets: {}", n),
+ ParseOctet(e) => write!(f, "failed to parse octet: {}", e),
+ }
+ }
+}
+
+/// An Ethernet mac address. This struct is compatible with the C `struct sockaddr`.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct MacAddress {
+ family: net_sys::sa_family_t,
+ addr: [u8; 6usize],
+ __pad: [u8; 8usize],
+}
+
+impl MacAddress {
+ pub fn octets(&self) -> [u8; 6usize] {
+ self.addr
+ }
+}
+
+impl FromStr for MacAddress {
+ type Err = MacAddressError;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ let octets: Vec<&str> = s.split(':').collect();
+ if octets.len() != 6usize {
+ return Err(MacAddressError::InvalidNumOctets(octets.len()));
+ }
+
+ let mut result = MacAddress {
+ family: net_sys::ARPHRD_ETHER,
+ addr: [0; 6usize],
+ __pad: [0; 8usize],
+ };
+
+ for (i, octet) in octets.iter().enumerate() {
+ result.addr[i] = u8::from_str_radix(octet, 16).map_err(MacAddressError::ParseOctet)?;
+ }
+
+ Ok(result)
+ }
+}
+
+impl Display for MacAddress {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
+ self.addr[0], self.addr[1], self.addr[2], self.addr[3], self.addr[4], self.addr[5]
+ )
+ }
+}
+
+/// Handle for a network tap interface.
+///
+/// For now, this simply wraps the file descriptor for the tap device so methods
+/// can run ioctls on the interface. The tap interface fd will be closed when
+/// Tap goes out of scope, and the kernel will clean up the interface
+/// automatically.
+#[derive(Debug)]
+pub struct Tap {
+ tap_file: File,
+ if_name: [c_char; 16usize],
+ if_flags: ::std::os::raw::c_short,
+}
+
+impl Tap {
+ pub unsafe fn from_raw_fd(fd: RawFd) -> Result<Tap> {
+ let tap_file = File::from_raw_fd(fd);
+
+ // Get the interface name since we will need it for some ioctls.
+ let mut ifreq: net_sys::ifreq = Default::default();
+ let ret = ioctl_with_mut_ref(&tap_file, net_sys::TUNGETIFF(), &mut ifreq);
+
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ Ok(Tap {
+ tap_file,
+ if_name: ifreq.ifr_ifrn.ifrn_name,
+ if_flags: ifreq.ifr_ifru.ifru_flags,
+ })
+ }
+}
+
+pub trait TapT: Read + Write + AsRawFd + 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.
+ fn new(vnet_hdr: bool) -> Result<Self>;
+
+ /// Get the host-side IP address for the tap interface.
+ fn ip_addr(&self) -> Result<net::Ipv4Addr>;
+
+ /// Set the host-side IP address for the tap interface.
+ fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()>;
+
+ /// Get the netmask for the tap interface's subnet.
+ fn netmask(&self) -> Result<net::Ipv4Addr>;
+
+ /// Set the netmask for the subnet that the tap interface will exist on.
+ fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()>;
+
+ /// Get the mac address for the tap interface.
+ fn mac_address(&self) -> Result<MacAddress>;
+
+ /// Set the mac address for the tap interface.
+ fn set_mac_address(&self, mac_addr: MacAddress) -> Result<()>;
+
+ /// Set the offload flags for the tap interface.
+ fn set_offload(&self, flags: c_uint) -> Result<()>;
+
+ /// Enable the tap interface.
+ fn enable(&self) -> Result<()>;
+
+ /// Set the size of the vnet hdr.
+ fn set_vnet_hdr_size(&self, size: c_int) -> Result<()>;
+
+ fn get_ifreq(&self) -> net_sys::ifreq;
+
+ /// Get the interface flags
+ fn if_flags(&self) -> u32;
+}
+
+impl TapT for Tap {
+ fn new(vnet_hdr: bool) -> Result<Tap> {
+ // Open calls are safe because we give a constant nul-terminated
+ // string and verify the result.
+ let fd = unsafe {
+ libc::open(
+ b"/dev/net/tun\0".as_ptr() as *const c_char,
+ libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC,
+ )
+ };
+ if fd < 0 {
+ return Err(Error::OpenTun(SysError::last()));
+ }
+
+ // We just checked that the fd is valid.
+ let tuntap = unsafe { File::from_raw_fd(fd) };
+
+ const TUNTAP_DEV_FORMAT: &[u8; 8usize] = b"vmtap%d\0";
+
+ // This is pretty messy because of the unions used by ifreq. Since we
+ // don't call as_mut on the same union field more than once, this block
+ // is safe.
+ let mut ifreq: net_sys::ifreq = Default::default();
+ unsafe {
+ let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
+ let name_slice = &mut ifrn_name[..TUNTAP_DEV_FORMAT.len()];
+ for (dst, src) in name_slice.iter_mut().zip(TUNTAP_DEV_FORMAT.iter()) {
+ *dst = *src as c_char;
+ }
+ ifreq.ifr_ifru.ifru_flags = (net_sys::IFF_TAP
+ | net_sys::IFF_NO_PI
+ | if vnet_hdr { net_sys::IFF_VNET_HDR } else { 0 })
+ as c_short;
+ }
+
+ // 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(), &mut ifreq) };
+
+ if ret < 0 {
+ let error = SysError::last();
+
+ // In a non-root, test environment, we won't have permission to call this; allow
+ if !(cfg!(test) && error.errno() == EPERM) {
+ return Err(Error::CreateTap(error));
+ }
+ }
+
+ // Safe since only the name is accessed, and it's copied out.
+ Ok(Tap {
+ tap_file: tuntap,
+ if_name: unsafe { ifreq.ifr_ifrn.ifrn_name },
+ if_flags: unsafe { ifreq.ifr_ifru.ifru_flags },
+ })
+ }
+
+ fn ip_addr(&self) -> Result<net::Ipv4Addr> {
+ let sock = create_socket()?;
+ let mut ifreq = self.get_ifreq();
+
+ // ioctl is safe. Called with a valid sock fd, and we check the return.
+ let ret = unsafe {
+ ioctl_with_mut_ref(&sock, net_sys::sockios::SIOCGIFADDR as c_ulong, &mut ifreq)
+ };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ // We only access one field of the ifru union, hence this is safe.
+ let addr = unsafe { ifreq.ifr_ifru.ifru_addr };
+
+ Ok(read_ipv4_addr(&addr))
+ }
+
+ fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> {
+ let sock = create_socket()?;
+ let addr = create_sockaddr(ip_addr);
+
+ let mut ifreq = self.get_ifreq();
+ ifreq.ifr_ifru.ifru_addr = addr;
+
+ // ioctl is safe. Called with a valid sock fd, and we check the return.
+ let ret =
+ unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFADDR as c_ulong, &ifreq) };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ Ok(())
+ }
+
+ fn netmask(&self) -> Result<net::Ipv4Addr> {
+ let sock = create_socket()?;
+ let mut ifreq = self.get_ifreq();
+
+ // ioctl is safe. Called with a valid sock fd, and we check the return.
+ let ret = unsafe {
+ ioctl_with_mut_ref(
+ &sock,
+ net_sys::sockios::SIOCGIFNETMASK as c_ulong,
+ &mut ifreq,
+ )
+ };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ // We only access one field of the ifru union, hence this is safe.
+ let addr = unsafe { ifreq.ifr_ifru.ifru_netmask };
+
+ Ok(read_ipv4_addr(&addr))
+ }
+
+ fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> {
+ let sock = create_socket()?;
+ let addr = create_sockaddr(netmask);
+
+ let mut ifreq = self.get_ifreq();
+ ifreq.ifr_ifru.ifru_netmask = addr;
+
+ // ioctl is safe. Called with a valid sock fd, and we check the return.
+ let ret =
+ unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFNETMASK as c_ulong, &ifreq) };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ Ok(())
+ }
+
+ fn mac_address(&self) -> Result<MacAddress> {
+ let sock = create_socket()?;
+ let mut ifreq = self.get_ifreq();
+
+ // ioctl is safe. Called with a valid sock fd, and we check the return.
+ let ret = unsafe {
+ ioctl_with_mut_ref(
+ &sock,
+ net_sys::sockios::SIOCGIFHWADDR as c_ulong,
+ &mut ifreq,
+ )
+ };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ // We only access one field of the ifru union, hence this is safe.
+ // This is safe since the MacAddress struct is already sized to match the C sockaddr
+ // struct. The address family has also been checked.
+ Ok(unsafe { mem::transmute(ifreq.ifr_ifru.ifru_hwaddr) })
+ }
+
+ fn set_mac_address(&self, mac_addr: MacAddress) -> Result<()> {
+ let sock = create_socket()?;
+
+ let mut ifreq = self.get_ifreq();
+
+ // We only access one field of the ifru union, hence this is safe.
+ unsafe {
+ // This is safe since the MacAddress struct is already sized to match the C sockaddr
+ // struct.
+ ifreq.ifr_ifru.ifru_hwaddr = std::mem::transmute(mac_addr);
+ }
+
+ // ioctl is safe. Called with a valid sock fd, and we check the return.
+ let ret =
+ unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFHWADDR as c_ulong, &ifreq) };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ Ok(())
+ }
+
+ fn set_offload(&self, flags: c_uint) -> Result<()> {
+ // ioctl is safe. Called with a valid tap fd, and we check the return.
+ let ret =
+ unsafe { ioctl_with_val(&self.tap_file, net_sys::TUNSETOFFLOAD(), flags as c_ulong) };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ Ok(())
+ }
+
+ fn enable(&self) -> Result<()> {
+ let sock = create_socket()?;
+
+ let mut ifreq = self.get_ifreq();
+ ifreq.ifr_ifru.ifru_flags =
+ (net_sys::net_device_flags_IFF_UP | net_sys::net_device_flags_IFF_RUNNING) as i16;
+
+ // ioctl is safe. Called with a valid sock fd, and we check the return.
+ let ret =
+ unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFFLAGS as c_ulong, &ifreq) };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ Ok(())
+ }
+
+ fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> {
+ // ioctl is safe. Called with a valid tap fd, and we check the return.
+ let ret = unsafe { ioctl_with_ref(&self.tap_file, net_sys::TUNSETVNETHDRSZ(), &size) };
+ if ret < 0 {
+ return Err(Error::IoctlError(SysError::last()));
+ }
+
+ Ok(())
+ }
+
+ fn get_ifreq(&self) -> net_sys::ifreq {
+ let mut ifreq: net_sys::ifreq = Default::default();
+
+ // This sets the name of the interface, which is the only entry
+ // in a single-field union.
+ unsafe {
+ let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
+ ifrn_name.clone_from_slice(&self.if_name);
+ }
+
+ // This sets the flags with which the interface was created, which is the only entry we set
+ // on the second union.
+ ifreq.ifr_ifru.ifru_flags = self.if_flags;
+
+ ifreq
+ }
+
+ fn if_flags(&self) -> u32 {
+ self.if_flags as u32
+ }
+}
+
+impl Read for Tap {
+ fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
+ self.tap_file.read(buf)
+ }
+}
+
+impl Write for Tap {
+ fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
+ self.tap_file.write(&buf)
+ }
+
+ fn flush(&mut self) -> IoResult<()> {
+ Ok(())
+ }
+}
+
+impl AsRawFd for Tap {
+ fn as_raw_fd(&self) -> RawFd {
+ self.tap_file.as_raw_fd()
+ }
+}
+
+pub mod fakes {
+ use super::*;
+ use std::fs::remove_file;
+ use std::fs::OpenOptions;
+
+ const TMP_FILE: &str = "/tmp/crosvm_tap_test_file";
+
+ pub struct FakeTap {
+ tap_file: File,
+ }
+
+ impl TapT for FakeTap {
+ fn new(_: bool) -> Result<FakeTap> {
+ Ok(FakeTap {
+ tap_file: OpenOptions::new()
+ .read(true)
+ .append(true)
+ .create(true)
+ .open(TMP_FILE)
+ .unwrap(),
+ })
+ }
+
+ fn ip_addr(&self) -> Result<net::Ipv4Addr> {
+ Ok(net::Ipv4Addr::new(1, 2, 3, 4))
+ }
+
+ fn set_ip_addr(&self, _: net::Ipv4Addr) -> Result<()> {
+ Ok(())
+ }
+
+ fn netmask(&self) -> Result<net::Ipv4Addr> {
+ Ok(net::Ipv4Addr::new(255, 255, 255, 252))
+ }
+
+ fn set_netmask(&self, _: net::Ipv4Addr) -> Result<()> {
+ Ok(())
+ }
+
+ fn mac_address(&self) -> Result<MacAddress> {
+ Ok("01:02:03:04:05:06".parse().unwrap())
+ }
+
+ fn set_mac_address(&self, _: MacAddress) -> Result<()> {
+ Ok(())
+ }
+
+ fn set_offload(&self, _: c_uint) -> Result<()> {
+ Ok(())
+ }
+
+ fn enable(&self) -> Result<()> {
+ Ok(())
+ }
+
+ fn set_vnet_hdr_size(&self, _: c_int) -> Result<()> {
+ Ok(())
+ }
+
+ fn get_ifreq(&self) -> net_sys::ifreq {
+ let ifreq: net_sys::ifreq = Default::default();
+ ifreq
+ }
+
+ fn if_flags(&self) -> u32 {
+ net_sys::IFF_TAP
+ }
+ }
+
+ impl Drop for FakeTap {
+ fn drop(&mut self) {
+ let _ = remove_file(TMP_FILE);
+ }
+ }
+
+ impl Read for FakeTap {
+ fn read(&mut self, _: &mut [u8]) -> IoResult<usize> {
+ Ok(0)
+ }
+ }
+
+ impl Write for FakeTap {
+ fn write(&mut self, _: &[u8]) -> IoResult<usize> {
+ Ok(0)
+ }
+
+ fn flush(&mut self) -> IoResult<()> {
+ Ok(())
+ }
+ }
+
+ impl AsRawFd for FakeTap {
+ fn as_raw_fd(&self) -> RawFd {
+ self.tap_file.as_raw_fd()
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn parse_mac_address() {
+ assert!("01:02:03:04:05:06".parse::<MacAddress>().is_ok());
+ assert!("01:06".parse::<MacAddress>().is_err());
+ assert!("01:02:03:04:05:06:07:08:09".parse::<MacAddress>().is_err());
+ assert!("not a mac address".parse::<MacAddress>().is_err());
+ }
+
+ #[test]
+ fn tap_create() {
+ Tap::new(true).unwrap();
+ }
+
+ #[test]
+ fn tap_configure() {
+ let tap = Tap::new(true).unwrap();
+ let ip_addr: net::Ipv4Addr = "100.115.92.5".parse().unwrap();
+ let netmask: net::Ipv4Addr = "255.255.255.252".parse().unwrap();
+ let mac_addr: MacAddress = "a2:06:b9:3d:68:4d".parse().unwrap();
+
+ let ret = tap.set_ip_addr(ip_addr);
+ assert_ok_or_perm_denied(ret);
+ let ret = tap.set_netmask(netmask);
+ assert_ok_or_perm_denied(ret);
+ let ret = tap.set_mac_address(mac_addr);
+ assert_ok_or_perm_denied(ret);
+ }
+
+ /// This test will only work if the test is run with root permissions and, unlike other tests
+ /// in this file, do not return PermissionDenied. They fail because the TAP FD is not
+ /// initialized (as opposed to permission denial). Run this with "cargo test -- --ignored".
+ #[test]
+ #[ignore]
+ fn root_only_tests() {
+ // This line will fail to provide an initialized FD if the test is not run as root.
+ let tap = Tap::new(true).unwrap();
+ tap.set_vnet_hdr_size(16).unwrap();
+ tap.set_offload(0).unwrap();
+ }
+
+ #[test]
+ fn tap_enable() {
+ let tap = Tap::new(true).unwrap();
+
+ let ret = tap.enable();
+ assert_ok_or_perm_denied(ret);
+ }
+
+ fn assert_ok_or_perm_denied<T>(res: Result<T>) {
+ match res {
+ // We won't have permission in test environments; allow that
+ Ok(_t) => {}
+ Err(Error::IoctlError(e)) if e.errno() == EPERM => {}
+ Err(e) => panic!("Unexpected Error:\n{}", e),
+ }
+ }
+}
diff --git a/p9/Cargo.toml b/p9/Cargo.toml
new file mode 100644
index 0000000..40b5d62
--- /dev/null
+++ b/p9/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "p9"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+libc = "*"
+wire_format_derive = { path = "wire_format_derive" }
+
+[features]
+trace = []
diff --git a/p9/src/lib.rs b/p9/src/lib.rs
new file mode 100644
index 0000000..56195f1
--- /dev/null
+++ b/p9/src/lib.rs
@@ -0,0 +1,8 @@
+// Copyright 2018 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.
+
+mod protocol;
+mod server;
+
+pub use crate::server::Server;
diff --git a/p9/src/protocol/messages.rs b/p9/src/protocol/messages.rs
new file mode 100644
index 0000000..c8429bb
--- /dev/null
+++ b/p9/src/protocol/messages.rs
@@ -0,0 +1,842 @@
+// Copyright 2018 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 wire_format_derive::P9WireFormat;
+
+use std::io::{self, ErrorKind, Read, Write};
+use std::mem;
+use std::string::String;
+use std::vec::Vec;
+
+use crate::protocol::wire_format::{Data, WireFormat};
+
+// Message type constants. Taken from "include/net/9p/9p.h" in the linux kernel
+// tree. The protocol specifies each R* message to be the corresponding T*
+// message plus one.
+const TLERROR: u8 = 6;
+const RLERROR: u8 = TLERROR + 1;
+const TSTATFS: u8 = 8;
+const RSTATFS: u8 = TSTATFS + 1;
+const TLOPEN: u8 = 12;
+const RLOPEN: u8 = TLOPEN + 1;
+const TLCREATE: u8 = 14;
+const RLCREATE: u8 = TLCREATE + 1;
+const TSYMLINK: u8 = 16;
+const RSYMLINK: u8 = TSYMLINK + 1;
+const TMKNOD: u8 = 18;
+const RMKNOD: u8 = TMKNOD + 1;
+const TRENAME: u8 = 20;
+const RRENAME: u8 = TRENAME + 1;
+const TREADLINK: u8 = 22;
+const RREADLINK: u8 = TREADLINK + 1;
+const TGETATTR: u8 = 24;
+const RGETATTR: u8 = TGETATTR + 1;
+const TSETATTR: u8 = 26;
+const RSETATTR: u8 = TSETATTR + 1;
+const TXATTRWALK: u8 = 30;
+const RXATTRWALK: u8 = TXATTRWALK + 1;
+const TXATTRCREATE: u8 = 32;
+const RXATTRCREATE: u8 = TXATTRCREATE + 1;
+const TREADDIR: u8 = 40;
+const RREADDIR: u8 = TREADDIR + 1;
+const TFSYNC: u8 = 50;
+const RFSYNC: u8 = TFSYNC + 1;
+const TLOCK: u8 = 52;
+const RLOCK: u8 = TLOCK + 1;
+const TGETLOCK: u8 = 54;
+const RGETLOCK: u8 = TGETLOCK + 1;
+const TLINK: u8 = 70;
+const RLINK: u8 = TLINK + 1;
+const TMKDIR: u8 = 72;
+const RMKDIR: u8 = TMKDIR + 1;
+const TRENAMEAT: u8 = 74;
+const RRENAMEAT: u8 = TRENAMEAT + 1;
+const TUNLINKAT: u8 = 76;
+const RUNLINKAT: u8 = TUNLINKAT + 1;
+const TVERSION: u8 = 100;
+const RVERSION: u8 = TVERSION + 1;
+const TAUTH: u8 = 102;
+const RAUTH: u8 = TAUTH + 1;
+const TATTACH: u8 = 104;
+const RATTACH: u8 = TATTACH + 1;
+const _TERROR: u8 = 106;
+const _RERROR: u8 = _TERROR + 1;
+const TFLUSH: u8 = 108;
+const RFLUSH: u8 = TFLUSH + 1;
+const TWALK: u8 = 110;
+const RWALK: u8 = TWALK + 1;
+const _TOPEN: u8 = 112;
+const _ROPEN: u8 = _TOPEN + 1;
+const _TCREATE: u8 = 114;
+const _RCREATE: u8 = _TCREATE + 1;
+const TREAD: u8 = 116;
+const RREAD: u8 = TREAD + 1;
+const TWRITE: u8 = 118;
+const RWRITE: u8 = TWRITE + 1;
+const TCLUNK: u8 = 120;
+const RCLUNK: u8 = TCLUNK + 1;
+const TREMOVE: u8 = 122;
+const RREMOVE: u8 = TREMOVE + 1;
+const _TSTAT: u8 = 124;
+const _RSTAT: u8 = _TSTAT + 1;
+const _TWSTAT: u8 = 126;
+const _RWSTAT: u8 = _TWSTAT + 1;
+
+/// A message sent from a 9P client to a 9P server.
+#[derive(Debug)]
+pub enum Tmessage {
+ Version(Tversion),
+ Flush(Tflush),
+ Walk(Twalk),
+ Read(Tread),
+ Write(Twrite),
+ Clunk(Tclunk),
+ Remove(Tremove),
+ Attach(Tattach),
+ Auth(Tauth),
+ Statfs(Tstatfs),
+ Lopen(Tlopen),
+ Lcreate(Tlcreate),
+ Symlink(Tsymlink),
+ Mknod(Tmknod),
+ Rename(Trename),
+ Readlink(Treadlink),
+ GetAttr(Tgetattr),
+ SetAttr(Tsetattr),
+ XattrWalk(Txattrwalk),
+ XattrCreate(Txattrcreate),
+ Readdir(Treaddir),
+ Fsync(Tfsync),
+ Lock(Tlock),
+ GetLock(Tgetlock),
+ Link(Tlink),
+ Mkdir(Tmkdir),
+ RenameAt(Trenameat),
+ UnlinkAt(Tunlinkat),
+}
+
+#[derive(Debug)]
+pub struct Tframe {
+ pub tag: u16,
+ pub msg: Tmessage,
+}
+
+impl WireFormat for Tframe {
+ fn byte_size(&self) -> u32 {
+ let msg_size = match &self.msg {
+ Tmessage::Version(version) => version.byte_size(),
+ Tmessage::Flush(flush) => flush.byte_size(),
+ Tmessage::Walk(walk) => walk.byte_size(),
+ Tmessage::Read(read) => read.byte_size(),
+ Tmessage::Write(write) => write.byte_size(),
+ Tmessage::Clunk(clunk) => clunk.byte_size(),
+ Tmessage::Remove(remove) => remove.byte_size(),
+ Tmessage::Attach(attach) => attach.byte_size(),
+ Tmessage::Auth(auth) => auth.byte_size(),
+ Tmessage::Statfs(statfs) => statfs.byte_size(),
+ Tmessage::Lopen(lopen) => lopen.byte_size(),
+ Tmessage::Lcreate(lcreate) => lcreate.byte_size(),
+ Tmessage::Symlink(symlink) => symlink.byte_size(),
+ Tmessage::Mknod(mknod) => mknod.byte_size(),
+ Tmessage::Rename(rename) => rename.byte_size(),
+ Tmessage::Readlink(readlink) => readlink.byte_size(),
+ Tmessage::GetAttr(getattr) => getattr.byte_size(),
+ Tmessage::SetAttr(setattr) => setattr.byte_size(),
+ Tmessage::XattrWalk(xattrwalk) => xattrwalk.byte_size(),
+ Tmessage::XattrCreate(xattrcreate) => xattrcreate.byte_size(),
+ Tmessage::Readdir(readdir) => readdir.byte_size(),
+ Tmessage::Fsync(fsync) => fsync.byte_size(),
+ Tmessage::Lock(lock) => lock.byte_size(),
+ Tmessage::GetLock(getlock) => getlock.byte_size(),
+ Tmessage::Link(link) => link.byte_size(),
+ Tmessage::Mkdir(mkdir) => mkdir.byte_size(),
+ Tmessage::RenameAt(renameat) => renameat.byte_size(),
+ Tmessage::UnlinkAt(unlinkat) => unlinkat.byte_size(),
+ };
+
+ // size + type + tag + message size
+ (mem::size_of::<u32>() + mem::size_of::<u8>() + mem::size_of::<u16>()) as u32 + msg_size
+ }
+
+ fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+ self.byte_size().encode(writer)?;
+
+ let ty = match self.msg {
+ Tmessage::Version(_) => TVERSION,
+ Tmessage::Flush(_) => TFLUSH,
+ Tmessage::Walk(_) => TWALK,
+ Tmessage::Read(_) => TREAD,
+ Tmessage::Write(_) => TWRITE,
+ Tmessage::Clunk(_) => TCLUNK,
+ Tmessage::Remove(_) => TREMOVE,
+ Tmessage::Attach(_) => TATTACH,
+ Tmessage::Auth(_) => TAUTH,
+ Tmessage::Statfs(_) => TSTATFS,
+ Tmessage::Lopen(_) => TLOPEN,
+ Tmessage::Lcreate(_) => TLCREATE,
+ Tmessage::Symlink(_) => TSYMLINK,
+ Tmessage::Mknod(_) => TMKNOD,
+ Tmessage::Rename(_) => TRENAME,
+ Tmessage::Readlink(_) => TREADLINK,
+ Tmessage::GetAttr(_) => TGETATTR,
+ Tmessage::SetAttr(_) => TSETATTR,
+ Tmessage::XattrWalk(_) => TXATTRWALK,
+ Tmessage::XattrCreate(_) => TXATTRCREATE,
+ Tmessage::Readdir(_) => TREADDIR,
+ Tmessage::Fsync(_) => TFSYNC,
+ Tmessage::Lock(_) => TLOCK,
+ Tmessage::GetLock(_) => TGETLOCK,
+ Tmessage::Link(_) => TLINK,
+ Tmessage::Mkdir(_) => TMKDIR,
+ Tmessage::RenameAt(_) => TRENAMEAT,
+ Tmessage::UnlinkAt(_) => TUNLINKAT,
+ };
+
+ ty.encode(writer)?;
+ self.tag.encode(writer)?;
+
+ match &self.msg {
+ Tmessage::Version(version) => version.encode(writer),
+ Tmessage::Flush(flush) => flush.encode(writer),
+ Tmessage::Walk(walk) => walk.encode(writer),
+ Tmessage::Read(read) => read.encode(writer),
+ Tmessage::Write(write) => write.encode(writer),
+ Tmessage::Clunk(clunk) => clunk.encode(writer),
+ Tmessage::Remove(remove) => remove.encode(writer),
+ Tmessage::Attach(attach) => attach.encode(writer),
+ Tmessage::Auth(auth) => auth.encode(writer),
+ Tmessage::Statfs(statfs) => statfs.encode(writer),
+ Tmessage::Lopen(lopen) => lopen.encode(writer),
+ Tmessage::Lcreate(lcreate) => lcreate.encode(writer),
+ Tmessage::Symlink(symlink) => symlink.encode(writer),
+ Tmessage::Mknod(mknod) => mknod.encode(writer),
+ Tmessage::Rename(rename) => rename.encode(writer),
+ Tmessage::Readlink(readlink) => readlink.encode(writer),
+ Tmessage::GetAttr(getattr) => getattr.encode(writer),
+ Tmessage::SetAttr(setattr) => setattr.encode(writer),
+ Tmessage::XattrWalk(xattrwalk) => xattrwalk.encode(writer),
+ Tmessage::XattrCreate(xattrcreate) => xattrcreate.encode(writer),
+ Tmessage::Readdir(readdir) => readdir.encode(writer),
+ Tmessage::Fsync(fsync) => fsync.encode(writer),
+ Tmessage::Lock(lock) => lock.encode(writer),
+ Tmessage::GetLock(getlock) => getlock.encode(writer),
+ Tmessage::Link(link) => link.encode(writer),
+ Tmessage::Mkdir(mkdir) => mkdir.encode(writer),
+ Tmessage::RenameAt(renameat) => renameat.encode(writer),
+ Tmessage::UnlinkAt(unlinkat) => unlinkat.encode(writer),
+ }
+ }
+
+ fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+ let byte_size: u32 = WireFormat::decode(reader)?;
+
+ // byte_size includes the size of byte_size so remove that from the
+ // expected length of the message. Also make sure that byte_size is at least
+ // that long to begin with.
+ if byte_size < mem::size_of::<u32>() as u32 {
+ return Err(io::Error::new(
+ ErrorKind::InvalidData,
+ format!("byte_size(= {}) is less than 4 bytes", byte_size),
+ ));
+ }
+
+ let reader = &mut reader.take((byte_size - mem::size_of::<u32>() as u32) as u64);
+
+ let mut ty = [0u8];
+ reader.read_exact(&mut ty)?;
+
+ let tag: u16 = WireFormat::decode(reader)?;
+
+ let msg = match ty[0] {
+ TVERSION => Ok(Tmessage::Version(WireFormat::decode(reader)?)),
+ TFLUSH => Ok(Tmessage::Flush(WireFormat::decode(reader)?)),
+ TWALK => Ok(Tmessage::Walk(WireFormat::decode(reader)?)),
+ TREAD => Ok(Tmessage::Read(WireFormat::decode(reader)?)),
+ TWRITE => Ok(Tmessage::Write(WireFormat::decode(reader)?)),
+ TCLUNK => Ok(Tmessage::Clunk(WireFormat::decode(reader)?)),
+ TREMOVE => Ok(Tmessage::Remove(WireFormat::decode(reader)?)),
+ TATTACH => Ok(Tmessage::Attach(WireFormat::decode(reader)?)),
+ TAUTH => Ok(Tmessage::Auth(WireFormat::decode(reader)?)),
+ TSTATFS => Ok(Tmessage::Statfs(WireFormat::decode(reader)?)),
+ TLOPEN => Ok(Tmessage::Lopen(WireFormat::decode(reader)?)),
+ TLCREATE => Ok(Tmessage::Lcreate(WireFormat::decode(reader)?)),
+ TSYMLINK => Ok(Tmessage::Symlink(WireFormat::decode(reader)?)),
+ TMKNOD => Ok(Tmessage::Mknod(WireFormat::decode(reader)?)),
+ TRENAME => Ok(Tmessage::Rename(WireFormat::decode(reader)?)),
+ TREADLINK => Ok(Tmessage::Readlink(WireFormat::decode(reader)?)),
+ TGETATTR => Ok(Tmessage::GetAttr(WireFormat::decode(reader)?)),
+ TSETATTR => Ok(Tmessage::SetAttr(WireFormat::decode(reader)?)),
+ TXATTRWALK => Ok(Tmessage::XattrWalk(WireFormat::decode(reader)?)),
+ TXATTRCREATE => Ok(Tmessage::XattrCreate(WireFormat::decode(reader)?)),
+ TREADDIR => Ok(Tmessage::Readdir(WireFormat::decode(reader)?)),
+ TFSYNC => Ok(Tmessage::Fsync(WireFormat::decode(reader)?)),
+ TLOCK => Ok(Tmessage::Lock(WireFormat::decode(reader)?)),
+ TGETLOCK => Ok(Tmessage::GetLock(WireFormat::decode(reader)?)),
+ TLINK => Ok(Tmessage::Link(WireFormat::decode(reader)?)),
+ TMKDIR => Ok(Tmessage::Mkdir(WireFormat::decode(reader)?)),
+ TRENAMEAT => Ok(Tmessage::RenameAt(WireFormat::decode(reader)?)),
+ TUNLINKAT => Ok(Tmessage::UnlinkAt(WireFormat::decode(reader)?)),
+ err => Err(io::Error::new(
+ ErrorKind::InvalidData,
+ format!("unknown message type {}", err),
+ )),
+ }?;
+
+ Ok(Tframe { tag, msg })
+ }
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tversion {
+ pub msize: u32,
+ pub version: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tflush {
+ pub oldtag: u16,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Twalk {
+ pub fid: u32,
+ pub newfid: u32,
+ pub wnames: Vec<String>,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tread {
+ pub fid: u32,
+ pub offset: u64,
+ pub count: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Twrite {
+ pub fid: u32,
+ pub offset: u64,
+ pub data: Data,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tclunk {
+ pub fid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tremove {
+ pub fid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tauth {
+ pub afid: u32,
+ pub uname: String,
+ pub aname: String,
+ pub n_uname: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tattach {
+ pub fid: u32,
+ pub afid: u32,
+ pub uname: String,
+ pub aname: String,
+ pub n_uname: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tstatfs {
+ pub fid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tlopen {
+ pub fid: u32,
+ pub flags: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tlcreate {
+ pub fid: u32,
+ pub name: String,
+ pub flags: u32,
+ pub mode: u32,
+ pub gid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tsymlink {
+ pub fid: u32,
+ pub name: String,
+ pub symtgt: String,
+ pub gid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tmknod {
+ pub dfid: u32,
+ pub name: String,
+ pub mode: u32,
+ pub major: u32,
+ pub minor: u32,
+ pub gid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Trename {
+ pub fid: u32,
+ pub dfid: u32,
+ pub name: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Treadlink {
+ pub fid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tgetattr {
+ pub fid: u32,
+ pub request_mask: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tsetattr {
+ pub fid: u32,
+ pub valid: u32,
+ pub mode: u32,
+ pub uid: u32,
+ pub gid: u32,
+ pub size: u64,
+ pub atime_sec: u64,
+ pub atime_nsec: u64,
+ pub mtime_sec: u64,
+ pub mtime_nsec: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Txattrwalk {
+ pub fid: u32,
+ pub newfid: u32,
+ pub name: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Txattrcreate {
+ pub fid: u32,
+ pub name: String,
+ pub attr_size: u64,
+ pub flags: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Treaddir {
+ pub fid: u32,
+ pub offset: u64,
+ pub count: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tfsync {
+ pub fid: u32,
+ pub datasync: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tlock {
+ pub fid: u32,
+ pub type_: u8,
+ pub flags: u32,
+ pub start: u64,
+ pub length: u64,
+ pub proc_id: u32,
+ pub client_id: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tgetlock {
+ pub fid: u32,
+ pub type_: u8,
+ pub start: u64,
+ pub length: u64,
+ pub proc_id: u32,
+ pub client_id: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tlink {
+ pub dfid: u32,
+ pub fid: u32,
+ pub name: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tmkdir {
+ pub dfid: u32,
+ pub name: String,
+ pub mode: u32,
+ pub gid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Trenameat {
+ pub olddirfid: u32,
+ pub oldname: String,
+ pub newdirfid: u32,
+ pub newname: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tunlinkat {
+ pub dirfd: u32,
+ pub name: String,
+ pub flags: u32,
+}
+
+/// A message sent from a 9P server to a 9P client in response to a request from
+/// that client. Encapsulates a full frame.
+#[derive(Debug)]
+pub enum Rmessage {
+ Version(Rversion),
+ Flush,
+ Walk(Rwalk),
+ Read(Rread),
+ Write(Rwrite),
+ Clunk,
+ Remove,
+ Attach(Rattach),
+ Auth(Rauth),
+ Statfs(Rstatfs),
+ Lopen(Rlopen),
+ Lcreate(Rlcreate),
+ Symlink(Rsymlink),
+ Mknod(Rmknod),
+ Rename,
+ Readlink(Rreadlink),
+ GetAttr(Rgetattr),
+ SetAttr,
+ XattrWalk(Rxattrwalk),
+ XattrCreate,
+ Readdir(Rreaddir),
+ Fsync,
+ Lock(Rlock),
+ GetLock(Rgetlock),
+ Link,
+ Mkdir(Rmkdir),
+ RenameAt,
+ UnlinkAt,
+ Lerror(Rlerror),
+}
+
+#[derive(Debug)]
+pub struct Rframe {
+ pub tag: u16,
+ pub msg: Rmessage,
+}
+
+impl WireFormat for Rframe {
+ fn byte_size(&self) -> u32 {
+ let msg_size = match &self.msg {
+ Rmessage::Version(version) => version.byte_size(),
+ Rmessage::Flush => 0,
+ Rmessage::Walk(walk) => walk.byte_size(),
+ Rmessage::Read(read) => read.byte_size(),
+ Rmessage::Write(write) => write.byte_size(),
+ Rmessage::Clunk => 0,
+ Rmessage::Remove => 0,
+ Rmessage::Attach(attach) => attach.byte_size(),
+ Rmessage::Auth(auth) => auth.byte_size(),
+ Rmessage::Statfs(statfs) => statfs.byte_size(),
+ Rmessage::Lopen(lopen) => lopen.byte_size(),
+ Rmessage::Lcreate(lcreate) => lcreate.byte_size(),
+ Rmessage::Symlink(symlink) => symlink.byte_size(),
+ Rmessage::Mknod(mknod) => mknod.byte_size(),
+ Rmessage::Rename => 0,
+ Rmessage::Readlink(readlink) => readlink.byte_size(),
+ Rmessage::GetAttr(getattr) => getattr.byte_size(),
+ Rmessage::SetAttr => 0,
+ Rmessage::XattrWalk(xattrwalk) => xattrwalk.byte_size(),
+ Rmessage::XattrCreate => 0,
+ Rmessage::Readdir(readdir) => readdir.byte_size(),
+ Rmessage::Fsync => 0,
+ Rmessage::Lock(lock) => lock.byte_size(),
+ Rmessage::GetLock(getlock) => getlock.byte_size(),
+ Rmessage::Link => 0,
+ Rmessage::Mkdir(mkdir) => mkdir.byte_size(),
+ Rmessage::RenameAt => 0,
+ Rmessage::UnlinkAt => 0,
+ Rmessage::Lerror(lerror) => lerror.byte_size(),
+ };
+
+ // size + type + tag + message size
+ (mem::size_of::<u32>() + mem::size_of::<u8>() + mem::size_of::<u16>()) as u32 + msg_size
+ }
+
+ fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+ self.byte_size().encode(writer)?;
+
+ let ty = match self.msg {
+ Rmessage::Version(_) => RVERSION,
+ Rmessage::Flush => RFLUSH,
+ Rmessage::Walk(_) => RWALK,
+ Rmessage::Read(_) => RREAD,
+ Rmessage::Write(_) => RWRITE,
+ Rmessage::Clunk => RCLUNK,
+ Rmessage::Remove => RREMOVE,
+ Rmessage::Attach(_) => RATTACH,
+ Rmessage::Auth(_) => RAUTH,
+ Rmessage::Statfs(_) => RSTATFS,
+ Rmessage::Lopen(_) => RLOPEN,
+ Rmessage::Lcreate(_) => RLCREATE,
+ Rmessage::Symlink(_) => RSYMLINK,
+ Rmessage::Mknod(_) => RMKNOD,
+ Rmessage::Rename => RRENAME,
+ Rmessage::Readlink(_) => RREADLINK,
+ Rmessage::GetAttr(_) => RGETATTR,
+ Rmessage::SetAttr => RSETATTR,
+ Rmessage::XattrWalk(_) => RXATTRWALK,
+ Rmessage::XattrCreate => RXATTRCREATE,
+ Rmessage::Readdir(_) => RREADDIR,
+ Rmessage::Fsync => RFSYNC,
+ Rmessage::Lock(_) => RLOCK,
+ Rmessage::GetLock(_) => RGETLOCK,
+ Rmessage::Link => RLINK,
+ Rmessage::Mkdir(_) => RMKDIR,
+ Rmessage::RenameAt => RRENAMEAT,
+ Rmessage::UnlinkAt => RUNLINKAT,
+ Rmessage::Lerror(_) => RLERROR,
+ };
+
+ ty.encode(writer)?;
+ self.tag.encode(writer)?;
+
+ match &self.msg {
+ Rmessage::Version(version) => version.encode(writer),
+ Rmessage::Flush => Ok(()),
+ Rmessage::Walk(walk) => walk.encode(writer),
+ Rmessage::Read(read) => read.encode(writer),
+ Rmessage::Write(write) => write.encode(writer),
+ Rmessage::Clunk => Ok(()),
+ Rmessage::Remove => Ok(()),
+ Rmessage::Attach(attach) => attach.encode(writer),
+ Rmessage::Auth(auth) => auth.encode(writer),
+ Rmessage::Statfs(statfs) => statfs.encode(writer),
+ Rmessage::Lopen(lopen) => lopen.encode(writer),
+ Rmessage::Lcreate(lcreate) => lcreate.encode(writer),
+ Rmessage::Symlink(symlink) => symlink.encode(writer),
+ Rmessage::Mknod(mknod) => mknod.encode(writer),
+ Rmessage::Rename => Ok(()),
+ Rmessage::Readlink(readlink) => readlink.encode(writer),
+ Rmessage::GetAttr(getattr) => getattr.encode(writer),
+ Rmessage::SetAttr => Ok(()),
+ Rmessage::XattrWalk(xattrwalk) => xattrwalk.encode(writer),
+ Rmessage::XattrCreate => Ok(()),
+ Rmessage::Readdir(readdir) => readdir.encode(writer),
+ Rmessage::Fsync => Ok(()),
+ Rmessage::Lock(lock) => lock.encode(writer),
+ Rmessage::GetLock(getlock) => getlock.encode(writer),
+ Rmessage::Link => Ok(()),
+ Rmessage::Mkdir(mkdir) => mkdir.encode(writer),
+ Rmessage::RenameAt => Ok(()),
+ Rmessage::UnlinkAt => Ok(()),
+ Rmessage::Lerror(lerror) => lerror.encode(writer),
+ }
+ }
+
+ fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+ let byte_size: u32 = WireFormat::decode(reader)?;
+
+ // byte_size includes the size of byte_size so remove that from the
+ // expected length of the message.
+ let reader = &mut reader.take((byte_size - mem::size_of::<u32>() as u32) as u64);
+
+ let mut ty = [0u8];
+ reader.read_exact(&mut ty)?;
+
+ let tag: u16 = WireFormat::decode(reader)?;
+
+ let msg = match ty[0] {
+ RVERSION => Ok(Rmessage::Version(WireFormat::decode(reader)?)),
+ RFLUSH => Ok(Rmessage::Flush),
+ RWALK => Ok(Rmessage::Walk(WireFormat::decode(reader)?)),
+ RREAD => Ok(Rmessage::Read(WireFormat::decode(reader)?)),
+ RWRITE => Ok(Rmessage::Write(WireFormat::decode(reader)?)),
+ RCLUNK => Ok(Rmessage::Clunk),
+ RREMOVE => Ok(Rmessage::Remove),
+ RATTACH => Ok(Rmessage::Attach(WireFormat::decode(reader)?)),
+ RAUTH => Ok(Rmessage::Auth(WireFormat::decode(reader)?)),
+ RSTATFS => Ok(Rmessage::Statfs(WireFormat::decode(reader)?)),
+ RLOPEN => Ok(Rmessage::Lopen(WireFormat::decode(reader)?)),
+ RLCREATE => Ok(Rmessage::Lcreate(WireFormat::decode(reader)?)),
+ RSYMLINK => Ok(Rmessage::Symlink(WireFormat::decode(reader)?)),
+ RMKNOD => Ok(Rmessage::Mknod(WireFormat::decode(reader)?)),
+ RRENAME => Ok(Rmessage::Rename),
+ RREADLINK => Ok(Rmessage::Readlink(WireFormat::decode(reader)?)),
+ RGETATTR => Ok(Rmessage::GetAttr(WireFormat::decode(reader)?)),
+ RSETATTR => Ok(Rmessage::SetAttr),
+ RXATTRWALK => Ok(Rmessage::XattrWalk(WireFormat::decode(reader)?)),
+ RXATTRCREATE => Ok(Rmessage::XattrCreate),
+ RREADDIR => Ok(Rmessage::Readdir(WireFormat::decode(reader)?)),
+ RFSYNC => Ok(Rmessage::Fsync),
+ RLOCK => Ok(Rmessage::Lock(WireFormat::decode(reader)?)),
+ RGETLOCK => Ok(Rmessage::GetLock(WireFormat::decode(reader)?)),
+ RLINK => Ok(Rmessage::Link),
+ RMKDIR => Ok(Rmessage::Mkdir(WireFormat::decode(reader)?)),
+ RRENAMEAT => Ok(Rmessage::RenameAt),
+ RUNLINKAT => Ok(Rmessage::UnlinkAt),
+ RLERROR => Ok(Rmessage::Lerror(WireFormat::decode(reader)?)),
+ err => Err(io::Error::new(
+ ErrorKind::InvalidData,
+ format!("unknown message type {}", err),
+ )),
+ }?;
+
+ Ok(Rframe { tag, msg })
+ }
+}
+
+#[derive(Debug, Copy, Clone, P9WireFormat)]
+pub struct Qid {
+ pub ty: u8,
+ pub version: u32,
+ pub path: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Dirent {
+ pub qid: Qid,
+ pub offset: u64,
+ pub ty: u8,
+ pub name: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rversion {
+ pub msize: u32,
+ pub version: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rwalk {
+ pub wqids: Vec<Qid>,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rread {
+ pub data: Data,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rwrite {
+ pub count: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rauth {
+ pub aqid: Qid,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rattach {
+ pub qid: Qid,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rlerror {
+ pub ecode: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rstatfs {
+ pub ty: u32,
+ pub bsize: u32,
+ pub blocks: u64,
+ pub bfree: u64,
+ pub bavail: u64,
+ pub files: u64,
+ pub ffree: u64,
+ pub fsid: u64,
+ pub namelen: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rlopen {
+ pub qid: Qid,
+ pub iounit: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rlcreate {
+ pub qid: Qid,
+ pub iounit: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rsymlink {
+ pub qid: Qid,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rmknod {
+ pub qid: Qid,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rreadlink {
+ pub target: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rgetattr {
+ pub valid: u64,
+ pub qid: Qid,
+ pub mode: u32,
+ pub uid: u32,
+ pub gid: u32,
+ pub nlink: u64,
+ pub rdev: u64,
+ pub size: u64,
+ pub blksize: u64,
+ pub blocks: u64,
+ pub atime_sec: u64,
+ pub atime_nsec: u64,
+ pub mtime_sec: u64,
+ pub mtime_nsec: u64,
+ pub ctime_sec: u64,
+ pub ctime_nsec: u64,
+ pub btime_sec: u64,
+ pub btime_nsec: u64,
+ pub gen: u64,
+ pub data_version: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rxattrwalk {
+ pub size: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rreaddir {
+ pub data: Data,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rlock {
+ pub status: u8,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rgetlock {
+ pub ty: u8,
+ pub start: u64,
+ pub length: u64,
+ pub proc_id: u32,
+ pub client_id: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rmkdir {
+ pub qid: Qid,
+}
diff --git a/p9/src/protocol/mod.rs b/p9/src/protocol/mod.rs
new file mode 100644
index 0000000..9c278ee
--- /dev/null
+++ b/p9/src/protocol/mod.rs
@@ -0,0 +1,9 @@
+// Copyright 2018 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.
+
+mod messages;
+mod wire_format;
+
+pub use self::messages::*;
+pub use self::wire_format::{Data, WireFormat};
diff --git a/p9/src/protocol/wire_format.rs b/p9/src/protocol/wire_format.rs
new file mode 100644
index 0000000..84408e2
--- /dev/null
+++ b/p9/src/protocol/wire_format.rs
@@ -0,0 +1,716 @@
+// Copyright 2018 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;
+use std::fmt;
+use std::io;
+use std::io::{ErrorKind, Read, Write};
+use std::mem;
+use std::ops::{Deref, DerefMut};
+use std::string::String;
+use std::vec::Vec;
+
+/// A type that can be encoded on the wire using the 9P protocol.
+pub trait WireFormat: std::marker::Sized {
+ /// Returns the number of bytes necessary to fully encode `self`.
+ fn byte_size(&self) -> u32;
+
+ /// Encodes `self` into `writer`.
+ fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()>;
+
+ /// Decodes `Self` from `reader`.
+ fn decode<R: Read>(reader: &mut R) -> io::Result<Self>;
+}
+
+// This doesn't really _need_ to be a macro but unfortunately there is no trait bound to
+// express "can be casted to another type", which means we can't write `T as u8` in a trait
+// based implementation. So instead we have this macro, which is implemented for all the
+// stable unsigned types with the added benefit of not being implemented for the signed
+// types which are not allowed by the protocol.
+macro_rules! uint_wire_format_impl {
+ ($Ty:ty) => {
+ impl WireFormat for $Ty {
+ fn byte_size(&self) -> u32 {
+ mem::size_of::<$Ty>() as u32
+ }
+
+ fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+ let mut buf = [0u8; mem::size_of::<$Ty>()];
+
+ // Encode the bytes into the buffer in little endian order.
+ for idx in 0..mem::size_of::<$Ty>() {
+ buf[idx] = (self >> (8 * idx)) as u8;
+ }
+
+ writer.write_all(&buf)
+ }
+
+ fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+ let mut buf = [0u8; mem::size_of::<$Ty>()];
+ reader.read_exact(&mut buf)?;
+
+ // Read bytes from the buffer in little endian order.
+ let mut result = 0;
+ for idx in 0..mem::size_of::<$Ty>() {
+ result |= (buf[idx] as $Ty) << (8 * idx);
+ }
+
+ Ok(result)
+ }
+ }
+ };
+}
+uint_wire_format_impl!(u8);
+uint_wire_format_impl!(u16);
+uint_wire_format_impl!(u32);
+uint_wire_format_impl!(u64);
+
+// The 9P protocol requires that strings are UTF-8 encoded. The wire format is a u16
+// count |N|, encoded in little endian, followed by |N| bytes of UTF-8 data.
+impl WireFormat for String {
+ fn byte_size(&self) -> u32 {
+ (mem::size_of::<u16>() + self.len()) as u32
+ }
+
+ fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+ if self.len() > std::u16::MAX as usize {
+ return Err(io::Error::new(
+ ErrorKind::InvalidInput,
+ "string is too long",
+ ));
+ }
+
+ (self.len() as u16).encode(writer)?;
+ writer.write_all(self.as_bytes())
+ }
+
+ fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+ let len: u16 = WireFormat::decode(reader)?;
+ let mut result = String::with_capacity(len as usize);
+ reader.take(len as u64).read_to_string(&mut result)?;
+ Ok(result)
+ }
+}
+
+// The wire format for repeated types is similar to that of strings: a little endian
+// encoded u16 |N|, followed by |N| instances of the given type.
+impl<T: WireFormat> WireFormat for Vec<T> {
+ fn byte_size(&self) -> u32 {
+ mem::size_of::<u16>() as u32 + self.iter().map(|elem| elem.byte_size()).sum::<u32>()
+ }
+
+ fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+ if self.len() > std::u16::MAX as usize {
+ return Err(io::Error::new(
+ ErrorKind::InvalidInput,
+ "too many elements in vector",
+ ));
+ }
+
+ (self.len() as u16).encode(writer)?;
+ for elem in self {
+ elem.encode(writer)?;
+ }
+
+ Ok(())
+ }
+
+ fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+ let len: u16 = WireFormat::decode(reader)?;
+ let mut result = Vec::with_capacity(len as usize);
+
+ for _ in 0..len {
+ result.push(WireFormat::decode(reader)?);
+ }
+
+ Ok(result)
+ }
+}
+
+/// A type that encodes an arbitrary number of bytes of data. Typically used for Rread
+/// Twrite messages. This differs from a `Vec<u8>` in that it encodes the number of bytes
+/// using a `u32` instead of a `u16`.
+#[derive(PartialEq)]
+pub struct Data(pub Vec<u8>);
+
+// The maximum length of a data buffer that we support. In practice the server's max message
+// size should prevent us from reading too much data so this check is mainly to ensure a
+// malicious client cannot trick us into allocating massive amounts of memory.
+const MAX_DATA_LENGTH: u32 = 32 * 1024 * 1024;
+
+impl fmt::Debug for Data {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // There may be a lot of data and we don't want to spew it all out in a trace. Instead
+ // just print out the number of bytes in the buffer.
+ write!(f, "Data({} bytes)", self.len())
+ }
+}
+
+// Implement Deref and DerefMut so that we don't have to use self.0 everywhere.
+impl Deref for Data {
+ type Target = Vec<u8>;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+impl DerefMut for Data {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+// Same as Vec<u8> except that it encodes the length as a u32 instead of a u16.
+impl WireFormat for Data {
+ fn byte_size(&self) -> u32 {
+ mem::size_of::<u32>() as u32 + self.iter().map(|elem| elem.byte_size()).sum::<u32>()
+ }
+
+ fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+ if self.len() > std::u32::MAX as usize {
+ return Err(io::Error::new(ErrorKind::InvalidInput, "data is too large"));
+ }
+ (self.len() as u32).encode(writer)?;
+ writer.write_all(self)
+ }
+
+ fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+ let len: u32 = WireFormat::decode(reader)?;
+ if len > MAX_DATA_LENGTH {
+ return Err(io::Error::new(
+ ErrorKind::InvalidData,
+ format!("data length ({} bytes) is too large", len),
+ ));
+ }
+
+ let mut buf = Vec::with_capacity(len as usize);
+ reader.take(len as u64).read_to_end(&mut buf)?;
+
+ if buf.len() == len as usize {
+ Ok(Data(buf))
+ } else {
+ Err(io::Error::new(
+ ErrorKind::UnexpectedEof,
+ format!(
+ "unexpected end of data: want: {} bytes, got: {} bytes",
+ len,
+ buf.len()
+ ),
+ ))
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::io::Cursor;
+ use std::mem;
+ use std::string::String;
+ use wire_format_derive::P9WireFormat;
+
+ #[test]
+ fn integer_byte_size() {
+ assert_eq!(1, 0u8.byte_size());
+ assert_eq!(2, 0u16.byte_size());
+ assert_eq!(4, 0u32.byte_size());
+ assert_eq!(8, 0u64.byte_size());
+ }
+
+ #[test]
+ fn integer_decode() {
+ let buf: [u8; 8] = [0xef, 0xbe, 0xad, 0xde, 0x0d, 0xf0, 0xad, 0x8b];
+
+ assert_eq!(
+ 0xef as u8,
+ WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+ );
+ assert_eq!(
+ 0xbeef as u16,
+ WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+ );
+ assert_eq!(
+ 0xdeadbeef as u32,
+ WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+ );
+ assert_eq!(
+ 0x8badf00d_deadbeef as u64,
+ WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+ );
+ }
+
+ #[test]
+ fn integer_encode() {
+ let value: u64 = 0x8badf00d_deadbeef;
+ let expected: [u8; 8] = [0xef, 0xbe, 0xad, 0xde, 0x0d, 0xf0, 0xad, 0x8b];
+
+ let mut buf = Vec::with_capacity(8);
+ buf.resize(8, 0);
+
+ (value as u8).encode(&mut Cursor::new(&mut *buf)).unwrap();
+ assert_eq!(expected[0..1], buf[0..1]);
+
+ (value as u16).encode(&mut Cursor::new(&mut *buf)).unwrap();
+ assert_eq!(expected[0..2], buf[0..2]);
+
+ (value as u32).encode(&mut Cursor::new(&mut *buf)).unwrap();
+ assert_eq!(expected[0..4], buf[0..4]);
+
+ value.encode(&mut Cursor::new(&mut *buf)).unwrap();
+ assert_eq!(expected[0..8], buf[0..8]);
+ }
+
+ #[test]
+ fn string_byte_size() {
+ let values = [
+ String::from("Google Video"),
+ String::from("网页 图片 资讯更多 »"),
+ String::from("Παγκόσμιος Ιστός"),
+ String::from("Поиск страниц на русском"),
+ String::from("전체서비스"),
+ ];
+
+ let exp = values
+ .iter()
+ .map(|v| (mem::size_of::<u16>() + v.len()) as u32);
+
+ for (value, expected) in values.iter().zip(exp) {
+ assert_eq!(expected, value.byte_size());
+ }
+ }
+
+ #[test]
+ fn zero_length_string() {
+ let s = String::from("");
+ assert_eq!(s.byte_size(), mem::size_of::<u16>() as u32);
+
+ let mut buf = [0xffu8; 4];
+
+ s.encode(&mut Cursor::new(&mut buf[..]))
+ .expect("failed to encode empty string");
+ assert_eq!(&[0, 0, 0xff, 0xff], &buf);
+
+ assert_eq!(
+ s,
+ <String as WireFormat>::decode(&mut Cursor::new(&[0, 0, 0x61, 0x61][..]))
+ .expect("failed to decode empty string")
+ );
+ }
+
+ #[test]
+ fn string_encode() {
+ let values = [
+ String::from("Google Video"),
+ String::from("网页 图片 资讯更多 »"),
+ String::from("Παγκόσμιος Ιστός"),
+ String::from("Поиск страниц на русском"),
+ String::from("전체서비스"),
+ ];
+
+ let expected = values.iter().map(|v| {
+ let len = v.as_bytes().len();
+ let mut buf = Vec::with_capacity(len + mem::size_of::<u16>());
+
+ buf.push(len as u8);
+ buf.push((len >> 8) as u8);
+
+ buf.extend_from_slice(v.as_bytes());
+
+ buf
+ });
+
+ for (val, exp) in values.iter().zip(expected) {
+ let mut buf = Vec::with_capacity(exp.len());
+ buf.resize(exp.len(), 0);
+
+ WireFormat::encode(val, &mut Cursor::new(&mut *buf)).unwrap();
+ assert_eq!(exp, buf);
+ }
+ }
+
+ #[test]
+ fn string_decode() {
+ assert_eq!(
+ String::from("Google Video"),
+ <String as WireFormat>::decode(&mut Cursor::new(
+ &[
+ 0x0c, 0x00, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x56, 0x69, 0x64, 0x65,
+ 0x6F,
+ ][..]
+ ))
+ .unwrap()
+ );
+ assert_eq!(
+ String::from("网页 图片 资讯更多 »"),
+ <String as WireFormat>::decode(&mut Cursor::new(
+ &[
+ 0x1d, 0x00, 0xE7, 0xBD, 0x91, 0xE9, 0xA1, 0xB5, 0x20, 0xE5, 0x9B, 0xBE, 0xE7,
+ 0x89, 0x87, 0x20, 0xE8, 0xB5, 0x84, 0xE8, 0xAE, 0xAF, 0xE6, 0x9B, 0xB4, 0xE5,
+ 0xA4, 0x9A, 0x20, 0xC2, 0xBB,
+ ][..]
+ ))
+ .unwrap()
+ );
+ assert_eq!(
+ String::from("Παγκόσμιος Ιστός"),
+ <String as WireFormat>::decode(&mut Cursor::new(
+ &[
+ 0x1f, 0x00, 0xCE, 0xA0, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBA, 0xCF, 0x8C, 0xCF,
+ 0x83, 0xCE, 0xBC, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x82, 0x20, 0xCE, 0x99, 0xCF,
+ 0x83, 0xCF, 0x84, 0xCF, 0x8C, 0xCF, 0x82,
+ ][..]
+ ))
+ .unwrap()
+ );
+ assert_eq!(
+ String::from("Поиск страниц на русском"),
+ <String as WireFormat>::decode(&mut Cursor::new(
+ &[
+ 0x2d, 0x00, 0xD0, 0x9F, 0xD0, 0xBE, 0xD0, 0xB8, 0xD1, 0x81, 0xD0, 0xBA, 0x20,
+ 0xD1, 0x81, 0xD1, 0x82, 0xD1, 0x80, 0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 0xB8, 0xD1,
+ 0x86, 0x20, 0xD0, 0xBD, 0xD0, 0xB0, 0x20, 0xD1, 0x80, 0xD1, 0x83, 0xD1, 0x81,
+ 0xD1, 0x81, 0xD0, 0xBA, 0xD0, 0xBE, 0xD0, 0xBC,
+ ][..]
+ ))
+ .unwrap()
+ );
+ assert_eq!(
+ String::from("전체서비스"),
+ <String as WireFormat>::decode(&mut Cursor::new(
+ &[
+ 0x0f, 0x00, 0xEC, 0xA0, 0x84, 0xEC, 0xB2, 0xB4, 0xEC, 0x84, 0x9C, 0xEB, 0xB9,
+ 0x84, 0xEC, 0x8A, 0xA4,
+ ][..]
+ ))
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn invalid_string_decode() {
+ let _ = <String as WireFormat>::decode(&mut Cursor::new(&[
+ 0x06, 0x00, 0xed, 0xa0, 0x80, 0xed, 0xbf, 0xbf,
+ ]))
+ .expect_err("surrogate code point");
+
+ let _ = <String as WireFormat>::decode(&mut Cursor::new(&[
+ 0x05, 0x00, 0xf8, 0x80, 0x80, 0x80, 0xbf,
+ ]))
+ .expect_err("overlong sequence");
+
+ let _ =
+ <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0xf4, 0x90, 0x80, 0x80]))
+ .expect_err("out of range");
+
+ let _ =
+ <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0x63, 0x61, 0x66, 0xe9]))
+ .expect_err("ISO-8859-1");
+
+ let _ =
+ <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0xb0, 0xa1, 0xb0, 0xa2]))
+ .expect_err("EUC-KR");
+ }
+
+ #[test]
+ fn vector_encode() {
+ let values: Vec<u32> = vec![291, 18_916, 2_497, 22, 797_162, 2_119_732, 3_213_929_716];
+ let mut expected: Vec<u8> =
+ Vec::with_capacity(values.len() * mem::size_of::<u32>() + mem::size_of::<u16>());
+ expected.push(values.len() as u8);
+ expected.push((values.len() >> 8) as u8);
+
+ const MASK: u32 = 0xff;
+ for val in &values {
+ expected.push((val & MASK) as u8);
+ expected.push(((val >> 8) & MASK) as u8);
+ expected.push(((val >> 16) & MASK) as u8);
+ expected.push(((val >> 24) & MASK) as u8);
+ }
+
+ let mut actual: Vec<u8> = Vec::with_capacity(expected.len());
+ actual.resize(expected.len(), 0);
+
+ WireFormat::encode(&values, &mut Cursor::new(&mut *actual))
+ .expect("failed to encode vector");
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn vector_decode() {
+ let expected: Vec<u32> = vec![
+ 2_498,
+ 24,
+ 897,
+ 4_097_789_579,
+ 8_498_119,
+ 684_279,
+ 961_189_198,
+ 7,
+ ];
+ let mut input: Vec<u8> =
+ Vec::with_capacity(expected.len() * mem::size_of::<u32>() + mem::size_of::<u16>());
+ input.push(expected.len() as u8);
+ input.push((expected.len() >> 8) as u8);
+
+ const MASK: u32 = 0xff;
+ for val in &expected {
+ input.push((val & MASK) as u8);
+ input.push(((val >> 8) & MASK) as u8);
+ input.push(((val >> 16) & MASK) as u8);
+ input.push(((val >> 24) & MASK) as u8);
+ }
+
+ assert_eq!(
+ expected,
+ <Vec<u32> as WireFormat>::decode(&mut Cursor::new(&*input))
+ .expect("failed to decode vector")
+ );
+ }
+
+ #[test]
+ fn data_encode() {
+ let values = Data(vec![169, 155, 79, 67, 182, 199, 25, 73, 129, 200]);
+ let mut expected: Vec<u8> =
+ Vec::with_capacity(values.len() * mem::size_of::<u8>() + mem::size_of::<u32>());
+ expected.push(values.len() as u8);
+ expected.push((values.len() >> 8) as u8);
+ expected.push((values.len() >> 16) as u8);
+ expected.push((values.len() >> 24) as u8);
+ expected.extend_from_slice(&values);
+
+ let mut actual: Vec<u8> = Vec::with_capacity(expected.len());
+ actual.resize(expected.len(), 0);
+
+ WireFormat::encode(&values, &mut Cursor::new(&mut *actual))
+ .expect("failed to encode datar");
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn data_decode() {
+ let expected = Data(vec![219, 15, 8, 155, 194, 129, 79, 91, 46, 53, 173]);
+ let mut input: Vec<u8> =
+ Vec::with_capacity(expected.len() * mem::size_of::<u8>() + mem::size_of::<u32>());
+ input.push(expected.len() as u8);
+ input.push((expected.len() >> 8) as u8);
+ input.push((expected.len() >> 16) as u8);
+ input.push((expected.len() >> 24) as u8);
+ input.extend_from_slice(&expected);
+
+ assert_eq!(
+ expected,
+ <Data as WireFormat>::decode(&mut Cursor::new(&mut *input))
+ .expect("failed to decode data")
+ );
+ }
+
+ #[test]
+ fn error_cases() {
+ // string is too long.
+ let mut foo = String::with_capacity(std::u16::MAX as usize);
+ while foo.len() < std::u16::MAX as usize {
+ foo.push_str("long");
+ }
+ foo.push_str("!");
+
+ let count = foo.len() + mem::size_of::<u16>();
+ let mut buf = Vec::with_capacity(count);
+ buf.resize(count, 0);
+
+ foo.encode(&mut Cursor::new(&mut *buf))
+ .expect_err("long string");
+
+ // vector is too long.
+ let mut bar: Vec<u32> = Vec::with_capacity(std::u16::MAX as usize);
+ while bar.len() < std::u16::MAX as usize {
+ bar.push(0x8bad_f00d);
+ }
+ bar.push(0x00ba_b10c);
+
+ let count = bar.len() * mem::size_of::<u32>();
+ let mut buf = Vec::with_capacity(count);
+ buf.resize(count, 0);
+
+ WireFormat::encode(&bar, &mut Cursor::new(&mut *buf)).expect_err("long vector");
+ }
+
+ #[derive(Debug, PartialEq, P9WireFormat)]
+ struct Item {
+ foo: u64,
+ bar: String,
+ baz: Vec<u16>,
+ buf: Data,
+ }
+
+ #[test]
+ fn struct_encode() {
+ let item = Item {
+ foo: 0xdead10cc_00bab10c,
+ bar: String::from("冻住,不许走!"),
+ baz: vec![359, 492, 8891],
+ buf: Data(vec![254, 129, 0, 62, 49, 172]),
+ };
+
+ let mut expected: Vec<u8> = vec![0x0c, 0xb1, 0xba, 0x00, 0xcc, 0x10, 0xad, 0xde];
+ let strlen = item.bar.len() as u16;
+ expected.push(strlen as u8);
+ expected.push((strlen >> 8) as u8);
+ expected.extend_from_slice(item.bar.as_bytes());
+
+ let veclen = item.baz.len() as u16;
+ expected.push(veclen as u8);
+ expected.push((veclen >> 8) as u8);
+ for val in &item.baz {
+ expected.push(*val as u8);
+ expected.push((val >> 8) as u8);
+ }
+
+ let buflen = item.buf.len() as u32;
+ expected.push(buflen as u8);
+ expected.push((buflen >> 8) as u8);
+ expected.push((buflen >> 16) as u8);
+ expected.push((buflen >> 24) as u8);
+ expected.extend_from_slice(&item.buf);
+
+ let mut actual = Vec::with_capacity(expected.len());
+ actual.resize(expected.len(), 0);
+
+ WireFormat::encode(&item, &mut Cursor::new(&mut *actual)).expect("failed to encode item");
+
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn struct_decode() {
+ let expected = Item {
+ foo: 0xface_b00c_0404_4b1d,
+ bar: String::from("Огонь по готовности!"),
+ baz: vec![20067, 32449, 549, 4972, 77, 1987],
+ buf: Data(vec![126, 236, 79, 59, 6, 159]),
+ };
+
+ let mut input: Vec<u8> = vec![0x1d, 0x4b, 0x04, 0x04, 0x0c, 0xb0, 0xce, 0xfa];
+ let strlen = expected.bar.len() as u16;
+ input.push(strlen as u8);
+ input.push((strlen >> 8) as u8);
+ input.extend_from_slice(expected.bar.as_bytes());
+
+ let veclen = expected.baz.len() as u16;
+ input.push(veclen as u8);
+ input.push((veclen >> 8) as u8);
+ for val in &expected.baz {
+ input.push(*val as u8);
+ input.push((val >> 8) as u8);
+ }
+
+ let buflen = expected.buf.len() as u32;
+ input.push(buflen as u8);
+ input.push((buflen >> 8) as u8);
+ input.push((buflen >> 16) as u8);
+ input.push((buflen >> 24) as u8);
+ input.extend_from_slice(&expected.buf);
+
+ let actual: Item =
+ WireFormat::decode(&mut Cursor::new(input)).expect("failed to decode item");
+
+ assert_eq!(expected, actual);
+ }
+
+ #[derive(Debug, PartialEq, P9WireFormat)]
+ struct Nested {
+ item: Item,
+ val: Vec<u64>,
+ }
+
+ fn build_encoded_buffer(value: &Nested) -> Vec<u8> {
+ let mut result: Vec<u8> = Vec::new();
+
+ // encode foo
+ result.push(value.item.foo as u8);
+ result.push((value.item.foo >> 8) as u8);
+ result.push((value.item.foo >> 16) as u8);
+ result.push((value.item.foo >> 24) as u8);
+ result.push((value.item.foo >> 32) as u8);
+ result.push((value.item.foo >> 40) as u8);
+ result.push((value.item.foo >> 48) as u8);
+ result.push((value.item.foo >> 56) as u8);
+
+ // encode bar
+ result.push(value.item.bar.len() as u8);
+ result.push((value.item.bar.len() >> 8) as u8);
+ result.extend_from_slice(value.item.bar.as_bytes());
+
+ // encode baz
+ result.push(value.item.baz.len() as u8);
+ result.push((value.item.baz.len() >> 8) as u8);
+ for val in &value.item.baz {
+ result.push((val & 0xffu16) as u8);
+ result.push(((val >> 8) & 0xffu16) as u8);
+ }
+
+ // encode buf
+ result.push(value.item.buf.len() as u8);
+ result.push((value.item.buf.len() >> 8) as u8);
+ result.push((value.item.buf.len() >> 16) as u8);
+ result.push((value.item.buf.len() >> 24) as u8);
+ result.extend_from_slice(&value.item.buf);
+
+ // encode val
+ result.push(value.val.len() as u8);
+ result.push((value.val.len() >> 8) as u8);
+ for val in &value.val {
+ result.push(*val as u8);
+ result.push((val >> 8) as u8);
+ result.push((val >> 16) as u8);
+ result.push((val >> 24) as u8);
+ result.push((val >> 32) as u8);
+ result.push((val >> 40) as u8);
+ result.push((val >> 48) as u8);
+ result.push((val >> 56) as u8);
+ }
+
+ result
+ }
+
+ #[test]
+ fn nested_encode() {
+ let value = Nested {
+ item: Item {
+ foo: 0xcafe_d00d_8bad_f00d,
+ bar: String::from("龍が我が敵を喰らう!"),
+ baz: vec![2679, 55_919, 44, 38_819, 792],
+ buf: Data(vec![129, 55, 200, 93, 7, 68]),
+ },
+ val: vec![1954978, 59, 4519, 15679],
+ };
+
+ let expected = build_encoded_buffer(&value);
+
+ let mut actual = Vec::with_capacity(expected.len());
+ actual.resize(expected.len(), 0);
+
+ WireFormat::encode(&value, &mut Cursor::new(&mut *actual)).expect("failed to encode value");
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn nested_decode() {
+ let expected = Nested {
+ item: Item {
+ foo: 0x0ff1ce,
+ bar: String::from("龍神の剣を喰らえ!"),
+ baz: vec![21687, 159, 55, 9217, 192],
+ buf: Data(vec![189, 22, 7, 59, 235]),
+ },
+ val: vec![15679, 8619196, 319746, 123957, 77, 0, 492],
+ };
+
+ let input = build_encoded_buffer(&expected);
+
+ assert_eq!(
+ expected,
+ <Nested as WireFormat>::decode(&mut Cursor::new(&*input))
+ .expect("failed to decode value")
+ );
+ }
+}
diff --git a/p9/src/server.rs b/p9/src/server.rs
new file mode 100644
index 0000000..b8a026f
--- /dev/null
+++ b/p9/src/server.rs
@@ -0,0 +1,949 @@
+// Copyright 2018 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 libc;
+
+use std::cmp::min;
+use std::collections::{btree_map, BTreeMap};
+use std::ffi::CString;
+use std::fs;
+use std::io::{self, Cursor, Read, Write};
+use std::mem;
+use std::os::linux::fs::MetadataExt;
+use std::os::unix::fs::{DirBuilderExt, FileExt, OpenOptionsExt};
+use std::os::unix::io::AsRawFd;
+use std::path::{Component, Path, PathBuf};
+
+use crate::protocol::*;
+
+// Tlopen and Tlcreate flags. Taken from "include/net/9p/9p.h" in the linux tree.
+const _P9_RDONLY: u32 = 0o00000000;
+const P9_WRONLY: u32 = 0o00000001;
+const P9_RDWR: u32 = 0o00000002;
+const P9_NOACCESS: u32 = 0o00000003;
+const P9_CREATE: u32 = 0o00000100;
+const P9_EXCL: u32 = 0o00000200;
+const P9_NOCTTY: u32 = 0o00000400;
+const P9_TRUNC: u32 = 0o00001000;
+const P9_APPEND: u32 = 0o00002000;
+const P9_NONBLOCK: u32 = 0o00004000;
+const P9_DSYNC: u32 = 0o00010000;
+const P9_FASYNC: u32 = 0o00020000;
+const P9_DIRECT: u32 = 0o00040000;
+const P9_LARGEFILE: u32 = 0o00100000;
+const P9_DIRECTORY: u32 = 0o00200000;
+const P9_NOFOLLOW: u32 = 0o00400000;
+const P9_NOATIME: u32 = 0o01000000;
+const _P9_CLOEXEC: u32 = 0o02000000;
+const P9_SYNC: u32 = 0o04000000;
+
+// Mapping from 9P flags to libc flags.
+const MAPPED_FLAGS: [(u32, i32); 10] = [
+ (P9_NOCTTY, libc::O_NOCTTY),
+ (P9_NONBLOCK, libc::O_NONBLOCK),
+ (P9_DSYNC, libc::O_DSYNC),
+ (P9_FASYNC, 0), // Unsupported
+ (P9_DIRECT, libc::O_DIRECT),
+ (P9_LARGEFILE, libc::O_LARGEFILE),
+ (P9_DIRECTORY, libc::O_DIRECTORY),
+ (P9_NOFOLLOW, libc::O_NOFOLLOW),
+ (P9_NOATIME, libc::O_NOATIME),
+ (P9_SYNC, libc::O_SYNC),
+];
+
+// 9P Qid types. Taken from "include/net/9p/9p.h" in the linux tree.
+const P9_QTDIR: u8 = 0x80;
+const _P9_QTAPPEND: u8 = 0x40;
+const _P9_QTEXCL: u8 = 0x20;
+const _P9_QTMOUNT: u8 = 0x10;
+const _P9_QTAUTH: u8 = 0x08;
+const _P9_QTTMP: u8 = 0x04;
+const _P9_QTSYMLINK: u8 = 0x02;
+const _P9_QTLINK: u8 = 0x01;
+const P9_QTFILE: u8 = 0x00;
+
+// Bitmask values for the getattr request.
+const _P9_GETATTR_MODE: u64 = 0x00000001;
+const _P9_GETATTR_NLINK: u64 = 0x00000002;
+const _P9_GETATTR_UID: u64 = 0x00000004;
+const _P9_GETATTR_GID: u64 = 0x00000008;
+const _P9_GETATTR_RDEV: u64 = 0x00000010;
+const _P9_GETATTR_ATIME: u64 = 0x00000020;
+const _P9_GETATTR_MTIME: u64 = 0x00000040;
+const _P9_GETATTR_CTIME: u64 = 0x00000080;
+const _P9_GETATTR_INO: u64 = 0x00000100;
+const _P9_GETATTR_SIZE: u64 = 0x00000200;
+const _P9_GETATTR_BLOCKS: u64 = 0x00000400;
+
+const _P9_GETATTR_BTIME: u64 = 0x00000800;
+const _P9_GETATTR_GEN: u64 = 0x00001000;
+const _P9_GETATTR_DATA_VERSION: u64 = 0x00002000;
+
+const P9_GETATTR_BASIC: u64 = 0x000007ff; /* Mask for fields up to BLOCKS */
+const _P9_GETATTR_ALL: u64 = 0x00003fff; /* Mask for All fields above */
+
+// Bitmask values for the setattr request.
+const P9_SETATTR_MODE: u32 = 0x00000001;
+const P9_SETATTR_UID: u32 = 0x00000002;
+const P9_SETATTR_GID: u32 = 0x00000004;
+const P9_SETATTR_SIZE: u32 = 0x00000008;
+const P9_SETATTR_ATIME: u32 = 0x00000010;
+const P9_SETATTR_MTIME: u32 = 0x00000020;
+const P9_SETATTR_CTIME: u32 = 0x00000040;
+const P9_SETATTR_ATIME_SET: u32 = 0x00000080;
+const P9_SETATTR_MTIME_SET: u32 = 0x00000100;
+
+// Minimum and maximum message size that we'll expect from the client.
+const MIN_MESSAGE_SIZE: u32 = 256;
+const MAX_MESSAGE_SIZE: u32 = ::std::u16::MAX as u32;
+
+// Represents state that the server is holding on behalf of a client. Fids are somewhat like file
+// descriptors but are not restricted to open files and directories. Fids are identified by a unique
+// 32-bit number chosen by the client. Most messages sent by clients include a fid on which to
+// operate. The fid in a Tattach message represents the root of the file system tree that the client
+// is allowed to access. A client can create more fids by walking the directory tree from that fid.
+struct Fid {
+ path: Box<Path>,
+ metadata: fs::Metadata,
+ file: Option<fs::File>,
+ dirents: Option<Vec<Dirent>>,
+}
+
+fn metadata_to_qid(metadata: &fs::Metadata) -> Qid {
+ let ty = if metadata.is_dir() {
+ P9_QTDIR
+ } else if metadata.is_file() {
+ P9_QTFILE
+ } else {
+ // Unknown file type...
+ 0
+ };
+
+ Qid {
+ ty,
+ // TODO: deal with the 2038 problem before 2038
+ version: metadata.st_mtime() as u32,
+ path: metadata.st_ino(),
+ }
+}
+
+fn error_to_rmessage(err: io::Error) -> Rmessage {
+ let errno = if let Some(errno) = err.raw_os_error() {
+ errno
+ } else {
+ // Make a best-effort guess based on the kind.
+ match err.kind() {
+ io::ErrorKind::NotFound => libc::ENOENT,
+ io::ErrorKind::PermissionDenied => libc::EPERM,
+ io::ErrorKind::ConnectionRefused => libc::ECONNREFUSED,
+ io::ErrorKind::ConnectionReset => libc::ECONNRESET,
+ io::ErrorKind::ConnectionAborted => libc::ECONNABORTED,
+ io::ErrorKind::NotConnected => libc::ENOTCONN,
+ io::ErrorKind::AddrInUse => libc::EADDRINUSE,
+ io::ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL,
+ io::ErrorKind::BrokenPipe => libc::EPIPE,
+ io::ErrorKind::AlreadyExists => libc::EEXIST,
+ io::ErrorKind::WouldBlock => libc::EWOULDBLOCK,
+ io::ErrorKind::InvalidInput => libc::EINVAL,
+ io::ErrorKind::InvalidData => libc::EINVAL,
+ io::ErrorKind::TimedOut => libc::ETIMEDOUT,
+ io::ErrorKind::WriteZero => libc::EIO,
+ io::ErrorKind::Interrupted => libc::EINTR,
+ io::ErrorKind::Other => libc::EIO,
+ io::ErrorKind::UnexpectedEof => libc::EIO,
+ _ => libc::EIO,
+ }
+ };
+
+ Rmessage::Lerror(Rlerror {
+ ecode: errno as u32,
+ })
+}
+
+// Joins `path` to `buf`. If `path` is '..', removes the last component from `buf`
+// only if `buf` != `root` but does nothing if `buf` == `root`. Pushes `path` onto
+// `buf` if it is a normal path component.
+//
+// Returns an error if `path` is absolute, has more than one component, or contains
+// a '.' component.
+fn join_path<P: AsRef<Path>, R: AsRef<Path>>(
+ mut buf: PathBuf,
+ path: P,
+ root: R,
+) -> io::Result<PathBuf> {
+ let path = path.as_ref();
+ let root = root.as_ref();
+ debug_assert!(buf.starts_with(root));
+
+ if path.components().count() > 1 {
+ return Err(io::Error::from_raw_os_error(libc::EINVAL));
+ }
+
+ for component in path.components() {
+ match component {
+ // Prefix should only appear on windows systems.
+ Component::Prefix(_) => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
+ // Absolute paths are not allowed.
+ Component::RootDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
+ // '.' elements are not allowed.
+ Component::CurDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
+ Component::ParentDir => {
+ // We only remove the parent path if we are not already at the root of the
+ // file system.
+ if buf != root {
+ buf.pop();
+ }
+ }
+ Component::Normal(element) => buf.push(element),
+ }
+ }
+
+ Ok(buf)
+}
+
+pub struct Server {
+ root: Box<Path>,
+ msize: u32,
+ fids: BTreeMap<u32, Fid>,
+}
+
+impl Server {
+ pub fn new<P: AsRef<Path>>(root: P) -> Server {
+ Server {
+ root: root.as_ref().into(),
+ msize: MAX_MESSAGE_SIZE,
+ fids: BTreeMap::new(),
+ }
+ }
+
+ pub fn handle_message<R: Read, W: Write>(
+ &mut self,
+ reader: &mut R,
+ writer: &mut W,
+ ) -> io::Result<()> {
+ let request: Tframe = WireFormat::decode(&mut reader.take(self.msize as u64))?;
+
+ if cfg!(feature = "trace") {
+ println!("{:?}", &request);
+ }
+
+ let rmsg = match &request.msg {
+ Tmessage::Version(version) => self.version(version),
+ Tmessage::Flush(flush) => self.flush(flush),
+ Tmessage::Walk(walk) => self.walk(walk),
+ Tmessage::Read(read) => self.read(read),
+ Tmessage::Write(write) => self.write(write),
+ Tmessage::Clunk(clunk) => self.clunk(clunk),
+ Tmessage::Remove(remove) => self.remove(remove),
+ Tmessage::Attach(attach) => self.attach(attach),
+ Tmessage::Auth(auth) => self.auth(auth),
+ Tmessage::Statfs(statfs) => self.statfs(statfs),
+ Tmessage::Lopen(lopen) => self.lopen(lopen),
+ Tmessage::Lcreate(lcreate) => self.lcreate(lcreate),
+ Tmessage::Symlink(symlink) => self.symlink(symlink),
+ Tmessage::Mknod(mknod) => self.mknod(mknod),
+ Tmessage::Rename(rename) => self.rename(rename),
+ Tmessage::Readlink(readlink) => self.readlink(readlink),
+ Tmessage::GetAttr(get_attr) => self.get_attr(get_attr),
+ Tmessage::SetAttr(set_attr) => self.set_attr(set_attr),
+ Tmessage::XattrWalk(xattr_walk) => self.xattr_walk(xattr_walk),
+ Tmessage::XattrCreate(xattr_create) => self.xattr_create(xattr_create),
+ Tmessage::Readdir(readdir) => self.readdir(readdir),
+ Tmessage::Fsync(fsync) => self.fsync(fsync),
+ Tmessage::Lock(lock) => self.lock(lock),
+ Tmessage::GetLock(get_lock) => self.get_lock(get_lock),
+ Tmessage::Link(link) => self.link(link),
+ Tmessage::Mkdir(mkdir) => self.mkdir(mkdir),
+ Tmessage::RenameAt(rename_at) => self.rename_at(rename_at),
+ Tmessage::UnlinkAt(unlink_at) => self.unlink_at(unlink_at),
+ };
+
+ // Errors while handling requests are never fatal.
+ let response = Rframe {
+ tag: request.tag,
+ msg: rmsg.unwrap_or_else(error_to_rmessage),
+ };
+
+ if cfg!(feature = "trace") {
+ println!("{:?}", &response);
+ }
+
+ response.encode(writer)?;
+ writer.flush()
+ }
+
+ fn auth(&mut self, _auth: &Tauth) -> io::Result<Rmessage> {
+ // Returning an error for the auth message means that the server does not require
+ // authentication.
+ Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+ }
+
+ fn attach(&mut self, attach: &Tattach) -> io::Result<Rmessage> {
+ // TODO: Check attach parameters
+ match self.fids.entry(attach.fid) {
+ btree_map::Entry::Vacant(entry) => {
+ let fid = Fid {
+ path: self.root.to_path_buf().into_boxed_path(),
+ metadata: fs::metadata(&self.root)?,
+ file: None,
+ dirents: None,
+ };
+ let response = Rattach {
+ qid: metadata_to_qid(&fid.metadata),
+ };
+ entry.insert(fid);
+ Ok(Rmessage::Attach(response))
+ }
+ btree_map::Entry::Occupied(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
+ }
+ }
+
+ fn version(&mut self, version: &Tversion) -> io::Result<Rmessage> {
+ if version.msize < MIN_MESSAGE_SIZE {
+ return Err(io::Error::from_raw_os_error(libc::EINVAL));
+ }
+
+ // A Tversion request clunks all open fids and terminates any pending I/O.
+ self.fids.clear();
+ self.msize = min(MAX_MESSAGE_SIZE, version.msize);
+
+ Ok(Rmessage::Version(Rversion {
+ msize: self.msize,
+ version: if version.version == "9P2000.L" {
+ String::from("9P2000.L")
+ } else {
+ String::from("unknown")
+ },
+ }))
+ }
+
+ fn flush(&mut self, _flush: &Tflush) -> io::Result<Rmessage> {
+ // TODO: Since everything is synchronous we can't actually flush requests.
+ Ok(Rmessage::Flush)
+ }
+
+ fn do_walk(
+ &self,
+ wnames: &[String],
+ mut buf: PathBuf,
+ mds: &mut Vec<fs::Metadata>,
+ ) -> io::Result<PathBuf> {
+ for wname in wnames {
+ let name = Path::new(wname);
+ buf = join_path(buf, name, &*self.root)?;
+ mds.push(fs::metadata(&buf)?);
+ }
+
+ Ok(buf)
+ }
+
+ fn walk(&mut self, walk: &Twalk) -> io::Result<Rmessage> {
+ // `newfid` must not currently be in use unless it is the same as `fid`.
+ if walk.fid != walk.newfid && self.fids.contains_key(&walk.newfid) {
+ return Err(io::Error::from_raw_os_error(libc::EBADF));
+ }
+
+ // We need to walk the tree. First get the starting path.
+ let (buf, oldmd) = self
+ .fids
+ .get(&walk.fid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))
+ .map(|fid| (fid.path.to_path_buf(), fid.metadata.clone()))?;
+
+ // Now walk the tree and break on the first error, if any.
+ let mut mds = Vec::with_capacity(walk.wnames.len());
+ match self.do_walk(&walk.wnames, buf, &mut mds) {
+ Ok(buf) => {
+ // Store the new fid if the full walk succeeded.
+ if mds.len() == walk.wnames.len() {
+ // This could just be a duplication operation.
+ let md = if let Some(md) = mds.last() {
+ md.clone()
+ } else {
+ oldmd
+ };
+
+ self.fids.insert(
+ walk.newfid,
+ Fid {
+ path: buf.into_boxed_path(),
+ metadata: md,
+ file: None,
+ dirents: None,
+ },
+ );
+ }
+ }
+ Err(e) => {
+ // Only return an error if it occurred on the first component.
+ if mds.is_empty() {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(Rmessage::Walk(Rwalk {
+ wqids: mds.iter().map(metadata_to_qid).collect(),
+ }))
+ }
+
+ fn read(&mut self, read: &Tread) -> io::Result<Rmessage> {
+ // Thankfully, `read` cannot be used to read directories in 9P2000.L.
+ let file = self
+ .fids
+ .get_mut(&read.fid)
+ .and_then(|fid| fid.file.as_mut())
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+ // Use an empty Rread struct to figure out the overhead of the header.
+ let header_size = Rframe {
+ tag: 0,
+ msg: Rmessage::Read(Rread {
+ data: Data(Vec::new()),
+ }),
+ }
+ .byte_size();
+
+ let capacity = min(self.msize - header_size, read.count);
+ let mut buf = Data(Vec::with_capacity(capacity as usize));
+ buf.resize(capacity as usize, 0);
+
+ let count = file.read_at(&mut buf, read.offset)?;
+ buf.resize(count, 0);
+
+ Ok(Rmessage::Read(Rread { data: buf }))
+ }
+
+ fn write(&mut self, write: &Twrite) -> io::Result<Rmessage> {
+ let file = self
+ .fids
+ .get_mut(&write.fid)
+ .and_then(|fid| fid.file.as_mut())
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+ let count = file.write_at(&write.data, write.offset)?;
+ Ok(Rmessage::Write(Rwrite {
+ count: count as u32,
+ }))
+ }
+
+ fn clunk(&mut self, clunk: &Tclunk) -> io::Result<Rmessage> {
+ match self.fids.entry(clunk.fid) {
+ btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
+ btree_map::Entry::Occupied(entry) => {
+ entry.remove();
+ Ok(Rmessage::Clunk)
+ }
+ }
+ }
+
+ fn remove(&mut self, remove: &Tremove) -> io::Result<Rmessage> {
+ match self.fids.entry(remove.fid) {
+ btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
+ btree_map::Entry::Occupied(o) => {
+ let (_, fid) = o.remove_entry();
+
+ if fid.metadata.is_dir() {
+ fs::remove_dir(&fid.path)?;
+ } else {
+ fs::remove_file(&fid.path)?;
+ }
+
+ Ok(Rmessage::Remove)
+ }
+ }
+ }
+
+ fn statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rmessage> {
+ let fid = self
+ .fids
+ .get(&statfs.fid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+ let path = fid
+ .path
+ .to_str()
+ .and_then(|path| CString::new(path).ok())
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
+
+ // Safe because we are zero-initializing a C struct with only primitive
+ // data members.
+ let mut out: libc::statfs64 = unsafe { mem::zeroed() };
+
+ // Safe because we know that `path` is valid and we have already initialized `out`.
+ let ret = unsafe { libc::statfs64(path.as_ptr(), &mut out) };
+ if ret != 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ Ok(Rmessage::Statfs(Rstatfs {
+ ty: out.f_type as u32,
+ bsize: out.f_bsize as u32,
+ blocks: out.f_blocks,
+ bfree: out.f_bfree,
+ bavail: out.f_bavail,
+ files: out.f_files,
+ ffree: out.f_ffree,
+ fsid: 0, // No way to get the fields of a libc::fsid_t
+ namelen: out.f_namelen as u32,
+ }))
+ }
+
+ fn lopen(&mut self, lopen: &Tlopen) -> io::Result<Rmessage> {
+ let fid = self
+ .fids
+ .get_mut(&lopen.fid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+ // We always open files with O_CLOEXEC.
+ let mut custom_flags: i32 = libc::O_CLOEXEC;
+ for &(p9f, of) in &MAPPED_FLAGS {
+ if (lopen.flags & p9f) != 0 {
+ custom_flags |= of;
+ }
+ }
+
+ let file = fs::OpenOptions::new()
+ .read((lopen.flags & P9_NOACCESS) == 0 || (lopen.flags & P9_RDWR) != 0)
+ .write((lopen.flags & P9_WRONLY) != 0 || (lopen.flags & P9_RDWR) != 0)
+ .append((lopen.flags & P9_APPEND) != 0)
+ .truncate((lopen.flags & P9_TRUNC) != 0)
+ .create((lopen.flags & P9_CREATE) != 0)
+ .create_new((lopen.flags & P9_CREATE) != 0 && (lopen.flags & P9_EXCL) != 0)
+ .custom_flags(custom_flags)
+ .open(&fid.path)?;
+
+ fid.metadata = file.metadata()?;
+ fid.file = Some(file);
+
+ Ok(Rmessage::Lopen(Rlopen {
+ qid: metadata_to_qid(&fid.metadata),
+ iounit: 0,
+ }))
+ }
+
+ fn lcreate(&mut self, lcreate: &Tlcreate) -> io::Result<Rmessage> {
+ let fid = self
+ .fids
+ .get_mut(&lcreate.fid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+ if !fid.metadata.is_dir() {
+ return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
+ }
+
+ let name = Path::new(&lcreate.name);
+ let path = join_path(fid.path.to_path_buf(), name, &*self.root)?;
+
+ let mut custom_flags: i32 = libc::O_CLOEXEC;
+ for &(p9f, of) in &MAPPED_FLAGS {
+ if (lcreate.flags & p9f) != 0 {
+ custom_flags |= of;
+ }
+ }
+
+ let file = fs::OpenOptions::new()
+ .read(false)
+ .write(true)
+ .truncate(true)
+ .create(true)
+ .append((lcreate.flags & P9_APPEND) != 0)
+ .create_new((lcreate.flags & P9_EXCL) != 0)
+ .custom_flags(custom_flags)
+ .mode(lcreate.mode & 0o755)
+ .open(&path)?;
+
+ fid.metadata = file.metadata()?;
+ fid.file = Some(file);
+ fid.path = path.into_boxed_path();
+
+ Ok(Rmessage::Lcreate(Rlcreate {
+ qid: metadata_to_qid(&fid.metadata),
+ iounit: 0,
+ }))
+ }
+
+ fn symlink(&mut self, _symlink: &Tsymlink) -> io::Result<Rmessage> {
+ // symlinks are not allowed.
+ Err(io::Error::from_raw_os_error(libc::EACCES))
+ }
+
+ fn mknod(&mut self, _mknod: &Tmknod) -> io::Result<Rmessage> {
+ // No nodes either.
+ Err(io::Error::from_raw_os_error(libc::EACCES))
+ }
+
+ fn rename(&mut self, rename: &Trename) -> io::Result<Rmessage> {
+ let newname = Path::new(&rename.name);
+ let buf = self
+ .fids
+ .get(&rename.dfid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))
+ .map(|dfid| dfid.path.to_path_buf())?;
+ let newpath = join_path(buf, newname, &*self.root)?;
+
+ let fid = self
+ .fids
+ .get_mut(&rename.fid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
+
+ fs::rename(&fid.path, &newpath)?;
+
+ // TODO: figure out if the client expects |fid.path| to point to
+ // the renamed path.
+ fid.path = newpath.into_boxed_path();
+ Ok(Rmessage::Rename)
+ }
+
+ fn readlink(&mut self, _readlink: &Treadlink) -> io::Result<Rmessage> {
+ // symlinks are not allowed
+ Err(io::Error::from_raw_os_error(libc::EACCES))
+ }
+
+ fn get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rmessage> {
+ let fid = self
+ .fids
+ .get_mut(&get_attr.fid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+ // Refresh the metadata since we were explicitly asked for it.
+ fid.metadata = fs::metadata(&fid.path)?;
+
+ Ok(Rmessage::GetAttr(Rgetattr {
+ valid: P9_GETATTR_BASIC,
+ qid: metadata_to_qid(&fid.metadata),
+ mode: fid.metadata.st_mode(),
+ uid: fid.metadata.st_uid(),
+ gid: fid.metadata.st_gid(),
+ nlink: fid.metadata.st_nlink(),
+ rdev: fid.metadata.st_rdev(),
+ size: fid.metadata.st_size(),
+ blksize: fid.metadata.st_blksize(),
+ blocks: fid.metadata.st_blocks(),
+ atime_sec: fid.metadata.st_atime() as u64,
+ atime_nsec: fid.metadata.st_atime_nsec() as u64,
+ mtime_sec: fid.metadata.st_mtime() as u64,
+ mtime_nsec: fid.metadata.st_mtime_nsec() as u64,
+ ctime_sec: fid.metadata.st_ctime() as u64,
+ ctime_nsec: fid.metadata.st_ctime_nsec() as u64,
+ btime_sec: 0,
+ btime_nsec: 0,
+ gen: 0,
+ data_version: 0,
+ }))
+ }
+
+ fn set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<Rmessage> {
+ let blocked_ops = P9_SETATTR_MODE | P9_SETATTR_UID | P9_SETATTR_GID;
+ if set_attr.valid & blocked_ops != 0 {
+ return Err(io::Error::from_raw_os_error(libc::EPERM));
+ }
+
+ let fid = self
+ .fids
+ .get_mut(&set_attr.fid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+ let file = fs::OpenOptions::new().write(true).open(&fid.path)?;
+
+ if set_attr.valid & P9_SETATTR_SIZE != 0 {
+ file.set_len(set_attr.size)?;
+ }
+
+ if set_attr.valid & (P9_SETATTR_ATIME | P9_SETATTR_MTIME) != 0 {
+ let times = [
+ libc::timespec {
+ tv_sec: set_attr.atime_sec as _,
+ tv_nsec: if set_attr.valid & P9_SETATTR_ATIME == 0 {
+ libc::UTIME_OMIT
+ } else if set_attr.valid & P9_SETATTR_ATIME_SET == 0 {
+ libc::UTIME_NOW
+ } else {
+ set_attr.atime_nsec as _
+ },
+ },
+ libc::timespec {
+ tv_sec: set_attr.mtime_sec as _,
+ tv_nsec: if set_attr.valid & P9_SETATTR_MTIME == 0 {
+ libc::UTIME_OMIT
+ } else if set_attr.valid & P9_SETATTR_MTIME_SET == 0 {
+ libc::UTIME_NOW
+ } else {
+ set_attr.mtime_nsec as _
+ },
+ },
+ ];
+
+ // Safe because file is valid and we have initialized times fully.
+ let ret = unsafe { libc::futimens(file.as_raw_fd(), × as *const libc::timespec) };
+ if ret < 0 {
+ return Err(io::Error::last_os_error());
+ }
+ }
+
+ // The ctime would have been updated by any of the above operations so we only
+ // need to change it if it was the only option given.
+ if set_attr.valid & P9_SETATTR_CTIME != 0 && set_attr.valid & (!P9_SETATTR_CTIME) == 0 {
+ // Setting -1 as the uid and gid will not actually change anything but will
+ // still update the ctime.
+ let ret = unsafe {
+ libc::fchown(
+ file.as_raw_fd(),
+ libc::uid_t::max_value(),
+ libc::gid_t::max_value(),
+ )
+ };
+ if ret < 0 {
+ return Err(io::Error::last_os_error());
+ }
+ }
+
+ Ok(Rmessage::SetAttr)
+ }
+
+ fn xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result<Rmessage> {
+ Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+ }
+
+ fn xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<Rmessage> {
+ Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+ }
+
+ fn readdir(&mut self, readdir: &Treaddir) -> io::Result<Rmessage> {
+ let fid = self
+ .fids
+ .get_mut(&readdir.fid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+ if !fid.metadata.is_dir() {
+ return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
+ }
+
+ // The p9 client implementation in the kernel doesn't fully read all the contents
+ // of the directory. This means that if some application performs a getdents()
+ // call, followed by removing some files, followed by another getdents() call,
+ // the offset that we get from the kernel is completely meaningless. Instead
+ // we fully read the contents of the directory here and only re-read the directory
+ // if the offset we get from the client is 0. Any other offset is served from the
+ // directory entries in memory. This ensures consistency even if the directory
+ // changes in between Treaddir messages.
+ if readdir.offset == 0 {
+ let mut offset = 0;
+ let iter = fs::read_dir(&fid.path)?;
+ let dirents = iter.map(|item| -> io::Result<Dirent> {
+ let entry = item?;
+
+ let md = entry.metadata()?;
+ let qid = metadata_to_qid(&md);
+
+ let ty = if md.is_dir() {
+ libc::DT_DIR
+ } else if md.is_file() {
+ libc::DT_REG
+ } else {
+ libc::DT_UNKNOWN
+ };
+
+ let name = entry
+ .file_name()
+ .into_string()
+ .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?;
+
+ let mut out = Dirent {
+ qid,
+ offset: 0, // set below
+ ty,
+ name,
+ };
+
+ offset += out.byte_size() as u64;
+ out.offset = offset;
+
+ Ok(out)
+ });
+
+ // This is taking advantage of the fact that we can turn a Iterator of Result<T, E>
+ // into a Result<FromIterator<T>, E> since Result implements FromIterator<Result<T, E>>.
+ fid.dirents = Some(dirents.collect::<io::Result<Vec<Dirent>>>()?);
+ }
+
+ let mut entries = fid
+ .dirents
+ .as_ref()
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?
+ .iter()
+ .skip_while(|entry| entry.offset <= readdir.offset)
+ .peekable();
+
+ // Use an empty Rreaddir struct to figure out the maximum number of bytes that
+ // can be returned.
+ let header_size = Rframe {
+ tag: 0,
+ msg: Rmessage::Readdir(Rreaddir {
+ data: Data(Vec::new()),
+ }),
+ }
+ .byte_size();
+ let count = min(self.msize - header_size, readdir.count);
+ let mut cursor = Cursor::new(Vec::with_capacity(count as usize));
+
+ while let Some(entry) = entries.peek() {
+ let byte_size = entry.byte_size() as usize;
+
+ if cursor.get_ref().capacity() - cursor.get_ref().len() < byte_size {
+ // No more room in the buffer.
+ break;
+ }
+
+ // Safe because we just checked that the iterator contains at least one more item.
+ entries.next().unwrap().encode(&mut cursor)?;
+ }
+
+ Ok(Rmessage::Readdir(Rreaddir {
+ data: Data(cursor.into_inner()),
+ }))
+ }
+
+ fn fsync(&mut self, fsync: &Tfsync) -> io::Result<Rmessage> {
+ let file = self
+ .fids
+ .get(&fsync.fid)
+ .and_then(|fid| fid.file.as_ref())
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+ if fsync.datasync == 0 {
+ file.sync_all()?;
+ } else {
+ file.sync_data()?;
+ }
+ Ok(Rmessage::Fsync)
+ }
+
+ fn lock(&mut self, _lock: &Tlock) -> io::Result<Rmessage> {
+ // File locking is not supported.
+ Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+ }
+ fn get_lock(&mut self, _get_lock: &Tgetlock) -> io::Result<Rmessage> {
+ // File locking is not supported.
+ Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+ }
+
+ fn link(&mut self, link: &Tlink) -> io::Result<Rmessage> {
+ let newname = Path::new(&link.name);
+ let buf = self
+ .fids
+ .get(&link.dfid)
+ .map(|dfid| dfid.path.to_path_buf())
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+ let newpath = join_path(buf, newname, &*self.root)?;
+
+ let path = self
+ .fids
+ .get(&link.fid)
+ .map(|fid| &fid.path)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+ fs::hard_link(path, &newpath)?;
+ Ok(Rmessage::Link)
+ }
+
+ fn mkdir(&mut self, mkdir: &Tmkdir) -> io::Result<Rmessage> {
+ let fid = self
+ .fids
+ .get(&mkdir.dfid)
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+ let name = Path::new(&mkdir.name);
+ let newpath = join_path(fid.path.to_path_buf(), name, &*self.root)?;
+
+ fs::DirBuilder::new()
+ .recursive(false)
+ .mode(mkdir.mode & 0o755)
+ .create(&newpath)?;
+
+ Ok(Rmessage::Mkdir(Rmkdir {
+ qid: metadata_to_qid(&fs::metadata(&newpath)?),
+ }))
+ }
+
+ fn rename_at(&mut self, rename_at: &Trenameat) -> io::Result<Rmessage> {
+ let oldname = Path::new(&rename_at.oldname);
+ let oldbuf = self
+ .fids
+ .get(&rename_at.olddirfid)
+ .map(|dfid| dfid.path.to_path_buf())
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+ let oldpath = join_path(oldbuf, oldname, &*self.root)?;
+
+ let newname = Path::new(&rename_at.newname);
+ let newbuf = self
+ .fids
+ .get(&rename_at.newdirfid)
+ .map(|dfid| dfid.path.to_path_buf())
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+ let newpath = join_path(newbuf, newname, &*self.root)?;
+
+ fs::rename(&oldpath, &newpath)?;
+ Ok(Rmessage::RenameAt)
+ }
+
+ fn unlink_at(&mut self, unlink_at: &Tunlinkat) -> io::Result<Rmessage> {
+ let name = Path::new(&unlink_at.name);
+ let buf = self
+ .fids
+ .get(&unlink_at.dirfd)
+ .map(|fid| fid.path.to_path_buf())
+ .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+ let path = join_path(buf, name, &*self.root)?;
+
+ let md = fs::metadata(&path)?;
+ if md.is_dir() && (unlink_at.flags & (libc::AT_REMOVEDIR as u32)) == 0 {
+ return Err(io::Error::from_raw_os_error(libc::EISDIR));
+ }
+
+ if md.is_dir() {
+ fs::remove_dir(&path)?;
+ } else {
+ fs::remove_file(&path)?;
+ }
+
+ Ok(Rmessage::UnlinkAt)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ // Most of the server implementation is tested via integration tests.
+ use super::*;
+
+ #[test]
+ fn path_joins() {
+ let root = PathBuf::from("/a/b/c");
+ let path = PathBuf::from("/a/b/c/d/e/f");
+
+ assert_eq!(
+ &join_path(path.clone(), "nested", &root).expect("normal"),
+ Path::new("/a/b/c/d/e/f/nested")
+ );
+
+ let p1 = join_path(path.clone(), "..", &root).expect("parent 1");
+ assert_eq!(&p1, Path::new("/a/b/c/d/e/"));
+
+ let p2 = join_path(p1, "..", &root).expect("parent 2");
+ assert_eq!(&p2, Path::new("/a/b/c/d/"));
+
+ let p3 = join_path(p2, "..", &root).expect("parent 3");
+ assert_eq!(&p3, Path::new("/a/b/c/"));
+
+ let p4 = join_path(p3, "..", &root).expect("parent of root");
+ assert_eq!(&p4, Path::new("/a/b/c/"));
+ }
+
+ #[test]
+ fn invalid_joins() {
+ let root = PathBuf::from("/a");
+ let path = PathBuf::from("/a/b");
+
+ join_path(path.clone(), ".", &root).expect_err("current directory");
+ join_path(path.clone(), "c/d/e", &root).expect_err("too many components");
+ join_path(path.clone(), "/c/d/e", &root).expect_err("absolute path");
+ }
+}
diff --git a/p9/wire_format_derive/Cargo.toml b/p9/wire_format_derive/Cargo.toml
new file mode 100644
index 0000000..da6cc52
--- /dev/null
+++ b/p9/wire_format_derive/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "wire_format_derive"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+proc-macro2 = "=0.4"
+quote = "=0.6"
+syn = "=0.15"
+
+[lib]
+proc-macro = true
+path = "wire_format_derive.rs"
diff --git a/p9/wire_format_derive/wire_format_derive.rs b/p9/wire_format_derive/wire_format_derive.rs
new file mode 100644
index 0000000..6d369ae
--- /dev/null
+++ b/p9/wire_format_derive/wire_format_derive.rs
@@ -0,0 +1,296 @@
+// Copyright 2018 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.
+
+//! Derives a 9P wire format encoding for a struct by recursively calling
+//! `WireFormat::encode` or `WireFormat::decode` on the fields of the struct.
+//! This is only intended to be used from within the `p9` crate.
+
+#![recursion_limit = "256"]
+
+extern crate proc_macro;
+
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, quote_spanned};
+use syn::spanned::Spanned;
+use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident};
+
+/// The function that derives the actual implementation.
+#[proc_macro_derive(P9WireFormat)]
+pub fn p9_wire_format(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ p9_wire_format_inner(input).into()
+}
+
+fn p9_wire_format_inner(input: DeriveInput) -> TokenStream {
+ if !input.generics.params.is_empty() {
+ return quote! {
+ compile_error!("derive(P9WireFormat) does not support generic parameters");
+ };
+ }
+
+ let container = input.ident;
+
+ let byte_size_impl = byte_size_sum(&input.data);
+ let encode_impl = encode_wire_format(&input.data);
+ let decode_impl = decode_wire_format(&input.data, &container);
+
+ let scope = format!("wire_format_{}", container).to_lowercase();
+ let scope = Ident::new(&scope, Span::call_site());
+ quote! {
+ mod #scope {
+ use std::io;
+ use std::result::Result::Ok;
+
+ use super::#container;
+
+ use crate::protocol::WireFormat;
+
+ impl WireFormat for #container {
+ fn byte_size(&self) -> u32 {
+ #byte_size_impl
+ }
+
+ fn encode<W: io::Write>(&self, _writer: &mut W) -> io::Result<()> {
+ #encode_impl
+ }
+
+ fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> {
+ #decode_impl
+ }
+ }
+ }
+ }
+}
+
+// Generate code that recursively calls byte_size on every field in the struct.
+fn byte_size_sum(data: &Data) -> TokenStream {
+ if let Data::Struct(data) = data {
+ if let Fields::Named(fields) = &data.fields {
+ let fields = fields.named.iter().map(|f| {
+ let field = &f.ident;
+ let span = field.span();
+ quote_spanned! {span=>
+ WireFormat::byte_size(&self.#field)
+ }
+ });
+
+ quote! {
+ 0 #(+ #fields)*
+ }
+ } else {
+ unimplemented!();
+ }
+ } else {
+ unimplemented!();
+ }
+}
+
+// Generate code that recursively calls encode on every field in the struct.
+fn encode_wire_format(data: &Data) -> TokenStream {
+ if let Data::Struct(data) = data {
+ if let Fields::Named(fields) = &data.fields {
+ let fields = fields.named.iter().map(|f| {
+ let field = &f.ident;
+ let span = field.span();
+ quote_spanned! {span=>
+ WireFormat::encode(&self.#field, _writer)?;
+ }
+ });
+
+ quote! {
+ #(#fields)*
+
+ Ok(())
+ }
+ } else {
+ unimplemented!();
+ }
+ } else {
+ unimplemented!();
+ }
+}
+
+// Generate code that recursively calls decode on every field in the struct.
+fn decode_wire_format(data: &Data, container: &Ident) -> TokenStream {
+ if let Data::Struct(data) = data {
+ if let Fields::Named(fields) = &data.fields {
+ let values = fields.named.iter().map(|f| {
+ let field = &f.ident;
+ let span = field.span();
+ quote_spanned! {span=>
+ let #field = WireFormat::decode(_reader)?;
+ }
+ });
+
+ let members = fields.named.iter().map(|f| {
+ let field = &f.ident;
+ quote! {
+ #field: #field,
+ }
+ });
+
+ quote! {
+ #(#values)*
+
+ Ok(#container {
+ #(#members)*
+ })
+ }
+ } else {
+ unimplemented!();
+ }
+ } else {
+ unimplemented!();
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use syn::parse_quote;
+
+ #[test]
+ fn byte_size() {
+ let input: DeriveInput = parse_quote! {
+ struct Item {
+ ident: u32,
+ with_underscores: String,
+ other: u8,
+ }
+ };
+
+ let expected = quote! {
+ 0
+ + WireFormat::byte_size(&self.ident)
+ + WireFormat::byte_size(&self.with_underscores)
+ + WireFormat::byte_size(&self.other)
+ };
+
+ assert_eq!(byte_size_sum(&input.data).to_string(), expected.to_string());
+ }
+
+ #[test]
+ fn encode() {
+ let input: DeriveInput = parse_quote! {
+ struct Item {
+ ident: u32,
+ with_underscores: String,
+ other: u8,
+ }
+ };
+
+ let expected = quote! {
+ WireFormat::encode(&self.ident, _writer)?;
+ WireFormat::encode(&self.with_underscores, _writer)?;
+ WireFormat::encode(&self.other, _writer)?;
+ Ok(())
+ };
+
+ assert_eq!(
+ encode_wire_format(&input.data).to_string(),
+ expected.to_string(),
+ );
+ }
+
+ #[test]
+ fn decode() {
+ let input: DeriveInput = parse_quote! {
+ struct Item {
+ ident: u32,
+ with_underscores: String,
+ other: u8,
+ }
+ };
+
+ let container = Ident::new("Item", Span::call_site());
+ let expected = quote! {
+ let ident = WireFormat::decode(_reader)?;
+ let with_underscores = WireFormat::decode(_reader)?;
+ let other = WireFormat::decode(_reader)?;
+ Ok(Item {
+ ident: ident,
+ with_underscores: with_underscores,
+ other: other,
+ })
+ };
+
+ assert_eq!(
+ decode_wire_format(&input.data, &container).to_string(),
+ expected.to_string(),
+ );
+ }
+
+ #[test]
+ fn end_to_end() {
+ let input: DeriveInput = parse_quote! {
+ struct Niijima_先輩 {
+ a: u8,
+ b: u16,
+ c: u32,
+ d: u64,
+ e: String,
+ f: Vec<String>,
+ g: Nested,
+ }
+ };
+
+ let expected = quote! {
+ mod wire_format_niijima_先輩 {
+ use std::io;
+ use std::result::Result::Ok;
+
+ use super::Niijima_先輩;
+
+ use crate::protocol::WireFormat;
+
+ impl WireFormat for Niijima_先輩 {
+ fn byte_size(&self) -> u32 {
+ 0
+ + WireFormat::byte_size(&self.a)
+ + WireFormat::byte_size(&self.b)
+ + WireFormat::byte_size(&self.c)
+ + WireFormat::byte_size(&self.d)
+ + WireFormat::byte_size(&self.e)
+ + WireFormat::byte_size(&self.f)
+ + WireFormat::byte_size(&self.g)
+ }
+
+ fn encode<W: io::Write>(&self, _writer: &mut W) -> io::Result<()> {
+ WireFormat::encode(&self.a, _writer)?;
+ WireFormat::encode(&self.b, _writer)?;
+ WireFormat::encode(&self.c, _writer)?;
+ WireFormat::encode(&self.d, _writer)?;
+ WireFormat::encode(&self.e, _writer)?;
+ WireFormat::encode(&self.f, _writer)?;
+ WireFormat::encode(&self.g, _writer)?;
+ Ok(())
+ }
+ fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> {
+ let a = WireFormat::decode(_reader)?;
+ let b = WireFormat::decode(_reader)?;
+ let c = WireFormat::decode(_reader)?;
+ let d = WireFormat::decode(_reader)?;
+ let e = WireFormat::decode(_reader)?;
+ let f = WireFormat::decode(_reader)?;
+ let g = WireFormat::decode(_reader)?;
+ Ok(Niijima_先輩 {
+ a: a,
+ b: b,
+ c: c,
+ d: d,
+ e: e,
+ f: f,
+ g: g,
+ })
+ }
+ }
+ }
+ };
+
+ assert_eq!(
+ p9_wire_format_inner(input).to_string(),
+ expected.to_string(),
+ );
+ }
+}
diff --git a/protos/Cargo.toml b/protos/Cargo.toml
new file mode 100644
index 0000000..9fd0e5a
--- /dev/null
+++ b/protos/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "protos"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[features]
+plugin = ["kvm_sys"]
+trunks = []
+
+[dependencies]
+kvm_sys = { path = "../kvm_sys", optional = true }
+protobuf = "2.3"
+
+[build-dependencies]
+protoc-rust = "2.3"
diff --git a/protos/build.rs b/protos/build.rs
new file mode 100644
index 0000000..b510e54
--- /dev/null
+++ b/protos/build.rs
@@ -0,0 +1,112 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::env;
+use std::error::Error;
+use std::fs::{self, File};
+use std::io::Write;
+use std::path::{Path, PathBuf};
+
+type Result<T> = std::result::Result<T, Box<dyn Error>>;
+
+struct ExternalProto {
+ // Where to find protos during builds within cros_sdk. Relative to
+ // $SYSROOT environment variable set by emerge builds.
+ dir_relative_to_sysroot: &'static str,
+
+ // Where to find protos during "cargo build" in a local developer
+ // environment. Relative to the platform/crosvm/protos directory.
+ dir_relative_to_us: &'static str,
+
+ // *.proto file expected to exist in both of the above directories.
+ proto_file_name: &'static str,
+
+ // Code generated by proto compiler will be placed under
+ // protos::generated::$module_name.
+ module: &'static str,
+}
+
+// Rustfmt bug: https://github.com/rust-lang/rustfmt/issues/3498
+#[rustfmt::skip]
+static EXTERNAL_PROTOS: &[ExternalProto] = &[
+ #[cfg(feature = "trunks")]
+ ExternalProto {
+ dir_relative_to_sysroot: "usr/include/chromeos/dbus/trunks",
+ dir_relative_to_us: "../../../platform2/trunks",
+ proto_file_name: "interface.proto",
+ module: "trunks",
+ },
+];
+
+struct LocalProto {
+ // Corresponding to the input file src/$module.proto.
+ module: &'static str,
+}
+
+#[rustfmt::skip]
+static LOCAL_PROTOS: &[LocalProto] = &[
+ #[cfg(feature = "plugin")]
+ LocalProto { module: "plugin" },
+];
+
+fn main() -> Result<()> {
+ let out_dir = env::var("OUT_DIR")?;
+ let sysroot = env::var_os("SYSROOT");
+
+ // Write out a Rust module that imports the modules generated by protoc.
+ let generated = PathBuf::from(&out_dir).join("generated.rs");
+ let out = File::create(generated)?;
+
+ // Compile external protos.
+ for proto in EXTERNAL_PROTOS {
+ let dir = match &sysroot {
+ Some(dir) => PathBuf::from(dir).join(proto.dir_relative_to_sysroot),
+ None => PathBuf::from(proto.dir_relative_to_us),
+ };
+ let input_path = dir.join(proto.proto_file_name);
+ protoc(proto.module, input_path, &out)?;
+ }
+
+ // Compile protos from the local src directory.
+ for proto in LOCAL_PROTOS {
+ let input_path = format!("src/{}.proto", proto.module);
+ protoc(proto.module, input_path, &out)?;
+ }
+
+ Ok(())
+}
+
+// Compile a single proto file located at $input_path, placing the generated
+// code at $OUT_DIR/$module and emitting the right `pub mod $module` into the
+// output file.
+fn protoc<P: AsRef<Path>>(module: &str, input_path: P, mut out: &File) -> Result<()> {
+ let input_path = input_path.as_ref();
+ let input_dir = input_path.parent().unwrap();
+
+ // Place output in a subdirectory so that different protos with the same
+ // common filename (like interface.proto) do not conflict.
+ let out_dir = format!("{}/{}", env::var("OUT_DIR")?, module);
+ fs::create_dir_all(&out_dir)?;
+
+ // Invoke protobuf compiler.
+ protoc_rust::run(protoc_rust::Args {
+ out_dir: &out_dir,
+ includes: &[input_dir.as_os_str().to_str().unwrap()],
+ input: &[input_path.as_os_str().to_str().unwrap()],
+ ..Default::default()
+ })?;
+
+ // Write out a `mod` that refers to the generated module.
+ //
+ // The lint suppression is because protoc-rust emits
+ // #![cfg_attr(feature = "cargo-clippy", allow(clippy))]
+ // which still works but is deprecated in favor of tool attributes:
+ // #![allow(clippy::all)].
+ let file_stem = input_path.file_stem().unwrap().to_str().unwrap();
+ writeln!(out, "#[path = \"{}/{}.rs\"]", out_dir, file_stem)?;
+ writeln!(out, "#[allow(renamed_and_removed_lints)]")?;
+ writeln!(out, "pub mod {};", module)?;
+
+ Ok(())
+}
diff --git a/protos/src/lib.rs b/protos/src/lib.rs
new file mode 100644
index 0000000..3fdbdc0
--- /dev/null
+++ b/protos/src/lib.rs
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+mod generated {
+ include!(concat!(env!("OUT_DIR"), "/generated.rs"));
+}
+
+#[cfg(feature = "plugin")]
+pub mod plugin;
+
+#[cfg(feature = "trunks")]
+pub use generated::trunks;
diff --git a/protos/src/plugin.proto b/protos/src/plugin.proto
new file mode 100644
index 0000000..de76089
--- /dev/null
+++ b/protos/src/plugin.proto
@@ -0,0 +1,409 @@
+syntax = "proto3";
+
+// The protocol defined here is actually two sub-protocols, one protocol for control of the main
+// process (MainRequest/MainResponse), and one for control of each VCPU thread
+// (VcpuRequest/VcpuResponse). Each protocol works the same: the client creates a protobuf of either
+// a MainRequest or VcpuRequest, sends it encoded over the main socket or one of the vcpu sockets,
+// reads the the MainResponse or VcpuResponse over the same socket and decodes it as a protobuf. For
+// specific information on the purpose of each request, see the C API in crosvm.h. Most requests
+// here map 1:1 to a function in that API. Only the intricacies unique to the wire protocol are
+// commented on here.
+
+enum AddressSpace {
+ IOPORT = 0;
+ MMIO = 1;
+ }
+
+message CpuidEntry {
+ uint32 function = 1;
+ bool has_index = 3;
+ uint32 index = 4;
+ uint32 eax = 5;
+ uint32 ebx = 6;
+ uint32 ecx = 7;
+ uint32 edx = 8;
+}
+
+// A request made to the crosvm main process that affects the global aspects of the VM.
+message MainRequest {
+ // Every message under the Create namespace will instantiate an object with the given ID. The
+ // type of object is determined by the oneof constructor field.
+ message Create {
+ message IoEvent {
+ AddressSpace space = 1;
+ uint64 address = 2;
+ uint32 length = 3;
+ uint64 datamatch = 4;
+ }
+
+ message Memory {
+ uint64 offset = 1;
+ uint64 start = 2;
+ uint64 length = 3;
+ bool read_only = 4;
+ // Must be true for the MemoryDirtyLog method to work on this object.
+ bool dirty_log = 5;
+ }
+
+ message IrqEvent {
+ uint32 irq_id = 1;
+ bool resample = 2;
+ }
+
+ uint32 id = 1;
+ oneof constructor {
+ IoEvent io_event = 2;
+ // This message also requires a memfd sent over the socket.
+ Memory memory = 3;
+ IrqEvent irq_event = 4;
+ }
+ }
+
+ // No matter what the type an object is, it can be destroyed using this common method.
+ message Destroy {
+ uint32 id = 1;
+ }
+
+ message NewConnection {}
+
+ message GetShutdownEventfd {}
+
+ message CheckExtension {
+ uint32 extension = 1;
+ }
+
+ message CpuidRequest {
+ }
+
+ message MsrListRequest {
+ }
+
+ message GetNetConfig {}
+
+ message ReserveRange {
+ AddressSpace space = 1;
+ uint64 start = 2;
+ uint64 length = 3;
+ }
+
+ message SetIrq {
+ uint32 irq_id = 1;
+ bool active = 2;
+ }
+
+ message SetIrqRouting {
+ message Route {
+ message Irqchip {
+ uint32 irqchip = 1;
+ uint32 pin = 2;
+ }
+
+ message Msi {
+ uint64 address = 1;
+ uint32 data = 2;
+ }
+
+ uint32 irq_id = 1;
+ oneof route {
+ Irqchip irqchip = 2;
+ Msi msi = 3;
+ }
+ }
+
+ repeated Route routes = 1;
+ }
+
+ // Each type refers to certain piece of VM state (such as PIT state).
+ // The structure of the data corresponds to the kvm structure.
+ enum StateSet {
+ // struct kvm_pic_state
+ PIC0 = 0;
+ // struct kvm_pic_state
+ PIC1 = 1;
+ // struct kvm_ioapic_state
+ IOAPIC = 2;
+ // struct kvm_pit_state2
+ PIT = 3;
+ // struct kvm_clock_data
+ CLOCK = 4;
+ }
+
+ message GetState {
+ StateSet set = 1;
+ }
+
+ message SetState {
+ StateSet set = 1;
+ // The in memory representation of certain state, depending on the value
+ // of the StateSet.
+ bytes state = 2;
+ }
+
+ message SetIdentityMapAddr {
+ uint32 address = 1;
+ }
+
+ message PauseVcpus {
+ uint64 cpu_mask = 1;
+ uint64 user = 2;
+ }
+
+ message GetVcpus {}
+ message Start {}
+
+ message MemoryDirtyLog {
+ uint32 id = 1;
+ }
+
+ // The type of the message is determined by which of these oneof fields is present in the
+ // protobuf.
+ oneof message {
+ // Common method for instantiating a new object of any type.
+ Create create = 1;
+ // Common method for destroying an object of any type.
+ Destroy destroy = 2;
+ NewConnection new_connection = 3;
+ GetShutdownEventfd get_shutdown_eventfd = 4;
+ CheckExtension check_extension = 5;
+ CpuidRequest get_supported_cpuid = 6;
+ CpuidRequest get_emulated_cpuid = 7;
+ MsrListRequest get_msr_index_list = 8;
+ GetNetConfig get_net_config = 9;
+ ReserveRange reserve_range = 10;
+ SetIrq set_irq = 11;
+ SetIrqRouting set_irq_routing = 12;
+ GetState get_state = 13;
+ SetState set_state = 14;
+ SetIdentityMapAddr set_identity_map_addr = 15;
+ PauseVcpus pause_vcpus = 16;
+ GetVcpus get_vcpus = 17;
+ Start start = 18;
+ // Method for a Memory type object for retrieving the dirty bitmap. Only valid if the memory
+ // object was created with dirty_log set.
+ MemoryDirtyLog dirty_log = 101;
+ }
+}
+
+message MainResponse {
+ // Depending on the object that was created, an fd might also come from the socket.
+ message Create {}
+ message Destroy {}
+ // NewMessage receives a socket fd along with the data from reading this socket.
+ // The returned socket can be used totally independently of the original socket, and can perform
+ // requests and responses independent of the other sockets.
+ message NewConnection {}
+ message GetShutdownEventfd {}
+ message CheckExtension {
+ bool has_extension = 1;
+ }
+ message CpuidResponse {
+ repeated CpuidEntry entries = 1;
+ }
+ message MsrListResponse {
+ repeated uint32 indices = 1;
+ }
+
+ // GetNetConfig messages also return a file descriptor for the tap device.
+ message GetNetConfig {
+ bytes host_mac_address = 1;
+ fixed32 host_ipv4_address = 2;
+ fixed32 netmask = 3;
+ }
+
+ message ReserveRange {}
+ message SetIrq {}
+ message SetIrqRouting {}
+ message GetState {
+ // The in memory representation of a state, depending on what StateSet was
+ // requested in GetState.
+ bytes state = 1;
+ }
+ message SetState {}
+ message SetIdentityMapAddr {}
+ message PauseVcpus {}
+ // This message should also receive a socket fd per VCPU along with the data from reading this
+ // socket. The VcpuRequest/VcpuResponse protocol is run over each of the returned fds.
+ message GetVcpus {}
+ message Start {}
+ message MemoryDirtyLog {
+ bytes bitmap = 1;
+ }
+
+ // This is zero on success, and a negative integer on failure.
+ sint32 errno = 1;
+ // The field present here is always the same as the one present in the corresponding
+ // MainRequest.
+ oneof message {
+ Create create = 2;
+ Destroy destroy = 3;
+ NewConnection new_connection = 4;
+ GetShutdownEventfd get_shutdown_eventfd = 5;
+ CheckExtension check_extension = 6;
+ CpuidResponse get_supported_cpuid = 7;
+ CpuidResponse get_emulated_cpuid = 8;
+ MsrListResponse get_msr_index_list = 9;
+ GetNetConfig get_net_config = 10;
+ ReserveRange reserve_range = 11;
+ SetIrq set_irq = 12;
+ SetIrqRouting set_irq_routing = 13;
+ GetState get_state = 14;
+ SetState set_state = 15;
+ SetIdentityMapAddr set_identity_map_addr = 16;
+ PauseVcpus pause_vcpus = 17;
+ GetVcpus get_vcpus = 18;
+ Start start = 19;
+ MemoryDirtyLog dirty_log = 101;
+ }
+}
+
+// A request made for a specific VCPU. These requests are sent over the sockets returned from the
+// GetVcpu MainRequest.
+message VcpuRequest {
+ // This message will block until a non-empty response can be sent. The first response will
+ // always be an Init wait reason.
+ message Wait {
+ }
+
+ message Resume {
+ // The data is only necessary for non-write (read) I/O accesses. In all other cases, data is
+ // ignored.
+ bytes data = 1;
+ }
+
+ // Each type refers to certain piece of VCPU state (a set registers, or something else).
+ // The structure of the data corresponds to the kvm structure.
+ enum StateSet {
+ // struct kvm_regs
+ REGS = 0;
+ // struct kvm_sregs
+ SREGS = 1;
+ // struct kvm_fpu
+ FPU = 2;
+ // struct kvm_debugregs
+ DEBUGREGS = 3;
+ // struct kvm_lapic_state
+ LAPIC = 4;
+ // struct kvm_mp_state
+ MP = 5;
+ // struct kvm_xcrs
+ XCREGS = 6;
+ // struct kvm_vcpu_events
+ EVENTS = 7;
+ }
+
+ message GetState {
+ StateSet set = 1;
+ }
+
+ message SetState {
+ StateSet set = 1;
+ // The in memory representation of a struct kvm_regs, struct kvm_sregs,
+ // struct kvm_fpu, struct kvm_debugregs, struct kvm_lapic_state,
+ // struct kvm_mp_state, struct kvm_xcrs or struct kvm_vcpu_events
+ // depending on the value of the StateSet.
+ bytes state = 2;
+ }
+
+ message GetMsrs {
+ // The entry data will be returned in the same order as this in the
+ // VcpuResponse::GetMsrs::entry_data array.
+ repeated uint32 entry_indices = 1;
+ }
+
+ message MsrEntry {
+ uint32 index = 1;
+ uint64 data = 2;
+ }
+
+ message SetMsrs {
+ repeated MsrEntry entries = 1;
+ }
+
+ message SetCpuid {
+ repeated CpuidEntry entries = 1;
+ }
+
+ message Shutdown {
+ }
+
+ // The type of the message is determined by which of these oneof fields is present in the
+ // protobuf.
+ oneof message {
+ Wait wait = 1;
+ Resume resume = 2;
+ GetState get_state = 3;
+ SetState set_state = 4;
+ GetMsrs get_msrs = 5;
+ SetMsrs set_msrs = 6;
+ SetCpuid set_cpuid = 7;
+ Shutdown shutdown = 8;
+ }
+}
+
+
+message VcpuResponse {
+ // Depending on the reason a VCPU has exited, the Wait request will unblock and return a field
+ // in the oneof exit. This is called the "wait reason."
+ message Wait {
+ // This request will always be the first wait reason returend by the first wait request.
+ message Init {
+ }
+
+ // This type of wait reason is only generated if the access occurred on this VCPU on an
+ // address previously reserved by a ReserveRange main request.
+ message Io {
+ AddressSpace space = 1;
+ uint64 address = 2;
+ bool is_write = 3;
+ bytes data = 4;
+ }
+
+ // This type of wait reason is only generated after a PuaseVcpus request on this VCPU.
+ message User {
+ uint64 user = 1;
+ }
+
+ oneof exit {
+ Init init = 1;
+ Io io = 2;
+ User user = 3;
+ }
+ }
+
+ message Resume {}
+
+ message GetState {
+ // The in memory representation of a struct kvm_regs, struct kvm_sregs,
+ // struct kvm_fpu, struct kvm_debugregs, struct kvm_lapic_state,
+ // struct kvm_mp_state, struct kvm_xcrs or struct kvm_vcpu_events
+ // depending on the value of the StateSet.
+ bytes state = 1;
+ }
+
+ message SetState {
+ }
+
+ message GetMsrs {
+ // The order of the entry_data values is the same order as the array of indices given in the
+ // corresponding request.
+ repeated uint64 entry_data = 1;
+ }
+
+ message SetMsrs {}
+
+ message SetCpuid {}
+
+ // This is zero on success, and a negative integer on failure.
+ sint32 errno = 1;
+ // The field present here is always the same as the one present in the corresponding
+ // VcpuRequest.
+ oneof message {
+ Wait wait = 2;
+ Resume resume = 3;
+ GetState get_state = 4;
+ SetState set_state = 5;
+ GetMsrs get_msrs = 6;
+ SetMsrs set_msrs = 7;
+ SetCpuid set_cpuid = 8;
+ }
+}
diff --git a/protos/src/plugin.rs b/protos/src/plugin.rs
new file mode 100644
index 0000000..ce491e7
--- /dev/null
+++ b/protos/src/plugin.rs
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+pub use crate::generated::plugin::*;
+
+/// Converts protobuf representation of CpuId data into KVM format.
+pub fn cpuid_proto_to_kvm(entry: &CpuidEntry) -> kvm_sys::kvm_cpuid_entry2 {
+ // Safe: C structures are expected to be zero-initialized.
+ let mut e: kvm_sys::kvm_cpuid_entry2 = unsafe { std::mem::zeroed() };
+ e.function = entry.function;
+ if entry.has_index {
+ e.flags = kvm_sys::KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ }
+ e.index = entry.index;
+ e.eax = entry.eax;
+ e.ebx = entry.ebx;
+ e.ecx = entry.ecx;
+ e.edx = entry.edx;
+
+ e
+}
+
+/// Converts KVM representation of CpuId data into protobuf format.
+pub fn cpuid_kvm_to_proto(entry: &kvm_sys::kvm_cpuid_entry2) -> CpuidEntry {
+ let mut e = CpuidEntry::new();
+ e.function = entry.function;
+ e.has_index = entry.flags & kvm_sys::KVM_CPUID_FLAG_SIGNIFCANT_INDEX != 0;
+ e.index = entry.index;
+ e.eax = entry.eax;
+ e.ebx = entry.ebx;
+ e.ecx = entry.ecx;
+ e.edx = entry.edx;
+
+ e
+}
diff --git a/protos/tests/common/mod.rs b/protos/tests/common/mod.rs
new file mode 100644
index 0000000..a5aaf07
--- /dev/null
+++ b/protos/tests/common/mod.rs
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use protobuf::Message;
+
+/// Asserts that a given protobuf message object can be serialized to bytes and
+/// read back into a value of the same type, preserving equality between the
+/// original object and the one read back.
+///
+/// This is helpful for confirming that proto files have been compiled
+/// successfully and are exposed as intended by the public API of the parent
+/// crate. It also lets us exercise the full set of methods we intend to use for
+/// building particular proto objects so that we notice breakage early in the
+/// event that a proto external to this repository would make a breaking change.
+///
+/// Note: assumes that the given `message` is not just `T::new` so that we can
+/// tell that deserialization has happened successfully.
+///
+/// # Example
+///
+/// ```ignore
+/// let mut request = SendCommandRequest::new();
+/// request.set_command(b"...".to_vec());
+/// test_round_trip(request);
+/// ```
+pub fn test_round_trip<T: Message + PartialEq>(message: T) {
+ let serialized = message.write_to_bytes().unwrap();
+
+ let mut back = T::new();
+ assert_ne!(message, back);
+
+ back.merge_from_bytes(&serialized).unwrap();
+ assert_eq!(message, back);
+}
diff --git a/protos/tests/trunks.rs b/protos/tests/trunks.rs
new file mode 100644
index 0000000..39723ae
--- /dev/null
+++ b/protos/tests/trunks.rs
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![cfg(feature = "trunks")]
+
+mod common;
+
+use crate::common::test_round_trip;
+use protos::trunks::{SendCommandRequest, SendCommandResponse};
+
+#[test]
+fn send_command_request() {
+ let mut request = SendCommandRequest::new();
+ request.set_command(b"...".to_vec());
+ test_round_trip(request);
+}
+
+#[test]
+fn send_command_response() {
+ let mut response = SendCommandResponse::new();
+ response.set_response(b"...".to_vec());
+ test_round_trip(response);
+}
diff --git a/qcow/Cargo.toml b/qcow/Cargo.toml
new file mode 100644
index 0000000..0c521fd
--- /dev/null
+++ b/qcow/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "qcow"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[lib]
+path = "src/qcow.rs"
+
+[dependencies]
+byteorder = "*"
+libc = "*"
+remain = "*"
+sys_util = { path = "../sys_util" }
+data_model = { path = "../data_model" }
\ No newline at end of file
diff --git a/qcow/src/qcow.rs b/qcow/src/qcow.rs
new file mode 100644
index 0000000..4aeb7e3
--- /dev/null
+++ b/qcow/src/qcow.rs
@@ -0,0 +1,2483 @@
+// Copyright 2018 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.
+
+mod qcow_raw_file;
+mod refcount;
+mod vec_cache;
+
+use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
+use data_model::{VolatileMemory, VolatileSlice};
+use libc::{EINVAL, ENOSPC, ENOTSUP};
+use remain::sorted;
+use sys_util::{
+ error, FileReadWriteVolatile, FileSetLen, FileSync, PunchHole, SeekHole, WriteZeroes,
+};
+
+use std::cmp::min;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::{self, Read, Seek, SeekFrom, Write};
+use std::mem::size_of;
+use std::os::unix::io::{AsRawFd, RawFd};
+
+use crate::qcow_raw_file::QcowRawFile;
+use crate::refcount::RefCount;
+use crate::vec_cache::{CacheMap, Cacheable, VecCache};
+
+#[sorted]
+#[derive(Debug)]
+pub enum Error {
+ BackingFilesNotSupported,
+ CompressedBlocksNotSupported,
+ EvictingCache(io::Error),
+ FileTooBig(u64),
+ GettingFileSize(io::Error),
+ GettingRefcount(refcount::Error),
+ InvalidClusterIndex,
+ InvalidClusterSize,
+ InvalidIndex,
+ InvalidL1TableOffset,
+ InvalidMagic,
+ InvalidOffset(u64),
+ InvalidRefcountTableOffset,
+ InvalidRefcountTableSize(u64),
+ NoFreeClusters,
+ NoRefcountClusters,
+ NotEnoughSpaceForRefcounts,
+ OpeningFile(io::Error),
+ ReadingData(io::Error),
+ ReadingHeader(io::Error),
+ ReadingPointers(io::Error),
+ ReadingRefCountBlock(refcount::Error),
+ ReadingRefCounts(io::Error),
+ RebuildingRefCounts(io::Error),
+ SeekingFile(io::Error),
+ SettingFileSize(io::Error),
+ SettingRefcountRefcount(io::Error),
+ SizeTooSmallForNumberOfClusters,
+ TooManyL1Entries(u64),
+ TooManyRefcounts(u64),
+ UnsupportedRefcountOrder,
+ UnsupportedVersion(u32),
+ WritingData(io::Error),
+ WritingHeader(io::Error),
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ #[remain::check]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ #[sorted]
+ match self {
+ BackingFilesNotSupported => write!(f, "backing files not supported"),
+ CompressedBlocksNotSupported => write!(f, "compressed blocks not supported"),
+ EvictingCache(e) => write!(f, "failed to evict cache: {}", e),
+ FileTooBig(size) => write!(f, "file larger than max of 1TB: {}", size),
+ GettingFileSize(e) => write!(f, "failed to get file size: {}", e),
+ GettingRefcount(e) => write!(f, "failed to get refcount: {}", e),
+ InvalidClusterIndex => write!(f, "invalid cluster index"),
+ InvalidClusterSize => write!(f, "invalid cluster size"),
+ InvalidIndex => write!(f, "invalid index"),
+ InvalidL1TableOffset => write!(f, "invalid L1 table offset"),
+ InvalidMagic => write!(f, "invalid magic"),
+ InvalidOffset(_) => write!(f, "invalid offset"),
+ InvalidRefcountTableOffset => write!(f, "invalid refcount table offset"),
+ InvalidRefcountTableSize(size) => write!(f, "invalid refcount table size: {}", size),
+ NoFreeClusters => write!(f, "no free clusters"),
+ NoRefcountClusters => write!(f, "no refcount clusters"),
+ NotEnoughSpaceForRefcounts => write!(f, "not enough space for refcounts"),
+ OpeningFile(e) => write!(f, "failed to open file: {}", e),
+ ReadingData(e) => write!(f, "failed to read data: {}", e),
+ ReadingHeader(e) => write!(f, "failed to read header: {}", e),
+ ReadingPointers(e) => write!(f, "failed to read pointers: {}", e),
+ ReadingRefCountBlock(e) => write!(f, "failed to read ref count block: {}", e),
+ ReadingRefCounts(e) => write!(f, "failed to read ref counts: {}", e),
+ RebuildingRefCounts(e) => write!(f, "failed to rebuild ref counts: {}", e),
+ SeekingFile(e) => write!(f, "failed to seek file: {}", e),
+ SettingFileSize(e) => write!(f, "failed to set file size: {}", e),
+ SettingRefcountRefcount(e) => write!(f, "failed to set refcount refcount: {}", e),
+ SizeTooSmallForNumberOfClusters => write!(f, "size too small for number of clusters"),
+ TooManyL1Entries(count) => write!(f, "l1 entry table too large: {}", count),
+ TooManyRefcounts(count) => write!(f, "ref count table too large: {}", count),
+ UnsupportedRefcountOrder => write!(f, "unsupported refcount order"),
+ UnsupportedVersion(v) => write!(f, "unsupported version: {}", v),
+ WritingData(e) => write!(f, "failed to write data: {}", e),
+ WritingHeader(e) => write!(f, "failed to write header: {}", e),
+ }
+ }
+}
+
+pub enum ImageType {
+ Raw,
+ Qcow2,
+}
+
+// QCOW magic constant that starts the header.
+const QCOW_MAGIC: u32 = 0x5146_49fb;
+// Default to a cluster size of 2^DEFAULT_CLUSTER_BITS
+const DEFAULT_CLUSTER_BITS: u32 = 16;
+// Limit clusters to reasonable sizes. Choose the same limits as qemu. Making the clusters smaller
+// increases the amount of overhead for book keeping.
+const MIN_CLUSTER_BITS: u32 = 9;
+const MAX_CLUSTER_BITS: u32 = 21;
+// The L1 and RefCount table are kept in RAM, only handle files that require less than 35M entries.
+// This easily covers 1 TB files. When support for bigger files is needed the assumptions made to
+// keep these tables in RAM needs to be thrown out.
+const MAX_RAM_POINTER_TABLE_SIZE: u64 = 35_000_000;
+// Only support 2 byte refcounts, 2^refcount_order bits.
+const DEFAULT_REFCOUNT_ORDER: u32 = 4;
+
+const V3_BARE_HEADER_SIZE: u32 = 104;
+
+// bits 0-8 and 56-63 are reserved.
+const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
+const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
+// Flags
+const COMPRESSED_FLAG: u64 = 1 << 62;
+const CLUSTER_USED_FLAG: u64 = 1 << 63;
+const COMPATIBLE_FEATURES_LAZY_REFCOUNTS: u64 = 1 << 0;
+
+/// Contains the information from the header of a qcow file.
+#[derive(Copy, Clone, Debug)]
+pub struct QcowHeader {
+ pub magic: u32,
+ pub version: u32,
+
+ pub backing_file_offset: u64,
+ pub backing_file_size: u32,
+
+ pub cluster_bits: u32,
+ pub size: u64,
+ pub crypt_method: u32,
+
+ pub l1_size: u32,
+ pub l1_table_offset: u64,
+
+ pub refcount_table_offset: u64,
+ pub refcount_table_clusters: u32,
+
+ pub nb_snapshots: u32,
+ pub snapshots_offset: u64,
+
+ // v3 entries
+ pub incompatible_features: u64,
+ pub compatible_features: u64,
+ pub autoclear_features: u64,
+ pub refcount_order: u32,
+ pub header_size: u32,
+}
+
+impl QcowHeader {
+ /// Creates a QcowHeader from a reference to a file.
+ pub fn new(f: &mut File) -> Result<QcowHeader> {
+ f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
+ let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
+ if magic != QCOW_MAGIC {
+ return Err(Error::InvalidMagic);
+ }
+
+ // Reads the next u32 from the file.
+ fn read_u32_from_file(f: &mut File) -> Result<u32> {
+ f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)
+ }
+
+ // Reads the next u64 from the file.
+ fn read_u64_from_file(f: &mut File) -> Result<u64> {
+ f.read_u64::<BigEndian>().map_err(Error::ReadingHeader)
+ }
+
+ Ok(QcowHeader {
+ magic,
+ version: read_u32_from_file(f)?,
+ backing_file_offset: read_u64_from_file(f)?,
+ backing_file_size: read_u32_from_file(f)?,
+ cluster_bits: read_u32_from_file(f)?,
+ size: read_u64_from_file(f)?,
+ crypt_method: read_u32_from_file(f)?,
+ l1_size: read_u32_from_file(f)?,
+ l1_table_offset: read_u64_from_file(f)?,
+ refcount_table_offset: read_u64_from_file(f)?,
+ refcount_table_clusters: read_u32_from_file(f)?,
+ nb_snapshots: read_u32_from_file(f)?,
+ snapshots_offset: read_u64_from_file(f)?,
+ incompatible_features: read_u64_from_file(f)?,
+ compatible_features: read_u64_from_file(f)?,
+ autoclear_features: read_u64_from_file(f)?,
+ refcount_order: read_u32_from_file(f)?,
+ header_size: read_u32_from_file(f)?,
+ })
+ }
+
+ /// Create a header for the given `size`.
+ pub fn create_for_size(size: u64) -> QcowHeader {
+ let cluster_bits: u32 = DEFAULT_CLUSTER_BITS;
+ let cluster_size: u32 = 0x01 << cluster_bits;
+ // L2 blocks are always one cluster long. They contain cluster_size/sizeof(u64) addresses.
+ let l2_size: u32 = cluster_size / size_of::<u64>() as u32;
+ let num_clusters: u32 = div_round_up_u64(size, u64::from(cluster_size)) as u32;
+ let num_l2_clusters: u32 = div_round_up_u32(num_clusters, l2_size);
+ let l1_clusters: u32 = div_round_up_u32(num_l2_clusters, cluster_size);
+ let header_clusters = div_round_up_u32(size_of::<QcowHeader>() as u32, cluster_size);
+ QcowHeader {
+ magic: QCOW_MAGIC,
+ version: 3,
+ backing_file_offset: 0,
+ backing_file_size: 0,
+ cluster_bits: DEFAULT_CLUSTER_BITS,
+ size,
+ crypt_method: 0,
+ l1_size: num_l2_clusters,
+ l1_table_offset: u64::from(cluster_size),
+ // The refcount table is after l1 + header.
+ refcount_table_offset: u64::from(cluster_size * (l1_clusters + 1)),
+ refcount_table_clusters: {
+ // Pre-allocate enough clusters for the entire refcount table as it must be
+ // continuous in the file. Allocate enough space to refcount all clusters, including
+ // the refcount clusters.
+ let max_refcount_clusters = max_refcount_clusters(
+ DEFAULT_REFCOUNT_ORDER,
+ cluster_size,
+ num_clusters + l1_clusters + num_l2_clusters + header_clusters,
+ ) as u32;
+ // The refcount table needs to store the offset of each refcount cluster.
+ div_round_up_u32(
+ max_refcount_clusters * size_of::<u64>() as u32,
+ cluster_size,
+ )
+ },
+ nb_snapshots: 0,
+ snapshots_offset: 0,
+ incompatible_features: 0,
+ compatible_features: 0,
+ autoclear_features: 0,
+ refcount_order: DEFAULT_REFCOUNT_ORDER,
+ header_size: V3_BARE_HEADER_SIZE,
+ }
+ }
+
+ /// Write the header to `file`.
+ pub fn write_to<F: Write + Seek>(&self, file: &mut F) -> Result<()> {
+ // Writes the next u32 to the file.
+ fn write_u32_to_file<F: Write>(f: &mut F, value: u32) -> Result<()> {
+ f.write_u32::<BigEndian>(value)
+ .map_err(Error::WritingHeader)
+ }
+
+ // Writes the next u64 to the file.
+ fn write_u64_to_file<F: Write>(f: &mut F, value: u64) -> Result<()> {
+ f.write_u64::<BigEndian>(value)
+ .map_err(Error::WritingHeader)
+ }
+
+ write_u32_to_file(file, self.magic)?;
+ write_u32_to_file(file, self.version)?;
+ write_u64_to_file(file, self.backing_file_offset)?;
+ write_u32_to_file(file, self.backing_file_size)?;
+ write_u32_to_file(file, self.cluster_bits)?;
+ write_u64_to_file(file, self.size)?;
+ write_u32_to_file(file, self.crypt_method)?;
+ write_u32_to_file(file, self.l1_size)?;
+ write_u64_to_file(file, self.l1_table_offset)?;
+ write_u64_to_file(file, self.refcount_table_offset)?;
+ write_u32_to_file(file, self.refcount_table_clusters)?;
+ write_u32_to_file(file, self.nb_snapshots)?;
+ write_u64_to_file(file, self.snapshots_offset)?;
+ write_u64_to_file(file, self.incompatible_features)?;
+ write_u64_to_file(file, self.compatible_features)?;
+ write_u64_to_file(file, self.autoclear_features)?;
+ write_u32_to_file(file, self.refcount_order)?;
+ write_u32_to_file(file, self.header_size)?;
+
+ // Set the file length by seeking and writing a zero to the last byte. This avoids needing
+ // a `File` instead of anything that implements seek as the `file` argument.
+ // Zeros out the l1 and refcount table clusters.
+ let cluster_size = 0x01u64 << self.cluster_bits;
+ let refcount_blocks_size = u64::from(self.refcount_table_clusters) * cluster_size;
+ file.seek(SeekFrom::Start(
+ self.refcount_table_offset + refcount_blocks_size - 2,
+ ))
+ .map_err(Error::WritingHeader)?;
+ file.write(&[0u8]).map_err(Error::WritingHeader)?;
+
+ Ok(())
+ }
+}
+
+fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u32) -> u64 {
+ // Use u64 as the product of the u32 inputs can overflow.
+ let refcount_bytes = (0x01 << refcount_order as u64) / 8;
+ let for_data = div_round_up_u64(num_clusters as u64 * refcount_bytes, cluster_size as u64);
+ let for_refcounts = div_round_up_u64(for_data * refcount_bytes, cluster_size as u64);
+ for_data + for_refcounts
+}
+
+/// Represents a qcow2 file. This is a sparse file format maintained by the qemu project.
+/// Full documentation of the format can be found in the qemu repository.
+///
+/// # Example
+///
+/// ```
+/// # use std::io::{Read, Seek, SeekFrom};
+/// # use qcow::{self, QcowFile};
+/// # fn test(file: std::fs::File) -> std::io::Result<()> {
+/// let mut q = QcowFile::from(file).expect("Can't open qcow file");
+/// let mut buf = [0u8; 12];
+/// q.seek(SeekFrom::Start(10 as u64))?;
+/// q.read(&mut buf[..])?;
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug)]
+pub struct QcowFile {
+ raw_file: QcowRawFile,
+ header: QcowHeader,
+ l1_table: VecCache<u64>,
+ l2_entries: u64,
+ l2_cache: CacheMap<VecCache<u64>>,
+ refcounts: RefCount,
+ current_offset: u64,
+ unref_clusters: Vec<u64>, // List of freshly unreferenced clusters.
+ // List of unreferenced clusters available to be used. unref clusters become available once the
+ // removal of references to them have been synced to disk.
+ avail_clusters: Vec<u64>,
+ //TODO(dgreid) Add support for backing files. - backing_file: Option<Box<QcowFile<T>>>,
+}
+
+impl QcowFile {
+ /// Creates a QcowFile from `file`. File must be a valid qcow2 image.
+ pub fn from(mut file: File) -> Result<QcowFile> {
+ let header = QcowHeader::new(&mut file)?;
+
+ // Only v3 files are supported.
+ if header.version != 3 {
+ return Err(Error::UnsupportedVersion(header.version));
+ }
+
+ let cluster_bits: u32 = header.cluster_bits;
+ if cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS {
+ return Err(Error::InvalidClusterSize);
+ }
+ let cluster_size = 0x01u64 << cluster_bits;
+
+ // No current support for backing files.
+ if header.backing_file_offset != 0 {
+ return Err(Error::BackingFilesNotSupported);
+ }
+
+ // Only support two byte refcounts.
+ let refcount_bits: u64 = 0x01u64
+ .checked_shl(header.refcount_order)
+ .ok_or(Error::UnsupportedRefcountOrder)?;
+ if refcount_bits != 16 {
+ return Err(Error::UnsupportedRefcountOrder);
+ }
+ let refcount_bytes = (refcount_bits + 7) / 8;
+
+ // Need at least one refcount cluster
+ if header.refcount_table_clusters == 0 {
+ return Err(Error::NoRefcountClusters);
+ }
+ offset_is_cluster_boundary(header.backing_file_offset, header.cluster_bits)?;
+ offset_is_cluster_boundary(header.l1_table_offset, header.cluster_bits)?;
+ offset_is_cluster_boundary(header.refcount_table_offset, header.cluster_bits)?;
+ offset_is_cluster_boundary(header.snapshots_offset, header.cluster_bits)?;
+
+ // The first cluster should always have a non-zero refcount, so if it is 0,
+ // this is an old file with broken refcounts, which requires a rebuild.
+ let mut refcount_rebuild_required = true;
+ file.seek(SeekFrom::Start(header.refcount_table_offset))
+ .map_err(Error::SeekingFile)?;
+ let first_refblock_addr = file.read_u64::<BigEndian>().map_err(Error::ReadingHeader)?;
+ if first_refblock_addr != 0 {
+ file.seek(SeekFrom::Start(first_refblock_addr))
+ .map_err(Error::SeekingFile)?;
+ let first_cluster_refcount =
+ file.read_u16::<BigEndian>().map_err(Error::ReadingHeader)?;
+ if first_cluster_refcount != 0 {
+ refcount_rebuild_required = false;
+ }
+ }
+
+ if (header.compatible_features & COMPATIBLE_FEATURES_LAZY_REFCOUNTS) != 0 {
+ refcount_rebuild_required = true;
+ }
+
+ let mut raw_file =
+ QcowRawFile::from(file, cluster_size).ok_or(Error::InvalidClusterSize)?;
+ if refcount_rebuild_required {
+ QcowFile::rebuild_refcounts(&mut raw_file, header)?;
+ }
+
+ let l2_size = cluster_size / size_of::<u64>() as u64;
+ let num_clusters = div_round_up_u64(header.size, cluster_size);
+ let num_l2_clusters = div_round_up_u64(num_clusters, l2_size);
+ let l1_clusters = div_round_up_u64(num_l2_clusters, cluster_size);
+ let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
+ if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
+ return Err(Error::TooManyL1Entries(num_l2_clusters));
+ }
+ let l1_table = VecCache::from_vec(
+ raw_file
+ .read_pointer_table(
+ header.l1_table_offset,
+ num_l2_clusters,
+ Some(L1_TABLE_OFFSET_MASK),
+ )
+ .map_err(Error::ReadingHeader)?,
+ );
+
+ let num_clusters = div_round_up_u64(header.size, cluster_size);
+ let refcount_clusters = max_refcount_clusters(
+ header.refcount_order,
+ cluster_size as u32,
+ (num_clusters + l1_clusters + num_l2_clusters + header_clusters) as u32,
+ );
+ if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE {
+ return Err(Error::TooManyRefcounts(refcount_clusters));
+ }
+ let refcount_block_entries = cluster_size / refcount_bytes;
+ let refcounts = RefCount::new(
+ &mut raw_file,
+ header.refcount_table_offset,
+ refcount_clusters,
+ refcount_block_entries,
+ cluster_size,
+ )
+ .map_err(Error::ReadingRefCounts)?;
+
+ let l2_entries = cluster_size / size_of::<u64>() as u64;
+
+ let mut qcow = QcowFile {
+ raw_file,
+ header,
+ l1_table,
+ l2_entries,
+ l2_cache: CacheMap::new(100),
+ refcounts,
+ current_offset: 0,
+ unref_clusters: Vec::new(),
+ avail_clusters: Vec::new(),
+ };
+
+ // Check that the L1 and refcount tables fit in a 64bit address space.
+ qcow.header
+ .l1_table_offset
+ .checked_add(qcow.l1_address_offset(qcow.virtual_size()))
+ .ok_or(Error::InvalidL1TableOffset)?;
+ qcow.header
+ .refcount_table_offset
+ .checked_add(u64::from(qcow.header.refcount_table_clusters) * cluster_size)
+ .ok_or(Error::InvalidRefcountTableOffset)?;
+
+ qcow.find_avail_clusters()?;
+
+ Ok(qcow)
+ }
+
+ /// Creates a new QcowFile at the given path.
+ pub fn new(mut file: File, virtual_size: u64) -> Result<QcowFile> {
+ let header = QcowHeader::create_for_size(virtual_size);
+ file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
+ header.write_to(&mut file)?;
+
+ let mut qcow = Self::from(file)?;
+
+ // Set the refcount for each refcount table cluster.
+ let cluster_size = 0x01u64 << qcow.header.cluster_bits;
+ let refcount_table_base = qcow.header.refcount_table_offset as u64;
+ let end_cluster_addr =
+ refcount_table_base + u64::from(qcow.header.refcount_table_clusters) * cluster_size;
+
+ let mut cluster_addr = 0;
+ while cluster_addr < end_cluster_addr {
+ let mut unref_clusters = qcow
+ .set_cluster_refcount(cluster_addr, 1)
+ .map_err(Error::SettingRefcountRefcount)?;
+ qcow.unref_clusters.append(&mut unref_clusters);
+ cluster_addr += cluster_size;
+ }
+
+ Ok(qcow)
+ }
+
+ /// Returns the `QcowHeader` for this file.
+ pub fn header(&self) -> &QcowHeader {
+ &self.header
+ }
+
+ /// Returns the L1 lookup table for this file. This is only useful for debugging.
+ pub fn l1_table(&self) -> &[u64] {
+ &self.l1_table.get_values()
+ }
+
+ /// Returns an L2_table of cluster addresses, only used for debugging.
+ pub fn l2_table(&mut self, l1_index: usize) -> Result<Option<&[u64]>> {
+ let l2_addr_disk = *self.l1_table.get(l1_index).ok_or(Error::InvalidIndex)?;
+
+ if l2_addr_disk == 0 {
+ // Reading from an unallocated cluster will return zeros.
+ return Ok(None);
+ }
+
+ if !self.l2_cache.contains_key(&l1_index) {
+ // Not in the cache.
+ let table = VecCache::from_vec(
+ Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)
+ .map_err(Error::ReadingPointers)?,
+ );
+ let l1_table = &self.l1_table;
+ let raw_file = &mut self.raw_file;
+ self.l2_cache
+ .insert(l1_index, table, |index, evicted| {
+ raw_file.write_pointer_table(
+ l1_table[index],
+ evicted.get_values(),
+ CLUSTER_USED_FLAG,
+ )
+ })
+ .map_err(Error::EvictingCache)?;
+ }
+
+ // The index must exist as it was just inserted if it didn't already.
+ Ok(Some(self.l2_cache.get(&l1_index).unwrap().get_values()))
+ }
+
+ /// Returns the refcount table for this file. This is only useful for debugging.
+ pub fn ref_table(&self) -> &[u64] {
+ &self.refcounts.ref_table()
+ }
+
+ /// Returns the `index`th refcount block from the file.
+ pub fn refcount_block(&mut self, index: usize) -> Result<Option<&[u16]>> {
+ self.refcounts
+ .refcount_block(&mut self.raw_file, index)
+ .map_err(Error::ReadingRefCountBlock)
+ }
+
+ /// Returns the first cluster in the file with a 0 refcount. Used for testing.
+ pub fn first_zero_refcount(&mut self) -> Result<Option<u64>> {
+ let file_size = self
+ .raw_file
+ .file_mut()
+ .metadata()
+ .map_err(Error::GettingFileSize)?
+ .len();
+ let cluster_size = 0x01u64 << self.header.cluster_bits;
+
+ let mut cluster_addr = 0;
+ while cluster_addr < file_size {
+ let cluster_refcount = self
+ .refcounts
+ .get_cluster_refcount(&mut self.raw_file, cluster_addr)
+ .map_err(Error::GettingRefcount)?;
+ if cluster_refcount == 0 {
+ return Ok(Some(cluster_addr));
+ }
+ cluster_addr += cluster_size;
+ }
+ Ok(None)
+ }
+
+ fn find_avail_clusters(&mut self) -> Result<()> {
+ let cluster_size = self.raw_file.cluster_size();
+
+ let file_size = self
+ .raw_file
+ .file_mut()
+ .metadata()
+ .map_err(Error::GettingFileSize)?
+ .len();
+
+ for i in (0..file_size).step_by(cluster_size as usize) {
+ let refcount = self
+ .refcounts
+ .get_cluster_refcount(&mut self.raw_file, i)
+ .map_err(Error::GettingRefcount)?;
+ if refcount == 0 {
+ self.avail_clusters.push(i);
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Rebuild the reference count tables.
+ fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> Result<()> {
+ fn add_ref(refcounts: &mut [u16], cluster_size: u64, cluster_address: u64) -> Result<()> {
+ let idx = (cluster_address / cluster_size) as usize;
+ if idx >= refcounts.len() {
+ return Err(Error::InvalidClusterIndex);
+ }
+ refcounts[idx] += 1;
+ Ok(())
+ }
+
+ // Add a reference to the first cluster (header plus extensions).
+ fn set_header_refcount(refcounts: &mut [u16], cluster_size: u64) -> Result<()> {
+ add_ref(refcounts, cluster_size, 0)
+ }
+
+ // Add references to the L1 table clusters.
+ fn set_l1_refcounts(
+ refcounts: &mut [u16],
+ header: QcowHeader,
+ cluster_size: u64,
+ ) -> Result<()> {
+ let l1_clusters = div_round_up_u64(header.l1_size as u64, cluster_size);
+ let l1_table_offset = header.l1_table_offset;
+ for i in 0..l1_clusters {
+ add_ref(refcounts, cluster_size, l1_table_offset + i * cluster_size)?;
+ }
+ Ok(())
+ }
+
+ // Traverse the L1 and L2 tables to find all reachable data clusters.
+ fn set_data_refcounts(
+ refcounts: &mut [u16],
+ header: QcowHeader,
+ cluster_size: u64,
+ raw_file: &mut QcowRawFile,
+ ) -> Result<()> {
+ let l1_table = raw_file
+ .read_pointer_table(
+ header.l1_table_offset,
+ header.l1_size as u64,
+ Some(L1_TABLE_OFFSET_MASK),
+ )
+ .map_err(Error::ReadingPointers)?;
+ for l1_index in 0..header.l1_size as usize {
+ let l2_addr_disk = *l1_table.get(l1_index).ok_or(Error::InvalidIndex)?;
+ if l2_addr_disk != 0 {
+ // Add a reference to the L2 table cluster itself.
+ add_ref(refcounts, cluster_size, l2_addr_disk)?;
+
+ // Read the L2 table and find all referenced data clusters.
+ let l2_table = raw_file
+ .read_pointer_table(
+ l2_addr_disk,
+ cluster_size / size_of::<u64>() as u64,
+ Some(L2_TABLE_OFFSET_MASK),
+ )
+ .map_err(Error::ReadingPointers)?;
+ for data_cluster_addr in l2_table {
+ if data_cluster_addr != 0 {
+ add_ref(refcounts, cluster_size, data_cluster_addr)?;
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ // Add references to the top-level refcount table clusters.
+ fn set_refcount_table_refcounts(
+ refcounts: &mut [u16],
+ header: QcowHeader,
+ cluster_size: u64,
+ ) -> Result<()> {
+ let refcount_table_offset = header.refcount_table_offset;
+ for i in 0..header.refcount_table_clusters as u64 {
+ add_ref(
+ refcounts,
+ cluster_size,
+ refcount_table_offset + i * cluster_size,
+ )?;
+ }
+ Ok(())
+ }
+
+ // Allocate clusters for refblocks.
+ // This needs to be done last so that we have the correct refcounts for all other
+ // clusters.
+ fn alloc_refblocks(
+ refcounts: &mut [u16],
+ cluster_size: u64,
+ refblock_clusters: u64,
+ pointers_per_cluster: u64,
+ ) -> Result<Vec<u64>> {
+ let refcount_table_entries = div_round_up_u64(refblock_clusters, pointers_per_cluster);
+ let mut ref_table = vec![0; refcount_table_entries as usize];
+ let mut first_free_cluster: u64 = 0;
+ for refblock_addr in &mut ref_table {
+ while refcounts[first_free_cluster as usize] != 0 {
+ first_free_cluster += 1;
+ if first_free_cluster >= refcounts.len() as u64 {
+ return Err(Error::NotEnoughSpaceForRefcounts);
+ }
+ }
+
+ *refblock_addr = first_free_cluster * cluster_size;
+ add_ref(refcounts, cluster_size, *refblock_addr)?;
+
+ first_free_cluster += 1;
+ }
+
+ Ok(ref_table)
+ }
+
+ // Write the updated reference count blocks and reftable.
+ fn write_refblocks(
+ refcounts: &[u16],
+ mut header: QcowHeader,
+ ref_table: &[u64],
+ raw_file: &mut QcowRawFile,
+ refcount_block_entries: u64,
+ ) -> Result<()> {
+ // Rewrite the header with lazy refcounts enabled while we are rebuilding the tables.
+ header.compatible_features |= COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
+ raw_file
+ .file_mut()
+ .seek(SeekFrom::Start(0))
+ .map_err(Error::SeekingFile)?;
+ header.write_to(raw_file.file_mut())?;
+
+ for (i, refblock_addr) in ref_table.iter().enumerate() {
+ // Write a block of refcounts to the location indicated by refblock_addr.
+ let refblock_start = i * (refcount_block_entries as usize);
+ let refblock_end = min(
+ refcounts.len(),
+ refblock_start + refcount_block_entries as usize,
+ );
+ let refblock = &refcounts[refblock_start..refblock_end];
+ raw_file
+ .write_refcount_block(*refblock_addr, refblock)
+ .map_err(Error::WritingHeader)?;
+
+ // If this is the last (partial) cluster, pad it out to a full refblock cluster.
+ if refblock.len() < refcount_block_entries as usize {
+ let refblock_padding =
+ vec![0u16; refcount_block_entries as usize - refblock.len()];
+ raw_file
+ .write_refcount_block(
+ *refblock_addr + refblock.len() as u64 * 2,
+ &refblock_padding,
+ )
+ .map_err(Error::WritingHeader)?;
+ }
+ }
+
+ // Rewrite the top-level refcount table.
+ raw_file
+ .write_pointer_table(header.refcount_table_offset, &ref_table, 0)
+ .map_err(Error::WritingHeader)?;
+
+ // Rewrite the header again, now with lazy refcounts disabled.
+ header.compatible_features &= !COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
+ raw_file
+ .file_mut()
+ .seek(SeekFrom::Start(0))
+ .map_err(Error::SeekingFile)?;
+ header.write_to(raw_file.file_mut())?;
+
+ Ok(())
+ }
+
+ let cluster_size = raw_file.cluster_size();
+
+ let file_size = raw_file
+ .file_mut()
+ .metadata()
+ .map_err(Error::GettingFileSize)?
+ .len();
+
+ let refcount_bits = 1u64 << header.refcount_order;
+ let refcount_bytes = div_round_up_u64(refcount_bits, 8);
+ let refcount_block_entries = cluster_size / refcount_bytes;
+ let pointers_per_cluster = cluster_size / size_of::<u64>() as u64;
+ let data_clusters = div_round_up_u64(header.size, cluster_size);
+ let l2_clusters = div_round_up_u64(data_clusters, pointers_per_cluster);
+ let l1_clusters = div_round_up_u64(l2_clusters, cluster_size);
+ let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
+ let max_clusters = data_clusters + l2_clusters + l1_clusters + header_clusters;
+ let mut max_valid_cluster_index = max_clusters;
+ let refblock_clusters = div_round_up_u64(max_valid_cluster_index, refcount_block_entries);
+ let reftable_clusters = div_round_up_u64(refblock_clusters, pointers_per_cluster);
+ // Account for refblocks and the ref table size needed to address them.
+ let refblocks_for_refs = div_round_up_u64(
+ refblock_clusters + reftable_clusters,
+ refcount_block_entries,
+ );
+ let reftable_clusters_for_refs =
+ div_round_up_u64(refblocks_for_refs, refcount_block_entries);
+ max_valid_cluster_index += refblock_clusters + reftable_clusters;
+ max_valid_cluster_index += refblocks_for_refs + reftable_clusters_for_refs;
+
+ if max_valid_cluster_index > MAX_RAM_POINTER_TABLE_SIZE {
+ return Err(Error::InvalidRefcountTableSize(max_valid_cluster_index));
+ }
+
+ let max_valid_cluster_offset = max_valid_cluster_index * cluster_size;
+ if max_valid_cluster_offset < file_size - cluster_size {
+ return Err(Error::InvalidRefcountTableSize(max_valid_cluster_offset));
+ }
+
+ let mut refcounts = vec![0; max_valid_cluster_index as usize];
+
+ // Find all references clusters and rebuild refcounts.
+ set_header_refcount(&mut refcounts, cluster_size)?;
+ set_l1_refcounts(&mut refcounts, header, cluster_size)?;
+ set_data_refcounts(&mut refcounts, header, cluster_size, raw_file)?;
+ set_refcount_table_refcounts(&mut refcounts, header, cluster_size)?;
+
+ // Allocate clusters to store the new reference count blocks.
+ let ref_table = alloc_refblocks(
+ &mut refcounts,
+ cluster_size,
+ refblock_clusters,
+ pointers_per_cluster,
+ )?;
+
+ // Write updated reference counts and point the reftable at them.
+ write_refblocks(
+ &refcounts,
+ header,
+ &ref_table,
+ raw_file,
+ refcount_block_entries,
+ )
+ }
+
+ // Limits the range so that it doesn't exceed the virtual size of the file.
+ fn limit_range_file(&self, address: u64, count: usize) -> usize {
+ if address.checked_add(count as u64).is_none() || address > self.virtual_size() {
+ return 0;
+ }
+ min(count as u64, self.virtual_size() - address) as usize
+ }
+
+ // Limits the range so that it doesn't overflow the end of a cluster.
+ fn limit_range_cluster(&self, address: u64, count: usize) -> usize {
+ let offset: u64 = self.raw_file.cluster_offset(address);
+ let limit = self.raw_file.cluster_size() - offset;
+ min(count as u64, limit) as usize
+ }
+
+ // Gets the maximum virtual size of this image.
+ fn virtual_size(&self) -> u64 {
+ self.header.size
+ }
+
+ // Gets the offset of `address` in the L1 table.
+ fn l1_address_offset(&self, address: u64) -> u64 {
+ let l1_index = self.l1_table_index(address);
+ l1_index * size_of::<u64>() as u64
+ }
+
+ // Gets the offset of `address` in the L1 table.
+ fn l1_table_index(&self, address: u64) -> u64 {
+ (address / self.raw_file.cluster_size()) / self.l2_entries
+ }
+
+ // Gets the offset of `address` in the L2 table.
+ fn l2_table_index(&self, address: u64) -> u64 {
+ (address / self.raw_file.cluster_size()) % self.l2_entries
+ }
+
+ // Gets the offset of the given guest address in the host file. If L1, L2, or data clusters have
+ // yet to be allocated, return None.
+ fn file_offset_read(&mut self, address: u64) -> std::io::Result<Option<u64>> {
+ if address >= self.virtual_size() as u64 {
+ return Err(std::io::Error::from_raw_os_error(EINVAL));
+ }
+
+ let l1_index = self.l1_table_index(address) as usize;
+ let l2_addr_disk = *self
+ .l1_table
+ .get(l1_index)
+ .ok_or(std::io::Error::from_raw_os_error(EINVAL))?;
+
+ if l2_addr_disk == 0 {
+ // Reading from an unallocated cluster will return zeros.
+ return Ok(None);
+ }
+
+ let l2_index = self.l2_table_index(address) as usize;
+
+ if !self.l2_cache.contains_key(&l1_index) {
+ // Not in the cache.
+ let table =
+ VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
+
+ let l1_table = &self.l1_table;
+ let raw_file = &mut self.raw_file;
+ self.l2_cache.insert(l1_index, table, |index, evicted| {
+ raw_file.write_pointer_table(
+ l1_table[index],
+ evicted.get_values(),
+ CLUSTER_USED_FLAG,
+ )
+ })?;
+ };
+
+ let cluster_addr = self.l2_cache.get(&l1_index).unwrap()[l2_index];
+ if cluster_addr == 0 {
+ return Ok(None);
+ }
+ Ok(Some(cluster_addr + self.raw_file.cluster_offset(address)))
+ }
+
+ // Gets the offset of the given guest address in the host file. If L1, L2, or data clusters need
+ // to be allocated, they will be.
+ fn file_offset_write(&mut self, address: u64) -> std::io::Result<u64> {
+ if address >= self.virtual_size() as u64 {
+ return Err(std::io::Error::from_raw_os_error(EINVAL));
+ }
+
+ let l1_index = self.l1_table_index(address) as usize;
+ let l2_addr_disk = *self
+ .l1_table
+ .get(l1_index)
+ .ok_or(std::io::Error::from_raw_os_error(EINVAL))?;
+ let l2_index = self.l2_table_index(address) as usize;
+
+ let mut set_refcounts = Vec::new();
+
+ if !self.l2_cache.contains_key(&l1_index) {
+ // Not in the cache.
+ let l2_table = if l2_addr_disk == 0 {
+ // Allocate a new cluster to store the L2 table and update the L1 table to point
+ // to the new table.
+ let new_addr: u64 = self.get_new_cluster()?;
+ // The cluster refcount starts at one meaning it is used but doesn't need COW.
+ set_refcounts.push((new_addr, 1));
+ self.l1_table[l1_index] = new_addr;
+ VecCache::new(self.l2_entries as usize)
+ } else {
+ VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?)
+ };
+ let l1_table = &self.l1_table;
+ let raw_file = &mut self.raw_file;
+ self.l2_cache.insert(l1_index, l2_table, |index, evicted| {
+ raw_file.write_pointer_table(
+ l1_table[index],
+ evicted.get_values(),
+ CLUSTER_USED_FLAG,
+ )
+ })?;
+ }
+
+ let cluster_addr = match self.l2_cache.get(&l1_index).unwrap()[l2_index] {
+ 0 => {
+ // Need to allocate a data cluster
+ let cluster_addr = self.append_data_cluster()?;
+ self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
+ cluster_addr
+ }
+ a => a,
+ };
+
+ for (addr, count) in set_refcounts {
+ let mut newly_unref = self.set_cluster_refcount(addr, count)?;
+ self.unref_clusters.append(&mut newly_unref);
+ }
+
+ Ok(cluster_addr + self.raw_file.cluster_offset(address))
+ }
+
+ // Updates the l1 and l2 tables to point to the new `cluster_addr`.
+ fn update_cluster_addr(
+ &mut self,
+ l1_index: usize,
+ l2_index: usize,
+ cluster_addr: u64,
+ set_refcounts: &mut Vec<(u64, u16)>,
+ ) -> io::Result<()> {
+ if !self.l2_cache.get(&l1_index).unwrap().dirty() {
+ // Free the previously used cluster if one exists. Modified tables are always
+ // witten to new clusters so the L1 table can be committed to disk after they
+ // are and L1 never points at an invalid table.
+ // The index must be valid from when it was insterted.
+ let addr = self.l1_table[l1_index];
+ if addr != 0 {
+ self.unref_clusters.push(addr);
+ set_refcounts.push((addr, 0));
+ }
+
+ // Allocate a new cluster to store the L2 table and update the L1 table to point
+ // to the new table. The cluster will be written when the cache is flushed, no
+ // need to copy the data now.
+ let new_addr: u64 = self.get_new_cluster()?;
+ // The cluster refcount starts at one indicating it is used but doesn't need
+ // COW.
+ set_refcounts.push((new_addr, 1));
+ self.l1_table[l1_index] = new_addr;
+ }
+ // 'unwrap' is OK because it was just added.
+ self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = cluster_addr;
+ Ok(())
+ }
+
+ // Allocate a new cluster and return its offset within the raw file.
+ fn get_new_cluster(&mut self) -> std::io::Result<u64> {
+ // First use a pre allocated cluster if one is available.
+ if let Some(free_cluster) = self.avail_clusters.pop() {
+ let cluster_size = self.raw_file.cluster_size() as usize;
+ self.raw_file
+ .file_mut()
+ .seek(SeekFrom::Start(free_cluster))?;
+ self.raw_file.file_mut().write_zeroes(cluster_size)?;
+ return Ok(free_cluster);
+ }
+
+ let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset();
+ if let Some(new_cluster) = self.raw_file.add_cluster_end(max_valid_cluster_offset)? {
+ return Ok(new_cluster);
+ } else {
+ error!("No free clusters in get_new_cluster()");
+ return Err(std::io::Error::from_raw_os_error(ENOSPC));
+ }
+ }
+
+ // Allocate and initialize a new data cluster. Returns the offset of the
+ // cluster in to the file on success.
+ fn append_data_cluster(&mut self) -> std::io::Result<u64> {
+ let new_addr: u64 = self.get_new_cluster()?;
+ // The cluster refcount starts at one indicating it is used but doesn't need COW.
+ let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
+ self.unref_clusters.append(&mut newly_unref);
+ Ok(new_addr)
+ }
+
+ // Returns true if the cluster containing `address` is already allocated.
+ fn cluster_allocated(&mut self, address: u64) -> std::io::Result<bool> {
+ if address >= self.virtual_size() as u64 {
+ return Err(std::io::Error::from_raw_os_error(EINVAL));
+ }
+
+ let l1_index = self.l1_table_index(address) as usize;
+ let l2_addr_disk = *self
+ .l1_table
+ .get(l1_index)
+ .ok_or(std::io::Error::from_raw_os_error(EINVAL))?;
+ let l2_index = self.l2_table_index(address) as usize;
+
+ if l2_addr_disk == 0 {
+ // The whole L2 table for this address is not allocated yet,
+ // so the cluster must also be unallocated.
+ return Ok(false);
+ }
+
+ if !self.l2_cache.contains_key(&l1_index) {
+ // Not in the cache.
+ let table =
+ VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
+ let l1_table = &self.l1_table;
+ let raw_file = &mut self.raw_file;
+ self.l2_cache.insert(l1_index, table, |index, evicted| {
+ raw_file.write_pointer_table(
+ l1_table[index],
+ evicted.get_values(),
+ CLUSTER_USED_FLAG,
+ )
+ })?;
+ }
+
+ let cluster_addr = self.l2_cache.get(&l1_index).unwrap()[l2_index];
+ // If cluster_addr != 0, the cluster is allocated.
+ Ok(cluster_addr != 0)
+ }
+
+ // Find the first guest address greater than or equal to `address` whose allocation state
+ // matches `allocated`.
+ fn find_allocated_cluster(
+ &mut self,
+ address: u64,
+ allocated: bool,
+ ) -> std::io::Result<Option<u64>> {
+ let size = self.virtual_size();
+ if address >= size {
+ return Ok(None);
+ }
+
+ // If offset is already within a hole, return it.
+ if self.cluster_allocated(address)? == allocated {
+ return Ok(Some(address));
+ }
+
+ // Skip to the next cluster boundary.
+ let cluster_size = self.raw_file.cluster_size();
+ let mut cluster_addr = (address / cluster_size + 1) * cluster_size;
+
+ // Search for clusters with the desired allocation state.
+ while cluster_addr < size {
+ if self.cluster_allocated(cluster_addr)? == allocated {
+ return Ok(Some(cluster_addr));
+ }
+ cluster_addr += cluster_size;
+ }
+
+ Ok(None)
+ }
+
+ // Deallocate the storage for the cluster starting at `address`.
+ // Any future reads of this cluster will return all zeroes.
+ fn deallocate_cluster(&mut self, address: u64) -> std::io::Result<()> {
+ if address >= self.virtual_size() as u64 {
+ return Err(std::io::Error::from_raw_os_error(EINVAL));
+ }
+
+ let l1_index = self.l1_table_index(address) as usize;
+ let l2_addr_disk = *self
+ .l1_table
+ .get(l1_index)
+ .ok_or(std::io::Error::from_raw_os_error(EINVAL))?;
+ let l2_index = self.l2_table_index(address) as usize;
+
+ if l2_addr_disk == 0 {
+ // The whole L2 table for this address is not allocated yet,
+ // so the cluster must also be unallocated.
+ return Ok(());
+ }
+
+ if !self.l2_cache.contains_key(&l1_index) {
+ // Not in the cache.
+ let table =
+ VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
+ let l1_table = &self.l1_table;
+ let raw_file = &mut self.raw_file;
+ self.l2_cache.insert(l1_index, table, |index, evicted| {
+ raw_file.write_pointer_table(
+ l1_table[index],
+ evicted.get_values(),
+ CLUSTER_USED_FLAG,
+ )
+ })?;
+ }
+
+ let cluster_addr = self.l2_cache.get(&l1_index).unwrap()[l2_index];
+ if cluster_addr == 0 {
+ // This cluster is already unallocated; nothing to do.
+ return Ok(());
+ }
+
+ // Decrement the refcount.
+ let refcount = self
+ .refcounts
+ .get_cluster_refcount(&mut self.raw_file, cluster_addr)
+ .map_err(|_| std::io::Error::from_raw_os_error(EINVAL))?;
+ if refcount == 0 {
+ return Err(std::io::Error::from_raw_os_error(EINVAL));
+ }
+
+ let new_refcount = refcount - 1;
+ let mut newly_unref = self.set_cluster_refcount(cluster_addr, new_refcount)?;
+ self.unref_clusters.append(&mut newly_unref);
+
+ // Rewrite the L2 entry to remove the cluster mapping.
+ // unwrap is safe as we just checked/inserted this entry.
+ self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = 0;
+
+ if new_refcount == 0 {
+ let cluster_size = self.raw_file.cluster_size();
+ // This cluster is no longer in use; deallocate the storage.
+ // The underlying FS may not support FALLOC_FL_PUNCH_HOLE,
+ // so don't treat an error as fatal. Future reads will return zeros anyways.
+ let _ = self
+ .raw_file
+ .file_mut()
+ .punch_hole(cluster_addr, cluster_size);
+ self.unref_clusters.push(cluster_addr);
+ }
+ Ok(())
+ }
+
+ // Deallocate the storage for `length` bytes starting at `address`.
+ // Any future reads of this range will return all zeroes.
+ fn deallocate_bytes(&mut self, address: u64, length: usize) -> std::io::Result<()> {
+ let write_count: usize = self.limit_range_file(address, length);
+
+ let mut nwritten: usize = 0;
+ while nwritten < write_count {
+ let curr_addr = address + nwritten as u64;
+ let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
+
+ if count == self.raw_file.cluster_size() as usize {
+ // Full cluster - deallocate the storage.
+ self.deallocate_cluster(curr_addr)?;
+ } else {
+ // Partial cluster - zero out the relevant bytes if it was allocated.
+ // Any space in unallocated clusters can be left alone, since
+ // unallocated clusters already read back as zeroes.
+ if let Some(offset) = self.file_offset_read(curr_addr)? {
+ // Partial cluster - zero it out.
+ self.raw_file.file_mut().seek(SeekFrom::Start(offset))?;
+ self.raw_file.file_mut().write_zeroes(count)?;
+ }
+ }
+
+ nwritten += count;
+ }
+ Ok(())
+ }
+
+ // Reads an L2 cluster from the disk, returning an error if the file can't be read or if any
+ // cluster is compressed.
+ fn read_l2_cluster(raw_file: &mut QcowRawFile, cluster_addr: u64) -> std::io::Result<Vec<u64>> {
+ let file_values = raw_file.read_pointer_cluster(cluster_addr, None)?;
+ if file_values.iter().any(|entry| entry & COMPRESSED_FLAG != 0) {
+ return Err(std::io::Error::from_raw_os_error(ENOTSUP));
+ }
+ Ok(file_values
+ .iter()
+ .map(|entry| *entry & L2_TABLE_OFFSET_MASK)
+ .collect())
+ }
+
+ // Set the refcount for a cluster with the given address.
+ // Returns a list of any refblocks that can be reused, this happens when a refblock is moved,
+ // the old location can be reused.
+ fn set_cluster_refcount(&mut self, address: u64, refcount: u16) -> std::io::Result<Vec<u64>> {
+ let mut added_clusters = Vec::new();
+ let mut unref_clusters = Vec::new();
+ let mut refcount_set = false;
+ let mut new_cluster = None;
+
+ while !refcount_set {
+ match self.refcounts.set_cluster_refcount(
+ &mut self.raw_file,
+ address,
+ refcount,
+ new_cluster.take(),
+ ) {
+ Ok(None) => {
+ refcount_set = true;
+ }
+ Ok(Some(freed_cluster)) => {
+ unref_clusters.push(freed_cluster);
+ refcount_set = true;
+ }
+ Err(refcount::Error::EvictingRefCounts(e)) => {
+ return Err(e);
+ }
+ Err(refcount::Error::InvalidIndex) => {
+ return Err(std::io::Error::from_raw_os_error(EINVAL));
+ }
+ Err(refcount::Error::NeedCluster(addr)) => {
+ // Read the address and call set_cluster_refcount again.
+ new_cluster = Some((
+ addr,
+ VecCache::from_vec(self.raw_file.read_refcount_block(addr)?),
+ ));
+ }
+ Err(refcount::Error::NeedNewCluster) => {
+ // Allocate the cluster and call set_cluster_refcount again.
+ let addr = self.get_new_cluster()?;
+ added_clusters.push(addr);
+ new_cluster = Some((
+ addr,
+ VecCache::new(self.refcounts.refcounts_per_block() as usize),
+ ));
+ }
+ Err(refcount::Error::ReadingRefCounts(e)) => {
+ return Err(e);
+ }
+ }
+ }
+
+ for addr in added_clusters {
+ self.set_cluster_refcount(addr, 1)?;
+ }
+ Ok(unref_clusters)
+ }
+
+ fn sync_caches(&mut self) -> std::io::Result<()> {
+ // Write out all dirty L2 tables.
+ for (l1_index, l2_table) in self.l2_cache.iter_mut().filter(|(_k, v)| v.dirty()) {
+ // The index must be valid from when we insterted it.
+ let addr = self.l1_table[*l1_index];
+ if addr != 0 {
+ self.raw_file.write_pointer_table(
+ addr,
+ l2_table.get_values(),
+ CLUSTER_USED_FLAG,
+ )?;
+ } else {
+ return Err(std::io::Error::from_raw_os_error(EINVAL));
+ }
+ l2_table.mark_clean();
+ }
+ // Write the modified refcount blocks.
+ self.refcounts.flush_blocks(&mut self.raw_file)?;
+ // Make sure metadata(file len) and all data clusters are written.
+ self.raw_file.file_mut().sync_all()?;
+
+ // Push L1 table and refcount table last as all the clusters they point to are now
+ // guaranteed to be valid.
+ let mut sync_required = false;
+ if self.l1_table.dirty() {
+ self.raw_file.write_pointer_table(
+ self.header.l1_table_offset,
+ &self.l1_table.get_values(),
+ 0,
+ )?;
+ self.l1_table.mark_clean();
+ sync_required = true;
+ }
+ sync_required |= self.refcounts.flush_table(&mut self.raw_file)?;
+ if sync_required {
+ self.raw_file.file_mut().sync_data()?;
+ }
+ Ok(())
+ }
+
+ // Reads `count` bytes from the cursor position, calling `cb` repeatedly with the backing file,
+ // number of bytes read so far, and number of bytes to read from the file in that invocation. If
+ // None is given to `cb` in place of the backing file, the `cb` should infer zeros would have
+ // been read.
+ fn read_cb<F>(&mut self, count: usize, mut cb: F) -> std::io::Result<usize>
+ where
+ F: FnMut(Option<&mut File>, usize, usize) -> std::io::Result<()>,
+ {
+ let address: u64 = self.current_offset as u64;
+ let read_count: usize = self.limit_range_file(address, count);
+
+ let mut nread: usize = 0;
+ while nread < read_count {
+ let curr_addr = address + nread as u64;
+ let file_offset = self.file_offset_read(curr_addr)?;
+ let count = self.limit_range_cluster(curr_addr, read_count - nread);
+
+ if let Some(offset) = file_offset {
+ self.raw_file.file_mut().seek(SeekFrom::Start(offset))?;
+ cb(Some(self.raw_file.file_mut()), nread, count)?;
+ } else {
+ cb(None, nread, count)?;
+ }
+
+ nread += count;
+ }
+ self.current_offset += read_count as u64;
+ Ok(read_count)
+ }
+
+ // Writes `count` bytes to the cursor position, calling `cb` repeatedly with the backing file,
+ // number of bytes written so far, and number of bytes to write to the file in that invocation.
+ fn write_cb<F>(&mut self, count: usize, mut cb: F) -> std::io::Result<usize>
+ where
+ F: FnMut(&mut File, usize, usize) -> std::io::Result<()>,
+ {
+ let address: u64 = self.current_offset as u64;
+ let write_count: usize = self.limit_range_file(address, count);
+
+ let mut nwritten: usize = 0;
+ while nwritten < write_count {
+ let curr_addr = address + nwritten as u64;
+ let offset = self.file_offset_write(curr_addr)?;
+ let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
+
+ if let Err(e) = self.raw_file.file_mut().seek(SeekFrom::Start(offset)) {
+ return Err(e);
+ }
+ if let Err(e) = cb(self.raw_file.file_mut(), nwritten, count) {
+ return Err(e);
+ }
+
+ nwritten += count;
+ }
+ self.current_offset += write_count as u64;
+ Ok(write_count)
+ }
+}
+
+impl Drop for QcowFile {
+ fn drop(&mut self) {
+ let _ = self.sync_caches();
+ }
+}
+
+impl AsRawFd for QcowFile {
+ fn as_raw_fd(&self) -> RawFd {
+ self.raw_file.file().as_raw_fd()
+ }
+}
+
+impl Read for QcowFile {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ self.read_cb(buf.len(), |file, offset, count| match file {
+ Some(f) => f.read_exact(&mut buf[offset..(offset + count)]),
+ None => {
+ for b in &mut buf[offset..(offset + count)] {
+ *b = 0;
+ }
+ Ok(())
+ }
+ })
+ }
+}
+
+impl Seek for QcowFile {
+ fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
+ let new_offset: Option<u64> = match pos {
+ SeekFrom::Start(off) => Some(off),
+ SeekFrom::End(off) => {
+ if off < 0 {
+ 0i64.checked_sub(off)
+ .and_then(|increment| self.virtual_size().checked_sub(increment as u64))
+ } else {
+ self.virtual_size().checked_add(off as u64)
+ }
+ }
+ SeekFrom::Current(off) => {
+ if off < 0 {
+ 0i64.checked_sub(off)
+ .and_then(|increment| self.current_offset.checked_sub(increment as u64))
+ } else {
+ self.current_offset.checked_add(off as u64)
+ }
+ }
+ };
+
+ if let Some(o) = new_offset {
+ if o <= self.virtual_size() {
+ self.current_offset = o;
+ return Ok(o);
+ }
+ }
+ Err(std::io::Error::from_raw_os_error(EINVAL))
+ }
+}
+
+impl Write for QcowFile {
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ self.write_cb(buf.len(), |file, offset, count| {
+ file.write_all(&buf[offset..(offset + count)])
+ })
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ self.sync_caches()?;
+ self.avail_clusters.append(&mut self.unref_clusters);
+ Ok(())
+ }
+}
+
+impl FileReadWriteVolatile for QcowFile {
+ fn read_volatile(&mut self, slice: VolatileSlice) -> io::Result<usize> {
+ self.read_cb(slice.size() as usize, |file, offset, count| {
+ let sub_slice = slice.get_slice(offset as u64, count as u64).unwrap();
+ match file {
+ Some(f) => f.read_exact_volatile(sub_slice),
+ None => {
+ sub_slice.write_bytes(0);
+ Ok(())
+ }
+ }
+ })
+ }
+
+ fn write_volatile(&mut self, slice: VolatileSlice) -> io::Result<usize> {
+ self.write_cb(slice.size() as usize, |file, offset, count| {
+ let sub_slice = slice.get_slice(offset as u64, count as u64).unwrap();
+ file.write_all_volatile(sub_slice)
+ })
+ }
+}
+
+impl FileSync for QcowFile {
+ fn fsync(&mut self) -> std::io::Result<()> {
+ self.flush()
+ }
+}
+
+impl FileSetLen for QcowFile {
+ fn set_len(&self, _len: u64) -> std::io::Result<()> {
+ Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "set_len() not supported for QcowFile",
+ ))
+ }
+}
+
+impl PunchHole for QcowFile {
+ fn punch_hole(&mut self, offset: u64, length: u64) -> std::io::Result<()> {
+ let mut remaining = length;
+ let mut offset = offset;
+ while remaining > 0 {
+ let chunk_length = min(remaining, std::usize::MAX as u64) as usize;
+ self.deallocate_bytes(offset, chunk_length)?;
+ remaining -= chunk_length as u64;
+ offset += chunk_length as u64;
+ }
+ Ok(())
+ }
+}
+
+impl SeekHole for QcowFile {
+ fn seek_hole(&mut self, offset: u64) -> io::Result<Option<u64>> {
+ match self.find_allocated_cluster(offset, false) {
+ Err(e) => Err(e),
+ Ok(None) => {
+ if offset < self.virtual_size() {
+ Ok(Some(self.seek(SeekFrom::End(0))?))
+ } else {
+ Ok(None)
+ }
+ }
+ Ok(Some(o)) => {
+ self.seek(SeekFrom::Start(o))?;
+ Ok(Some(o))
+ }
+ }
+ }
+
+ fn seek_data(&mut self, offset: u64) -> io::Result<Option<u64>> {
+ match self.find_allocated_cluster(offset, true) {
+ Err(e) => Err(e),
+ Ok(None) => Ok(None),
+ Ok(Some(o)) => {
+ self.seek(SeekFrom::Start(o))?;
+ Ok(Some(o))
+ }
+ }
+ }
+}
+
+// Returns an Error if the given offset doesn't align to a cluster boundary.
+fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> {
+ if offset & ((0x01 << cluster_bits) - 1) != 0 {
+ return Err(Error::InvalidOffset(offset));
+ }
+ Ok(())
+}
+
+// Ceiling of the division of `dividend`/`divisor`.
+fn div_round_up_u64(dividend: u64, divisor: u64) -> u64 {
+ (dividend + divisor - 1) / divisor
+}
+
+// Ceiling of the division of `dividend`/`divisor`.
+fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
+ (dividend + divisor - 1) / divisor
+}
+
+fn convert_copy<R, W>(reader: &mut R, writer: &mut W, offset: u64, size: u64) -> Result<()>
+where
+ R: Read + Seek,
+ W: Write + Seek,
+{
+ const CHUNK_SIZE: usize = 65536;
+ let mut buf = [0; CHUNK_SIZE];
+ let mut read_count = 0;
+ reader
+ .seek(SeekFrom::Start(offset))
+ .map_err(Error::SeekingFile)?;
+ writer
+ .seek(SeekFrom::Start(offset))
+ .map_err(Error::SeekingFile)?;
+ loop {
+ let this_count = min(CHUNK_SIZE as u64, size - read_count) as usize;
+ let nread = reader
+ .read(&mut buf[..this_count])
+ .map_err(Error::ReadingData)?;
+ writer.write(&buf[..nread]).map_err(Error::WritingData)?;
+ read_count += nread as u64;
+ if nread == 0 || read_count == size {
+ break;
+ }
+ }
+
+ Ok(())
+}
+
+fn convert_reader_writer<R, W>(reader: &mut R, writer: &mut W, size: u64) -> Result<()>
+where
+ R: Read + Seek + SeekHole,
+ W: Write + Seek,
+{
+ let mut offset = 0;
+ while offset < size {
+ // Find the next range of data.
+ let next_data = match reader.seek_data(offset).map_err(Error::SeekingFile)? {
+ Some(o) => o,
+ None => {
+ // No more data in the file.
+ break;
+ }
+ };
+ let next_hole = match reader.seek_hole(next_data).map_err(Error::SeekingFile)? {
+ Some(o) => o,
+ None => {
+ // This should not happen - there should always be at least one hole
+ // after any data.
+ return Err(Error::SeekingFile(io::Error::from_raw_os_error(EINVAL)));
+ }
+ };
+ let count = next_hole - next_data;
+ convert_copy(reader, writer, next_data, count)?;
+ offset = next_hole;
+ }
+
+ Ok(())
+}
+
+fn convert_reader<R>(reader: &mut R, dst_file: File, dst_type: ImageType) -> Result<()>
+where
+ R: Read + Seek + SeekHole,
+{
+ let src_size = reader.seek(SeekFrom::End(0)).map_err(Error::SeekingFile)?;
+ reader
+ .seek(SeekFrom::Start(0))
+ .map_err(Error::SeekingFile)?;
+
+ // Ensure the destination file is empty before writing to it.
+ dst_file.set_len(0).map_err(Error::SettingFileSize)?;
+
+ match dst_type {
+ ImageType::Qcow2 => {
+ let mut dst_writer = QcowFile::new(dst_file, src_size)?;
+ convert_reader_writer(reader, &mut dst_writer, src_size)
+ }
+ ImageType::Raw => {
+ let mut dst_writer = dst_file;
+ // Set the length of the destination file to convert it into a sparse file
+ // of the desired size.
+ dst_writer
+ .set_len(src_size)
+ .map_err(Error::SettingFileSize)?;
+ convert_reader_writer(reader, &mut dst_writer, src_size)
+ }
+ }
+}
+
+/// Copy the contents of a disk image in `src_file` into `dst_file`.
+/// The type of `src_file` is automatically detected, and the output file type is
+/// determined by `dst_type`.
+pub fn convert(src_file: File, dst_file: File, dst_type: ImageType) -> Result<()> {
+ let src_type = detect_image_type(&src_file)?;
+ match src_type {
+ ImageType::Qcow2 => {
+ let mut src_reader = QcowFile::from(src_file)?;
+ convert_reader(&mut src_reader, dst_file, dst_type)
+ }
+ ImageType::Raw => {
+ // src_file is a raw file.
+ let mut src_reader = src_file;
+ convert_reader(&mut src_reader, dst_file, dst_type)
+ }
+ }
+}
+
+/// Detect the type of an image file by checking for a valid qcow2 header.
+pub fn detect_image_type(file: &File) -> Result<ImageType> {
+ let mut f = file;
+ let orig_seek = f.seek(SeekFrom::Current(0)).map_err(Error::SeekingFile)?;
+ f.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
+ let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
+ let image_type = if magic == QCOW_MAGIC {
+ ImageType::Qcow2
+ } else {
+ ImageType::Raw
+ };
+ f.seek(SeekFrom::Start(orig_seek))
+ .map_err(Error::SeekingFile)?;
+ Ok(image_type)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::fs::File;
+ use std::io::{Read, Seek, SeekFrom, Write};
+ use sys_util::SharedMemory;
+
+ fn valid_header() -> Vec<u8> {
+ vec![
+ 0x51u8, 0x46, 0x49, 0xfb, // magic
+ 0x00, 0x00, 0x00, 0x03, // version
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // backing file offset
+ 0x00, 0x00, 0x00, 0x00, // backing file size
+ 0x00, 0x00, 0x00, 0x10, // cluster_bits
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, // size
+ 0x00, 0x00, 0x00, 0x00, // crypt method
+ 0x00, 0x00, 0x01, 0x00, // L1 size
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // L1 table offset
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // refcount table offset
+ 0x00, 0x00, 0x00, 0x03, // refcount table clusters
+ 0x00, 0x00, 0x00, 0x00, // nb snapshots
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // snapshots offset
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // incompatible_features
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compatible_features
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // autoclear_features
+ 0x00, 0x00, 0x00, 0x04, // refcount_order
+ 0x00, 0x00, 0x00, 0x68, // header_length
+ ]
+ }
+
+ // Test case found by clusterfuzz to allocate excessive memory.
+ fn test_huge_header() -> Vec<u8> {
+ vec![
+ 0x51, 0x46, 0x49, 0xfb, // magic
+ 0x00, 0x00, 0x00, 0x03, // version
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // backing file offset
+ 0x00, 0x00, 0x00, 0x00, // backing file size
+ 0x00, 0x00, 0x00, 0x09, // cluster_bits
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // size
+ 0x00, 0x00, 0x00, 0x00, // crypt method
+ 0x00, 0x00, 0x01, 0x00, // L1 size
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // L1 table offset
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // refcount table offset
+ 0x00, 0x00, 0x00, 0x03, // refcount table clusters
+ 0x00, 0x00, 0x00, 0x00, // nb snapshots
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // snapshots offset
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // incompatible_features
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compatible_features
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // autoclear_features
+ 0x00, 0x00, 0x00, 0x04, // refcount_order
+ 0x00, 0x00, 0x00, 0x68, // header_length
+ ]
+ }
+
+ fn with_basic_file<F>(header: &[u8], mut testfn: F)
+ where
+ F: FnMut(File),
+ {
+ let shm = SharedMemory::new(None).unwrap();
+ let mut disk_file: File = shm.into();
+ disk_file.write_all(&header).unwrap();
+ disk_file.set_len(0x1_0000_0000).unwrap();
+ disk_file.seek(SeekFrom::Start(0)).unwrap();
+
+ testfn(disk_file); // File closed when the function exits.
+ }
+
+ fn with_default_file<F>(file_size: u64, mut testfn: F)
+ where
+ F: FnMut(QcowFile),
+ {
+ let shm = SharedMemory::new(None).unwrap();
+ let qcow_file = QcowFile::new(shm.into(), file_size).unwrap();
+
+ testfn(qcow_file); // File closed when the function exits.
+ }
+
+ #[test]
+ fn default_header() {
+ let header = QcowHeader::create_for_size(0x10_0000);
+ let shm = SharedMemory::new(None).unwrap();
+ let mut disk_file: File = shm.into();
+ header
+ .write_to(&mut disk_file)
+ .expect("Failed to write header to shm.");
+ disk_file.seek(SeekFrom::Start(0)).unwrap();
+ QcowFile::from(disk_file).expect("Failed to create Qcow from default Header");
+ }
+
+ #[test]
+ fn header_read() {
+ with_basic_file(&valid_header(), |mut disk_file: File| {
+ QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
+ });
+ }
+
+ #[test]
+ fn invalid_magic() {
+ let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb];
+ with_basic_file(&invalid_header, |mut disk_file: File| {
+ QcowHeader::new(&mut disk_file).expect_err("Invalid header worked.");
+ });
+ }
+
+ #[test]
+ fn invalid_refcount_order() {
+ let mut header = valid_header();
+ header[99] = 2;
+ with_basic_file(&header, |disk_file: File| {
+ QcowFile::from(disk_file).expect_err("Invalid refcount order worked.");
+ });
+ }
+
+ #[test]
+ fn invalid_cluster_bits() {
+ let mut header = test_huge_header();
+ header[23] = 3;
+ with_basic_file(&test_huge_header(), |disk_file: File| {
+ QcowFile::from(disk_file).expect_err("Failed to create file.");
+ });
+ }
+
+ #[test]
+ fn test_header_huge_file() {
+ let header = test_huge_header();
+ with_basic_file(&header, |disk_file: File| {
+ QcowFile::from(disk_file).expect_err("Failed to create file.");
+ });
+ }
+
+ #[test]
+ fn test_header_1_tb_file_min_cluster() {
+ let mut header = test_huge_header();
+ header[24] = 0;
+ header[26] = 1;
+ header[31] = 0;
+ // 1 TB with the min cluster size makes the arrays too big, it should fail.
+ with_basic_file(&header, |disk_file: File| {
+ QcowFile::from(disk_file).expect_err("Failed to create file.");
+ });
+ }
+
+ #[test]
+ fn test_header_1_tb_file() {
+ let mut header = test_huge_header();
+ // reset to 1 TB size.
+ header[24] = 0;
+ header[26] = 1;
+ header[31] = 0;
+ // set cluster_bits
+ header[23] = 16;
+ with_basic_file(&header, |disk_file: File| {
+ let mut qcow = QcowFile::from(disk_file).expect("Failed to create file.");
+ qcow.seek(SeekFrom::Start(0x100_0000_0000 - 8))
+ .expect("Failed to seek.");
+ let value = 0x0000_0040_3f00_ffffu64;
+ qcow.write_all(&value.to_le_bytes())
+ .expect("failed to write data");
+ });
+ }
+
+ #[test]
+ fn write_read_start() {
+ with_basic_file(&valid_header(), |disk_file: File| {
+ let mut q = QcowFile::from(disk_file).unwrap();
+ q.write(b"test first bytes")
+ .expect("Failed to write test string.");
+ let mut buf = [0u8; 4];
+ q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
+ q.read(&mut buf).expect("Failed to read.");
+ assert_eq!(&buf, b"test");
+ });
+ }
+
+ #[test]
+ fn offset_write_read() {
+ with_basic_file(&valid_header(), |disk_file: File| {
+ let mut q = QcowFile::from(disk_file).unwrap();
+ let b = [0x55u8; 0x1000];
+ q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
+ q.write(&b).expect("Failed to write test string.");
+ let mut buf = [0u8; 4];
+ q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
+ q.read(&mut buf).expect("Failed to read.");
+ assert_eq!(buf[0], 0x55);
+ });
+ }
+
+ #[test]
+ fn write_zeroes_read() {
+ with_basic_file(&valid_header(), |disk_file: File| {
+ let mut q = QcowFile::from(disk_file).unwrap();
+ // Write some test data.
+ let b = [0x55u8; 0x1000];
+ q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
+ q.write(&b).expect("Failed to write test string.");
+ // Overwrite the test data with zeroes.
+ q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
+ let nwritten = q.write_zeroes(0x200).expect("Failed to write zeroes.");
+ assert_eq!(nwritten, 0x200);
+ // Verify that the correct part of the data was zeroed out.
+ let mut buf = [0u8; 0x1000];
+ q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
+ q.read(&mut buf).expect("Failed to read.");
+ assert_eq!(buf[0], 0);
+ assert_eq!(buf[0x1FF], 0);
+ assert_eq!(buf[0x200], 0x55);
+ assert_eq!(buf[0xFFF], 0x55);
+ });
+ }
+
+ #[test]
+ fn write_zeroes_full_cluster() {
+ // Choose a size that is larger than a cluster.
+ // valid_header uses cluster_bits = 12, which corresponds to a cluster size of 4096.
+ const CHUNK_SIZE: usize = 4096 * 2 + 512;
+ with_basic_file(&valid_header(), |disk_file: File| {
+ let mut q = QcowFile::from(disk_file).unwrap();
+ // Write some test data.
+ let b = [0x55u8; CHUNK_SIZE];
+ q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
+ q.write(&b).expect("Failed to write test string.");
+ // Overwrite the full cluster with zeroes.
+ q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
+ let nwritten = q.write_zeroes(CHUNK_SIZE).expect("Failed to write zeroes.");
+ assert_eq!(nwritten, CHUNK_SIZE);
+ // Verify that the data was zeroed out.
+ let mut buf = [0u8; CHUNK_SIZE];
+ q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
+ q.read(&mut buf).expect("Failed to read.");
+ assert_eq!(buf[0], 0);
+ assert_eq!(buf[CHUNK_SIZE - 1], 0);
+ });
+ }
+
+ #[test]
+ fn test_header() {
+ with_basic_file(&valid_header(), |disk_file: File| {
+ let q = QcowFile::from(disk_file).unwrap();
+ assert_eq!(q.virtual_size(), 0x20_0000_0000);
+ });
+ }
+
+ #[test]
+ fn read_small_buffer() {
+ with_basic_file(&valid_header(), |disk_file: File| {
+ let mut q = QcowFile::from(disk_file).unwrap();
+ let mut b = [5u8; 16];
+ q.seek(SeekFrom::Start(1000)).expect("Failed to seek.");
+ q.read(&mut b).expect("Failed to read.");
+ assert_eq!(0, b[0]);
+ assert_eq!(0, b[15]);
+ });
+ }
+
+ #[test]
+ fn replay_ext4() {
+ with_basic_file(&valid_header(), |disk_file: File| {
+ let mut q = QcowFile::from(disk_file).unwrap();
+ const BUF_SIZE: usize = 0x1000;
+ let mut b = [0u8; BUF_SIZE];
+
+ struct Transfer {
+ pub write: bool,
+ pub addr: u64,
+ };
+
+ // Write transactions from mkfs.ext4.
+ let xfers: Vec<Transfer> = vec![
+ Transfer {
+ write: false,
+ addr: 0xfff0000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xfffe000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x0,
+ },
+ Transfer {
+ write: false,
+ addr: 0x1000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xffff000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xffdf000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xfff8000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xffe0000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xffce000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xffb6000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xffab000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xffa4000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xff8e000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xff86000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xff84000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xff89000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xfe7e000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x100000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x3000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x7000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xf000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x2000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x4000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x5000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x6000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x8000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x9000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xa000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xb000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xc000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xd000,
+ },
+ Transfer {
+ write: false,
+ addr: 0xe000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x10000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x11000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x12000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x13000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x14000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x15000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x16000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x17000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x18000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x19000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x1a000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x1b000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x1c000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x1d000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x1e000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x1f000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x21000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x22000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x24000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x40000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x0,
+ },
+ Transfer {
+ write: false,
+ addr: 0x3000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x7000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x0,
+ },
+ Transfer {
+ write: false,
+ addr: 0x1000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x2000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x3000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x0,
+ },
+ Transfer {
+ write: false,
+ addr: 0x449000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x48000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x48000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x448000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x44a000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x48000,
+ },
+ Transfer {
+ write: false,
+ addr: 0x48000,
+ },
+ Transfer {
+ write: true,
+ addr: 0x0,
+ },
+ Transfer {
+ write: true,
+ addr: 0x448000,
+ },
+ Transfer {
+ write: true,
+ addr: 0x449000,
+ },
+ Transfer {
+ write: true,
+ addr: 0x44a000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff0000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff1000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff2000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff3000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff4000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff5000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff6000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff7000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff8000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfff9000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfffa000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfffb000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfffc000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfffd000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xfffe000,
+ },
+ Transfer {
+ write: true,
+ addr: 0xffff000,
+ },
+ ];
+
+ for xfer in &xfers {
+ q.seek(SeekFrom::Start(xfer.addr)).expect("Failed to seek.");
+ if xfer.write {
+ q.write(&b).expect("Failed to write.");
+ } else {
+ let read_count: usize = q.read(&mut b).expect("Failed to read.");
+ assert_eq!(read_count, BUF_SIZE);
+ }
+ }
+ });
+ }
+
+ #[test]
+ fn combo_write_read() {
+ with_default_file(1024 * 1024 * 1024 * 256, |mut qcow_file| {
+ const NUM_BLOCKS: usize = 555;
+ const BLOCK_SIZE: usize = 0x1_0000;
+ const OFFSET: usize = 0x1_0000_0020;
+ let data = [0x55u8; BLOCK_SIZE];
+ let mut readback = [0u8; BLOCK_SIZE];
+ for i in 0..NUM_BLOCKS {
+ let seek_offset = OFFSET + i * BLOCK_SIZE;
+ qcow_file
+ .seek(SeekFrom::Start(seek_offset as u64))
+ .expect("Failed to seek.");
+ let nwritten = qcow_file.write(&data).expect("Failed to write test data.");
+ assert_eq!(nwritten, BLOCK_SIZE);
+ // Read back the data to check it was written correctly.
+ qcow_file
+ .seek(SeekFrom::Start(seek_offset as u64))
+ .expect("Failed to seek.");
+ let nread = qcow_file.read(&mut readback).expect("Failed to read.");
+ assert_eq!(nread, BLOCK_SIZE);
+ for (orig, read) in data.iter().zip(readback.iter()) {
+ assert_eq!(orig, read);
+ }
+ }
+ // Check that address 0 is still zeros.
+ qcow_file.seek(SeekFrom::Start(0)).expect("Failed to seek.");
+ let nread = qcow_file.read(&mut readback).expect("Failed to read.");
+ assert_eq!(nread, BLOCK_SIZE);
+ for read in readback.iter() {
+ assert_eq!(*read, 0);
+ }
+ // Check the data again after the writes have happened.
+ for i in 0..NUM_BLOCKS {
+ let seek_offset = OFFSET + i * BLOCK_SIZE;
+ qcow_file
+ .seek(SeekFrom::Start(seek_offset as u64))
+ .expect("Failed to seek.");
+ let nread = qcow_file.read(&mut readback).expect("Failed to read.");
+ assert_eq!(nread, BLOCK_SIZE);
+ for (orig, read) in data.iter().zip(readback.iter()) {
+ assert_eq!(orig, read);
+ }
+ }
+
+ assert_eq!(qcow_file.first_zero_refcount().unwrap(), None);
+ });
+ }
+
+ fn seek_cur(file: &mut QcowFile) -> u64 {
+ file.seek(SeekFrom::Current(0)).unwrap()
+ }
+
+ #[test]
+ fn seek_data() {
+ with_default_file(0x30000, |mut file| {
+ // seek_data at or after the end of the file should return None
+ assert_eq!(file.seek_data(0x10000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_data(0x10001).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // Write some data to [0x10000, 0x20000)
+ let b = [0x55u8; 0x10000];
+ file.seek(SeekFrom::Start(0x10000)).unwrap();
+ file.write_all(&b).unwrap();
+ assert_eq!(file.seek_data(0).unwrap(), Some(0x10000));
+ assert_eq!(seek_cur(&mut file), 0x10000);
+
+ // seek_data within data should return the same offset
+ assert_eq!(file.seek_data(0x10000).unwrap(), Some(0x10000));
+ assert_eq!(seek_cur(&mut file), 0x10000);
+ assert_eq!(file.seek_data(0x10001).unwrap(), Some(0x10001));
+ assert_eq!(seek_cur(&mut file), 0x10001);
+ assert_eq!(file.seek_data(0x1FFFF).unwrap(), Some(0x1FFFF));
+ assert_eq!(seek_cur(&mut file), 0x1FFFF);
+
+ assert_eq!(file.seek_data(0).unwrap(), Some(0x10000));
+ assert_eq!(seek_cur(&mut file), 0x10000);
+ assert_eq!(file.seek_data(0x1FFFF).unwrap(), Some(0x1FFFF));
+ assert_eq!(seek_cur(&mut file), 0x1FFFF);
+ assert_eq!(file.seek_data(0x20000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0x1FFFF);
+ });
+ }
+
+ #[test]
+ fn seek_hole() {
+ with_default_file(0x30000, |mut file| {
+ // File consisting entirely of a hole
+ assert_eq!(file.seek_hole(0).unwrap(), Some(0));
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
+ assert_eq!(seek_cur(&mut file), 0xFFFF);
+
+ // seek_hole at or after the end of the file should return None
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x30000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_hole(0x30001).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // Write some data to [0x10000, 0x20000)
+ let b = [0x55u8; 0x10000];
+ file.seek(SeekFrom::Start(0x10000)).unwrap();
+ file.write_all(&b).unwrap();
+
+ // seek_hole within a hole should return the same offset
+ assert_eq!(file.seek_hole(0).unwrap(), Some(0));
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
+ assert_eq!(seek_cur(&mut file), 0xFFFF);
+
+ // seek_hole within data should return the next hole
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x10001).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
+ assert_eq!(seek_cur(&mut file), 0xFFFF);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x20001));
+ assert_eq!(seek_cur(&mut file), 0x20001);
+
+ // seek_hole at EOF should return None
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x30000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // Write some data to [0x20000, 0x30000)
+ file.seek(SeekFrom::Start(0x20000)).unwrap();
+ file.write_all(&b).unwrap();
+
+ // seek_hole within [0x20000, 0x30000) should now find the hole at EOF
+ assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x30000));
+ assert_eq!(seek_cur(&mut file), 0x30000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x30000));
+ assert_eq!(seek_cur(&mut file), 0x30000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x30000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+ });
+ }
+
+ #[test]
+ fn rebuild_refcounts() {
+ with_basic_file(&valid_header(), |mut disk_file: File| {
+ let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
+ let cluster_size = 65536;
+ let mut raw_file =
+ QcowRawFile::from(disk_file, cluster_size).expect("Failed to create QcowRawFile.");
+ QcowFile::rebuild_refcounts(&mut raw_file, header)
+ .expect("Failed to rebuild recounts.");
+ });
+ }
+}
diff --git a/qcow/src/qcow_raw_file.rs b/qcow/src/qcow_raw_file.rs
new file mode 100644
index 0000000..456b986
--- /dev/null
+++ b/qcow/src/qcow_raw_file.rs
@@ -0,0 +1,136 @@
+// Copyright 2018 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, BufWriter, Seek, SeekFrom};
+use std::mem::size_of;
+
+use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
+
+/// A qcow file. Allows reading/writing clusters and appending clusters.
+#[derive(Debug)]
+pub struct QcowRawFile {
+ file: File,
+ cluster_size: u64,
+ cluster_mask: u64,
+}
+
+impl QcowRawFile {
+ /// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
+ /// a power of two.
+ pub fn from(file: File, cluster_size: u64) -> Option<Self> {
+ if cluster_size.count_ones() != 1 {
+ return None;
+ }
+ Some(QcowRawFile {
+ file,
+ cluster_size,
+ cluster_mask: cluster_size - 1,
+ })
+ }
+
+ /// Reads `count` 64 bit offsets and returns them as a vector.
+ /// `mask` optionally ands out some of the bits on the file.
+ pub fn read_pointer_table(
+ &mut self,
+ offset: u64,
+ count: u64,
+ mask: Option<u64>,
+ ) -> io::Result<Vec<u64>> {
+ let mut table = vec![0; count as usize];
+ self.file.seek(SeekFrom::Start(offset))?;
+ self.file.read_u64_into::<BigEndian>(&mut table)?;
+ if let Some(m) = mask {
+ for ptr in &mut table {
+ *ptr &= m;
+ }
+ }
+ Ok(table)
+ }
+
+ /// Reads a cluster's worth of 64 bit offsets and returns them as a vector.
+ /// `mask` optionally ands out some of the bits on the file.
+ pub fn read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>> {
+ let count = self.cluster_size / size_of::<u64>() as u64;
+ self.read_pointer_table(offset, count, mask)
+ }
+
+ /// Writes `table` of u64 pointers to `offset` in the file.
+ /// `non_zero_flags` will be ORed with all non-zero values in `table`.
+ /// writing.
+ pub fn write_pointer_table(
+ &mut self,
+ offset: u64,
+ table: &[u64],
+ non_zero_flags: u64,
+ ) -> io::Result<()> {
+ self.file.seek(SeekFrom::Start(offset))?;
+ let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u64>(), &self.file);
+ for addr in table {
+ let val = if *addr == 0 {
+ 0
+ } else {
+ *addr | non_zero_flags
+ };
+ buffer.write_u64::<BigEndian>(val)?;
+ }
+ Ok(())
+ }
+
+ /// Read a refcount block from the file and returns a Vec containing the block.
+ /// Always returns a cluster's worth of data.
+ pub fn read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u16>> {
+ let count = self.cluster_size / size_of::<u16>() as u64;
+ let mut table = vec![0; count as usize];
+ self.file.seek(SeekFrom::Start(offset))?;
+ self.file.read_u16_into::<BigEndian>(&mut table)?;
+ Ok(table)
+ }
+
+ /// Writes a refcount block to the file.
+ pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
+ self.file.seek(SeekFrom::Start(offset))?;
+ let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u16>(), &self.file);
+ for count in table {
+ buffer.write_u16::<BigEndian>(*count)?;
+ }
+ Ok(())
+ }
+
+ /// Allocates a new cluster at the end of the current file, return the address.
+ pub fn add_cluster_end(&mut self, max_valid_cluster_offset: u64) -> io::Result<Option<u64>> {
+ // Determine where the new end of the file should be and set_len, which
+ // translates to truncate(2).
+ let file_end: u64 = self.file.seek(SeekFrom::End(0))?;
+ let new_cluster_address: u64 = (file_end + self.cluster_size - 1) & !self.cluster_mask;
+
+ if new_cluster_address > max_valid_cluster_offset {
+ return Ok(None);
+ }
+
+ self.file.set_len(new_cluster_address + self.cluster_size)?;
+
+ Ok(Some(new_cluster_address))
+ }
+
+ /// Returns a reference to the underlying file.
+ pub fn file(&self) -> &File {
+ &self.file
+ }
+
+ /// Returns a mutable reference to the underlying file.
+ pub fn file_mut(&mut self) -> &mut File {
+ &mut self.file
+ }
+
+ /// Returns the size of the file's clusters.
+ pub fn cluster_size(&self) -> u64 {
+ self.cluster_size
+ }
+
+ /// Returns the offset of `address` within a cluster.
+ pub fn cluster_offset(&self, address: u64) -> u64 {
+ address & self.cluster_mask
+ }
+}
diff --git a/qcow/src/refcount.rs b/qcow/src/refcount.rs
new file mode 100644
index 0000000..5a87d86
--- /dev/null
+++ b/qcow/src/refcount.rs
@@ -0,0 +1,253 @@
+// Copyright 2018 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;
+use std::fmt::{self, Display};
+use std::io;
+
+use libc::EINVAL;
+
+use crate::qcow_raw_file::QcowRawFile;
+use crate::vec_cache::{CacheMap, Cacheable, VecCache};
+
+#[derive(Debug)]
+pub enum Error {
+ /// `EvictingCache` - Error writing a refblock from the cache to disk.
+ EvictingRefCounts(io::Error),
+ /// `InvalidIndex` - Address requested isn't within the range of the disk.
+ InvalidIndex,
+ /// `NeedCluster` - Handle this error by reading the cluster and calling the function again.
+ NeedCluster(u64),
+ /// `NeedNewCluster` - Handle this error by allocating a cluster and calling the function again.
+ NeedNewCluster,
+ /// `ReadingRefCounts` - Error reading the file in to the refcount cache.
+ ReadingRefCounts(io::Error),
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ EvictingRefCounts(e) => write!(
+ f,
+ "failed to write a refblock from the cache to disk: {}",
+ e
+ ),
+ InvalidIndex => write!(f, "address requested is not within the range of the disk"),
+ NeedCluster(addr) => write!(f, "cluster with addr={} needs to be read", addr),
+ NeedNewCluster => write!(f, "new cluster needs to be allocated for refcounts"),
+ ReadingRefCounts(e) => {
+ write!(f, "failed to read the file into the refcount cache: {}", e)
+ }
+ }
+ }
+}
+
+/// Represents the refcount entries for an open qcow file.
+#[derive(Debug)]
+pub struct RefCount {
+ ref_table: VecCache<u64>,
+ refcount_table_offset: u64,
+ refblock_cache: CacheMap<VecCache<u16>>,
+ refcount_block_entries: u64, // number of refcounts in a cluster.
+ cluster_size: u64,
+ max_valid_cluster_offset: u64,
+}
+
+impl RefCount {
+ /// Creates a `RefCount` from `file`, reading the refcount table from `refcount_table_offset`.
+ /// `refcount_table_entries` specifies the number of refcount blocks used by this image.
+ /// `refcount_block_entries` indicates the number of refcounts in each refcount block.
+ /// Each refcount table entry points to a refcount block.
+ pub fn new(
+ raw_file: &mut QcowRawFile,
+ refcount_table_offset: u64,
+ refcount_table_entries: u64,
+ refcount_block_entries: u64,
+ cluster_size: u64,
+ ) -> io::Result<RefCount> {
+ let ref_table = VecCache::from_vec(raw_file.read_pointer_table(
+ refcount_table_offset,
+ refcount_table_entries,
+ None,
+ )?);
+ let max_valid_cluster_index = (ref_table.len() as u64) * refcount_block_entries - 1;
+ let max_valid_cluster_offset = max_valid_cluster_index * cluster_size;
+ Ok(RefCount {
+ ref_table,
+ refcount_table_offset,
+ refblock_cache: CacheMap::new(50),
+ refcount_block_entries,
+ cluster_size,
+ max_valid_cluster_offset,
+ })
+ }
+
+ /// Returns the number of refcounts per block.
+ pub fn refcounts_per_block(&self) -> u64 {
+ self.refcount_block_entries
+ }
+
+ /// Returns the maximum valid cluster offset in the raw file for this refcount table.
+ pub fn max_valid_cluster_offset(&self) -> u64 {
+ self.max_valid_cluster_offset
+ }
+
+ /// Returns `NeedNewCluster` if a new cluster needs to be allocated for refcounts. If an
+ /// existing cluster needs to be read, `NeedCluster(addr)` is returned. The Caller should
+ /// allocate a cluster or read the required one and call this function again with the cluster.
+ /// On success, an optional address of a dropped cluster is returned. The dropped cluster can
+ /// be reused for other purposes.
+ pub fn set_cluster_refcount(
+ &mut self,
+ raw_file: &mut QcowRawFile,
+ cluster_address: u64,
+ refcount: u16,
+ mut new_cluster: Option<(u64, VecCache<u16>)>,
+ ) -> Result<Option<u64>> {
+ let (table_index, block_index) = self.get_refcount_index(cluster_address);
+
+ let block_addr_disk = *self.ref_table.get(table_index).ok_or(Error::InvalidIndex)?;
+
+ // Fill the cache if this block isn't yet there.
+ if !self.refblock_cache.contains_key(&table_index) {
+ // Need a new cluster
+ if let Some((addr, table)) = new_cluster.take() {
+ self.ref_table[table_index] = addr;
+ let ref_table = &self.ref_table;
+ self.refblock_cache
+ .insert(table_index, table, |index, evicted| {
+ raw_file.write_refcount_block(ref_table[index], evicted.get_values())
+ })
+ .map_err(Error::EvictingRefCounts)?;
+ } else {
+ if block_addr_disk == 0 {
+ return Err(Error::NeedNewCluster);
+ }
+ return Err(Error::NeedCluster(block_addr_disk));
+ }
+ }
+
+ // Unwrap is safe here as the entry was filled directly above.
+ let dropped_cluster = if !self.refblock_cache.get(&table_index).unwrap().dirty() {
+ // Free the previously used block and use a new one. Writing modified counts to new
+ // blocks keeps the on-disk state consistent even if it's out of date.
+ if let Some((addr, _)) = new_cluster.take() {
+ self.ref_table[table_index] = addr;
+ Some(block_addr_disk)
+ } else {
+ return Err(Error::NeedNewCluster);
+ }
+ } else {
+ None
+ };
+
+ self.refblock_cache.get_mut(&table_index).unwrap()[block_index] = refcount;
+ Ok(dropped_cluster)
+ }
+
+ /// Flush the dirty refcount blocks. This must be done before flushing the table that points to
+ /// the blocks.
+ pub fn flush_blocks(&mut self, raw_file: &mut QcowRawFile) -> io::Result<()> {
+ // Write out all dirty L2 tables.
+ for (table_index, block) in self.refblock_cache.iter_mut().filter(|(_k, v)| v.dirty()) {
+ let addr = self.ref_table[*table_index];
+ if addr != 0 {
+ raw_file.write_refcount_block(addr, block.get_values())?;
+ } else {
+ return Err(std::io::Error::from_raw_os_error(EINVAL));
+ }
+ block.mark_clean();
+ }
+ Ok(())
+ }
+
+ /// Flush the refcount table that keeps the address of the refcounts blocks.
+ /// Returns true if the table changed since the previous `flush_table()` call.
+ pub fn flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result<bool> {
+ if self.ref_table.dirty() {
+ raw_file.write_pointer_table(
+ self.refcount_table_offset,
+ &self.ref_table.get_values(),
+ 0,
+ )?;
+ self.ref_table.mark_clean();
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+ }
+
+ /// Gets the refcount for a cluster with the given address.
+ pub fn get_cluster_refcount(
+ &mut self,
+ raw_file: &mut QcowRawFile,
+ address: u64,
+ ) -> Result<u16> {
+ let (table_index, block_index) = self.get_refcount_index(address);
+ let block_addr_disk = *self.ref_table.get(table_index).ok_or(Error::InvalidIndex)?;
+ if block_addr_disk == 0 {
+ return Ok(0);
+ }
+ if !self.refblock_cache.contains_key(&table_index) {
+ let table = VecCache::from_vec(
+ raw_file
+ .read_refcount_block(block_addr_disk)
+ .map_err(Error::ReadingRefCounts)?,
+ );
+ let ref_table = &self.ref_table;
+ self.refblock_cache
+ .insert(table_index, table, |index, evicted| {
+ raw_file.write_refcount_block(ref_table[index], evicted.get_values())
+ })
+ .map_err(Error::EvictingRefCounts)?;
+ }
+ Ok(self.refblock_cache.get(&table_index).unwrap()[block_index])
+ }
+
+ /// Returns the refcount table for this file. This is only useful for debugging.
+ pub fn ref_table(&self) -> &[u64] {
+ &self.ref_table.get_values()
+ }
+
+ /// Returns the refcounts stored in the given block.
+ pub fn refcount_block(
+ &mut self,
+ raw_file: &mut QcowRawFile,
+ table_index: usize,
+ ) -> Result<Option<&[u16]>> {
+ let block_addr_disk = *self.ref_table.get(table_index).ok_or(Error::InvalidIndex)?;
+ if block_addr_disk == 0 {
+ return Ok(None);
+ }
+ if !self.refblock_cache.contains_key(&table_index) {
+ let table = VecCache::from_vec(
+ raw_file
+ .read_refcount_block(block_addr_disk)
+ .map_err(Error::ReadingRefCounts)?,
+ );
+ // TODO(dgreid) - closure needs to return an error.
+ let ref_table = &self.ref_table;
+ self.refblock_cache
+ .insert(table_index, table, |index, evicted| {
+ raw_file.write_refcount_block(ref_table[index], evicted.get_values())
+ })
+ .map_err(Error::EvictingRefCounts)?;
+ }
+ // The index must exist as it was just inserted if it didn't already.
+ Ok(Some(
+ self.refblock_cache.get(&table_index).unwrap().get_values(),
+ ))
+ }
+
+ // Gets the address of the refcount block and the index into the block for the given address.
+ fn get_refcount_index(&self, address: u64) -> (usize, usize) {
+ let block_index = (address / self.cluster_size) % self.refcount_block_entries;
+ let refcount_table_index = (address / self.cluster_size) / self.refcount_block_entries;
+ (refcount_table_index as usize, block_index as usize)
+ }
+}
diff --git a/qcow/src/vec_cache.rs b/qcow/src/vec_cache.rs
new file mode 100644
index 0000000..ecd7673
--- /dev/null
+++ b/qcow/src/vec_cache.rs
@@ -0,0 +1,185 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::collections::hash_map::IterMut;
+use std::collections::HashMap;
+use std::io;
+use std::ops::{Index, IndexMut};
+use std::slice::SliceIndex;
+
+/// Trait that allows for checking if an implementor is dirty. Useful for types that are cached so
+/// it can be checked if they need to be committed to disk.
+pub trait Cacheable {
+ /// Used to check if the item needs to be written out or if it can be discarded.
+ fn dirty(&self) -> bool;
+}
+
+#[derive(Debug)]
+/// Represents a vector that implements the `Cacheable` trait so it can be held in a cache.
+pub struct VecCache<T: 'static + Copy + Default> {
+ vec: Box<[T]>,
+ dirty: bool,
+}
+
+impl<T: 'static + Copy + Default> VecCache<T> {
+ /// Creates a `VecCache` that can hold `count` elements.
+ pub fn new(count: usize) -> VecCache<T> {
+ VecCache {
+ vec: vec![Default::default(); count].into_boxed_slice(),
+ dirty: true,
+ }
+ }
+
+ /// Creates a `VecCache` from the passed in `vec`.
+ pub fn from_vec(vec: Vec<T>) -> VecCache<T> {
+ VecCache {
+ vec: vec.into_boxed_slice(),
+ dirty: false,
+ }
+ }
+
+ pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
+ where
+ I: SliceIndex<[T]>,
+ {
+ self.vec.get(index)
+ }
+
+ /// Gets a reference to the underlying vector.
+ pub fn get_values(&self) -> &[T] {
+ &self.vec
+ }
+
+ /// Mark this cache element as clean.
+ pub fn mark_clean(&mut self) {
+ self.dirty = false;
+ }
+
+ /// Returns the number of elements in the vector.
+ pub fn len(&self) -> usize {
+ self.vec.len()
+ }
+}
+
+impl<T: 'static + Copy + Default> Cacheable for VecCache<T> {
+ fn dirty(&self) -> bool {
+ self.dirty
+ }
+}
+
+impl<T: 'static + Copy + Default> Index<usize> for VecCache<T> {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &T {
+ self.vec.index(index)
+ }
+}
+
+impl<T: 'static + Copy + Default> IndexMut<usize> for VecCache<T> {
+ fn index_mut(&mut self, index: usize) -> &mut T {
+ self.dirty = true;
+ self.vec.index_mut(index)
+ }
+}
+
+#[derive(Debug)]
+pub struct CacheMap<T: Cacheable> {
+ capacity: usize,
+ map: HashMap<usize, T>,
+}
+
+impl<T: Cacheable> CacheMap<T> {
+ pub fn new(capacity: usize) -> Self {
+ CacheMap {
+ capacity,
+ map: HashMap::with_capacity(capacity),
+ }
+ }
+
+ pub fn contains_key(&self, key: &usize) -> bool {
+ self.map.contains_key(key)
+ }
+
+ pub fn get(&self, index: &usize) -> Option<&T> {
+ self.map.get(index)
+ }
+
+ pub fn get_mut(&mut self, index: &usize) -> Option<&mut T> {
+ self.map.get_mut(index)
+ }
+
+ pub fn iter_mut(&mut self) -> IterMut<usize, T> {
+ self.map.iter_mut()
+ }
+
+ // Check if the refblock cache is full and we need to evict.
+ pub fn insert<F>(&mut self, index: usize, block: T, write_callback: F) -> io::Result<()>
+ where
+ F: FnOnce(usize, T) -> io::Result<()>,
+ {
+ if self.map.len() == self.capacity {
+ // TODO(dgreid) - smarter eviction strategy.
+ let to_evict = *self.map.iter().nth(0).unwrap().0;
+ if let Some(evicted) = self.map.remove(&to_evict) {
+ if evicted.dirty() {
+ write_callback(to_evict, evicted)?;
+ }
+ }
+ }
+ self.map.insert(index, block);
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ struct NumCache(pub u64);
+ impl Cacheable for NumCache {
+ fn dirty(&self) -> bool {
+ true
+ }
+ }
+
+ #[test]
+ fn evicts_when_full() {
+ let mut cache = CacheMap::<NumCache>::new(3);
+ let mut evicted = None;
+ cache
+ .insert(0, NumCache(5), |index, _| {
+ evicted = Some(index);
+ Ok(())
+ })
+ .unwrap();
+ assert_eq!(evicted, None);
+ cache
+ .insert(1, NumCache(6), |index, _| {
+ evicted = Some(index);
+ Ok(())
+ })
+ .unwrap();
+ assert_eq!(evicted, None);
+ cache
+ .insert(2, NumCache(7), |index, _| {
+ evicted = Some(index);
+ Ok(())
+ })
+ .unwrap();
+ assert_eq!(evicted, None);
+ cache
+ .insert(3, NumCache(8), |index, _| {
+ evicted = Some(index);
+ Ok(())
+ })
+ .unwrap();
+ assert!(evicted.is_some());
+
+ // Check that three of the four items inserted are still there and that the most recently
+ // inserted is one of them.
+ let num_items = (0..=3).filter(|k| cache.contains_key(&k)).count();
+ assert_eq!(num_items, 3);
+ assert!(cache.contains_key(&3));
+ }
+}
diff --git a/qcow_utils/Cargo.toml b/qcow_utils/Cargo.toml
new file mode 100644
index 0000000..67b9d28
--- /dev/null
+++ b/qcow_utils/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "qcow_utils"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[lib]
+path = "src/qcow_utils.rs"
+crate-type = ["cdylib"]
+
+[[bin]]
+name = "qcow_img"
+path = "src/qcow_img.rs"
+
+[dependencies]
+getopts = "*"
+libc = "*"
+qcow = { path = "../qcow" }
+sys_util = { path = "../sys_util" }
diff --git a/qcow_utils/libqcow_utils.pc.in b/qcow_utils/libqcow_utils.pc.in
new file mode 100644
index 0000000..a5f0f5f
--- /dev/null
+++ b/qcow_utils/libqcow_utils.pc.in
@@ -0,0 +1,8 @@
+bslot=@BSLOT@
+include_dir=@INCLUDE_DIR@
+
+Name: libqcow_utils
+Description: QCOW2 shared library.
+Version: ${bslot}
+CFlags: -I${include_dir}
+Libs: -lqcow_utils
diff --git a/qcow_utils/platform2_preinstall.sh b/qcow_utils/platform2_preinstall.sh
new file mode 100755
index 0000000..5029010
--- /dev/null
+++ b/qcow_utils/platform2_preinstall.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Copyright 2018 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.
+
+set -e
+
+v=$1
+include_dir=$2
+target_dir=$3
+
+sed \
+ -e "s/@BSLOT@/${v}/g" \
+ -e "s:@INCLUDE_DIR@:${include_dir}:g" \
+ "qcow_utils/libqcow_utils.pc.in" > "${target_dir}/libqcow_utils.pc"
diff --git a/qcow_utils/src/qcow_img.rs b/qcow_utils/src/qcow_img.rs
new file mode 100644
index 0000000..66230da
--- /dev/null
+++ b/qcow_utils/src/qcow_img.rs
@@ -0,0 +1,322 @@
+// Copyright 2018 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::OpenOptions;
+use std::io::{Read, Write};
+
+use getopts::Options;
+
+use qcow::QcowFile;
+use sys_util::WriteZeroes;
+
+fn show_usage(program_name: &str) {
+ println!("Usage: {} [subcommand] <subcommand args>", program_name);
+ println!("\nSubcommands:");
+ println!(
+ "{} header <file name> - Show the qcow2 header for a file.",
+ program_name
+ );
+ println!(
+ "{} l1_table <file name> - Show the L1 table entries for a file.",
+ program_name
+ );
+ println!(
+ "{} l22table <file name> <l1 index> - Show the L2 table pointed to by the nth L1 entry.",
+ program_name
+ );
+ println!(
+ "{} ref_table <file name> - Show the refblock table for the file.",
+ program_name
+ );
+ println!(
+ "{} ref_block <file_name> <table index> - Show the nth reblock in the file.",
+ program_name
+ );
+ println!(
+ "{} dd <file_name> <source_file> - Write bytes from the raw source_file to the file.",
+ program_name
+ );
+ println!(
+ "{} convert <src_file> <dst_file> - Convert from src_file to dst_file.",
+ program_name
+ );
+}
+
+fn main() -> std::result::Result<(), ()> {
+ let args: Vec<String> = std::env::args().collect();
+ let opts = Options::new();
+
+ let matches = match opts.parse(&args[1..]) {
+ Ok(m) => m,
+ Err(f) => panic!(f.to_string()),
+ };
+
+ if matches.free.len() < 2 {
+ println!("Must specify the subcommand and the QCOW file to operate on.");
+ show_usage(&args[0]);
+ return Err(());
+ }
+
+ match matches.free[0].as_ref() {
+ "header" => show_header(&matches.free[1]),
+ "help" => {
+ show_usage(&args[0]);
+ Ok(())
+ }
+ "l1_table" => show_l1_table(&matches.free[1]),
+ "l2_table" => {
+ if matches.free.len() < 2 {
+ println!("Filename and table index are required.");
+ show_usage(&args[0]);
+ return Err(());
+ }
+ show_l2_table(&matches.free[1], matches.free[2].parse().unwrap())
+ }
+ "ref_table" => show_ref_table(&matches.free[1]),
+ "ref_block" => {
+ if matches.free.len() < 2 {
+ println!("Filename and block index are required.");
+ show_usage(&args[0]);
+ return Err(());
+ }
+ show_ref_block(&matches.free[1], matches.free[2].parse().unwrap())
+ }
+ "dd" => {
+ if matches.free.len() < 2 {
+ println!("Qcow and source file are required.");
+ show_usage(&args[0]);
+ return Err(());
+ }
+ let count = if matches.free.len() > 3 {
+ Some(matches.free[3].parse().unwrap())
+ } else {
+ None
+ };
+ dd(&matches.free[1], &matches.free[2], count)
+ }
+ "convert" => {
+ if matches.free.len() < 2 {
+ println!("Source and destination files are required.");
+ show_usage(&args[0]);
+ return Err(());
+ }
+ convert(&matches.free[1], &matches.free[2])
+ }
+ c => {
+ println!("invalid subcommand: {:?}", c);
+ Err(())
+ }
+ }
+}
+
+fn show_header(file_path: &str) -> std::result::Result<(), ()> {
+ let file = match OpenOptions::new().read(true).open(file_path) {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open {}", file_path);
+ return Err(());
+ }
+ };
+
+ let qcow_file = QcowFile::from(file).map_err(|_| ())?;
+ let header = qcow_file.header();
+
+ println!("magic {:x}", header.magic);
+ println!("version {:x}", header.version);
+ println!("backing_file_offset {:x}", header.backing_file_offset);
+ println!("backing_file_size {:x}", header.backing_file_size);
+ println!("cluster_bits {:x}", header.cluster_bits);
+ println!("size {:x}", header.size);
+ println!("crypt_method {:x}", header.crypt_method);
+ println!("l1_size {:x}", header.l1_size);
+ println!("l1_table_offset {:x}", header.l1_table_offset);
+ println!("refcount_table_offset {:x}", header.refcount_table_offset);
+ println!(
+ "refcount_table_clusters {:x}",
+ header.refcount_table_clusters
+ );
+ println!("nb_snapshots {:x}", header.nb_snapshots);
+ println!("snapshots_offset {:x}", header.snapshots_offset);
+ println!("incompatible_features {:x}", header.incompatible_features);
+ println!("compatible_features {:x}", header.compatible_features);
+ println!("autoclear_features {:x}", header.autoclear_features);
+ println!("refcount_order {:x}", header.refcount_order);
+ println!("header_size {:x}", header.header_size);
+ Ok(())
+}
+
+fn show_l1_table(file_path: &str) -> std::result::Result<(), ()> {
+ let file = match OpenOptions::new().read(true).open(file_path) {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open {}", file_path);
+ return Err(());
+ }
+ };
+
+ let qcow_file = QcowFile::from(file).map_err(|_| ())?;
+ let l1_table = qcow_file.l1_table();
+
+ for (i, l2_offset) in l1_table.iter().enumerate() {
+ println!("{}: {:x}", i, l2_offset);
+ }
+
+ Ok(())
+}
+
+fn show_l2_table(file_path: &str, index: usize) -> std::result::Result<(), ()> {
+ let file = match OpenOptions::new().read(true).open(file_path) {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open {}", file_path);
+ return Err(());
+ }
+ };
+
+ let mut qcow_file = QcowFile::from(file).map_err(|_| ())?;
+ let l2_table = qcow_file.l2_table(index).unwrap();
+
+ if let Some(cluster_addrs) = l2_table {
+ for (i, addr) in cluster_addrs.iter().enumerate() {
+ if i % 16 == 0 {
+ print!("\n{:x}:", i);
+ }
+ print!(" {:x}", addr);
+ }
+ }
+
+ Ok(())
+}
+
+fn show_ref_table(file_path: &str) -> std::result::Result<(), ()> {
+ let file = match OpenOptions::new().read(true).open(file_path) {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open {}", file_path);
+ return Err(());
+ }
+ };
+
+ let qcow_file = QcowFile::from(file).map_err(|_| ())?;
+ let ref_table = qcow_file.ref_table();
+
+ for (i, block_offset) in ref_table.iter().enumerate() {
+ println!("{}: {:x}", i, block_offset);
+ }
+
+ Ok(())
+}
+
+fn show_ref_block(file_path: &str, index: usize) -> std::result::Result<(), ()> {
+ let file = match OpenOptions::new().read(true).open(file_path) {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open {}", file_path);
+ return Err(());
+ }
+ };
+
+ let mut qcow_file = QcowFile::from(file).map_err(|_| ())?;
+ let ref_table = qcow_file.refcount_block(index).unwrap();
+
+ if let Some(counts) = ref_table {
+ for (i, count) in counts.iter().enumerate() {
+ if i % 16 == 0 {
+ print!("\n{:x}:", i);
+ }
+ print!(" {:x}", count);
+ }
+ }
+
+ Ok(())
+}
+
+// Transfers from a raw file specifiec in `source_path` to the qcow file specified in `file_path`.
+fn dd(file_path: &str, source_path: &str, count: Option<usize>) -> std::result::Result<(), ()> {
+ let file = match OpenOptions::new().read(true).write(true).open(file_path) {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open {}", file_path);
+ return Err(());
+ }
+ };
+
+ let mut qcow_file = QcowFile::from(file).map_err(|_| ())?;
+
+ let mut src_file = match OpenOptions::new().read(true).open(source_path) {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open {}", file_path);
+ return Err(());
+ }
+ };
+
+ let mut read_count = 0;
+ const CHUNK_SIZE: usize = 65536;
+ let mut buf = [0; CHUNK_SIZE];
+ loop {
+ let this_count = if let Some(count) = count {
+ std::cmp::min(CHUNK_SIZE, count - read_count)
+ } else {
+ CHUNK_SIZE
+ };
+ let nread = src_file.read(&mut buf[..this_count]).map_err(|_| ())?;
+ // If this block is all zeros, then use write_zeros so the output file is sparse.
+ if buf.iter().all(|b| *b == 0) {
+ qcow_file.write_zeroes(CHUNK_SIZE).map_err(|_| ())?;
+ } else {
+ qcow_file.write(&buf).map_err(|_| ())?;
+ }
+ read_count = read_count + nread;
+ if nread == 0 || Some(read_count) == count {
+ break;
+ }
+ }
+
+ println!("wrote {} bytes", read_count);
+
+ Ok(())
+}
+
+// Reads the file at `src_path` and writes it to `dst_path`.
+// The output format is detected based on the `dst_path` file extension.
+fn convert(src_path: &str, dst_path: &str) -> std::result::Result<(), ()> {
+ let src_file = match OpenOptions::new().read(true).open(src_path) {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open source file {}", src_path);
+ return Err(());
+ }
+ };
+
+ let dst_file = match OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .open(dst_path)
+ {
+ Ok(f) => f,
+ Err(_) => {
+ println!("Failed to open destination file {}", dst_path);
+ return Err(());
+ }
+ };
+
+ let dst_type = if dst_path.ends_with("qcow2") {
+ qcow::ImageType::Qcow2
+ } else {
+ qcow::ImageType::Raw
+ };
+
+ match qcow::convert(src_file, dst_file, dst_type) {
+ Ok(_) => {
+ println!("Converted {} to {}", src_path, dst_path);
+ Ok(())
+ }
+ Err(_) => {
+ println!("Failed to copy from {} to {}", src_path, dst_path);
+ Err(())
+ }
+ }
+}
diff --git a/qcow_utils/src/qcow_utils.h b/qcow_utils/src/qcow_utils.h
new file mode 100644
index 0000000..90aa23b
--- /dev/null
+++ b/qcow_utils/src/qcow_utils.h
@@ -0,0 +1,30 @@
+// Copyright 2018 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 <stdint.h>
+
+// Exported interface to basic qcow functionality to be used from C.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Create a basic, empty qcow2 file that can grow to `virtual_size` at `path`.
+int create_qcow_with_size(const char *path, uint64_t virtual_size);
+
+// Attempt to resize the disk image at `path` to `virtual_size` bytes if
+// the disk image is currently smaller than the requested size.
+int expand_disk_image(const char *path, uint64_t virtual_size);
+
+// Copy the source disk image from `src_fd` into `dst_fd` as a qcow2 image file.
+// Returns 0 on success or a negated errno value on failure.
+int convert_to_qcow2(int src_fd, int dst_fd);
+
+// Copy the source disk image from `src_fd` into `dst_fd` as a raw image file.
+// Returns 0 on success or a negated errno value on failure.
+int convert_to_raw(int src_fd, int dst_fd);
+
+#ifdef __cplusplus
+};
+#endif
diff --git a/qcow_utils/src/qcow_utils.rs b/qcow_utils/src/qcow_utils.rs
new file mode 100644
index 0000000..6c8b86f
--- /dev/null
+++ b/qcow_utils/src/qcow_utils.rs
@@ -0,0 +1,156 @@
+// Copyright 2018 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.
+
+// Exported interface to basic qcow functionality to be used from C.
+
+use libc::{EBADFD, EINVAL, EIO, ENOSYS};
+use std::ffi::CStr;
+use std::fs::{File, OpenOptions};
+use std::io::{Seek, SeekFrom};
+use std::mem::forget;
+use std::os::raw::{c_char, c_int};
+use std::os::unix::io::FromRawFd;
+use std::panic::catch_unwind;
+
+use qcow::{ImageType, QcowFile};
+use sys_util::{flock, FileSetLen, FlockOperation};
+
+trait DiskFile: FileSetLen + Seek {}
+impl<D: FileSetLen + Seek> DiskFile for D {}
+
+#[no_mangle]
+pub unsafe extern "C" fn create_qcow_with_size(path: *const c_char, virtual_size: u64) -> c_int {
+ // NULL pointers are checked, but this will access any other invalid pointer passed from C
+ // code. It's the caller's responsibility to pass a valid pointer.
+ if path.is_null() {
+ return -EINVAL;
+ }
+ let c_str = CStr::from_ptr(path);
+ let file_path = match c_str.to_str() {
+ Ok(s) => s,
+ Err(_) => return -EINVAL,
+ };
+
+ let file = match OpenOptions::new()
+ .create(true)
+ .read(true)
+ .write(true)
+ .open(file_path)
+ {
+ Ok(f) => f,
+ Err(_) => return -1,
+ };
+
+ match QcowFile::new(file, virtual_size) {
+ Ok(_) => 0,
+ Err(_) => -1,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn expand_disk_image(path: *const c_char, virtual_size: u64) -> c_int {
+ // NULL pointers are checked, but this will access any other invalid pointer passed from C
+ // code. It's the caller's responsibility to pass a valid pointer.
+ if path.is_null() {
+ return -EINVAL;
+ }
+ let c_str = CStr::from_ptr(path);
+ let file_path = match c_str.to_str() {
+ Ok(s) => s,
+ Err(_) => return -EINVAL,
+ };
+
+ let raw_image = match OpenOptions::new().read(true).write(true).open(file_path) {
+ Ok(f) => f,
+ Err(_) => return -EIO,
+ };
+
+ // Lock the disk image to prevent other processes from using it.
+ if let Err(_) = flock(&raw_image, FlockOperation::LockExclusive, true) {
+ return -EIO;
+ }
+
+ let image_type = match qcow::detect_image_type(&raw_image) {
+ Ok(t) => t,
+ Err(_) => return -EINVAL,
+ };
+
+ let mut disk_image: Box<DiskFile> = match image_type {
+ ImageType::Raw => Box::new(raw_image),
+ ImageType::Qcow2 => match QcowFile::from(raw_image) {
+ Ok(f) => Box::new(f),
+ Err(_) => return -EINVAL,
+ },
+ };
+
+ // For safety against accidentally shrinking the disk image due to a
+ // programming error elsewhere, verify that the new size is larger than
+ // the current size. This is safe against races due to the exclusive
+ // flock() taken above - this is only an advisory lock, but it is also
+ // acquired by other instances of this function as well as crosvm
+ // itself when running a VM, so this should be safe in all cases that
+ // can access a disk image in normal operation.
+ let current_size = match disk_image.seek(SeekFrom::End(0)) {
+ Ok(len) => len,
+ Err(_) => return -EIO,
+ };
+ if current_size >= virtual_size {
+ return 0;
+ }
+
+ match disk_image.set_len(virtual_size) {
+ Ok(_) => 0,
+ Err(_) => -ENOSYS,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn convert_to_qcow2(src_fd: c_int, dst_fd: c_int) -> c_int {
+ // The caller is responsible for passing valid file descriptors.
+ // The caller retains ownership of the file descriptors.
+ let src_file = File::from_raw_fd(src_fd);
+ let src_file_owned = src_file.try_clone();
+ forget(src_file);
+
+ let dst_file = File::from_raw_fd(dst_fd);
+ let dst_file_owned = dst_file.try_clone();
+ forget(dst_file);
+
+ match (src_file_owned, dst_file_owned) {
+ (Ok(src_file), Ok(dst_file)) => {
+ catch_unwind(
+ || match qcow::convert(src_file, dst_file, ImageType::Qcow2) {
+ Ok(_) => 0,
+ Err(_) => -EIO,
+ },
+ )
+ .unwrap_or(-EIO)
+ }
+ _ => -EBADFD,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn convert_to_raw(src_fd: c_int, dst_fd: c_int) -> c_int {
+ // The caller is responsible for passing valid file descriptors.
+ // The caller retains ownership of the file descriptors.
+ let src_file = File::from_raw_fd(src_fd);
+ let src_file_owned = src_file.try_clone();
+ forget(src_file);
+
+ let dst_file = File::from_raw_fd(dst_fd);
+ let dst_file_owned = dst_file.try_clone();
+ forget(dst_file);
+
+ match (src_file_owned, dst_file_owned) {
+ (Ok(src_file), Ok(dst_file)) => {
+ catch_unwind(|| match qcow::convert(src_file, dst_file, ImageType::Raw) {
+ Ok(_) => 0,
+ Err(_) => -EIO,
+ })
+ .unwrap_or(-EIO)
+ }
+ _ => -EBADFD,
+ }
+}
diff --git a/rand_ish/Cargo.toml b/rand_ish/Cargo.toml
new file mode 100644
index 0000000..63bb001
--- /dev/null
+++ b/rand_ish/Cargo.toml
@@ -0,0 +1,5 @@
+[package]
+name = "rand_ish"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
diff --git a/rand_ish/src/lib.rs b/rand_ish/src/lib.rs
new file mode 100644
index 0000000..84d1a16
--- /dev/null
+++ b/rand_ish/src/lib.rs
@@ -0,0 +1,75 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::fs::File;
+use std::io::{self, Read};
+
+/// A simple prng based on a Linear congruential generator
+/// https://en.wikipedia.org/wiki/Linear_congruential_generator
+pub struct SimpleRng {
+ seed: u64,
+}
+
+impl SimpleRng {
+ /// Create a new SimpleRng
+ pub fn new(seed: u64) -> SimpleRng {
+ SimpleRng { seed }
+ }
+
+ /// Generate random u64
+ pub fn rng(&mut self) -> u64 {
+ // a simple Linear congruential generator
+ let a: u64 = 6364136223846793005;
+ let c: u64 = 1442695040888963407;
+ self.seed = a.wrapping_mul(self.seed).wrapping_add(c);
+ self.seed
+ }
+}
+
+// Uniformly samples the ASCII alphanumeric characters given a random variable. If an `Err` is
+// passed in, the error is returned as `Some(Err(...))`. If the the random variable can not be used
+// to uniformly sample, `None` is returned.
+fn uniform_sample_ascii_alphanumeric(
+ b: Result<u8, std::io::Error>,
+) -> Option<Result<char, std::io::Error>> {
+ const ASCII_CHARS: &[u8] = b"\
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+ abcdefghijklmnopqrstuvwxyz\
+ 0123456789";
+ let char_index = match b {
+ Ok(c) => c as usize,
+ Err(e) => return Some(Err(e)),
+ };
+ // Throw away numbers that would cause sampling bias.
+ if char_index >= ASCII_CHARS.len() * 4 {
+ None
+ } else {
+ Some(Ok(ASCII_CHARS[char_index % ASCII_CHARS.len()] as char))
+ }
+}
+
+/// Samples `/dev/urandom` and generates a random ASCII string of length `len`
+pub fn urandom_str(len: usize) -> io::Result<String> {
+ File::open("/dev/urandom")?
+ .bytes()
+ .filter_map(uniform_sample_ascii_alphanumeric)
+ .take(len)
+ .collect::<io::Result<String>>()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn check_100() {
+ for i in 0..100 {
+ let s = urandom_str(i).unwrap();
+ assert_eq!(s.len(), i);
+ assert!(s.is_ascii());
+ assert!(!s.contains(' '));
+ assert!(!s.contains(|c: char| !c.is_ascii_alphanumeric()));
+ }
+ }
+}
diff --git a/render_node_forward/Cargo.toml b/render_node_forward/Cargo.toml
new file mode 100644
index 0000000..d23b46a
--- /dev/null
+++ b/render_node_forward/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "render_node_forward"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+sys_util = { path = "../sys_util" }
diff --git a/render_node_forward/lib.rs b/render_node_forward/lib.rs
new file mode 100644
index 0000000..f96fdd1
--- /dev/null
+++ b/render_node_forward/lib.rs
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use sys_util::{GuestAddress, GuestMemory, MemoryMapping};
+
+#[link(name = "rendernodehost")]
+extern "C" {
+ fn start_render_node_host(
+ gpu_host_mem: *mut u8,
+ gpu_guest_mem_start: u64,
+ gpu_guest_mem_size: u64,
+ host_start: *const u8,
+ host_4g_start: *const u8,
+ );
+}
+
+/// The number of bytes in 4 GiB.
+pub const FOUR_GB: u64 = (1 << 32);
+/// The size required for the render node host in host and guest address space.
+pub const RENDER_NODE_HOST_SIZE: u64 = FOUR_GB;
+
+/// A render node host device that interfaces with the guest render node forwarder.
+pub struct RenderNodeHost {
+ #[allow(dead_code)]
+ guest_mem: GuestMemory,
+}
+
+impl RenderNodeHost {
+ /// Starts the render node host forwarding service over the given guest and host address ranges.
+ pub fn start(
+ mmap: &MemoryMapping,
+ gpu_guest_address: u64,
+ guest_mem: GuestMemory,
+ ) -> RenderNodeHost {
+ // Render node forward library need to do address translation between host user space
+ // address and guest physical address. We could call Rust function from C library. But
+ // since it's actually a linear mapping now, we just pass the host start address to
+ // render node forward library. We need two start address here since there would be a
+ // hole below 4G if guest memory size is bigger than 4G.
+
+ let host_start_addr = guest_mem.get_host_address(GuestAddress(0)).unwrap();
+ let host_4g_addr = if guest_mem.memory_size() > FOUR_GB {
+ guest_mem.get_host_address(GuestAddress(FOUR_GB)).unwrap()
+ } else {
+ host_start_addr
+ };
+ // Safe because only valid addresses are given.
+ unsafe {
+ start_render_node_host(
+ mmap.as_ptr(),
+ gpu_guest_address,
+ mmap.size() as u64,
+ host_start_addr,
+ host_4g_addr,
+ )
+ }
+ RenderNodeHost { guest_mem }
+ }
+}
diff --git a/resources/Cargo.toml b/resources/Cargo.toml
new file mode 100644
index 0000000..d7458b9
--- /dev/null
+++ b/resources/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "resources"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[features]
+wl-dmabuf = ["gpu_buffer"]
+
+[dependencies]
+gpu_buffer = { path = "../gpu_buffer", optional = true }
+libc = "*"
+msg_socket = { path = "../msg_socket" }
+sys_util = { path = "../sys_util" }
diff --git a/resources/src/address_allocator.rs b/resources/src/address_allocator.rs
new file mode 100644
index 0000000..11978ee
--- /dev/null
+++ b/resources/src/address_allocator.rs
@@ -0,0 +1,246 @@
+// Copyright 2018 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::cmp;
+use std::collections::HashMap;
+
+use crate::{Alloc, Error, Result};
+
+/// Manages allocating address ranges.
+/// Use `AddressAllocator` whenever an address range needs to be allocated to different users.
+/// Allocations must be uniquely tagged with an Alloc enum, which can be used for lookup.
+/// An human-readable tag String must also be provided for debugging / reference.
+///
+/// # Examples
+///
+/// ```
+/// // Anon is used for brevity. Don't manually instantiate Anon allocs!
+/// # use resources::{Alloc, AddressAllocator};
+/// AddressAllocator::new(0x1000, 0x10000, Some(0x100)).map(|mut pool| {
+/// assert_eq!(pool.allocate(0x110, Alloc::Anon(0), "caps".to_string()), Ok(0x1000));
+/// assert_eq!(pool.allocate(0x100, Alloc::Anon(1), "cache".to_string()), Ok(0x1200));
+/// assert_eq!(pool.allocate(0x100, Alloc::Anon(2), "etc".to_string()), Ok(0x1300));
+/// assert_eq!(pool.get(&Alloc::Anon(1)), Some(&(0x1200, 0x100, "cache".to_string())));
+/// });
+/// ```
+#[derive(Debug, Eq, PartialEq)]
+pub struct AddressAllocator {
+ pool_base: u64,
+ pool_end: u64,
+ alignment: u64,
+ next_addr: u64,
+ allocs: HashMap<Alloc, (u64, u64, String)>,
+}
+
+impl AddressAllocator {
+ /// Creates a new `AddressAllocator` for managing a range of addresses.
+ /// Can return `None` if `pool_base` + `pool_size` overflows a u64 or if alignment isn't a power
+ /// of two.
+ ///
+ /// * `pool_base` - The starting address of the range to manage.
+ /// * `pool_size` - The size of the address range in bytes.
+ /// * `align_size` - The minimum size of an address region to align to, defaults to four.
+ pub fn new(pool_base: u64, pool_size: u64, align_size: Option<u64>) -> Result<Self> {
+ if pool_size == 0 {
+ return Err(Error::PoolSizeZero);
+ }
+ let pool_end = pool_base
+ .checked_add(pool_size - 1)
+ .ok_or(Error::PoolOverflow {
+ base: pool_base,
+ size: pool_size,
+ })?;
+ let alignment = align_size.unwrap_or(4);
+ if !alignment.is_power_of_two() || alignment == 0 {
+ return Err(Error::BadAlignment);
+ }
+ Ok(AddressAllocator {
+ pool_base,
+ pool_end,
+ alignment,
+ next_addr: pool_base,
+ allocs: HashMap::new(),
+ })
+ }
+
+ /// Allocates a range of addresses from the managed region with an optional tag
+ /// and minimal alignment. Returns allocated_address. (allocated_address, size, tag)
+ /// can be retrieved through the `get` method.
+ pub fn allocate_with_align(
+ &mut self,
+ size: u64,
+ alloc: Alloc,
+ tag: String,
+ alignment: u64,
+ ) -> Result<u64> {
+ let alignment = cmp::max(self.alignment, alignment);
+
+ if self.allocs.contains_key(&alloc) {
+ return Err(Error::ExistingAlloc(alloc));
+ }
+ if size == 0 {
+ return Err(Error::AllocSizeZero);
+ }
+ if !alignment.is_power_of_two() {
+ return Err(Error::BadAlignment);
+ }
+ let align_adjust = if self.next_addr % alignment != 0 {
+ alignment - (self.next_addr % alignment)
+ } else {
+ 0
+ };
+ let addr = self
+ .next_addr
+ .checked_add(align_adjust)
+ .ok_or(Error::OutOfSpace)?;
+ let end_addr = addr.checked_add(size - 1).ok_or(Error::OutOfSpace)?;
+ if end_addr > self.pool_end {
+ return Err(Error::OutOfSpace);
+ }
+
+ // TODO(dgreid): Use a smarter allocation strategy. The current strategy is just
+ // bumping this pointer, meaning it will eventually exhaust available addresses.
+ self.next_addr = end_addr.saturating_add(1);
+
+ self.allocs.insert(alloc, (addr, size, tag));
+ Ok(addr)
+ }
+
+ pub fn allocate(&mut self, size: u64, alloc: Alloc, tag: String) -> Result<u64> {
+ self.allocate_with_align(size, alloc, tag, self.alignment)
+ }
+
+ /// Returns allocation associated with `alloc`, or None if no such allocation exists.
+ pub fn get(&self, alloc: &Alloc) -> Option<&(u64, u64, String)> {
+ self.allocs.get(alloc)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn new_fails_overflow() {
+ assert!(AddressAllocator::new(u64::max_value(), 0x100, None).is_err());
+ }
+
+ #[test]
+ fn new_fails_size_zero() {
+ assert!(AddressAllocator::new(0x1000, 0, None).is_err());
+ }
+
+ #[test]
+ fn new_fails_alignment_zero() {
+ assert!(AddressAllocator::new(0x1000, 0x10000, Some(0)).is_err());
+ }
+
+ #[test]
+ fn new_fails_alignment_non_power_of_two() {
+ assert!(AddressAllocator::new(0x1000, 0x10000, Some(200)).is_err());
+ }
+
+ #[test]
+ fn allocate_fails_exising_alloc() {
+ let mut pool = AddressAllocator::new(0x1000, 0x1000, Some(0x100)).unwrap();
+ assert_eq!(
+ pool.allocate(0x800, Alloc::Anon(0), String::from("bar0")),
+ Ok(0x1000)
+ );
+ assert_eq!(
+ pool.allocate(0x800, Alloc::Anon(0), String::from("bar0")),
+ Err(Error::ExistingAlloc(Alloc::Anon(0)))
+ );
+ }
+
+ #[test]
+ fn allocate_fails_not_enough_space() {
+ let mut pool = AddressAllocator::new(0x1000, 0x1000, Some(0x100)).unwrap();
+ assert_eq!(
+ pool.allocate(0x800, Alloc::Anon(0), String::from("bar0")),
+ Ok(0x1000)
+ );
+ assert_eq!(
+ pool.allocate(0x900, Alloc::Anon(1), String::from("bar1")),
+ Err(Error::OutOfSpace)
+ );
+ assert_eq!(
+ pool.allocate(0x800, Alloc::Anon(2), String::from("bar2")),
+ Ok(0x1800)
+ );
+ }
+
+ #[test]
+ fn allocate_alignment() {
+ let mut pool = AddressAllocator::new(0x1000, 0x10000, Some(0x100)).unwrap();
+ assert_eq!(
+ pool.allocate(0x110, Alloc::Anon(0), String::from("bar0")),
+ Ok(0x1000)
+ );
+ assert_eq!(
+ pool.allocate(0x100, Alloc::Anon(1), String::from("bar1")),
+ Ok(0x1200)
+ );
+ }
+
+ #[test]
+ fn allocate_retrieve_alloc() {
+ let mut pool = AddressAllocator::new(0x1000, 0x10000, Some(0x100)).unwrap();
+ assert_eq!(
+ pool.allocate(0x110, Alloc::Anon(0), String::from("bar0")),
+ Ok(0x1000)
+ );
+ assert_eq!(
+ pool.get(&Alloc::Anon(0)),
+ Some(&(0x1000, 0x110, String::from("bar0")))
+ );
+ }
+
+ #[test]
+ fn allocate_with_alignment_allocator_alignment() {
+ let mut pool = AddressAllocator::new(0x1000, 0x10000, Some(0x100)).unwrap();
+ assert_eq!(
+ pool.allocate_with_align(0x110, Alloc::Anon(0), String::from("bar0"), 0x1),
+ Ok(0x1000)
+ );
+ assert_eq!(
+ pool.allocate_with_align(0x100, Alloc::Anon(1), String::from("bar1"), 0x1),
+ Ok(0x1200)
+ );
+ }
+
+ #[test]
+ fn allocate_with_alignment_custom_alignment() {
+ let mut pool = AddressAllocator::new(0x1000, 0x10000, Some(0x4)).unwrap();
+ assert_eq!(
+ pool.allocate_with_align(0x110, Alloc::Anon(0), String::from("bar0"), 0x100),
+ Ok(0x1000)
+ );
+ assert_eq!(
+ pool.allocate_with_align(0x100, Alloc::Anon(1), String::from("bar1"), 0x100),
+ Ok(0x1200)
+ );
+ }
+
+ #[test]
+ fn allocate_with_alignment_no_allocator_alignment() {
+ let mut pool = AddressAllocator::new(0x1000, 0x10000, None).unwrap();
+ assert_eq!(
+ pool.allocate_with_align(0x110, Alloc::Anon(0), String::from("bar0"), 0x100),
+ Ok(0x1000)
+ );
+ assert_eq!(
+ pool.allocate_with_align(0x100, Alloc::Anon(1), String::from("bar1"), 0x100),
+ Ok(0x1200)
+ );
+ }
+
+ #[test]
+ fn allocate_with_alignment_alignment_non_power_of_two() {
+ let mut pool = AddressAllocator::new(0x1000, 0x10000, None).unwrap();
+ assert!(pool
+ .allocate_with_align(0x110, Alloc::Anon(0), String::from("bar0"), 200)
+ .is_err());
+ }
+}
diff --git a/resources/src/gpu_allocator.rs b/resources/src/gpu_allocator.rs
new file mode 100644
index 0000000..a44d3c8
--- /dev/null
+++ b/resources/src/gpu_allocator.rs
@@ -0,0 +1,127 @@
+// Copyright 2018 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::fmt::Debug;
+use std::fs::File;
+
+#[cfg(feature = "wl-dmabuf")]
+use libc::EINVAL;
+
+#[cfg(feature = "wl-dmabuf")]
+use gpu_buffer;
+use msg_socket::MsgOnSocket;
+use sys_util;
+
+#[allow(dead_code)]
+#[derive(Debug, Eq, PartialEq)]
+pub enum GpuAllocatorError {
+ OpenGpuBufferDevice,
+ CreateGpuBufferDevice,
+}
+
+/// Struct that describes the offset and stride of a plane located in GPU memory.
+#[derive(Clone, Copy, Debug, PartialEq, Default, MsgOnSocket)]
+pub struct GpuMemoryPlaneDesc {
+ pub stride: u32,
+ pub offset: u32,
+}
+
+/// Struct that describes a GPU memory allocation that consists of up to 3 planes.
+#[derive(Clone, Copy, Debug, Default, MsgOnSocket)]
+pub struct GpuMemoryDesc {
+ pub planes: [GpuMemoryPlaneDesc; 3],
+}
+
+/// Trait that needs to be implemented in order to service GPU memory allocation
+/// requests. Implementations are expected to support some set of buffer sizes and
+/// formats but every possible combination is not required.
+pub trait GpuMemoryAllocator: Debug {
+ /// Allocates GPU memory for a buffer of a specific size and format. The memory
+ /// layout for the returned buffer must be linear. A file handle and the
+ /// description of the planes for the buffer are returned on success.
+ ///
+ /// # Arguments
+ /// * `width` - Width of buffer.
+ /// * `height` - Height of buffer.
+ /// * `format` - Fourcc format of buffer.
+ fn allocate(
+ &self,
+ width: u32,
+ height: u32,
+ format: u32,
+ ) -> sys_util::Result<(File, GpuMemoryDesc)>;
+}
+
+#[cfg(feature = "wl-dmabuf")]
+pub struct GpuBufferDevice {
+ device: gpu_buffer::Device,
+}
+
+#[cfg(feature = "wl-dmabuf")]
+impl std::fmt::Debug for GpuBufferDevice {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "GpuBufferDevice {{opaque}}")
+ }
+}
+
+#[cfg(feature = "wl-dmabuf")]
+impl GpuMemoryAllocator for GpuBufferDevice {
+ fn allocate(
+ &self,
+ width: u32,
+ height: u32,
+ format: u32,
+ ) -> sys_util::Result<(File, GpuMemoryDesc)> {
+ let buffer = match self.device.create_buffer(
+ width,
+ height,
+ gpu_buffer::Format::from(format),
+ // Linear layout is a requirement as virtio wayland guest expects
+ // this for CPU access to the buffer. Scanout and texturing are
+ // optional as the consumer (wayland compositor) is expected to
+ // fall-back to a less efficient meachnisms for presentation if
+ // neccesary. In practice, linear buffers for commonly used formats
+ // will also support scanout and texturing.
+ gpu_buffer::Flags::empty().use_linear(true),
+ ) {
+ Ok(v) => v,
+ Err(_) => return Err(sys_util::Error::new(EINVAL)),
+ };
+ // We only support one FD. Buffers with multiple planes are supported
+ // as long as each plane is associated with the same handle.
+ let fd = match buffer.export_plane_fd(0) {
+ Ok(v) => v,
+ Err(e) => return Err(sys_util::Error::new(e)),
+ };
+
+ let mut desc = GpuMemoryDesc::default();
+ for i in 0..buffer.num_planes() {
+ // Use stride and offset for plane if handle matches first plane.
+ if buffer.plane_handle(i) == buffer.plane_handle(0) {
+ desc.planes[i] = GpuMemoryPlaneDesc {
+ stride: buffer.plane_stride(i),
+ offset: buffer.plane_offset(i),
+ }
+ }
+ }
+
+ Ok((fd, desc))
+ }
+}
+
+#[cfg(feature = "wl-dmabuf")]
+pub fn create_gpu_memory_allocator() -> Result<Option<Box<GpuMemoryAllocator>>, GpuAllocatorError> {
+ let undesired: &[&str] = &["vgem", "pvr"];
+ let fd = gpu_buffer::rendernode::open_device(undesired)
+ .map_err(|_| GpuAllocatorError::OpenGpuBufferDevice)?;
+ let device =
+ gpu_buffer::Device::new(fd).map_err(|_| GpuAllocatorError::CreateGpuBufferDevice)?;
+ Ok(Some(Box::new(GpuBufferDevice { device })))
+}
+
+#[cfg(not(feature = "wl-dmabuf"))]
+pub fn create_gpu_memory_allocator(
+) -> Result<Option<Box<dyn GpuMemoryAllocator>>, GpuAllocatorError> {
+ Ok(None)
+}
diff --git a/resources/src/lib.rs b/resources/src/lib.rs
new file mode 100644
index 0000000..c10608e
--- /dev/null
+++ b/resources/src/lib.rs
@@ -0,0 +1,72 @@
+// Copyright 2018 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.
+
+//! Manages system resources that can be allocated to VMs and their devices.
+
+#[cfg(feature = "wl-dmabuf")]
+extern crate gpu_buffer;
+extern crate libc;
+extern crate msg_socket;
+extern crate sys_util;
+
+use std::fmt::Display;
+
+pub use crate::address_allocator::AddressAllocator;
+pub use crate::gpu_allocator::{
+ GpuAllocatorError, GpuMemoryAllocator, GpuMemoryDesc, GpuMemoryPlaneDesc,
+};
+pub use crate::system_allocator::SystemAllocator;
+
+mod address_allocator;
+mod gpu_allocator;
+mod system_allocator;
+
+/// Used to tag SystemAllocator allocations.
+#[derive(Debug, Eq, PartialEq, Hash)]
+pub enum Alloc {
+ /// An anonymous resource allocation.
+ /// Should only be instantiated through `SystemAllocator::get_anon_alloc()`.
+ /// Avoid using these. Instead, use / create a more descriptive Alloc variant.
+ Anon(usize),
+ /// A PCI BAR region with associated bus, device, and bar numbers.
+ PciBar { bus: u8, dev: u8, bar: u8 },
+ /// GPU render node region.
+ GpuRenderNode,
+ /// Pmem device region with associated device index.
+ PmemDevice(usize),
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum Error {
+ AllocSizeZero,
+ BadAlignment,
+ CreateGpuAllocator(GpuAllocatorError),
+ ExistingAlloc(Alloc),
+ MissingDeviceAddresses,
+ MissingMMIOAddresses,
+ NoIoAllocator,
+ OutOfSpace,
+ PoolOverflow { base: u64, size: u64 },
+ PoolSizeZero,
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ use self::Error::*;
+ match self {
+ AllocSizeZero => write!(f, "Allocation cannot have size of 0"),
+ BadAlignment => write!(f, "Pool alignment must be a power of 2"),
+ CreateGpuAllocator(e) => write!(f, "Failed to create GPU allocator: {:?}", e),
+ ExistingAlloc(tag) => write!(f, "Alloc already exists: {:?}", tag),
+ MissingDeviceAddresses => write!(f, "Device address range not specified"),
+ MissingMMIOAddresses => write!(f, "MMIO address range not specified"),
+ NoIoAllocator => write!(f, "No IO address range specified"),
+ OutOfSpace => write!(f, "Out of space"),
+ PoolOverflow { base, size } => write!(f, "base={} + size={} overflows", base, size),
+ PoolSizeZero => write!(f, "Pool cannot have size of 0"),
+ }
+ }
+}
diff --git a/resources/src/system_allocator.rs b/resources/src/system_allocator.rs
new file mode 100644
index 0000000..6cdb70e
--- /dev/null
+++ b/resources/src/system_allocator.rs
@@ -0,0 +1,189 @@
+// Copyright 2018 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 sys_util::pagesize;
+
+use crate::address_allocator::AddressAllocator;
+use crate::gpu_allocator::{self, GpuMemoryAllocator};
+use crate::{Alloc, Error, Result};
+
+/// Manages allocating system resources such as address space and interrupt numbers.
+///
+/// # Example - Use the `SystemAddress` builder.
+///
+/// ```
+/// # use resources::{Alloc, SystemAllocator};
+/// if let Ok(mut a) = SystemAllocator::builder()
+/// .add_io_addresses(0x1000, 0x10000)
+/// .add_device_addresses(0x10000000, 0x10000000)
+/// .add_mmio_addresses(0x30000000, 0x10000)
+/// .create_allocator(5, false) {
+/// assert_eq!(a.allocate_irq(), Some(5));
+/// assert_eq!(a.allocate_irq(), Some(6));
+/// assert_eq!(
+/// a.device_allocator()
+/// .allocate(
+/// 0x100,
+/// Alloc::PciBar { bus: 0, dev: 0, bar: 0 },
+/// "bar0".to_string()
+/// ),
+/// Ok(0x10000000)
+/// );
+/// assert_eq!(
+/// a.device_allocator().get(&Alloc::PciBar { bus: 0, dev: 0, bar: 0 }),
+/// Some(&(0x10000000, 0x100, "bar0".to_string()))
+/// );
+/// }
+/// ```
+#[derive(Debug)]
+pub struct SystemAllocator {
+ io_address_space: Option<AddressAllocator>,
+ device_address_space: AddressAllocator,
+ mmio_address_space: AddressAllocator,
+ gpu_allocator: Option<Box<dyn GpuMemoryAllocator>>,
+ next_irq: u32,
+ next_anon_id: usize,
+}
+
+impl SystemAllocator {
+ /// Creates a new `SystemAllocator` for managing addresses and irq numvers.
+ /// Can return `None` if `base` + `size` overflows a u64 or if alignment isn't a power
+ /// of two.
+ ///
+ /// * `io_base` - The starting address of IO memory.
+ /// * `io_size` - The size of IO memory.
+ /// * `dev_base` - The starting address of device memory.
+ /// * `dev_size` - The size of device memory.
+ /// * `mmio_base` - The starting address of MMIO space.
+ /// * `mmio_size` - The size of MMIO space.
+ /// * `create_gpu_allocator` - If true, enable gpu memory allocation.
+ /// * `first_irq` - The first irq number to give out.
+ fn new(
+ io_base: Option<u64>,
+ io_size: Option<u64>,
+ dev_base: u64,
+ dev_size: u64,
+ mmio_base: u64,
+ mmio_size: u64,
+ create_gpu_allocator: bool,
+ first_irq: u32,
+ ) -> Result<Self> {
+ let page_size = pagesize() as u64;
+ Ok(SystemAllocator {
+ io_address_space: if let (Some(b), Some(s)) = (io_base, io_size) {
+ Some(AddressAllocator::new(b, s, Some(0x400))?)
+ } else {
+ None
+ },
+ device_address_space: AddressAllocator::new(dev_base, dev_size, Some(page_size))?,
+ mmio_address_space: AddressAllocator::new(mmio_base, mmio_size, Some(page_size))?,
+ gpu_allocator: if create_gpu_allocator {
+ gpu_allocator::create_gpu_memory_allocator().map_err(Error::CreateGpuAllocator)?
+ } else {
+ None
+ },
+ next_irq: first_irq,
+ next_anon_id: 0,
+ })
+ }
+
+ /// Returns a `SystemAllocatorBuilder` that can create a new `SystemAllocator`.
+ pub fn builder() -> SystemAllocatorBuilder {
+ SystemAllocatorBuilder::new()
+ }
+
+ /// Reserves the next available system irq number.
+ pub fn allocate_irq(&mut self) -> Option<u32> {
+ if let Some(irq_num) = self.next_irq.checked_add(1) {
+ self.next_irq = irq_num;
+ Some(irq_num - 1)
+ } else {
+ None
+ }
+ }
+
+ /// Gets an allocator to be used for IO memory.
+ pub fn io_allocator(&mut self) -> Option<&mut AddressAllocator> {
+ self.io_address_space.as_mut()
+ }
+
+ /// Gets an allocator to be used for device memory.
+ pub fn device_allocator(&mut self) -> &mut AddressAllocator {
+ &mut self.device_address_space
+ }
+
+ /// Gets an allocator to be used for MMIO memory.
+ pub fn mmio_allocator(&mut self) -> &mut AddressAllocator {
+ &mut self.mmio_address_space
+ }
+
+ /// Gets an allocator to be used for GPU memory.
+ pub fn gpu_memory_allocator(&self) -> Option<&dyn GpuMemoryAllocator> {
+ self.gpu_allocator.as_ref().map(|v| v.as_ref())
+ }
+
+ /// Gets a unique anonymous allocation
+ pub fn get_anon_alloc(&mut self) -> Alloc {
+ self.next_anon_id += 1;
+ Alloc::Anon(self.next_anon_id)
+ }
+}
+
+/// Used to build a system address map for use in creating a `SystemAllocator`.
+pub struct SystemAllocatorBuilder {
+ io_base: Option<u64>,
+ io_size: Option<u64>,
+ mmio_base: Option<u64>,
+ mmio_size: Option<u64>,
+ device_base: Option<u64>,
+ device_size: Option<u64>,
+}
+
+impl SystemAllocatorBuilder {
+ pub fn new() -> Self {
+ SystemAllocatorBuilder {
+ io_base: None,
+ io_size: None,
+ mmio_base: None,
+ mmio_size: None,
+ device_base: None,
+ device_size: None,
+ }
+ }
+
+ pub fn add_io_addresses(mut self, base: u64, size: u64) -> Self {
+ self.io_base = Some(base);
+ self.io_size = Some(size);
+ self
+ }
+
+ pub fn add_mmio_addresses(mut self, base: u64, size: u64) -> Self {
+ self.mmio_base = Some(base);
+ self.mmio_size = Some(size);
+ self
+ }
+
+ pub fn add_device_addresses(mut self, base: u64, size: u64) -> Self {
+ self.device_base = Some(base);
+ self.device_size = Some(size);
+ self
+ }
+
+ pub fn create_allocator(
+ &self,
+ first_irq: u32,
+ gpu_allocation: bool,
+ ) -> Result<SystemAllocator> {
+ SystemAllocator::new(
+ self.io_base,
+ self.io_size,
+ self.device_base.ok_or(Error::MissingDeviceAddresses)?,
+ self.device_size.ok_or(Error::MissingDeviceAddresses)?,
+ self.mmio_base.ok_or(Error::MissingMMIOAddresses)?,
+ self.mmio_size.ok_or(Error::MissingMMIOAddresses)?,
+ gpu_allocation,
+ first_irq,
+ )
+ }
+}
diff --git a/rust-toolchain b/rust-toolchain
new file mode 100644
index 0000000..2aeaa11
--- /dev/null
+++ b/rust-toolchain
@@ -0,0 +1 @@
+1.35.0
diff --git a/seccomp/arm/9p_device.policy b/seccomp/arm/9p_device.policy
new file mode 100644
index 0000000..b3167b9
--- /dev/null
+++ b/seccomp/arm/9p_device.policy
@@ -0,0 +1,29 @@
+# Copyright 2018 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
+
+pread64: 1
+pwrite64: 1
+lstat64: 1
+stat64: 1
+open: 1
+openat: 1
+fstat64: 1
+ioctl: arg1 == FIOCLEX
+getdents64: 1
+fdatasync: 1
+fsync: 1
+mkdir: 1
+rmdir: 1
+rename: 1
+writev: 1
+link: 1
+unlink: 1
+socket: arg0 == AF_UNIX
+utimensat: 1
+ftruncate64: 1
+fchown: arg1 == 0xffffffff && arg2 == 0xffffffff
+statfs64: 1
+fstatat64: 1
diff --git a/seccomp/arm/balloon_device.policy b/seccomp/arm/balloon_device.policy
new file mode 100644
index 0000000..4f7aafd
--- /dev/null
+++ b/seccomp/arm/balloon_device.policy
@@ -0,0 +1,5 @@
+# Copyright 2018 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/block_device.policy b/seccomp/arm/block_device.policy
new file mode 100644
index 0000000..62f4ee7
--- /dev/null
+++ b/seccomp/arm/block_device.policy
@@ -0,0 +1,15 @@
+# Copyright 2018 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
+
+fallocate: 1
+fdatasync: 1
+fstat64: 1
+fsync: 1
+ftruncate64: 1
+_llseek: 1
+timerfd_create: 1
+timerfd_gettime: 1
+timerfd_settime: 1
diff --git a/seccomp/arm/common_device.policy b/seccomp/arm/common_device.policy
new file mode 100644
index 0000000..d2b5a6b
--- /dev/null
+++ b/seccomp/arm/common_device.policy
@@ -0,0 +1,44 @@
+# Copyright 2018 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.
+
+brk: 1
+clone: arg0 & CLONE_THREAD
+close: 1
+dup2: 1
+dup: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_wait: 1
+eventfd2: 1
+exit: 1
+exit_group: 1
+futex: 1
+getpid: 1
+gettimeofday: 1
+kill: 1
+madvise: arg2 == MADV_DONTNEED || arg2 == MADV_DONTDUMP || arg2 == MADV_REMOVE
+mmap2: arg2 in ~PROT_EXEC
+mprotect: arg2 in ~PROT_EXEC
+mremap: 1
+munmap: 1
+nanosleep: 1
+open: return ENOENT
+openat: return ENOENT
+pipe2: 1
+poll: 1
+ppoll: 1
+prctl: arg0 == PR_SET_NAME
+read: 1
+recv: 1
+recvfrom: 1
+recvmsg: 1
+restart_syscall: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+rt_sigreturn: 1
+sched_getaffinity: 1
+sendmsg: 1
+set_robust_list: 1
+sigaltstack: 1
+write: 1
diff --git a/seccomp/arm/cras_audio_device.policy b/seccomp/arm/cras_audio_device.policy
new file mode 100644
index 0000000..06d63d1
--- /dev/null
+++ b/seccomp/arm/cras_audio_device.policy
@@ -0,0 +1,13 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+@include /usr/share/policy/crosvm/common_device.policy
+
+madvise: 1
+prlimit64: 1
+setrlimit: 1
+recvmsg: 1
+sendmsg: 1
+socketpair: arg0 == AF_UNIX
+clock_gettime: 1
diff --git a/seccomp/arm/input_device.policy b/seccomp/arm/input_device.policy
new file mode 100644
index 0000000..f26998e
--- /dev/null
+++ b/seccomp/arm/input_device.policy
@@ -0,0 +1,9 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+@include /usr/share/policy/crosvm/common_device.policy
+
+ioctl: 1
+fcntl: 1
+getsockname: 1
diff --git a/seccomp/arm/net_device.policy b/seccomp/arm/net_device.policy
new file mode 100644
index 0000000..4f7aafd
--- /dev/null
+++ b/seccomp/arm/net_device.policy
@@ -0,0 +1,5 @@
+# Copyright 2018 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/null_audio_device.policy b/seccomp/arm/null_audio_device.policy
new file mode 100644
index 0000000..089d1bd
--- /dev/null
+++ b/seccomp/arm/null_audio_device.policy
@@ -0,0 +1,10 @@
+# Copyright 2018 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
+
+madvise: 1
+prlimit64: 1
+setrlimit: 1
+clock_gettime: 1
diff --git a/seccomp/arm/rng_device.policy b/seccomp/arm/rng_device.policy
new file mode 100644
index 0000000..4f7aafd
--- /dev/null
+++ b/seccomp/arm/rng_device.policy
@@ -0,0 +1,5 @@
+# Copyright 2018 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/tpm_device.policy b/seccomp/arm/tpm_device.policy
new file mode 100644
index 0000000..72b78fb
--- /dev/null
+++ b/seccomp/arm/tpm_device.policy
@@ -0,0 +1,57 @@
+# Copyright 2018 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.
+
+# common policy
+brk: 1
+clone: arg0 & CLONE_THREAD
+close: 1
+dup2: 1
+dup: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_wait: 1
+eventfd2: 1
+exit: 1
+exit_group: 1
+futex: 1
+getpid: 1
+gettimeofday: 1
+kill: 1
+madvise: arg2 == MADV_DONTNEED || arg2 == MADV_DONTDUMP || arg2 == MADV_REMOVE
+mmap2: arg2 in ~PROT_EXEC
+mprotect: arg2 in ~PROT_EXEC
+mremap: 1
+munmap: 1
+nanosleep: 1
+#open: return ENOENT
+#openat: return ENOENT
+pipe2: 1
+poll: 1
+ppoll: 1
+prctl: arg0 == PR_SET_NAME
+read: 1
+recv: 1
+recvfrom: 1
+recvmsg: 1
+restart_syscall: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+rt_sigreturn: 1
+sched_getaffinity: 1
+sendmsg: 1
+set_robust_list: 1
+sigaltstack: 1
+write: 1
+
+# tpm-specific policy
+chdir: 1
+fstat: 1
+fsync: 1
+ftruncate: 1
+getuid: 1
+lseek: 1
+mkdir: 1
+open: 1
+openat: 1
+stat: 1
diff --git a/seccomp/arm/vhost_net_device.policy b/seccomp/arm/vhost_net_device.policy
new file mode 100644
index 0000000..58fc7ef
--- /dev/null
+++ b/seccomp/arm/vhost_net_device.policy
@@ -0,0 +1,23 @@
+# Copyright 2018 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
+
+# Whitelist vhost_net ioctls only.
+# arg1 == VHOST_GET_FEATURES ||
+# arg1 == VHOST_SET_FEATURES ||
+# arg1 == VHOST_SET_OWNER ||
+# arg1 == VHOST_RESET_OWNER ||
+# arg1 == VHOST_SET_MEM_TABLE ||
+# arg1 == VHOST_SET_LOG_BASE ||
+# arg1 == VHOST_SET_LOG_FD ||
+# arg1 == VHOST_SET_VRING_NUM ||
+# arg1 == VHOST_SET_VRING_ADDR ||
+# arg1 == VHOST_SET_VRING_BASE ||
+# arg1 == VHOST_GET_VRING_BASE ||
+# arg1 == VHOST_SET_VRING_KICK ||
+# arg1 == VHOST_SET_VRING_CALL ||
+# arg1 == VHOST_SET_VRING_ERR ||
+# arg1 == VHOST_NET_SET_BACKEND
+ioctl: arg1 == 0x8008af00 || arg1 == 0x4008af00 || arg1 == 0x0000af01 || arg1 == 0x0000af02 || arg1 == 0x4008af03 || arg1 == 0x4008af04 || arg1 == 0x4004af07 || arg1 == 0x4008af10 || arg1 == 0x4028af11 || arg1 == 0x4008af12 || arg1 == 0xc008af12 || arg1 == 0x4008af20 || arg1 == 0x4008af21 || arg1 == 0x4008af22 || arg1 == 0x4008af30
diff --git a/seccomp/arm/vhost_vsock_device.policy b/seccomp/arm/vhost_vsock_device.policy
new file mode 100644
index 0000000..9d9ca59
--- /dev/null
+++ b/seccomp/arm/vhost_vsock_device.policy
@@ -0,0 +1,24 @@
+# Copyright 2018 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
+
+# Whitelist vhost_vsock ioctls only.
+# arg1 == VHOST_GET_FEATURES ||
+# arg1 == VHOST_SET_FEATURES ||
+# arg1 == VHOST_SET_OWNER ||
+# arg1 == VHOST_RESET_OWNER ||
+# arg1 == VHOST_SET_MEM_TABLE ||
+# arg1 == VHOST_SET_LOG_BASE ||
+# arg1 == VHOST_SET_LOG_FD ||
+# arg1 == VHOST_SET_VRING_NUM ||
+# arg1 == VHOST_SET_VRING_ADDR ||
+# arg1 == VHOST_SET_VRING_BASE ||
+# arg1 == VHOST_GET_VRING_BASE ||
+# arg1 == VHOST_SET_VRING_KICK ||
+# arg1 == VHOST_SET_VRING_CALL ||
+# arg1 == VHOST_SET_VRING_ERR ||
+# arg1 == VHOST_VSOCK_SET_GUEST_CID ||
+# arg1 == VHOST_VSOCK_SET_RUNNING
+ioctl: arg1 == 0x8008af00 || arg1 == 0x4008af00 || arg1 == 0x0000af01 || arg1 == 0x0000af02 || arg1 == 0x4008af03 || arg1 == 0x4008af04 || arg1 == 0x4004af07 || arg1 == 0x4008af10 || arg1 == 0x4028af11 || arg1 == 0x4008af12 || arg1 == 0xc008af12 || arg1 == 0x4008af20 || arg1 == 0x4008af21 || arg1 == 0x4008af22 || arg1 == 0x4008af60 || arg1 == 0x4004af61
diff --git a/seccomp/arm/wl_device.policy b/seccomp/arm/wl_device.policy
new file mode 100644
index 0000000..1104fba
--- /dev/null
+++ b/seccomp/arm/wl_device.policy
@@ -0,0 +1,22 @@
+# Copyright 2018 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
+
+# Used to connect to wayland. arg0 == AF_UNIX && arg1 == SOCK_STREAM|SOCK_CLOEXEC
+socket: arg0 == 1 && arg1 == 0x80001 && arg2 == 0
+# arg1 == FIONBIO || arg1 == DMA_BUF_IOCTL_SYNC
+ioctl: arg1 == 0x5421 || arg1 == 0x40086200
+connect: 1
+# Used to communicate with wayland
+recvmsg: 1
+sendmsg: 1
+# Used for sharing memory with wayland. arg1 == MFD_CLOEXEC|MFD_ALLOW_SEALING
+memfd_create: arg1 == 3
+# Used to set of size new memfd
+ftruncate64: 1
+# Used to determine shm size after recvmsg with fd
+_llseek: 1
+# Allow F_GETFL only
+fcntl64: arg1 == 3
diff --git a/seccomp/arm/xhci.policy b/seccomp/arm/xhci.policy
new file mode 100644
index 0000000..4fca144
--- /dev/null
+++ b/seccomp/arm/xhci.policy
@@ -0,0 +1,47 @@
+# Copyright 2018 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.
+
+openat: 1
+@include /usr/share/policy/crosvm/common_device.policy
+
+stat64: 1
+fcntl64: 1
+lstat64: 1
+readlink: 1
+readlinkat: 1
+getdents64: 1
+getrandom: 1
+name_to_handle_at: 1
+access: 1
+gettid: 1
+clock_gettime: 1
+timerfd_create: 1
+getsockname: 1
+pipe: 1
+setsockopt: 1
+bind: 1
+fcntl: 1
+socket: arg0 == AF_NETLINK
+stat: 1
+uname: 1
+# The following ioctls are:
+# 0x4004550d == USBDEVFS_REAPURBNDELAY32
+# 0x550b == USBDEVFS_DISCARDURB
+# 0x8004550f == USBDEVFS_CLAIMINTERFACE
+# 0x80045510 == USBDEVFS_RELEASEINTERFACE
+# 0x8004551a == USBDEVFS_GET_CAPABILITIES
+# 0x802c550a == USBDEVFS_SUBMITURB
+# 0xc0105500 == USBDEVFS_CONTROL
+# 0x5514 == USBDEVFS_RESET
+# 0x80045505 == USBDEVFS_SETCONFIGURATION
+# 0x8108551b == USBDEVFS_DISCONNECT_CLAIM
+# 0x40085511 == USBDEVFS_CONNECTINFO
+# 0x80185520 == USBDEVFS_CONNINFO_EX
+ioctl: arg1 == 0xc0105500 || arg1 == 0x802c550a || arg1 == 0x8004551a || arg1 == 0x4004550d || arg1 == 0x8004550f || arg1 == 0x80045510 || arg1 == 0x550b || arg1 == 0x5514 || arg1 == 0x80045505 || arg1 == 0x8108551b || arg1 == 0x40085511 || arg1 == 0x80185520
+fstat: 1
+sigaltstack: 1
+recvmsg: 1
+getrandom: 1
+getdents: 1
+lseek: 1
diff --git a/seccomp/x86_64/9p_device.policy b/seccomp/x86_64/9p_device.policy
new file mode 100644
index 0000000..ddd7417
--- /dev/null
+++ b/seccomp/x86_64/9p_device.policy
@@ -0,0 +1,28 @@
+# Copyright 2018 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
+
+writev: 1
+pwrite64: 1
+stat: 1
+lstat: 1
+open: 1
+openat: 1
+fstat: 1
+ioctl: arg1 == FIOCLEX
+link: 1
+unlink: 1
+rename: 1
+pread64: 1
+getdents: 1
+mkdir: 1
+rmdir: 1
+fsync: 1
+fdatasync: 1
+utimensat: 1
+ftruncate: 1
+fchown: arg1 == 0xffffffff && arg2 == 0xffffffff
+statfs: 1
+newfstatat: 1
diff --git a/seccomp/x86_64/balloon_device.policy b/seccomp/x86_64/balloon_device.policy
new file mode 100644
index 0000000..72ecd5a
--- /dev/null
+++ b/seccomp/x86_64/balloon_device.policy
@@ -0,0 +1,5 @@
+# Copyright 2017 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/block_device.policy b/seccomp/x86_64/block_device.policy
new file mode 100644
index 0000000..20eca1a
--- /dev/null
+++ b/seccomp/x86_64/block_device.policy
@@ -0,0 +1,15 @@
+# Copyright 2017 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
+
+fallocate: 1
+fdatasync: 1
+fstat: 1
+fsync: 1
+ftruncate: 1
+lseek: 1
+timerfd_create: 1
+timerfd_gettime: 1
+timerfd_settime: 1
diff --git a/seccomp/x86_64/common_device.policy b/seccomp/x86_64/common_device.policy
new file mode 100644
index 0000000..2379b95
--- /dev/null
+++ b/seccomp/x86_64/common_device.policy
@@ -0,0 +1,43 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+brk: 1
+clone: arg0 & CLONE_THREAD
+close: 1
+dup2: 1
+dup: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_wait: 1
+eventfd2: 1
+exit: 1
+exit_group: 1
+futex: 1
+getpid: 1
+gettimeofday: 1
+kill: 1
+madvise: arg2 == MADV_DONTNEED || arg2 == MADV_DONTDUMP || arg2 == MADV_REMOVE
+mmap: arg2 in ~PROT_EXEC
+mprotect: arg2 in ~PROT_EXEC
+mremap: 1
+munmap: 1
+nanosleep: 1
+open: return ENOENT
+openat: return ENOENT
+pipe2: 1
+poll: 1
+ppoll: 1
+prctl: arg0 == PR_SET_NAME
+read: 1
+recvfrom: 1
+recvmsg: 1
+restart_syscall: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+rt_sigreturn: 1
+sched_getaffinity: 1
+sendmsg: 1
+set_robust_list: 1
+sigaltstack: 1
+write: 1
diff --git a/seccomp/x86_64/cras_audio_device.policy b/seccomp/x86_64/cras_audio_device.policy
new file mode 100644
index 0000000..e5a074e
--- /dev/null
+++ b/seccomp/x86_64/cras_audio_device.policy
@@ -0,0 +1,12 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+@include /usr/share/policy/crosvm/common_device.policy
+
+madvise: 1
+prlimit64: 1
+setrlimit: 1
+recvmsg: 1
+sendmsg: 1
+socketpair: arg0 == AF_UNIX
diff --git a/seccomp/x86_64/gpu_device.policy b/seccomp/x86_64/gpu_device.policy
new file mode 100644
index 0000000..233f00f
--- /dev/null
+++ b/seccomp/x86_64/gpu_device.policy
@@ -0,0 +1,71 @@
+# Copyright 2018 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.
+
+# Rules from common_device.policy with some rules removed because they block certain flags needed
+# for gpu.
+brk: 1
+clone: arg0 & CLONE_THREAD
+close: 1
+dup2: 1
+dup: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_wait: 1
+eventfd2: 1
+exit: 1
+exit_group: 1
+futex: 1
+getpid: 1
+gettimeofday: 1
+kill: 1
+madvise: arg2 == MADV_DONTNEED || arg2 == MADV_DONTDUMP || arg2 == MADV_REMOVE
+mremap: 1
+munmap: 1
+nanosleep: 1
+pipe2: 1
+poll: 1
+ppoll: 1
+prctl: arg0 == PR_SET_NAME
+read: 1
+recvfrom: 1
+recvmsg: 1
+restart_syscall: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+rt_sigreturn: 1
+sched_getaffinity: 1
+sendmsg: 1
+set_robust_list: 1
+sigaltstack: 1
+write: 1
+
+# Rules specific to gpu
+connect: 1
+fcntl: arg1 == F_DUPFD_CLOEXEC
+fstat: 1
+# Used to set of size new memfd.
+ftruncate: 1
+getdents: 1
+geteuid: 1
+getrandom: 1
+getuid: 1
+ioctl: arg1 == FIONBIO || arg1 == FIOCLEX || arg1 == 0x40086200 || arg1 & 0x6400
+lseek: 1
+lstat: 1
+# Used for sharing memory with wayland. arg1 == MFD_CLOEXEC|MFD_ALLOW_SEALING
+memfd_create: arg1 == 3
+# mmap/mprotect/open/openat differ from the common_device.policy
+mmap: arg2 == PROT_READ|PROT_WRITE || arg2 == PROT_NONE || arg2 == PROT_READ|PROT_EXEC
+mprotect: arg2 == PROT_READ|PROT_WRITE || arg2 == PROT_NONE || arg2 == PROT_READ
+open: 1
+openat: 1
+readlink: 1
+socket: arg0 == 1 && arg1 == 0x80001 && arg2 == 0
+stat: 1
+sysinfo: 1
+
+# Rules specific to AMD gpus.
+uname: 1
+sched_setscheduler: 1
+sched_setaffinity: 1
diff --git a/seccomp/x86_64/input_device.policy b/seccomp/x86_64/input_device.policy
new file mode 100644
index 0000000..f26998e
--- /dev/null
+++ b/seccomp/x86_64/input_device.policy
@@ -0,0 +1,9 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+@include /usr/share/policy/crosvm/common_device.policy
+
+ioctl: 1
+fcntl: 1
+getsockname: 1
diff --git a/seccomp/x86_64/net_device.policy b/seccomp/x86_64/net_device.policy
new file mode 100644
index 0000000..72ecd5a
--- /dev/null
+++ b/seccomp/x86_64/net_device.policy
@@ -0,0 +1,5 @@
+# Copyright 2017 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/null_audio_device.policy b/seccomp/x86_64/null_audio_device.policy
new file mode 100644
index 0000000..9ea7015
--- /dev/null
+++ b/seccomp/x86_64/null_audio_device.policy
@@ -0,0 +1,9 @@
+# Copyright 2017 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
+
+madvise: 1
+prlimit64: 1
+setrlimit: 1
diff --git a/seccomp/x86_64/rng_device.policy b/seccomp/x86_64/rng_device.policy
new file mode 100644
index 0000000..72ecd5a
--- /dev/null
+++ b/seccomp/x86_64/rng_device.policy
@@ -0,0 +1,5 @@
+# Copyright 2017 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/tpm_device.policy b/seccomp/x86_64/tpm_device.policy
new file mode 100644
index 0000000..5c21480
--- /dev/null
+++ b/seccomp/x86_64/tpm_device.policy
@@ -0,0 +1,56 @@
+# Copyright 2018 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.
+
+# common policy
+brk: 1
+clone: arg0 & CLONE_THREAD
+close: 1
+dup2: 1
+dup: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_wait: 1
+eventfd2: 1
+exit: 1
+exit_group: 1
+futex: 1
+getpid: 1
+gettimeofday: 1
+kill: 1
+madvise: arg2 == MADV_DONTNEED || arg2 == MADV_DONTDUMP || arg2 == MADV_REMOVE
+mmap: arg2 in ~PROT_EXEC
+mprotect: arg2 in ~PROT_EXEC
+mremap: 1
+munmap: 1
+nanosleep: 1
+#open: return ENOENT
+#openat: return ENOENT
+pipe2: 1
+poll: 1
+ppoll: 1
+prctl: arg0 == PR_SET_NAME
+read: 1
+recvfrom: 1
+recvmsg: 1
+restart_syscall: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+rt_sigreturn: 1
+sched_getaffinity: 1
+sendmsg: 1
+set_robust_list: 1
+sigaltstack: 1
+write: 1
+
+# tpm-specific policy
+chdir: 1
+fstat: 1
+fsync: 1
+ftruncate: 1
+getuid: 1
+lseek: 1
+mkdir: 1
+open: 1
+openat: 1
+stat: 1
diff --git a/seccomp/x86_64/vhost_net_device.policy b/seccomp/x86_64/vhost_net_device.policy
new file mode 100644
index 0000000..306328b
--- /dev/null
+++ b/seccomp/x86_64/vhost_net_device.policy
@@ -0,0 +1,23 @@
+# Copyright 2017 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
+
+# Whitelist vhost_net ioctls only.
+# arg1 == VHOST_GET_FEATURES ||
+# arg1 == VHOST_SET_FEATURES ||
+# arg1 == VHOST_SET_OWNER ||
+# arg1 == VHOST_RESET_OWNER ||
+# arg1 == VHOST_SET_MEM_TABLE ||
+# arg1 == VHOST_SET_LOG_BASE ||
+# arg1 == VHOST_SET_LOG_FD ||
+# arg1 == VHOST_SET_VRING_NUM ||
+# arg1 == VHOST_SET_VRING_ADDR ||
+# arg1 == VHOST_SET_VRING_BASE ||
+# arg1 == VHOST_GET_VRING_BASE ||
+# arg1 == VHOST_SET_VRING_KICK ||
+# arg1 == VHOST_SET_VRING_CALL ||
+# arg1 == VHOST_SET_VRING_ERR ||
+# arg1 == VHOST_NET_SET_BACKEND
+ioctl: arg1 == 0x8008af00 || arg1 == 0x4008af00 || arg1 == 0x0000af01 || arg1 == 0x0000af02 || arg1 == 0x4008af03 || arg1 == 0x4008af04 || arg1 == 0x4004af07 || arg1 == 0x4008af10 || arg1 == 0x4028af11 || arg1 == 0x4008af12 || arg1 == 0xc008af12 || arg1 == 0x4008af20 || arg1 == 0x4008af21 || arg1 == 0x4008af22 || arg1 == 0x4008af30
diff --git a/seccomp/x86_64/vhost_vsock_device.policy b/seccomp/x86_64/vhost_vsock_device.policy
new file mode 100644
index 0000000..9c2274c
--- /dev/null
+++ b/seccomp/x86_64/vhost_vsock_device.policy
@@ -0,0 +1,26 @@
+# Copyright 2017 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
+
+# Whitelist vhost_vsock ioctls only.
+# arg1 == VHOST_GET_FEATURES ||
+# arg1 == VHOST_SET_FEATURES ||
+# arg1 == VHOST_SET_OWNER ||
+# arg1 == VHOST_RESET_OWNER ||
+# arg1 == VHOST_SET_MEM_TABLE ||
+# arg1 == VHOST_SET_LOG_BASE ||
+# arg1 == VHOST_SET_LOG_FD ||
+# arg1 == VHOST_SET_VRING_NUM ||
+# arg1 == VHOST_SET_VRING_ADDR ||
+# arg1 == VHOST_SET_VRING_BASE ||
+# arg1 == VHOST_GET_VRING_BASE ||
+# arg1 == VHOST_SET_VRING_KICK ||
+# arg1 == VHOST_SET_VRING_CALL ||
+# arg1 == VHOST_SET_VRING_ERR ||
+# arg1 == VHOST_VSOCK_SET_GUEST_CID ||
+# arg1 == VHOST_VSOCK_SET_RUNNING
+ioctl: arg1 == 0x8008af00 || arg1 == 0x4008af00 || arg1 == 0x0000af01 || arg1 == 0x0000af02 || arg1 == 0x4008af03 || arg1 == 0x4008af04 || arg1 == 0x4004af07 || arg1 == 0x4008af10 || arg1 == 0x4028af11 || arg1 == 0x4008af12 || arg1 == 0xc008af12 || arg1 == 0x4008af20 || arg1 == 0x4008af21 || arg1 == 0x4008af22 || arg1 == 0x4008af60 || arg1 == 0x4004af61
+connect: 1
+sendto: 1
diff --git a/seccomp/x86_64/wl_device.policy b/seccomp/x86_64/wl_device.policy
new file mode 100644
index 0000000..2ca7ed9
--- /dev/null
+++ b/seccomp/x86_64/wl_device.policy
@@ -0,0 +1,22 @@
+# Copyright 2018 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
+
+# Used to connect to wayland. arg0 == AF_UNIX && arg1 == SOCK_STREAM|SOCK_CLOEXEC
+socket: arg0 == 1 && arg1 == 0x80001 && arg2 == 0
+# arg1 == FIONBIO || arg1 == DMA_BUF_IOCTL_SYNC
+ioctl: arg1 == 0x5421 || arg1 == 0x40086200
+connect: 1
+# Used to communicate with wayland
+recvmsg: 1
+sendmsg: 1
+# Used for sharing memory with wayland. arg1 == MFD_CLOEXEC|MFD_ALLOW_SEALING
+memfd_create: arg1 == 3
+# Used to set of size new memfd
+ftruncate: 1
+# Used to determine shm size after recvmsg with fd
+lseek: 1
+# Allow F_GETFL only
+fcntl: arg1 == 3
diff --git a/seccomp/x86_64/xhci.policy b/seccomp/x86_64/xhci.policy
new file mode 100644
index 0000000..98e3335
--- /dev/null
+++ b/seccomp/x86_64/xhci.policy
@@ -0,0 +1,45 @@
+# Copyright 2018 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.
+
+# xhci need "openat" to enumerate device. "openat" is disabled in comman_device policy.
+openat: 1
+@include /usr/share/policy/crosvm/common_device.policy
+
+lstat: 1
+gettid: 1
+readlink: 1
+readlinkat: 1
+timerfd_create: 1
+name_to_handle_at: 1
+access: 1
+timerfd_create: 1
+getsockname: 1
+pipe: 1
+setsockopt: 1
+bind: 1
+fcntl: 1
+socket: arg0 == AF_NETLINK
+stat: 1
+uname: 1
+# The following ioctls are:
+# 0x4008550d == USBDEVFS_REAPURBNDELAY
+# 0x41045508 == USBDEVFS_GETDRIVER
+# 0x550b == USBDEVFS_DISCARDURB
+# 0x8004550f == USBDEVFS_CLAIMINTERFACE
+# 0x80045510 == USBDEVFS_RELEASEINTERFACE
+# 0x8004551a == USBDEVFS_GET_CAPABILITIES
+# 0x8038550a == USBDEVFS_SUBMITURB
+# 0xc0185500 == USBDEVFS_CONTROL
+# 0x5514 == USBDEVFS_RESET
+# 0x80045505 == USBDEVFS_SETCONFIGURATION
+# 0x8108551b == USBDEVFS_DISCONNECT_CLAIM
+# 0x40085511 == USBDEVFS_CONNECTINFO
+# 0x80185520 == USBDEVFS_CONNINFO_EX
+ioctl: arg1 == 0xc0185500 || arg1 == 0x41045508 || arg1 == 0x8004550f || arg1 == 0x4008550d || arg1 == 0x8004551a || arg1 == 0x550b || arg1 == 0x80045510 || arg1 == 0x8038550a || arg1 == 0x5514 || arg1 == 0x80045505 || arg1 == 0x8108551b || arg1 == 0x40085511 || arg1 == 0x80185520
+fstat: 1
+sigaltstack: 1
+recvmsg: 1
+getrandom: 1
+getdents: 1
+lseek: 1
diff --git a/src/argument.rs b/src/argument.rs
new file mode 100644
index 0000000..7cb56d6
--- /dev/null
+++ b/src/argument.rs
@@ -0,0 +1,425 @@
+// Copyright 2017 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.
+
+//! Handles argument parsing.
+//!
+//! # Example
+//!
+//! ```
+//! const ARGUMENTS: &'static [Argument] = &[
+//! Argument::positional("FILES", "files to operate on"),
+//! Argument::short_value('p', "program", "PROGRAM", "Program to apply to each file"),
+//! Argument::short_value('c', "cpus", "N", "Number of CPUs to use. (default: 1)"),
+//! Argument::flag("unmount", "Unmount the root"),
+//! Argument::short_flag('h', "help", "Print help message."),
+//! ];
+//!
+//! let match_res = set_arguments(args, ARGUMENTS, |name, value| {
+//! match name {
+//! "" => println!("positional arg! {}", value.unwrap()),
+//! "program" => println!("gonna use program {}", value.unwrap()),
+//! "cpus" => {
+//! let v: u32 = value.unwrap().parse().map_err(|_| {
+//! Error::InvalidValue {
+//! value: value.unwrap().to_owned(),
+//! expected: "this value for `cpus` needs to be integer",
+//! }
+//! })?;
+//! }
+//! "unmount" => println!("gonna unmount"),
+//! "help" => return Err(Error::PrintHelp),
+//! _ => unreachable!(),
+//! }
+//! }
+//!
+//! match match_res {
+//! Ok(_) => println!("running with settings"),
+//! Err(Error::PrintHelp) => print_help("best_program", "FILES", ARGUMENTS),
+//! Err(e) => println!("{}", e),
+//! }
+//! ```
+
+use std::fmt::{self, Display};
+use std::result;
+
+/// An error with argument parsing.
+#[derive(Debug)]
+pub enum Error {
+ /// There was a syntax error with the argument.
+ Syntax(String),
+ /// The argumen's name is unused.
+ UnknownArgument(String),
+ /// The argument was required.
+ ExpectedArgument(String),
+ /// The argument's given value is invalid.
+ InvalidValue {
+ value: String,
+ expected: &'static str,
+ },
+ /// The argument was already given and none more are expected.
+ TooManyArguments(String),
+ /// The argument expects a value.
+ ExpectedValue(String),
+ /// The help information was requested
+ PrintHelp,
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ Syntax(s) => write!(f, "syntax error: {}", s),
+ UnknownArgument(s) => write!(f, "unknown argument: {}", s),
+ ExpectedArgument(s) => write!(f, "expected argument: {}", s),
+ InvalidValue { value, expected } => {
+ write!(f, "invalid value {:?}: {}", value, expected)
+ }
+ TooManyArguments(s) => write!(f, "too many arguments: {}", s),
+ ExpectedValue(s) => write!(f, "expected parameter value: {}", s),
+ PrintHelp => write!(f, "help was requested"),
+ }
+ }
+}
+
+/// Result of a argument parsing.
+pub type Result<T> = result::Result<T, Error>;
+
+/// Information about an argument expected from the command line.
+///
+/// # Examples
+///
+/// To indicate a flag style argument:
+///
+/// ```
+/// Argument::short_flag('f', "flag", "enable awesome mode")
+/// ```
+///
+/// To indicate a parameter style argument that expects a value:
+///
+/// ```
+/// // "VALUE" and "NETMASK" are placeholder values displayed in the help message for these
+/// // arguments.
+/// Argument::short_value('v', "val", "VALUE", "how much do you value this usage information")
+/// Argument::value("netmask", "NETMASK", "hides your netface")
+/// ```
+///
+/// To indicate an argument with no short version:
+///
+/// ```
+/// Argument::flag("verbose", "this option is hard to type quickly")
+/// ```
+///
+/// To indicate a positional argument:
+///
+/// ```
+/// Argument::positional("VALUES", "these are positional arguments")
+/// ```
+#[derive(Default)]
+pub struct Argument {
+ /// The name of the value to display in the usage information. Use None to indicate that there
+ /// is no value expected for this argument.
+ pub value: Option<&'static str>,
+ /// Optional single character shortened argument name.
+ pub short: Option<char>,
+ /// The long name of this argument.
+ pub long: &'static str,
+ /// Helpfuly usage information for this argument to display to the user.
+ pub help: &'static str,
+}
+
+impl Argument {
+ pub fn positional(value: &'static str, help: &'static str) -> Argument {
+ Argument {
+ value: Some(value),
+ long: "",
+ help,
+ ..Default::default()
+ }
+ }
+
+ pub fn value(long: &'static str, value: &'static str, help: &'static str) -> Argument {
+ Argument {
+ value: Some(value),
+ long,
+ help,
+ ..Default::default()
+ }
+ }
+
+ pub fn short_value(
+ short: char,
+ long: &'static str,
+ value: &'static str,
+ help: &'static str,
+ ) -> Argument {
+ Argument {
+ value: Some(value),
+ short: Some(short),
+ long,
+ help,
+ }
+ }
+
+ pub fn flag(long: &'static str, help: &'static str) -> Argument {
+ Argument {
+ long,
+ help,
+ ..Default::default()
+ }
+ }
+
+ pub fn short_flag(short: char, long: &'static str, help: &'static str) -> Argument {
+ Argument {
+ short: Some(short),
+ long,
+ help,
+ ..Default::default()
+ }
+ }
+}
+
+fn parse_arguments<I, R, F>(args: I, mut f: F) -> Result<()>
+where
+ I: Iterator<Item = R>,
+ R: AsRef<str>,
+ F: FnMut(&str, Option<&str>) -> Result<()>,
+{
+ enum State {
+ // Initial state at the start and after finishing a single argument/value.
+ Top,
+ // The remaining arguments are all positional.
+ Positional,
+ // The next string is the value for the argument `name`.
+ Value { name: String },
+ }
+ let mut s = State::Top;
+ for arg in args {
+ let arg = arg.as_ref();
+ s = match s {
+ State::Top => {
+ if arg == "--" {
+ State::Positional
+ } else if arg.starts_with("--") {
+ let param = arg.trim_start_matches('-');
+ if param.contains('=') {
+ let mut iter = param.splitn(2, '=');
+ let name = iter.next().unwrap();
+ let value = iter.next().unwrap();
+ if name.is_empty() {
+ return Err(Error::Syntax(
+ "expected parameter name before `=`".to_owned(),
+ ));
+ }
+ if value.is_empty() {
+ return Err(Error::Syntax(
+ "expected parameter value after `=`".to_owned(),
+ ));
+ }
+ f(name, Some(value))?;
+ State::Top
+ } else if let Err(e) = f(param, None) {
+ if let Error::ExpectedValue(_) = e {
+ State::Value {
+ name: param.to_owned(),
+ }
+ } else {
+ return Err(e);
+ }
+ } else {
+ State::Top
+ }
+ } else if arg.starts_with('-') {
+ if arg.len() == 1 {
+ return Err(Error::Syntax(
+ "expected argument short name after `-`".to_owned(),
+ ));
+ }
+ let name = &arg[1..2];
+ let value = if arg.len() > 2 { Some(&arg[2..]) } else { None };
+ if let Err(e) = f(name, value) {
+ if let Error::ExpectedValue(_) = e {
+ State::Value {
+ name: name.to_owned(),
+ }
+ } else {
+ return Err(e);
+ }
+ } else {
+ State::Top
+ }
+ } else {
+ f("", Some(&arg))?;
+ State::Positional
+ }
+ }
+ State::Positional => {
+ f("", Some(&arg))?;
+ State::Positional
+ }
+ State::Value { name } => {
+ f(&name, Some(&arg))?;
+ State::Top
+ }
+ };
+ }
+ Ok(())
+}
+
+/// Parses the given `args` against the list of know arguments `arg_list` and calls `f` with each
+/// present argument and value if required.
+///
+/// This function guarantees that only valid long argument names from `arg_list` are sent to the
+/// callback `f`. It is also guaranteed that if an arg requires a value (i.e.
+/// `arg.value.is_some()`), the value will be `Some` in the callbacks arguments. If the callback
+/// returns `Err`, this function will end parsing and return that `Err`.
+///
+/// See the [module level](index.html) example for a usage example.
+pub fn set_arguments<I, R, F>(args: I, arg_list: &[Argument], mut f: F) -> Result<()>
+where
+ I: Iterator<Item = R>,
+ R: AsRef<str>,
+ F: FnMut(&str, Option<&str>) -> Result<()>,
+{
+ parse_arguments(args, |name, value| {
+ let mut matches = None;
+ for arg in arg_list {
+ if let Some(short) = arg.short {
+ if name.len() == 1 && name.starts_with(short) {
+ if value.is_some() != arg.value.is_some() {
+ return Err(Error::ExpectedValue(short.to_string()));
+ }
+ matches = Some(arg.long);
+ }
+ }
+ if matches.is_none() && arg.long == name {
+ if value.is_some() != arg.value.is_some() {
+ return Err(Error::ExpectedValue(arg.long.to_owned()));
+ }
+ matches = Some(arg.long);
+ }
+ }
+ match matches {
+ Some(long) => f(long, value),
+ None => Err(Error::UnknownArgument(name.to_owned())),
+ }
+ })
+}
+
+/// Prints command line usage information to stdout.
+///
+/// Usage information is printed according to the help fields in `args` with a leading usage line.
+/// The usage line is of the format "`program_name` [ARGUMENTS] `required_arg`".
+pub fn print_help(program_name: &str, required_arg: &str, args: &[Argument]) {
+ println!(
+ "Usage: {} {}{}\n",
+ program_name,
+ if args.is_empty() { "" } else { "[ARGUMENTS] " },
+ required_arg
+ );
+ if args.is_empty() {
+ return;
+ }
+ println!("Argument{}:", if args.len() > 1 { "s" } else { "" });
+ for arg in args {
+ match arg.short {
+ Some(s) => print!(" -{}, ", s),
+ None => print!(" "),
+ }
+ if arg.long.is_empty() {
+ print!(" ");
+ } else {
+ print!("--");
+ }
+ print!("{:<12}", arg.long);
+ if let Some(v) = arg.value {
+ if arg.long.is_empty() {
+ print!(" ");
+ } else {
+ print!("=");
+ }
+ print!("{:<10}", v);
+ } else {
+ print!("{:<11}", "");
+ }
+ println!("{}", arg.help);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn request_help() {
+ let arguments = [Argument::short_flag('h', "help", "Print help message.")];
+
+ let match_res = set_arguments(["-h"].iter(), &arguments[..], |name, _| {
+ match name {
+ "help" => return Err(Error::PrintHelp),
+ _ => unreachable!(),
+ };
+ });
+ match match_res {
+ Err(Error::PrintHelp) => {}
+ _ => unreachable!(),
+ }
+ }
+
+ #[test]
+ fn mixed_args() {
+ let arguments = [
+ Argument::positional("FILES", "files to operate on"),
+ Argument::short_value('p', "program", "PROGRAM", "Program to apply to each file"),
+ Argument::short_value('c', "cpus", "N", "Number of CPUs to use. (default: 1)"),
+ Argument::flag("unmount", "Unmount the root"),
+ Argument::short_flag('h', "help", "Print help message."),
+ ];
+
+ let mut unmount = false;
+ let match_res = set_arguments(
+ ["--cpus", "3", "--program", "hello", "--unmount", "file"].iter(),
+ &arguments[..],
+ |name, value| {
+ match name {
+ "" => assert_eq!(value.unwrap(), "file"),
+ "program" => assert_eq!(value.unwrap(), "hello"),
+ "cpus" => {
+ let c: u32 = value.unwrap().parse().map_err(|_| Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this value for `cpus` needs to be integer",
+ })?;
+ assert_eq!(c, 3);
+ }
+ "unmount" => unmount = true,
+ "help" => return Err(Error::PrintHelp),
+ _ => unreachable!(),
+ };
+ Ok(())
+ },
+ );
+ assert!(match_res.is_ok());
+ assert!(unmount);
+ }
+
+ #[test]
+ fn name_value_pair() {
+ let arguments = [Argument::short_value(
+ 'c',
+ "cpus",
+ "N",
+ "Number of CPUs to use. (default: 1)",
+ )];
+ let match_res = set_arguments(
+ ["-c", "5", "--cpus", "5", "-c5", "--cpus=5"].iter(),
+ &arguments[..],
+ |name, value| {
+ assert_eq!(name, "cpus");
+ assert_eq!(value, Some("5"));
+ Ok(())
+ },
+ );
+ assert!(match_res.is_ok());
+ }
+}
diff --git a/src/linux.rs b/src/linux.rs
new file mode 100644
index 0000000..9de014a
--- /dev/null
+++ b/src/linux.rs
@@ -0,0 +1,1772 @@
+// Copyright 2017 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;
+use std::cmp::min;
+use std::convert::TryFrom;
+use std::error::Error as StdError;
+use std::ffi::CStr;
+use std::fmt::{self, Display};
+use std::fs::{File, OpenOptions};
+use std::io::{self, stdin, Read};
+use std::net::Ipv4Addr;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::os::unix::net::UnixStream;
+use std::path::{Path, PathBuf};
+use std::str;
+use std::sync::{Arc, Barrier};
+use std::thread;
+use std::thread::JoinHandle;
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
+use libc::{self, c_int, gid_t, uid_t};
+
+use audio_streams::DummyStreamSource;
+use devices::virtio::{self, VirtioDevice};
+use devices::{self, HostBackendDeviceProvider, PciDevice, VirtioPciDevice, XhciController};
+use io_jail::{self, Minijail};
+use kvm::*;
+use libcras::CrasClient;
+use msg_socket::{MsgError, MsgReceiver, MsgSender, MsgSocket};
+use net_util::{Error as NetError, MacAddress, Tap};
+use qcow::{self, ImageType, QcowFile};
+use rand_ish::SimpleRng;
+use remain::sorted;
+use resources::{Alloc, SystemAllocator};
+use sync::{Condvar, Mutex};
+use sys_util::net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener};
+
+use sys_util::{
+ self, block_signal, clear_signal, drop_capabilities, error, flock, get_blocked_signals,
+ get_group_id, get_user_id, getegid, geteuid, info, register_signal_handler, set_cpu_affinity,
+ validate_raw_fd, warn, EventFd, FlockOperation, GuestAddress, GuestMemory, Killable,
+ MemoryMapping, PollContext, PollToken, Protection, SignalFd, Terminal, TimerFd, WatchingEvents,
+ SIGRTMIN,
+};
+use vhost;
+use vm_control::{
+ BalloonControlCommand, BalloonControlRequestSocket, BalloonControlResponseSocket,
+ DiskControlCommand, DiskControlRequestSocket, DiskControlResponseSocket, DiskControlResult,
+ UsbControlSocket, VmControlResponseSocket, VmMemoryControlRequestSocket,
+ VmMemoryControlResponseSocket, VmMemoryRequest, VmMemoryResponse, VmRunMode,
+};
+
+use crate::{Config, DiskOption, Executable, TouchDeviceOption};
+
+use arch::{self, LinuxArch, RunnableLinuxVm, VirtioDeviceStub, VmComponents, VmImage};
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+use aarch64::AArch64 as Arch;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+use x86_64::X8664arch as Arch;
+
+#[cfg(feature = "gpu-forward")]
+use render_node_forward::*;
+#[cfg(not(feature = "gpu-forward"))]
+type RenderNodeHost = ();
+
+#[sorted]
+#[derive(Debug)]
+pub enum Error {
+ AddGpuDeviceMemory(sys_util::Error),
+ AddPmemDeviceMemory(sys_util::Error),
+ AllocateGpuDeviceAddress,
+ AllocatePmemDeviceAddress(resources::Error),
+ BalloonDeviceNew(virtio::BalloonError),
+ BlockDeviceNew(sys_util::Error),
+ BlockSignal(sys_util::signal::Error),
+ BuildVm(<Arch as LinuxArch>::Error),
+ ChownTpmStorage(sys_util::Error),
+ CloneEventFd(sys_util::Error),
+ CreateCrasClient(libcras::Error),
+ CreateEventFd(sys_util::Error),
+ CreatePollContext(sys_util::Error),
+ CreateSignalFd(sys_util::SignalFdError),
+ CreateSocket(io::Error),
+ CreateTapDevice(NetError),
+ CreateTimerFd(sys_util::Error),
+ CreateTpmStorage(PathBuf, io::Error),
+ CreateUsbProvider(devices::usb::host_backend::error::Error),
+ DetectImageType(qcow::Error),
+ DeviceJail(io_jail::Error),
+ DevicePivotRoot(io_jail::Error),
+ Disk(io::Error),
+ DiskImageLock(sys_util::Error),
+ DropCapabilities(sys_util::Error),
+ InputDeviceNew(virtio::InputError),
+ InputEventsOpen(std::io::Error),
+ InvalidFdPath,
+ InvalidWaylandPath,
+ IoJail(io_jail::Error),
+ LoadKernel(Box<dyn StdError>),
+ NetDeviceNew(virtio::NetError),
+ OpenAndroidFstab(PathBuf, io::Error),
+ OpenBios(PathBuf, io::Error),
+ OpenInitrd(PathBuf, io::Error),
+ OpenKernel(PathBuf, io::Error),
+ OpenVinput(PathBuf, io::Error),
+ P9DeviceNew(virtio::P9Error),
+ PivotRootDoesntExist(&'static str),
+ PmemDeviceImageTooBig,
+ PmemDeviceNew(sys_util::Error),
+ PollContextAdd(sys_util::Error),
+ PollContextDelete(sys_util::Error),
+ QcowDeviceCreate(qcow::Error),
+ ReadLowmemAvailable(io::Error),
+ ReadLowmemMargin(io::Error),
+ RegisterBalloon(arch::DeviceRegistrationError),
+ RegisterBlock(arch::DeviceRegistrationError),
+ RegisterGpu(arch::DeviceRegistrationError),
+ RegisterNet(arch::DeviceRegistrationError),
+ RegisterP9(arch::DeviceRegistrationError),
+ RegisterRng(arch::DeviceRegistrationError),
+ RegisterSignalHandler(sys_util::Error),
+ RegisterWayland(arch::DeviceRegistrationError),
+ ReserveGpuMemory(sys_util::MmapError),
+ ReserveMemory(sys_util::Error),
+ ReservePmemMemory(sys_util::MmapError),
+ ResetTimerFd(sys_util::Error),
+ RngDeviceNew(virtio::RngError),
+ SettingGidMap(io_jail::Error),
+ SettingUidMap(io_jail::Error),
+ SignalFd(sys_util::SignalFdError),
+ SpawnVcpu(io::Error),
+ TimerFd(sys_util::Error),
+ ValidateRawFd(sys_util::Error),
+ VhostNetDeviceNew(virtio::vhost::Error),
+ VhostVsockDeviceNew(virtio::vhost::Error),
+ VirtioPciDev(sys_util::Error),
+ WaylandDeviceNew(sys_util::Error),
+}
+
+impl Display for Error {
+ #[remain::check]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ #[sorted]
+ match self {
+ AddGpuDeviceMemory(e) => write!(f, "failed to add gpu device memory: {}", e),
+ AddPmemDeviceMemory(e) => write!(f, "failed to add pmem device memory: {}", e),
+ AllocateGpuDeviceAddress => write!(f, "failed to allocate gpu device guest address"),
+ AllocatePmemDeviceAddress(e) => {
+ write!(f, "failed to allocate memory for pmem device: {}", e)
+ }
+ BalloonDeviceNew(e) => write!(f, "failed to create balloon: {}", e),
+ BlockDeviceNew(e) => write!(f, "failed to create block device: {}", e),
+ BlockSignal(e) => write!(f, "failed to block signal: {}", e),
+ BuildVm(e) => write!(f, "The architecture failed to build the vm: {}", e),
+ ChownTpmStorage(e) => write!(f, "failed to chown tpm storage: {}", e),
+ CloneEventFd(e) => write!(f, "failed to clone eventfd: {}", e),
+ CreateCrasClient(e) => write!(f, "failed to create cras client: {}", e),
+ CreateEventFd(e) => write!(f, "failed to create eventfd: {}", 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),
+ CreateTimerFd(e) => write!(f, "failed to create timerfd: {}", e),
+ CreateTpmStorage(p, e) => {
+ write!(f, "failed to create tpm storage dir {}: {}", p.display(), e)
+ }
+ CreateUsbProvider(e) => write!(f, "failed to create usb provider: {}", e),
+ DetectImageType(e) => write!(f, "failed to detect disk image type: {}", e),
+ DeviceJail(e) => write!(f, "failed to jail device: {}", e),
+ DevicePivotRoot(e) => write!(f, "failed to pivot root device: {}", e),
+ Disk(e) => write!(f, "failed to load disk image: {}", e),
+ DiskImageLock(e) => write!(f, "failed to lock disk image: {}", e),
+ DropCapabilities(e) => write!(f, "failed to drop process capabilities: {}", 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/*"),
+ InvalidWaylandPath => write!(f, "wayland socket path has no parent or file name"),
+ IoJail(e) => write!(f, "{}", e),
+ LoadKernel(e) => write!(f, "failed to load kernel: {}", e),
+ NetDeviceNew(e) => write!(f, "failed to set up virtio networking: {}", e),
+ OpenAndroidFstab(p, e) => write!(
+ f,
+ "failed to open android fstab file {}: {}",
+ p.display(),
+ e
+ ),
+ OpenBios(p, e) => write!(f, "failed to open bios {}: {}", p.display(), e),
+ OpenInitrd(p, e) => write!(f, "failed to open initrd {}: {}", p.display(), e),
+ OpenKernel(p, e) => write!(f, "failed to open kernel image {}: {}", p.display(), e),
+ OpenVinput(p, e) => write!(f, "failed to open vinput device {}: {}", p.display(), e),
+ P9DeviceNew(e) => write!(f, "failed to create 9p device: {}", e),
+ PivotRootDoesntExist(p) => write!(f, "{} doesn't exist, can't jail devices.", p),
+ PmemDeviceImageTooBig => {
+ 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),
+ QcowDeviceCreate(e) => write!(f, "failed to read qcow formatted file {}", e),
+ ReadLowmemAvailable(e) => write!(
+ f,
+ "failed to read /sys/kernel/mm/chromeos-low_mem/available: {}",
+ e
+ ),
+ ReadLowmemMargin(e) => write!(
+ f,
+ "failed to read /sys/kernel/mm/chromeos-low_mem/margin: {}",
+ e
+ ),
+ RegisterBalloon(e) => write!(f, "error registering balloon device: {}", e),
+ RegisterBlock(e) => write!(f, "error registering block device: {}", e),
+ RegisterGpu(e) => write!(f, "error registering gpu device: {}", e),
+ RegisterNet(e) => write!(f, "error registering net device: {}", e),
+ RegisterP9(e) => write!(f, "error registering 9p device: {}", e),
+ RegisterRng(e) => write!(f, "error registering rng device: {}", e),
+ RegisterSignalHandler(e) => write!(f, "error registering signal handler: {}", e),
+ RegisterWayland(e) => write!(f, "error registering wayland device: {}", e),
+ ReserveGpuMemory(e) => write!(f, "failed to reserve gpu memory: {}", e),
+ ReserveMemory(e) => write!(f, "failed to reserve memory: {}", e),
+ ReservePmemMemory(e) => write!(f, "failed to reserve pmem memory: {}", e),
+ ResetTimerFd(e) => write!(f, "failed to reset timerfd: {}", e),
+ RngDeviceNew(e) => write!(f, "failed to set up rng: {}", e),
+ SettingGidMap(e) => write!(f, "error setting GID map: {}", e),
+ SettingUidMap(e) => write!(f, "error setting UID map: {}", e),
+ SignalFd(e) => write!(f, "failed to read signal fd: {}", e),
+ SpawnVcpu(e) => write!(f, "failed to spawn VCPU thread: {}", e),
+ TimerFd(e) => write!(f, "failed to read timer fd: {}", e),
+ ValidateRawFd(e) => write!(f, "failed to validate raw fd: {}", 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),
+ WaylandDeviceNew(e) => write!(f, "failed to create wayland device: {}", e),
+ }
+ }
+}
+
+impl From<io_jail::Error> for Error {
+ fn from(err: io_jail::Error) -> Self {
+ Error::IoJail(err)
+ }
+}
+
+impl std::error::Error for Error {}
+
+type Result<T> = std::result::Result<T, Error>;
+
+enum TaggedControlSocket {
+ Vm(VmControlResponseSocket),
+ VmMemory(VmMemoryControlResponseSocket),
+}
+
+impl AsRef<UnixSeqpacket> for TaggedControlSocket {
+ fn as_ref(&self) -> &UnixSeqpacket {
+ use self::TaggedControlSocket::*;
+ match &self {
+ Vm(ref socket) => socket,
+ VmMemory(ref socket) => socket,
+ }
+ }
+}
+
+impl AsRawFd for TaggedControlSocket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_ref().as_raw_fd()
+ }
+}
+
+fn create_base_minijail(root: &Path, seccomp_policy: &Path) -> Result<Minijail> {
+ // All child jails run in a new user namespace without any users mapped,
+ // they run as nobody unless otherwise configured.
+ let mut j = Minijail::new().map_err(Error::DeviceJail)?;
+ j.namespace_pids();
+ j.namespace_user();
+ j.namespace_user_disable_setgroups();
+ // Don't need any capabilities.
+ j.use_caps(0);
+ // Create a new mount namespace with an empty root FS.
+ j.namespace_vfs();
+ j.enter_pivot_root(root).map_err(Error::DevicePivotRoot)?;
+ // Run in an empty network namespace.
+ j.namespace_net();
+ // Apply the block device seccomp policy.
+ j.no_new_privs();
+ // Use TSYNC only for the side effect of it using SECCOMP_RET_TRAP, which will correctly kill
+ // the entire device process if a worker thread commits a seccomp violation.
+ j.set_seccomp_filter_tsync();
+ #[cfg(debug_assertions)]
+ j.log_seccomp_filter_failures();
+ j.parse_seccomp_filters(seccomp_policy)
+ .map_err(Error::DeviceJail)?;
+ j.use_seccomp_filter();
+ // Don't do init setup.
+ j.run_as_init();
+ Ok(j)
+}
+
+fn simple_jail(cfg: &Config, policy: &str) -> Result<Option<Minijail>> {
+ if cfg.sandbox {
+ let pivot_root: &str = option_env!("DEFAULT_PIVOT_ROOT").unwrap_or("/var/empty");
+ // A directory for a jailed device's pivot root.
+ let root_path = Path::new(pivot_root);
+ if !root_path.exists() {
+ return Err(Error::PivotRootDoesntExist(pivot_root));
+ }
+ let policy_path: PathBuf = cfg.seccomp_policy_dir.join(policy);
+ Ok(Some(create_base_minijail(root_path, &policy_path)?))
+ } else {
+ Ok(None)
+ }
+}
+
+type DeviceResult<T = VirtioDeviceStub> = std::result::Result<T, Error>;
+
+fn create_block_device(
+ cfg: &Config,
+ disk: &DiskOption,
+ disk_device_socket: DiskControlResponseSocket,
+) -> DeviceResult {
+ // 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)?) }
+ } else {
+ OpenOptions::new()
+ .read(true)
+ .write(!disk.read_only)
+ .open(&disk.path)
+ .map_err(Error::Disk)?
+ };
+ // Lock the disk image to prevent other crosvm instances from using it.
+ let lock_op = if disk.read_only {
+ FlockOperation::LockShared
+ } else {
+ FlockOperation::LockExclusive
+ };
+ flock(&raw_image, lock_op, true).map_err(Error::DiskImageLock)?;
+
+ let image_type = qcow::detect_image_type(&raw_image).map_err(Error::DetectImageType)?;
+ let dev = match image_type {
+ ImageType::Raw => {
+ // Access as a raw block device.
+ let dev = virtio::Block::new(raw_image, disk.read_only, Some(disk_device_socket))
+ .map_err(Error::BlockDeviceNew)?;
+ Box::new(dev) as Box<dyn VirtioDevice>
+ }
+ ImageType::Qcow2 => {
+ // Valid qcow header present
+ let qcow_image = QcowFile::from(raw_image).map_err(Error::QcowDeviceCreate)?;
+ let dev = virtio::Block::new(qcow_image, disk.read_only, Some(disk_device_socket))
+ .map_err(Error::BlockDeviceNew)?;
+ Box::new(dev) as Box<dyn VirtioDevice>
+ }
+ };
+
+ Ok(VirtioDeviceStub {
+ dev,
+ jail: simple_jail(&cfg, "block_device.policy")?,
+ })
+}
+
+fn create_rng_device(cfg: &Config) -> DeviceResult {
+ let dev = virtio::Rng::new().map_err(Error::RngDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "rng_device.policy")?,
+ })
+}
+
+#[cfg(feature = "tpm")]
+fn create_tpm_device(cfg: &Config) -> DeviceResult {
+ use std::ffi::CString;
+ use std::fs;
+ use std::process;
+ use sys_util::chown;
+
+ let tpm_storage: PathBuf;
+ let mut tpm_jail = simple_jail(&cfg, "tpm_device.policy")?;
+
+ match &mut tpm_jail {
+ Some(jail) => {
+ // Create a tmpfs in the device's root directory for tpm
+ // simulator storage. The size is 20*1024, or 20 KB.
+ jail.mount_with_data(
+ Path::new("none"),
+ Path::new("/"),
+ "tmpfs",
+ (libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC) as usize,
+ "size=20480",
+ )?;
+
+ let crosvm_ids = add_crosvm_user_to_jail(jail, "tpm")?;
+
+ let pid = process::id();
+ let tpm_pid_dir = format!("/run/vm/tpm.{}", pid);
+ tpm_storage = Path::new(&tpm_pid_dir).to_owned();
+ fs::create_dir_all(&tpm_storage)
+ .map_err(|e| Error::CreateTpmStorage(tpm_storage.to_owned(), e))?;
+ let tpm_pid_dir_c = CString::new(tpm_pid_dir).expect("no nul bytes");
+ chown(&tpm_pid_dir_c, crosvm_ids.uid, crosvm_ids.gid)
+ .map_err(Error::ChownTpmStorage)?;
+
+ jail.mount_bind(&tpm_storage, &tpm_storage, true)?;
+ }
+ None => {
+ // Path used inside cros_sdk which does not have /run/vm.
+ tpm_storage = Path::new("/tmp/tpm-simulator").to_owned();
+ }
+ }
+
+ let dev = virtio::Tpm::new(tpm_storage);
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: tpm_jail,
+ })
+}
+
+fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOption) -> DeviceResult {
+ let socket = create_input_socket(&single_touch_spec.path).map_err(|e| {
+ error!("failed configuring virtio single touch: {:?}", e);
+ e
+ })?;
+
+ let dev = virtio::new_single_touch(socket, single_touch_spec.width, single_touch_spec.height)
+ .map_err(Error::InputDeviceNew)?;
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "input_device.policy")?,
+ })
+}
+
+fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> DeviceResult {
+ let socket = create_input_socket(&trackpad_spec.path).map_err(|e| {
+ error!("failed configuring virtio trackpad: {}", e);
+ e
+ })?;
+
+ let dev = virtio::new_trackpad(socket, trackpad_spec.width, trackpad_spec.height)
+ .map_err(Error::InputDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "input_device.policy")?,
+ })
+}
+
+fn create_mouse_device(cfg: &Config, mouse_socket: &Path) -> DeviceResult {
+ let socket = create_input_socket(&mouse_socket).map_err(|e| {
+ error!("failed configuring virtio mouse: {}", e);
+ e
+ })?;
+
+ let dev = virtio::new_mouse(socket).map_err(Error::InputDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "input_device.policy")?,
+ })
+}
+
+fn create_keyboard_device(cfg: &Config, keyboard_socket: &Path) -> DeviceResult {
+ let socket = create_input_socket(&keyboard_socket).map_err(|e| {
+ error!("failed configuring virtio keyboard: {}", e);
+ e
+ })?;
+
+ let dev = virtio::new_keyboard(socket).map_err(Error::InputDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "input_device.policy")?,
+ })
+}
+
+fn create_vinput_device(cfg: &Config, dev_path: &Path) -> DeviceResult {
+ let dev_file = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .open(dev_path)
+ .map_err(|e| Error::OpenVinput(dev_path.to_owned(), e))?;
+
+ let dev = virtio::new_evdev(dev_file).map_err(Error::InputDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "input_device.policy")?,
+ })
+}
+
+fn create_balloon_device(cfg: &Config, socket: BalloonControlResponseSocket) -> DeviceResult {
+ let dev = virtio::Balloon::new(socket).map_err(Error::BalloonDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "balloon_device.policy")?,
+ })
+}
+
+fn create_tap_net_device(cfg: &Config, tap_fd: RawFd) -> 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)?
+ };
+
+ let dev = virtio::Net::from(tap).map_err(Error::NetDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "net_device.policy")?,
+ })
+}
+
+fn create_net_device(
+ cfg: &Config,
+ host_ip: Ipv4Addr,
+ netmask: Ipv4Addr,
+ mac_address: MacAddress,
+ mem: &GuestMemory,
+) -> DeviceResult {
+ let dev = if cfg.vhost_net {
+ let dev =
+ virtio::vhost::Net::<Tap, vhost::Net<Tap>>::new(host_ip, netmask, mac_address, mem)
+ .map_err(Error::VhostNetDeviceNew)?;
+ Box::new(dev) as Box<dyn VirtioDevice>
+ } else {
+ let dev =
+ virtio::Net::<Tap>::new(host_ip, netmask, mac_address).map_err(Error::NetDeviceNew)?;
+ Box::new(dev) as Box<dyn VirtioDevice>
+ };
+
+ let policy = if cfg.vhost_net {
+ "vhost_net_device.policy"
+ } else {
+ "net_device.policy"
+ };
+
+ Ok(VirtioDeviceStub {
+ dev,
+ jail: simple_jail(&cfg, policy)?,
+ })
+}
+
+#[cfg(feature = "gpu")]
+fn create_gpu_device(
+ cfg: &Config,
+ exit_evt: &EventFd,
+ gpu_device_socket: VmMemoryControlRequestSocket,
+ gpu_socket: virtio::resource_bridge::ResourceResponseSocket,
+ wayland_socket_path: &Path,
+) -> DeviceResult {
+ let jailed_wayland_path = Path::new("/wayland-0");
+
+ let dev = virtio::Gpu::new(
+ exit_evt.try_clone().map_err(Error::CloneEventFd)?,
+ Some(gpu_device_socket),
+ Some(gpu_socket),
+ if cfg.sandbox {
+ &jailed_wayland_path
+ } else {
+ wayland_socket_path
+ },
+ );
+
+ let jail = match simple_jail(&cfg, "gpu_device.policy")? {
+ Some(mut jail) => {
+ // Create a tmpfs in the device's root directory so that we can bind mount the
+ // dri directory into it. The size=67108864 is size=64*1024*1024 or size=64MB.
+ jail.mount_with_data(
+ Path::new("none"),
+ Path::new("/"),
+ "tmpfs",
+ (libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC) as usize,
+ "size=67108864",
+ )?;
+
+ // Device nodes required for DRM.
+ let sys_dev_char_path = Path::new("/sys/dev/char");
+ jail.mount_bind(sys_dev_char_path, sys_dev_char_path, false)?;
+ let sys_devices_path = Path::new("/sys/devices");
+ jail.mount_bind(sys_devices_path, sys_devices_path, false)?;
+ let drm_dri_path = Path::new("/dev/dri");
+ jail.mount_bind(drm_dri_path, drm_dri_path, false)?;
+
+ // Libraries that are required when mesa drivers are dynamically loaded.
+ let lib_path = Path::new("/lib64");
+ jail.mount_bind(lib_path, lib_path, false)?;
+ let usr_lib_path = Path::new("/usr/lib64");
+ jail.mount_bind(usr_lib_path, usr_lib_path, false)?;
+
+ // Bind mount the wayland socket into jail's root. This is necessary since each
+ // new wayland context must open() the socket.
+ jail.mount_bind(wayland_socket_path, jailed_wayland_path, true)?;
+
+ add_crosvm_user_to_jail(&mut jail, "gpu")?;
+
+ Some(jail)
+ }
+ None => None,
+ };
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail,
+ })
+}
+
+fn create_wayland_device(
+ cfg: &Config,
+ socket_path: &Path,
+ socket: VmMemoryControlRequestSocket,
+ resource_bridge: Option<virtio::resource_bridge::ResourceRequestSocket>,
+) -> DeviceResult {
+ let wayland_socket_dir = socket_path.parent().ok_or(Error::InvalidWaylandPath)?;
+ let wayland_socket_name = socket_path.file_name().ok_or(Error::InvalidWaylandPath)?;
+ let jailed_wayland_dir = Path::new("/wayland");
+ let jailed_wayland_path = jailed_wayland_dir.join(wayland_socket_name);
+
+ let dev = virtio::Wl::new(
+ if cfg.sandbox {
+ &jailed_wayland_path
+ } else {
+ socket_path
+ },
+ socket,
+ resource_bridge,
+ )
+ .map_err(Error::WaylandDeviceNew)?;
+
+ let jail = match simple_jail(&cfg, "wl_device.policy")? {
+ Some(mut jail) => {
+ // Create a tmpfs in the device's root directory so that we can bind mount the wayland
+ // socket directory into it. The size=67108864 is size=64*1024*1024 or size=64MB.
+ jail.mount_with_data(
+ Path::new("none"),
+ Path::new("/"),
+ "tmpfs",
+ (libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC) as usize,
+ "size=67108864",
+ )?;
+
+ // Bind mount the wayland socket's directory into jail's root. This is necessary since
+ // each new wayland context must open() the socket. If the wayland socket is ever
+ // destroyed and remade in the same host directory, new connections will be possible
+ // without restarting the wayland device.
+ jail.mount_bind(wayland_socket_dir, jailed_wayland_dir, true)?;
+
+ add_crosvm_user_to_jail(&mut jail, "Wayland")?;
+
+ Some(jail)
+ }
+ None => None,
+ };
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail,
+ })
+}
+
+fn create_vhost_vsock_device(cfg: &Config, cid: u64, mem: &GuestMemory) -> DeviceResult {
+ let dev = virtio::vhost::Vsock::new(cid, mem).map_err(Error::VhostVsockDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail: simple_jail(&cfg, "vhost_vsock_device.policy")?,
+ })
+}
+
+fn create_9p_device(cfg: &Config, chronos: Ids, src: &Path, tag: &str) -> DeviceResult {
+ let (jail, root) = match simple_jail(&cfg, "9p_device.policy")? {
+ Some(mut jail) => {
+ // The shared directory becomes the root of the device's file system.
+ let root = Path::new("/");
+ jail.mount_bind(src, root, true)?;
+
+ // Set the uid/gid for the jailed process, and give a basic id map. This
+ // is required for the above bind mount to work.
+ jail.change_uid(chronos.uid);
+ jail.change_gid(chronos.gid);
+ jail.uidmap(&format!("{0} {0} 1", chronos.uid))
+ .map_err(Error::SettingUidMap)?;
+ jail.gidmap(&format!("{0} {0} 1", chronos.gid))
+ .map_err(Error::SettingGidMap)?;
+
+ (Some(jail), root)
+ }
+ None => {
+ // There's no bind mount so we tell the server to treat the source directory as the
+ // root.
+ (None, src)
+ }
+ };
+
+ let dev = virtio::P9::new(root, tag).map_err(Error::P9DeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev),
+ jail,
+ })
+}
+
+fn create_pmem_device(
+ cfg: &Config,
+ vm: &mut Vm,
+ resources: &mut SystemAllocator,
+ disk: &DiskOption,
+ index: usize,
+) -> DeviceResult {
+ let fd = OpenOptions::new()
+ .read(true)
+ .write(!disk.read_only)
+ .open(&disk.path)
+ .map_err(Error::Disk)?;
+
+ let image_size = {
+ let metadata = std::fs::metadata(&disk.path).map_err(Error::Disk)?;
+ metadata.len()
+ };
+
+ let protection = {
+ if disk.read_only {
+ Protection::read()
+ } else {
+ Protection::read_write()
+ }
+ };
+
+ let memory_mapping = {
+ // Conversion from u64 to usize may fail on 32bit system.
+ let image_size = usize::try_from(image_size).map_err(|_| Error::PmemDeviceImageTooBig)?;
+
+ MemoryMapping::from_fd_offset_protection(&fd, image_size, 0, protection)
+ .map_err(Error::ReservePmemMemory)?
+ };
+
+ let mapping_address = resources
+ .device_allocator()
+ .allocate_with_align(
+ image_size,
+ Alloc::PmemDevice(index),
+ format!("pmem_disk_image_{}", index),
+ // Linux kernel requires pmem namespaces to be 128 MiB aligned.
+ 128 * 1024 * 1024, /* 128 MiB */
+ )
+ .map_err(Error::AllocatePmemDeviceAddress)?;
+
+ vm.add_device_memory(
+ GuestAddress(mapping_address),
+ memory_mapping,
+ /* read_only = */ disk.read_only,
+ /* log_dirty_pages = */ false,
+ )
+ .map_err(Error::AddPmemDeviceMemory)?;
+
+ let dev = virtio::Pmem::new(fd, GuestAddress(mapping_address), image_size)
+ .map_err(Error::PmemDeviceNew)?;
+
+ Ok(VirtioDeviceStub {
+ dev: Box::new(dev) as Box<dyn VirtioDevice>,
+ /// TODO(jstaron) Create separate device policy for pmem_device.
+ jail: simple_jail(&cfg, "block_device.policy")?,
+ })
+}
+
+// gpu_device_socket is not used when GPU support is disabled.
+#[cfg_attr(not(feature = "gpu"), allow(unused_variables))]
+fn create_virtio_devices(
+ cfg: &Config,
+ mem: &GuestMemory,
+ vm: &mut Vm,
+ resources: &mut SystemAllocator,
+ _exit_evt: &EventFd,
+ wayland_device_socket: VmMemoryControlRequestSocket,
+ gpu_device_socket: VmMemoryControlRequestSocket,
+ balloon_device_socket: BalloonControlResponseSocket,
+ disk_device_sockets: &mut Vec<DiskControlResponseSocket>,
+) -> DeviceResult<Vec<VirtioDeviceStub>> {
+ let mut devs = Vec::new();
+
+ for disk in &cfg.disks {
+ let disk_device_socket = disk_device_sockets.remove(0);
+ devs.push(create_block_device(cfg, disk, disk_device_socket)?);
+ }
+
+ for (index, pmem_disk) in cfg.pmem_devices.iter().enumerate() {
+ devs.push(create_pmem_device(cfg, vm, resources, pmem_disk, index)?);
+ }
+
+ devs.push(create_rng_device(cfg)?);
+
+ #[cfg(feature = "tpm")]
+ {
+ if cfg.software_tpm {
+ devs.push(create_tpm_device(cfg)?);
+ }
+ }
+
+ if let Some(single_touch_spec) = &cfg.virtio_single_touch {
+ devs.push(create_single_touch_device(cfg, single_touch_spec)?);
+ }
+
+ if let Some(trackpad_spec) = &cfg.virtio_trackpad {
+ devs.push(create_trackpad_device(cfg, trackpad_spec)?);
+ }
+
+ if let Some(mouse_socket) = &cfg.virtio_mouse {
+ devs.push(create_mouse_device(cfg, mouse_socket)?);
+ }
+
+ if let Some(keyboard_socket) = &cfg.virtio_keyboard {
+ devs.push(create_keyboard_device(cfg, keyboard_socket)?);
+ }
+
+ for dev_path in &cfg.virtio_input_evdevs {
+ devs.push(create_vinput_device(cfg, dev_path)?);
+ }
+
+ devs.push(create_balloon_device(cfg, balloon_device_socket)?);
+
+ // We checked above that if the IP is defined, then the netmask is, too.
+ for tap_fd in &cfg.tap_fd {
+ devs.push(create_tap_net_device(cfg, *tap_fd)?);
+ }
+
+ if let (Some(host_ip), Some(netmask), Some(mac_address)) =
+ (cfg.host_ip, cfg.netmask, cfg.mac_address)
+ {
+ devs.push(create_net_device(cfg, host_ip, netmask, mac_address, mem)?);
+ }
+
+ #[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
+ let mut resource_bridge_wl_socket = None::<virtio::resource_bridge::ResourceRequestSocket>;
+
+ #[cfg(feature = "gpu")]
+ {
+ if cfg.gpu {
+ if let Some(wayland_socket_path) = &cfg.wayland_socket_path {
+ let (wl_socket, gpu_socket) =
+ virtio::resource_bridge::pair().map_err(Error::CreateSocket)?;
+ resource_bridge_wl_socket = Some(wl_socket);
+
+ devs.push(create_gpu_device(
+ cfg,
+ _exit_evt,
+ gpu_device_socket,
+ gpu_socket,
+ wayland_socket_path,
+ )?);
+ }
+ }
+ }
+
+ if let Some(wayland_socket_path) = cfg.wayland_socket_path.as_ref() {
+ devs.push(create_wayland_device(
+ cfg,
+ wayland_socket_path,
+ wayland_device_socket,
+ resource_bridge_wl_socket,
+ )?);
+ }
+
+ if let Some(cid) = cfg.cid {
+ devs.push(create_vhost_vsock_device(cfg, cid, mem)?);
+ }
+
+ let chronos = get_chronos_ids();
+
+ for (src, tag) in &cfg.shared_dirs {
+ devs.push(create_9p_device(cfg, chronos, src, tag)?);
+ }
+
+ Ok(devs)
+}
+
+fn create_devices(
+ cfg: &Config,
+ mem: &GuestMemory,
+ vm: &mut Vm,
+ resources: &mut SystemAllocator,
+ exit_evt: &EventFd,
+ wayland_device_socket: VmMemoryControlRequestSocket,
+ gpu_device_socket: VmMemoryControlRequestSocket,
+ balloon_device_socket: BalloonControlResponseSocket,
+ disk_device_sockets: &mut Vec<DiskControlResponseSocket>,
+ usb_provider: HostBackendDeviceProvider,
+) -> DeviceResult<Vec<(Box<dyn PciDevice>, Option<Minijail>)>> {
+ let stubs = create_virtio_devices(
+ &cfg,
+ mem,
+ vm,
+ resources,
+ exit_evt,
+ wayland_device_socket,
+ gpu_device_socket,
+ balloon_device_socket,
+ disk_device_sockets,
+ )?;
+
+ let mut pci_devices = Vec::new();
+
+ for stub in stubs {
+ let dev = VirtioPciDevice::new(mem.clone(), stub.dev).map_err(Error::VirtioPciDev)?;
+ let dev = Box::new(dev) as Box<dyn PciDevice>;
+ pci_devices.push((dev, stub.jail));
+ }
+
+ if cfg.cras_audio {
+ let mut server = Box::new(CrasClient::new().map_err(Error::CreateCrasClient)?);
+ if cfg.cras_capture {
+ server.enable_cras_capture();
+ }
+ let cras_audio = devices::Ac97Dev::new(mem.clone(), server);
+
+ pci_devices.push((
+ Box::new(cras_audio),
+ simple_jail(&cfg, "cras_audio_device.policy")?,
+ ));
+ }
+
+ if cfg.null_audio {
+ let server = Box::new(DummyStreamSource::new());
+ let null_audio = devices::Ac97Dev::new(mem.clone(), server);
+
+ pci_devices.push((
+ Box::new(null_audio),
+ simple_jail(&cfg, "null_audio_device.policy")?,
+ ));
+ }
+ // Create xhci controller.
+ let usb_controller = Box::new(XhciController::new(mem.clone(), usb_provider));
+ pci_devices.push((usb_controller, simple_jail(&cfg, "xhci.policy")?));
+
+ Ok(pci_devices)
+}
+
+#[derive(Copy, Clone)]
+struct Ids {
+ uid: uid_t,
+ gid: gid_t,
+}
+
+fn get_chronos_ids() -> Ids {
+ let chronos_user_group = CStr::from_bytes_with_nul(b"chronos\0").unwrap();
+
+ let chronos_uid = match get_user_id(&chronos_user_group) {
+ Ok(u) => u,
+ Err(e) => {
+ warn!("falling back to current user id for 9p: {}", e);
+ geteuid()
+ }
+ };
+
+ let chronos_gid = match get_group_id(&chronos_user_group) {
+ Ok(u) => u,
+ Err(e) => {
+ warn!("falling back to current group id for 9p: {}", e);
+ getegid()
+ }
+ };
+
+ Ids {
+ uid: chronos_uid,
+ gid: chronos_gid,
+ }
+}
+
+// Set the uid/gid for the jailed process and give a basic id map. This is
+// required for bind mounts to work.
+fn add_crosvm_user_to_jail(jail: &mut Minijail, feature: &str) -> Result<Ids> {
+ let crosvm_user_group = CStr::from_bytes_with_nul(b"crosvm\0").unwrap();
+
+ let crosvm_uid = match get_user_id(&crosvm_user_group) {
+ Ok(u) => u,
+ Err(e) => {
+ warn!("falling back to current user id for {}: {}", feature, e);
+ geteuid()
+ }
+ };
+
+ let crosvm_gid = match get_group_id(&crosvm_user_group) {
+ Ok(u) => u,
+ Err(e) => {
+ warn!("falling back to current group id for {}: {}", feature, e);
+ getegid()
+ }
+ };
+
+ jail.change_uid(crosvm_uid);
+ jail.change_gid(crosvm_gid);
+ jail.uidmap(&format!("{0} {0} 1", crosvm_uid))
+ .map_err(Error::SettingUidMap)?;
+ jail.gidmap(&format!("{0} {0} 1", crosvm_gid))
+ .map_err(Error::SettingGidMap)?;
+
+ Ok(Ids {
+ uid: crosvm_uid,
+ gid: crosvm_gid,
+ })
+}
+
+fn raw_fd_from_path(path: &Path) -> Result<RawFd> {
+ if !path.is_file() {
+ return Err(Error::InvalidFdPath);
+ }
+ let raw_fd = 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)
+}
+
+fn create_input_socket(path: &Path) -> Result<UnixStream> {
+ if path.parent() == Some(Path::new("/proc/self/fd")) {
+ // Safe because we will validate |raw_fd|.
+ unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(path)?)) }
+ } else {
+ UnixStream::connect(path).map_err(Error::InputEventsOpen)
+ }
+}
+
+fn setup_vcpu_signal_handler() -> Result<()> {
+ unsafe {
+ extern "C" fn handle_signal() {}
+ // Our signal handler does nothing and is trivially async signal safe.
+ register_signal_handler(SIGRTMIN() + 0, handle_signal)
+ .map_err(Error::RegisterSignalHandler)?;
+ }
+ block_signal(SIGRTMIN() + 0).map_err(Error::BlockSignal)?;
+ 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();
+ }
+}
+
+fn run_vcpu(
+ vcpu: Vcpu,
+ cpu_id: u32,
+ vcpu_affinity: Vec<usize>,
+ start_barrier: Arc<Barrier>,
+ io_bus: devices::Bus,
+ mmio_bus: devices::Bus,
+ exit_evt: EventFd,
+ requires_kvmclock_ctrl: bool,
+ run_mode_arc: Arc<VcpuRunMode>,
+) -> Result<JoinHandle<()>> {
+ thread::Builder::new()
+ .name(format!("crosvm_vcpu{}", cpu_id))
+ .spawn(move || {
+ if vcpu_affinity.len() != 0 {
+ if let Err(e) = set_cpu_affinity(vcpu_affinity) {
+ error!("Failed to set CPU affinity: {}", e);
+ }
+ }
+
+ let mut sig_ok = true;
+ match get_blocked_signals() {
+ Ok(mut v) => {
+ v.retain(|&x| x != SIGRTMIN() + 0);
+ if let Err(e) = vcpu.set_signal_mask(&v) {
+ error!(
+ "Failed to set the KVM_SIGNAL_MASK for vcpu {} : {}",
+ cpu_id, e
+ );
+ sig_ok = false;
+ }
+ }
+ Err(e) => {
+ error!(
+ "Failed to retrieve signal mask for vcpu {} : {}",
+ cpu_id, e
+ );
+ sig_ok = false;
+ }
+ };
+
+ start_barrier.wait();
+
+ if sig_ok {
+ 'vcpu_loop: loop {
+ let mut interrupted_by_signal = false;
+ match vcpu.run() {
+ 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);
+ }
+ }
+ 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::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::Hlt) => break,
+ Ok(VcpuExit::Shutdown) => 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);
+ break;
+ }
+ },
+ }
+
+ if interrupted_by_signal {
+ // Try to clear the signal that we use to kick VCPU if it is pending before
+ // attempting to handle pause requests.
+ if let Err(e) = clear_signal(SIGRTMIN() + 0) {
+ error!("failed to clear pending signal: {}", e);
+ break;
+ }
+ 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_kvmclock_ctrl {
+ if let Err(e) = vcpu.kvmclock_ctrl() {
+ error!("failed to signal to kvm that vcpu {} is being suspended: {}", cpu_id, e);
+ }
+ }
+ }
+ VmRunMode::Exiting => break 'vcpu_loop,
+ }
+ // 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);
+ }
+ }
+ }
+ }
+ exit_evt
+ .write(1)
+ .expect("failed to signal vcpu exit eventfd");
+ })
+ .map_err(Error::SpawnVcpu)
+}
+
+// Reads the contents of a file and converts the space-separated fields into a Vec of u64s.
+// Returns an error if any of the fields fail to parse.
+fn file_fields_to_u64<P: AsRef<Path>>(path: P) -> io::Result<Vec<u64>> {
+ let mut file = File::open(path)?;
+
+ let mut buf = [0u8; 32];
+ let count = file.read(&mut buf)?;
+
+ let content =
+ str::from_utf8(&buf[..count]).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
+ content
+ .trim()
+ .split_whitespace()
+ .map(|x| {
+ x.parse::<u64>()
+ .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
+ })
+ .collect()
+}
+
+// Reads the contents of a file and converts them into a u64, and if there
+// are multiple fields it only returns the first one.
+fn file_to_u64<P: AsRef<Path>>(path: P) -> io::Result<u64> {
+ file_fields_to_u64(path)?
+ .into_iter()
+ .next()
+ .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "empty file"))
+}
+
+pub fn run_config(cfg: Config) -> Result<()> {
+ if cfg.sandbox {
+ // Printing something to the syslog before entering minijail so that libc's syslogger has a
+ // chance to open files necessary for its operation, like `/etc/localtime`. After jailing,
+ // access to those files will not be possible.
+ info!("crosvm entering multiprocess mode");
+ }
+
+ let (usb_control_socket, usb_provider) =
+ HostBackendDeviceProvider::new().map_err(Error::CreateUsbProvider)?;
+ // Masking signals is inherently dangerous, since this can persist across clones/execs. Do this
+ // before any jailed devices have been spawned, so that we can catch any of them that fail very
+ // quickly.
+ let sigchld_fd = SignalFd::new(libc::SIGCHLD).map_err(Error::CreateSignalFd)?;
+
+ let initrd_image = if let Some(initrd_path) = &cfg.initrd_path {
+ Some(File::open(initrd_path).map_err(|e| Error::OpenInitrd(initrd_path.clone(), e))?)
+ } else {
+ None
+ };
+
+ let vm_image = match cfg.executable_path {
+ Some(Executable::Kernel(ref kernel_path)) => VmImage::Kernel(
+ File::open(kernel_path).map_err(|e| Error::OpenKernel(kernel_path.to_path_buf(), e))?,
+ ),
+ Some(Executable::Bios(ref bios_path)) => VmImage::Bios(
+ File::open(bios_path).map_err(|e| Error::OpenBios(bios_path.to_path_buf(), e))?,
+ ),
+ _ => panic!("Did not receive a bios or kernel, should be impossible."),
+ };
+
+ let components = VmComponents {
+ memory_size: (cfg.memory.unwrap_or(256) << 20) as u64,
+ vcpu_count: cfg.vcpu_count.unwrap_or(1),
+ vcpu_affinity: cfg.vcpu_affinity.clone(),
+ vm_image,
+ android_fstab: cfg
+ .android_fstab
+ .as_ref()
+ .map(|x| File::open(x).map_err(|e| Error::OpenAndroidFstab(x.to_path_buf(), e)))
+ .map_or(Ok(None), |v| v.map(Some))?,
+ initrd_image,
+ extra_kernel_params: cfg.params.clone(),
+ wayland_dmabuf: cfg.wayland_dmabuf,
+ };
+
+ let control_server_socket = match &cfg.socket_path {
+ Some(path) => Some(UnlinkUnixSeqpacketListener(
+ UnixSeqpacketListener::bind(path).map_err(Error::CreateSocket)?,
+ )),
+ 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));
+ // Balloon gets a special socket so balloon requests can be forwarded from the main process.
+ let (balloon_host_socket, balloon_device_socket) =
+ msg_socket::pair::<BalloonControlCommand, ()>().map_err(Error::CreateSocket)?;
+
+ // Create one control socket per disk.
+ let mut disk_device_sockets = Vec::new();
+ let mut disk_host_sockets = Vec::new();
+ let disk_count = cfg.disks.len();
+ for _ in 0..disk_count {
+ let (disk_host_socket, disk_device_socket) =
+ msg_socket::pair::<DiskControlCommand, DiskControlResult>()
+ .map_err(Error::CreateSocket)?;
+ disk_host_sockets.push(disk_host_socket);
+ disk_device_sockets.push(disk_device_socket);
+ }
+
+ let (gpu_host_socket, gpu_device_socket) =
+ msg_socket::pair::<VmMemoryResponse, VmMemoryRequest>().map_err(Error::CreateSocket)?;
+ control_sockets.push(TaggedControlSocket::VmMemory(gpu_host_socket));
+
+ let sandbox = cfg.sandbox;
+ let linux = Arch::build_vm(
+ components,
+ cfg.split_irqchip,
+ &cfg.serial_parameters,
+ |mem, vm, sys_allocator, exit_evt| {
+ create_devices(
+ &cfg,
+ mem,
+ vm,
+ sys_allocator,
+ exit_evt,
+ wayland_device_socket,
+ gpu_device_socket,
+ balloon_device_socket,
+ &mut disk_device_sockets,
+ usb_provider,
+ )
+ },
+ )
+ .map_err(Error::BuildVm)?;
+
+ let _render_node_host = ();
+ #[cfg(feature = "gpu-forward")]
+ let (_render_node_host, linux) = {
+ // Rebinds linux as mutable.
+ let mut linux = linux;
+
+ // Reserve memory range for GPU buffer allocation in advance to bypass region count
+ // limitation. We use mremap/MAP_FIXED later to make sure GPU buffers fall into this range.
+ let gpu_mmap =
+ MemoryMapping::new_protection(RENDER_NODE_HOST_SIZE as usize, Protection::none())
+ .map_err(Error::ReserveGpuMemory)?;
+
+ // Put the non-accessible memory map into device memory so that no other devices use that
+ // guest address space.
+ let gpu_addr = linux
+ .resources
+ .device_allocator()
+ .allocate(
+ RENDER_NODE_HOST_SIZE,
+ Alloc::GpuRenderNode,
+ "gpu_render_node".to_string(),
+ )
+ .map_err(|_| Error::AllocateGpuDeviceAddress)?;
+
+ let host = RenderNodeHost::start(&gpu_mmap, gpu_addr, linux.vm.get_memory().clone());
+
+ // Makes the gpu memory accessible at allocated address.
+ linux
+ .vm
+ .add_device_memory(
+ GuestAddress(gpu_addr),
+ gpu_mmap,
+ /* read_only = */ false,
+ /* log_dirty_pages = */ false,
+ )
+ .map_err(Error::AddGpuDeviceMemory)?;
+ (host, linux)
+ };
+
+ run_control(
+ linux,
+ control_server_socket,
+ control_sockets,
+ balloon_host_socket,
+ &disk_host_sockets,
+ usb_control_socket,
+ sigchld_fd,
+ _render_node_host,
+ sandbox,
+ )
+}
+
+fn run_control(
+ mut linux: RunnableLinuxVm,
+ control_server_socket: Option<UnlinkUnixSeqpacketListener>,
+ mut control_sockets: Vec<TaggedControlSocket>,
+ balloon_host_socket: BalloonControlRequestSocket,
+ disk_host_sockets: &[DiskControlRequestSocket],
+ usb_control_socket: UsbControlSocket,
+ sigchld_fd: SignalFd,
+ _render_node_host: RenderNodeHost,
+ sandbox: bool,
+) -> Result<()> {
+ // Paths to get the currently available memory and the low memory threshold.
+ const LOWMEM_MARGIN: &str = "/sys/kernel/mm/chromeos-low_mem/margin";
+ const LOWMEM_AVAILABLE: &str = "/sys/kernel/mm/chromeos-low_mem/available";
+
+ // The amount of additional memory to claim back from the VM whenever the system is
+ // low on memory.
+ const ONE_GB: u64 = (1 << 30);
+
+ let max_balloon_memory = match linux.vm.get_memory().memory_size() {
+ // If the VM has at least 1.5 GB, the balloon driver can consume all but the last 1 GB.
+ n if n >= (ONE_GB / 2) * 3 => n - ONE_GB,
+ // Otherwise, if the VM has at least 500MB the balloon driver will consume at most
+ // half of it.
+ n if n >= (ONE_GB / 2) => n / 2,
+ // Otherwise, the VM is too small for us to take memory away from it.
+ _ => 0,
+ };
+ let mut current_balloon_memory: u64 = 0;
+ let balloon_memory_increment: u64 = max_balloon_memory / 16;
+
+ #[derive(PollToken)]
+ enum Token {
+ Exit,
+ Stdin,
+ ChildSignal,
+ CheckAvailableMemory,
+ LowMemory,
+ LowmemTimer,
+ VmControlServer,
+ VmControl { index: usize },
+ }
+
+ let stdin_handle = stdin();
+ let stdin_lock = stdin_handle.lock();
+ stdin_lock
+ .set_raw_mode()
+ .expect("failed to set terminal raw mode");
+
+ let poll_ctx = PollContext::new().map_err(Error::CreatePollContext)?;
+ poll_ctx
+ .add(&linux.exit_evt, Token::Exit)
+ .map_err(Error::PollContextAdd)?;
+ if let Err(e) = poll_ctx.add(&stdin_handle, Token::Stdin) {
+ warn!("failed to add stdin to poll context: {}", e);
+ }
+ poll_ctx
+ .add(&sigchld_fd, Token::ChildSignal)
+ .map_err(Error::PollContextAdd)?;
+
+ if let Some(socket_server) = &control_server_socket {
+ poll_ctx
+ .add(socket_server, Token::VmControlServer)
+ .map_err(Error::PollContextAdd)?;
+ }
+ for (index, socket) in control_sockets.iter().enumerate() {
+ poll_ctx
+ .add(socket.as_ref(), Token::VmControl { index })
+ .map_err(Error::PollContextAdd)?;
+ }
+
+ // Watch for low memory notifications and take memory back from the VM.
+ let low_mem = File::open("/dev/chromeos-low-mem").ok();
+ if let Some(low_mem) = &low_mem {
+ poll_ctx
+ .add(low_mem, Token::LowMemory)
+ .map_err(Error::PollContextAdd)?;
+ } else {
+ warn!("Unable to open low mem indicator, maybe not a chrome os kernel");
+ }
+
+ // Used to rate limit balloon requests.
+ let mut lowmem_timer = TimerFd::new().map_err(Error::CreateTimerFd)?;
+ poll_ctx
+ .add(&lowmem_timer, Token::LowmemTimer)
+ .map_err(Error::PollContextAdd)?;
+
+ // Used to check whether it's ok to start giving memory back to the VM.
+ let mut freemem_timer = TimerFd::new().map_err(Error::CreateTimerFd)?;
+ poll_ctx
+ .add(&freemem_timer, Token::CheckAvailableMemory)
+ .map_err(Error::PollContextAdd)?;
+
+ // Used to add jitter to timer values so that we don't have a thundering herd problem when
+ // multiple VMs are running.
+ let mut simple_rng = SimpleRng::new(
+ SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .expect("time went backwards")
+ .subsec_nanos() as u64,
+ );
+
+ if sandbox {
+ // Before starting VCPUs, in case we started with some capabilities, drop them all.
+ drop_capabilities().map_err(Error::DropCapabilities)?;
+ }
+
+ let mut vcpu_handles = Vec::with_capacity(linux.vcpus.len());
+ let vcpu_thread_barrier = Arc::new(Barrier::new(linux.vcpus.len() + 1));
+ let run_mode_arc = Arc::new(VcpuRunMode::default());
+ setup_vcpu_signal_handler()?;
+ for (cpu_id, vcpu) in linux.vcpus.into_iter().enumerate() {
+ let handle = run_vcpu(
+ vcpu,
+ cpu_id as u32,
+ linux.vcpu_affinity.clone(),
+ vcpu_thread_barrier.clone(),
+ linux.io_bus.clone(),
+ linux.mmio_bus.clone(),
+ linux.exit_evt.try_clone().map_err(Error::CloneEventFd)?,
+ linux.vm.check_extension(Cap::KvmclockCtrl),
+ run_mode_arc.clone(),
+ )?;
+ vcpu_handles.push(handle);
+ }
+ vcpu_thread_barrier.wait();
+
+ 'poll: loop {
+ let events = {
+ match poll_ctx.wait() {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed to poll: {}", e);
+ break;
+ }
+ }
+ };
+
+ let mut vm_control_indices_to_remove = Vec::new();
+ for event in events.iter_readable() {
+ match event.token() {
+ Token::Exit => {
+ info!("vcpu requested shutdown");
+ break 'poll;
+ }
+ Token::Stdin => {
+ let mut out = [0u8; 64];
+ match stdin_lock.read_raw(&mut out[..]) {
+ Ok(0) => {
+ // Zero-length read indicates EOF. Remove from pollables.
+ let _ = poll_ctx.delete(&stdin_handle);
+ }
+ Err(e) => {
+ warn!("error while reading stdin: {}", e);
+ let _ = poll_ctx.delete(&stdin_handle);
+ }
+ Ok(count) => {
+ if let Some(ref stdio_serial) = linux.stdio_serial {
+ stdio_serial
+ .lock()
+ .queue_input_bytes(&out[..count])
+ .expect("failed to queue bytes into serial port");
+ }
+ }
+ }
+ }
+ Token::ChildSignal => {
+ // Print all available siginfo structs, then exit the loop.
+ while let Some(siginfo) = sigchld_fd.read().map_err(Error::SignalFd)? {
+ let pid = siginfo.ssi_pid;
+ let pid_label = match linux.pid_debug_label_map.get(&pid) {
+ Some(label) => format!("{} (pid {})", label, pid),
+ None => format!("pid {}", pid),
+ };
+ error!(
+ "child {} died: signo {}, status {}, code {}",
+ pid_label, siginfo.ssi_signo, siginfo.ssi_status, siginfo.ssi_code
+ );
+ }
+ break 'poll;
+ }
+ Token::CheckAvailableMemory => {
+ // Acknowledge the timer.
+ freemem_timer.wait().map_err(Error::TimerFd)?;
+ if current_balloon_memory == 0 {
+ // Nothing to see here.
+ if let Err(e) = freemem_timer.clear() {
+ warn!("unable to clear available memory check timer: {}", e);
+ }
+ continue;
+ }
+
+ // Otherwise see if we can free up some memory.
+ let margin = file_to_u64(LOWMEM_MARGIN).map_err(Error::ReadLowmemMargin)?;
+ let available =
+ file_to_u64(LOWMEM_AVAILABLE).map_err(Error::ReadLowmemAvailable)?;
+
+ // `available` and `margin` are specified in MB while `balloon_memory_increment` is in
+ // bytes. So to correctly compare them we need to turn the increment value into MB.
+ if available >= margin + 2 * (balloon_memory_increment >> 20) {
+ current_balloon_memory =
+ if current_balloon_memory >= balloon_memory_increment {
+ current_balloon_memory - balloon_memory_increment
+ } else {
+ 0
+ };
+ let command = BalloonControlCommand::Adjust {
+ num_bytes: current_balloon_memory,
+ };
+ if let Err(e) = balloon_host_socket.send(&command) {
+ warn!("failed to send memory value to balloon device: {}", e);
+ }
+ }
+ }
+ Token::LowMemory => {
+ if let Some(low_mem) = &low_mem {
+ let old_balloon_memory = current_balloon_memory;
+ current_balloon_memory = min(
+ current_balloon_memory + balloon_memory_increment,
+ max_balloon_memory,
+ );
+ if current_balloon_memory != old_balloon_memory {
+ let command = BalloonControlCommand::Adjust {
+ num_bytes: current_balloon_memory,
+ };
+ if let Err(e) = balloon_host_socket.send(&command) {
+ warn!("failed to send memory value to balloon device: {}", e);
+ }
+ }
+
+ // Stop polling the lowmem device until the timer fires.
+ poll_ctx.delete(low_mem).map_err(Error::PollContextDelete)?;
+
+ // Add some jitter to the timer so that if there are multiple VMs running
+ // they don't all start ballooning at exactly the same time.
+ let lowmem_dur = Duration::from_millis(1000 + simple_rng.rng() % 200);
+ lowmem_timer
+ .reset(lowmem_dur, None)
+ .map_err(Error::ResetTimerFd)?;
+
+ // Also start a timer to check when we can start giving memory back. Do the
+ // first check after a minute (with jitter) and subsequent checks after
+ // every 30 seconds (with jitter).
+ let freemem_dur = Duration::from_secs(60 + simple_rng.rng() % 12);
+ let freemem_int = Duration::from_secs(30 + simple_rng.rng() % 6);
+ freemem_timer
+ .reset(freemem_dur, Some(freemem_int))
+ .map_err(Error::ResetTimerFd)?;
+ }
+ }
+ Token::LowmemTimer => {
+ // Acknowledge the timer.
+ lowmem_timer.wait().map_err(Error::TimerFd)?;
+
+ if let Some(low_mem) = &low_mem {
+ // Start polling the lowmem device again.
+ poll_ctx
+ .add(low_mem, Token::LowMemory)
+ .map_err(Error::PollContextAdd)?;
+ }
+ }
+ Token::VmControlServer => {
+ if let Some(socket_server) = &control_server_socket {
+ match socket_server.accept() {
+ Ok(socket) => {
+ poll_ctx
+ .add(
+ &socket,
+ Token::VmControl {
+ index: control_sockets.len(),
+ },
+ )
+ .map_err(Error::PollContextAdd)?;
+ control_sockets
+ .push(TaggedControlSocket::Vm(MsgSocket::new(socket)));
+ }
+ Err(e) => error!("failed to accept socket: {}", e),
+ }
+ }
+ }
+ Token::VmControl { index } => {
+ if let Some(socket) = control_sockets.get(index) {
+ match socket {
+ TaggedControlSocket::Vm(socket) => match socket.recv() {
+ Ok(request) => {
+ let mut run_mode_opt = None;
+ let response = request.execute(
+ &mut run_mode_opt,
+ &balloon_host_socket,
+ disk_host_sockets,
+ &usb_control_socket,
+ );
+ if let Err(e) = socket.send(&response) {
+ error!("failed to send VmResponse: {}", e);
+ }
+ if let Some(run_mode) = run_mode_opt {
+ info!("control socket changed run mode to {}", run_mode);
+ match run_mode {
+ VmRunMode::Exiting => {
+ break 'poll;
+ }
+ other => {
+ run_mode_arc.set_and_notify(other);
+ for handle in &vcpu_handles {
+ let _ = handle.kill(SIGRTMIN() + 0);
+ }
+ }
+ }
+ }
+ }
+ Err(e) => {
+ if let MsgError::BadRecvSize { actual: 0, .. } = e {
+ vm_control_indices_to_remove.push(index);
+ } else {
+ error!("failed to recv VmRequest: {}", e);
+ }
+ }
+ },
+ TaggedControlSocket::VmMemory(socket) => match socket.recv() {
+ Ok(request) => {
+ let response =
+ request.execute(&mut linux.vm, &mut linux.resources);
+ if let Err(e) = socket.send(&response) {
+ error!("failed to send VmMemoryControlResponse: {}", e);
+ }
+ }
+ Err(e) => {
+ if let MsgError::BadRecvSize { actual: 0, .. } = e {
+ vm_control_indices_to_remove.push(index);
+ } else {
+ error!("failed to recv VmMemoryControlRequest: {}", e);
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ }
+
+ for event in events.iter_hungup() {
+ match event.token() {
+ Token::Exit => {}
+ Token::Stdin => {
+ let _ = poll_ctx.delete(&stdin_handle);
+ }
+ Token::ChildSignal => {}
+ Token::CheckAvailableMemory => {}
+ Token::LowMemory => {}
+ Token::LowmemTimer => {}
+ Token::VmControlServer => {}
+ Token::VmControl { index } => {
+ // It's possible more data is readable and buffered while the socket is hungup,
+ // so don't delete the socket from the poll context until we're sure all the
+ // data is read.
+ match control_sockets
+ .get(index)
+ .map(|s| s.as_ref().get_readable_bytes())
+ {
+ Some(Ok(0)) | Some(Err(_)) => vm_control_indices_to_remove.push(index),
+ Some(Ok(x)) => info!("control index {} has {} bytes readable", index, x),
+ _ => {}
+ }
+ }
+ }
+ }
+
+ // Sort in reverse so the highest indexes are removed first. This removal algorithm
+ // preserved correct indexes as each element is removed.
+ vm_control_indices_to_remove.sort_unstable_by(|a, b| b.cmp(a));
+ vm_control_indices_to_remove.dedup();
+ for index in vm_control_indices_to_remove {
+ 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)?;
+ }
+ }
+ }
+
+ // 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),
+ }
+ }
+
+ stdin_lock
+ .set_canon_mode()
+ .expect("failed to restore canonical mode for terminal");
+
+ Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..64001a4
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,1449 @@
+// Copyright 2017 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.
+
+//! Runs a virtual machine under KVM
+
+pub mod argument;
+pub mod linux;
+pub mod panic_hook;
+#[cfg(feature = "plugin")]
+pub mod plugin;
+
+use std::collections::BTreeMap;
+use std::fmt;
+use std::fs::{File, OpenOptions};
+use std::net;
+use std::num::ParseIntError;
+use std::os::unix::io::{FromRawFd, RawFd};
+use std::path::{Path, PathBuf};
+use std::string::String;
+use std::thread::sleep;
+use std::time::Duration;
+
+use devices::{SerialParameters, SerialType};
+use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
+use qcow::QcowFile;
+use sys_util::{
+ debug, error, getpid, info, kill_process_group, net::UnixSeqpacket, reap_child, syslog,
+ validate_raw_fd, warn,
+};
+use vm_control::{
+ BalloonControlCommand, DiskControlCommand, MaybeOwnedFd, UsbControlCommand, UsbControlResult,
+ VmControlRequestSocket, VmRequest, VmResponse, USB_CONTROL_MAX_PORTS,
+};
+
+use crate::argument::{print_help, set_arguments, Argument};
+
+static SECCOMP_POLICY_DIR: &'static str = "/usr/share/policy/crosvm";
+
+struct DiskOption {
+ path: PathBuf,
+ read_only: bool,
+}
+
+#[allow(dead_code)]
+struct BindMount {
+ src: PathBuf,
+ dst: PathBuf,
+ writable: bool,
+}
+
+#[allow(dead_code)]
+struct GidMap {
+ inner: libc::gid_t,
+ outer: libc::gid_t,
+ count: u32,
+}
+
+const DEFAULT_TOUCH_DEVICE_WIDTH: u32 = 800;
+const DEFAULT_TOUCH_DEVICE_HEIGHT: u32 = 1280;
+
+struct TouchDeviceOption {
+ path: PathBuf,
+ width: u32,
+ height: u32,
+}
+
+impl TouchDeviceOption {
+ fn new(path: PathBuf) -> TouchDeviceOption {
+ TouchDeviceOption {
+ path,
+ width: DEFAULT_TOUCH_DEVICE_WIDTH,
+ height: DEFAULT_TOUCH_DEVICE_HEIGHT,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum Executable {
+ Bios(PathBuf),
+ Kernel(PathBuf),
+ Plugin(PathBuf),
+}
+
+fn executable_is_plugin(executable: &Option<Executable>) -> bool {
+ match executable {
+ Some(Executable::Plugin(_)) => true,
+ _ => false,
+ }
+}
+
+pub struct Config {
+ vcpu_count: Option<u32>,
+ vcpu_affinity: Vec<usize>,
+ memory: Option<usize>,
+ executable_path: Option<Executable>,
+ android_fstab: Option<PathBuf>,
+ initrd_path: Option<PathBuf>,
+ params: Vec<String>,
+ socket_path: Option<PathBuf>,
+ plugin_root: Option<PathBuf>,
+ plugin_mounts: Vec<BindMount>,
+ plugin_gid_maps: Vec<GidMap>,
+ disks: Vec<DiskOption>,
+ pmem_devices: Vec<DiskOption>,
+ host_ip: Option<net::Ipv4Addr>,
+ netmask: Option<net::Ipv4Addr>,
+ mac_address: Option<net_util::MacAddress>,
+ vhost_net: bool,
+ tap_fd: Vec<RawFd>,
+ cid: Option<u64>,
+ wayland_socket_path: Option<PathBuf>,
+ wayland_dmabuf: bool,
+ shared_dirs: Vec<(PathBuf, String)>,
+ sandbox: bool,
+ seccomp_policy_dir: PathBuf,
+ gpu: bool,
+ software_tpm: bool,
+ cras_audio: bool,
+ cras_capture: bool,
+ null_audio: bool,
+ serial_parameters: BTreeMap<u8, SerialParameters>,
+ syslog_tag: Option<String>,
+ virtio_single_touch: Option<TouchDeviceOption>,
+ virtio_trackpad: Option<TouchDeviceOption>,
+ virtio_mouse: Option<PathBuf>,
+ virtio_keyboard: Option<PathBuf>,
+ virtio_input_evdevs: Vec<PathBuf>,
+ split_irqchip: bool,
+}
+
+impl Default for Config {
+ fn default() -> Config {
+ Config {
+ vcpu_count: None,
+ vcpu_affinity: Vec::new(),
+ memory: None,
+ executable_path: None,
+ android_fstab: None,
+ initrd_path: None,
+ params: Vec::new(),
+ socket_path: None,
+ plugin_root: None,
+ plugin_mounts: Vec::new(),
+ plugin_gid_maps: Vec::new(),
+ disks: Vec::new(),
+ pmem_devices: Vec::new(),
+ host_ip: None,
+ netmask: None,
+ mac_address: None,
+ vhost_net: false,
+ tap_fd: Vec::new(),
+ cid: None,
+ gpu: false,
+ software_tpm: false,
+ wayland_socket_path: None,
+ wayland_dmabuf: false,
+ shared_dirs: Vec::new(),
+ sandbox: !cfg!(feature = "default-no-sandbox"),
+ seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
+ cras_audio: false,
+ cras_capture: false,
+ null_audio: false,
+ serial_parameters: BTreeMap::new(),
+ syslog_tag: None,
+ virtio_single_touch: None,
+ virtio_trackpad: None,
+ virtio_mouse: None,
+ virtio_keyboard: None,
+ virtio_input_evdevs: Vec::new(),
+ split_irqchip: false,
+ }
+ }
+}
+
+// Wait for all children to exit. Return true if they have all exited, false
+// otherwise.
+fn wait_all_children() -> bool {
+ const CHILD_WAIT_MAX_ITER: isize = 100;
+ const CHILD_WAIT_MS: u64 = 10;
+ for _ in 0..CHILD_WAIT_MAX_ITER {
+ loop {
+ match reap_child() {
+ Ok(0) => break,
+ // We expect ECHILD which indicates that there were no children left.
+ Err(e) if e.errno() == libc::ECHILD => return true,
+ Err(e) => {
+ warn!("error while waiting for children: {}", e);
+ return false;
+ }
+ // We reaped one child, so continue reaping.
+ _ => {}
+ }
+ }
+ // There's no timeout option for waitpid which reap_child calls internally, so our only
+ // recourse is to sleep while waiting for the children to exit.
+ sleep(Duration::from_millis(CHILD_WAIT_MS));
+ }
+
+ // If we've made it to this point, not all of the children have exited.
+ false
+}
+
+/// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
+fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
+ let mut cpuset = Vec::new();
+ for part in s.split(',') {
+ let range: Vec<&str> = part.split('-').collect();
+ if range.len() == 0 || range.len() > 2 {
+ return Err(argument::Error::InvalidValue {
+ value: part.to_owned(),
+ expected: "invalid list syntax",
+ });
+ }
+ let first_cpu: usize = range[0]
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: part.to_owned(),
+ expected: "CPU index must be a non-negative integer",
+ })?;
+ let last_cpu: usize = if range.len() == 2 {
+ range[1]
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: part.to_owned(),
+ expected: "CPU index must be a non-negative integer",
+ })?
+ } else {
+ first_cpu
+ };
+
+ if last_cpu < first_cpu {
+ return Err(argument::Error::InvalidValue {
+ value: part.to_owned(),
+ expected: "CPU ranges must be from low to high",
+ });
+ }
+
+ for cpu in first_cpu..=last_cpu {
+ cpuset.push(cpu);
+ }
+ }
+ Ok(cpuset)
+}
+
+fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
+ let mut serial_setting = SerialParameters {
+ type_: SerialType::Sink,
+ path: None,
+ num: 1,
+ console: false,
+ };
+
+ 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" => {
+ serial_setting.type_ = v
+ .parse::<SerialType>()
+ .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
+ }
+ "num" => {
+ let num = v.parse::<u8>().map_err(|e| {
+ argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
+ })?;
+ if num < 1 || num > 4 {
+ return Err(argument::Error::InvalidValue {
+ value: num.to_string(),
+ expected: "Serial port num must be between 1 - 4",
+ });
+ }
+ serial_setting.num = num;
+ }
+ "console" => {
+ serial_setting.console = v.parse::<bool>().map_err(|e| {
+ argument::Error::Syntax(format!(
+ "serial device console is not parseable: {}",
+ e
+ ))
+ })?
+ }
+ "path" => serial_setting.path = Some(PathBuf::from(v)),
+ _ => {
+ return Err(argument::Error::UnknownArgument(format!(
+ "serial parameter {}",
+ k
+ )));
+ }
+ }
+ }
+
+ Ok(serial_setting)
+}
+
+fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
+ match name {
+ "" => {
+ if cfg.executable_path.is_some() {
+ return Err(argument::Error::TooManyArguments(format!(
+ "A VM executable was already specified: {:?}",
+ cfg.executable_path
+ )));
+ }
+ let kernel_path = PathBuf::from(value.unwrap());
+ if !kernel_path.exists() {
+ return Err(argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this kernel path does not exist",
+ });
+ }
+ cfg.executable_path = Some(Executable::Kernel(kernel_path));
+ }
+ "android-fstab" => {
+ if cfg.android_fstab.is_some()
+ && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
+ {
+ return Err(argument::Error::TooManyArguments(
+ "expected exactly one android fstab path".to_owned(),
+ ));
+ } else {
+ let android_fstab = PathBuf::from(value.unwrap());
+ if !android_fstab.exists() {
+ return Err(argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this android fstab path does not exist",
+ });
+ }
+ cfg.android_fstab = Some(android_fstab);
+ }
+ }
+ "params" => {
+ cfg.params.push(value.unwrap().to_owned());
+ }
+ "cpus" => {
+ if cfg.vcpu_count.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`cpus` already given".to_owned(),
+ ));
+ }
+ cfg.vcpu_count =
+ Some(
+ value
+ .unwrap()
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this value for `cpus` needs to be integer",
+ })?,
+ )
+ }
+ "cpu-affinity" => {
+ if cfg.vcpu_affinity.len() != 0 {
+ return Err(argument::Error::TooManyArguments(
+ "`cpu-affinity` already given".to_owned(),
+ ));
+ }
+ cfg.vcpu_affinity = parse_cpu_set(value.unwrap())?;
+ }
+ "mem" => {
+ if cfg.memory.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`mem` already given".to_owned(),
+ ));
+ }
+ cfg.memory =
+ Some(
+ value
+ .unwrap()
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this value for `mem` needs to be integer",
+ })?,
+ )
+ }
+ "cras-audio" => {
+ cfg.cras_audio = true;
+ }
+ "cras-capture" => {
+ cfg.cras_capture = true;
+ }
+ "null-audio" => {
+ cfg.null_audio = true;
+ }
+ "serial" => {
+ let serial_params = parse_serial_options(value.unwrap())?;
+ let num = serial_params.num;
+ if cfg.serial_parameters.contains_key(&num) {
+ return Err(argument::Error::TooManyArguments(format!(
+ "serial num {}",
+ num
+ )));
+ }
+
+ if serial_params.console {
+ for params in cfg.serial_parameters.values() {
+ if params.console {
+ return Err(argument::Error::TooManyArguments(format!(
+ "serial device {} already set as console",
+ params.num
+ )));
+ }
+ }
+ }
+
+ cfg.serial_parameters.insert(num, serial_params);
+ }
+ "syslog-tag" => {
+ if cfg.syslog_tag.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`syslog-tag` already given".to_owned(),
+ ));
+ }
+ syslog::set_proc_name(value.unwrap());
+ cfg.syslog_tag = Some(value.unwrap().to_owned());
+ }
+ "root" | "disk" | "rwdisk" | "qcow" | "rwqcow" => {
+ let disk_path = PathBuf::from(value.unwrap());
+ if !disk_path.exists() {
+ return Err(argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this disk path does not exist",
+ });
+ }
+ if name == "root" {
+ if cfg.disks.len() >= 26 {
+ return Err(argument::Error::TooManyArguments(
+ "ran out of letters for to assign to root disk".to_owned(),
+ ));
+ }
+ cfg.params.push(format!(
+ "root=/dev/vd{} ro",
+ char::from(b'a' + cfg.disks.len() as u8)
+ ));
+ }
+ cfg.disks.push(DiskOption {
+ path: disk_path,
+ read_only: !name.starts_with("rw"),
+ });
+ }
+ "pmem-device" | "rw-pmem-device" => {
+ let disk_path = PathBuf::from(value.unwrap());
+ if !disk_path.exists() {
+ return Err(argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this disk path does not exist",
+ });
+ }
+
+ cfg.pmem_devices.push(DiskOption {
+ path: disk_path,
+ read_only: !name.starts_with("rw"),
+ });
+ }
+ "host_ip" => {
+ if cfg.host_ip.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`host_ip` already given".to_owned(),
+ ));
+ }
+ cfg.host_ip =
+ Some(
+ value
+ .unwrap()
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "`host_ip` needs to be in the form \"x.x.x.x\"",
+ })?,
+ )
+ }
+ "netmask" => {
+ if cfg.netmask.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`netmask` already given".to_owned(),
+ ));
+ }
+ cfg.netmask =
+ Some(
+ value
+ .unwrap()
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "`netmask` needs to be in the form \"x.x.x.x\"",
+ })?,
+ )
+ }
+ "mac" => {
+ if cfg.mac_address.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`mac` already given".to_owned(),
+ ));
+ }
+ cfg.mac_address =
+ Some(
+ value
+ .unwrap()
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
+ })?,
+ )
+ }
+ "wayland-sock" => {
+ if cfg.wayland_socket_path.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`wayland-sock` already given".to_owned(),
+ ));
+ }
+ let wayland_socket_path = PathBuf::from(value.unwrap());
+ if !wayland_socket_path.exists() {
+ return Err(argument::Error::InvalidValue {
+ value: value.unwrap().to_string(),
+ expected: "Wayland socket does not exist",
+ });
+ }
+ cfg.wayland_socket_path = Some(wayland_socket_path);
+ }
+ #[cfg(feature = "wl-dmabuf")]
+ "wayland-dmabuf" => cfg.wayland_dmabuf = true,
+ "socket" => {
+ if cfg.socket_path.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`socket` already given".to_owned(),
+ ));
+ }
+ let mut socket_path = PathBuf::from(value.unwrap());
+ if socket_path.is_dir() {
+ socket_path.push(format!("crosvm-{}.sock", getpid()));
+ }
+ if socket_path.exists() {
+ return Err(argument::Error::InvalidValue {
+ value: socket_path.to_string_lossy().into_owned(),
+ expected: "this socket path already exists",
+ });
+ }
+ cfg.socket_path = Some(socket_path);
+ }
+ "disable-sandbox" => {
+ cfg.sandbox = false;
+ }
+ "cid" => {
+ if cfg.cid.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`cid` alread given".to_owned(),
+ ));
+ }
+ cfg.cid = Some(
+ value
+ .unwrap()
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this value for `cid` must be an unsigned integer",
+ })?,
+ );
+ }
+ "shared-dir" => {
+ // Formatted as <src:tag>.
+ let param = value.unwrap();
+ let mut components = param.splitn(2, ':');
+ let src =
+ PathBuf::from(
+ components
+ .next()
+ .ok_or_else(|| argument::Error::InvalidValue {
+ value: param.to_owned(),
+ expected: "missing source path for `shared-dir`",
+ })?,
+ );
+ let tag = components
+ .next()
+ .ok_or_else(|| argument::Error::InvalidValue {
+ value: param.to_owned(),
+ expected: "missing tag for `shared-dir`",
+ })?
+ .to_owned();
+
+ if !src.is_dir() {
+ return Err(argument::Error::InvalidValue {
+ value: param.to_owned(),
+ expected: "source path for `shared-dir` must be a directory",
+ });
+ }
+
+ cfg.shared_dirs.push((src, tag));
+ }
+ "seccomp-policy-dir" => {
+ // `value` is Some because we are in this match so it's safe to unwrap.
+ cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
+ }
+ "plugin" => {
+ if cfg.executable_path.is_some() {
+ return Err(argument::Error::TooManyArguments(format!(
+ "A VM executable was already specified: {:?}",
+ cfg.executable_path
+ )));
+ }
+ let plugin = PathBuf::from(value.unwrap().to_owned());
+ if plugin.is_relative() {
+ return Err(argument::Error::InvalidValue {
+ value: plugin.to_string_lossy().into_owned(),
+ expected: "the plugin path must be an absolute path",
+ });
+ }
+ cfg.executable_path = Some(Executable::Plugin(plugin));
+ }
+ "plugin-root" => {
+ cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
+ }
+ "plugin-mount" => {
+ let components: Vec<&str> = value.unwrap().split(":").collect();
+ if components.len() != 3 {
+ return Err(argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected:
+ "`plugin-mount` must have exactly 3 components: <src>:<dst>:<writable>",
+ });
+ }
+
+ let src = PathBuf::from(components[0]);
+ if src.is_relative() {
+ return Err(argument::Error::InvalidValue {
+ value: components[0].to_owned(),
+ expected: "the source path for `plugin-mount` must be absolute",
+ });
+ }
+ if !src.exists() {
+ return Err(argument::Error::InvalidValue {
+ value: components[0].to_owned(),
+ expected: "the source path for `plugin-mount` does not exist",
+ });
+ }
+
+ let dst = PathBuf::from(components[1]);
+ if dst.is_relative() {
+ return Err(argument::Error::InvalidValue {
+ value: components[1].to_owned(),
+ expected: "the destination path for `plugin-mount` must be absolute",
+ });
+ }
+
+ let writable: bool =
+ components[2]
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: components[2].to_owned(),
+ expected: "the <writable> component for `plugin-mount` is not valid bool",
+ })?;
+
+ cfg.plugin_mounts.push(BindMount { src, dst, writable });
+ }
+ "plugin-gid-map" => {
+ let components: Vec<&str> = value.unwrap().split(":").collect();
+ if components.len() != 3 {
+ return Err(argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected:
+ "`plugin-gid-map` must have exactly 3 components: <inner>:<outer>:<count>",
+ });
+ }
+
+ let inner: libc::gid_t =
+ components[0]
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: components[0].to_owned(),
+ expected: "the <inner> component for `plugin-gid-map` is not valid gid",
+ })?;
+
+ let outer: libc::gid_t =
+ components[1]
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: components[1].to_owned(),
+ expected: "the <outer> component for `plugin-gid-map` is not valid gid",
+ })?;
+
+ let count: u32 = components[2]
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: components[2].to_owned(),
+ expected: "the <count> component for `plugin-gid-map` is not valid number",
+ })?;
+
+ cfg.plugin_gid_maps.push(GidMap {
+ inner,
+ outer,
+ count,
+ });
+ }
+ "vhost-net" => cfg.vhost_net = true,
+ "tap-fd" => {
+ cfg.tap_fd.push(
+ value
+ .unwrap()
+ .parse()
+ .map_err(|_| argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this value for `tap-fd` must be an unsigned integer",
+ })?,
+ );
+ }
+ "gpu" => {
+ cfg.gpu = true;
+ }
+ "software-tpm" => {
+ cfg.software_tpm = true;
+ }
+ "single-touch" => {
+ if cfg.virtio_single_touch.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`single-touch` already given".to_owned(),
+ ));
+ }
+ let mut it = value.unwrap().split(":");
+
+ let mut single_touch_spec =
+ TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
+ if let Some(width) = it.next() {
+ single_touch_spec.width = width.trim().parse().unwrap();
+ }
+ if let Some(height) = it.next() {
+ single_touch_spec.height = height.trim().parse().unwrap();
+ }
+
+ cfg.virtio_single_touch = Some(single_touch_spec);
+ }
+ "trackpad" => {
+ if cfg.virtio_trackpad.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`trackpad` already given".to_owned(),
+ ));
+ }
+ let mut it = value.unwrap().split(":");
+
+ let mut trackpad_spec =
+ TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
+ if let Some(width) = it.next() {
+ trackpad_spec.width = width.trim().parse().unwrap();
+ }
+ if let Some(height) = it.next() {
+ trackpad_spec.height = height.trim().parse().unwrap();
+ }
+
+ cfg.virtio_trackpad = Some(trackpad_spec);
+ }
+ "mouse" => {
+ if cfg.virtio_mouse.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`mouse` already given".to_owned(),
+ ));
+ }
+ cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
+ }
+ "keyboard" => {
+ if cfg.virtio_keyboard.is_some() {
+ return Err(argument::Error::TooManyArguments(
+ "`keyboard` already given".to_owned(),
+ ));
+ }
+ cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
+ }
+ "evdev" => {
+ let dev_path = PathBuf::from(value.unwrap());
+ if !dev_path.exists() {
+ return Err(argument::Error::InvalidValue {
+ value: value.unwrap().to_owned(),
+ expected: "this input device path does not exist",
+ });
+ }
+ cfg.virtio_input_evdevs.push(dev_path);
+ }
+ "split-irqchip" => {
+ cfg.split_irqchip = true;
+ }
+ "initrd" => {
+ cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
+ }
+ "bios" => {
+ if cfg.executable_path.is_some() {
+ return Err(argument::Error::TooManyArguments(format!(
+ "A VM executable was already specified: {:?}",
+ cfg.executable_path
+ )));
+ }
+ cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
+ }
+ "help" => return Err(argument::Error::PrintHelp),
+ _ => unreachable!(),
+ }
+ Ok(())
+}
+
+fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
+ let arguments =
+ &[Argument::positional("KERNEL", "bzImage of kernel to run"),
+ Argument::value("android-fstab", "PATH", "Path to Android fstab"),
+ Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
+ Argument::short_value('p',
+ "params",
+ "PARAMS",
+ "Extra kernel or plugin command line arguments. Can be given more than once."),
+ Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
+ Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on. (e.g. 0,1-3,5) (default: no mask)"),
+ Argument::short_value('m',
+ "mem",
+ "N",
+ "Amount of guest memory in MiB. (default: 256)"),
+ Argument::short_value('r',
+ "root",
+ "PATH",
+ "Path to a root disk image. Like `--disk` but adds appropriate kernel command line option."),
+ Argument::short_value('d', "disk", "PATH", "Path to a disk image."),
+ Argument::value("qcow", "PATH", "Path to a qcow2 disk image. (Deprecated; use --disk instead.)"),
+ Argument::value("rwdisk", "PATH", "Path to a writable disk image."),
+ Argument::value("rwqcow", "PATH", "Path to a writable qcow2 disk image. (Deprecated; use --rwdisk instead.)"),
+ Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
+ Argument::value("pmem-device", "PATH", "Path to a disk image."),
+ Argument::value("host_ip",
+ "IP",
+ "IP address to assign to host tap interface."),
+ Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
+ Argument::value("mac", "MAC", "MAC address for VM."),
+ Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"),
+ Argument::flag("cras-capture", "Enable capturing audio from CRAS server to the cras-audio device"),
+ Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"),
+ Argument::value("serial",
+ "type=TYPE,[num=NUM,path=PATH,console]",
+ "Comma seperated key=value pairs for setting up serial devices. Can be given more than once.
+ Possible key values:
+ type=(stdout,syslog,sink,file) - Where to route the serial device
+ num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
+ path=PATH - The path to the file to write to when type=file
+ console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
+ "),
+ Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
+ Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
+ #[cfg(feature = "wl-dmabuf")]
+ Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
+ Argument::short_value('s',
+ "socket",
+ "PATH",
+ "Path to put the control socket. If PATH is a directory, a name will be generated."),
+ Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
+ Argument::value("cid", "CID", "Context ID for virtual sockets."),
+ Argument::value("shared-dir", "PATH:TAG",
+ "Directory to be shared with a VM as a source:tag pair. Can be given more than once."),
+ Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
+ #[cfg(feature = "plugin")]
+ Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
+ #[cfg(feature = "plugin")]
+ Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."),
+ #[cfg(feature = "plugin")]
+ Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."),
+ #[cfg(feature = "plugin")]
+ Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
+ Argument::flag("vhost-net", "Use vhost for networking."),
+ Argument::value("tap-fd",
+ "fd",
+ "File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
+ #[cfg(feature = "gpu")]
+ Argument::flag("gpu", "(EXPERIMENTAL) enable virtio-gpu device"),
+ #[cfg(feature = "tpm")]
+ Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
+ Argument::value("evdev", "PATH", "Path to an event device node. The device will be grabbed (unusable from the host) and made available to the guest with the same configuration it shows on the host"),
+ Argument::value("single-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read single touch input events (such as those from a touchscreen) and write status updates to, optionally followed by width and height (defaults to 800x1280)."),
+ Argument::value("trackpad", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read trackpad input events and write status updates to, optionally followed by screen width and height (defaults to 800x1280)."),
+ Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
+ Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
+ Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
+ Argument::short_flag('h', "help", "Print help message.")];
+
+ let mut cfg = Config::default();
+ let match_res = set_arguments(args, &arguments[..], |name, value| {
+ set_argument(&mut cfg, name, value)
+ })
+ .and_then(|_| {
+ if cfg.executable_path.is_none() {
+ return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
+ }
+ if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
+ if cfg.host_ip.is_none() {
+ return Err(argument::Error::ExpectedArgument(
+ "`host_ip` missing from network config".to_owned(),
+ ));
+ }
+ if cfg.netmask.is_none() {
+ return Err(argument::Error::ExpectedArgument(
+ "`netmask` missing from network config".to_owned(),
+ ));
+ }
+ if cfg.mac_address.is_none() {
+ return Err(argument::Error::ExpectedArgument(
+ "`mac` missing from network config".to_owned(),
+ ));
+ }
+ }
+ if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
+ return Err(argument::Error::ExpectedArgument(
+ "`plugin-root` requires `plugin`".to_owned(),
+ ));
+ }
+ Ok(())
+ });
+
+ match match_res {
+ #[cfg(feature = "plugin")]
+ Ok(()) if executable_is_plugin(&cfg.executable_path) => match plugin::run_config(cfg) {
+ Ok(_) => {
+ info!("crosvm and plugin have exited normally");
+ Ok(())
+ }
+ Err(e) => {
+ error!("{}", e);
+ Err(())
+ }
+ },
+ Ok(()) => match linux::run_config(cfg) {
+ Ok(_) => {
+ info!("crosvm has exited normally");
+ Ok(())
+ }
+ Err(e) => {
+ error!("{}", e);
+ Err(())
+ }
+ },
+ Err(argument::Error::PrintHelp) => {
+ print_help("crosvm run", "KERNEL", &arguments[..]);
+ Ok(())
+ }
+ Err(e) => {
+ println!("{}", e);
+ Err(())
+ }
+ }
+}
+
+fn handle_request(
+ request: &VmRequest,
+ args: std::env::Args,
+) -> std::result::Result<VmResponse, ()> {
+ let mut return_result = Err(());
+ for socket_path in args {
+ match UnixSeqpacket::connect(&socket_path) {
+ Ok(s) => {
+ let socket: VmControlRequestSocket = MsgSocket::new(s);
+ if let Err(e) = socket.send(request) {
+ error!(
+ "failed to send request to socket at '{}': {}",
+ socket_path, e
+ );
+ return_result = Err(());
+ continue;
+ }
+ match socket.recv() {
+ Ok(response) => return_result = Ok(response),
+ Err(e) => {
+ error!(
+ "failed to send request to socket at2 '{}': {}",
+ socket_path, e
+ );
+ return_result = Err(());
+ continue;
+ }
+ }
+ }
+ Err(e) => {
+ error!("failed to connect to socket at '{}': {}", socket_path, e);
+ return_result = Err(());
+ }
+ }
+ }
+
+ return_result
+}
+
+fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> {
+ let response = handle_request(request, args)?;
+ info!("request response was {}", response);
+ Ok(())
+}
+
+fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
+ if args.len() == 0 {
+ print_help("crosvm stop", "VM_SOCKET...", &[]);
+ println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
+ return Err(());
+ }
+ vms_request(&VmRequest::Exit, args)
+}
+
+fn suspend_vms(args: std::env::Args) -> std::result::Result<(), ()> {
+ if args.len() == 0 {
+ print_help("crosvm suspend", "VM_SOCKET...", &[]);
+ println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
+ return Err(());
+ }
+ vms_request(&VmRequest::Suspend, args)
+}
+
+fn resume_vms(args: std::env::Args) -> std::result::Result<(), ()> {
+ if args.len() == 0 {
+ print_help("crosvm resume", "VM_SOCKET...", &[]);
+ println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
+ return Err(());
+ }
+ vms_request(&VmRequest::Resume, args)
+}
+
+fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
+ if args.len() < 2 {
+ print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
+ println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
+ return Err(());
+ }
+ let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
+ Ok(n) => n,
+ Err(_) => {
+ error!("Failed to parse number of bytes");
+ return Err(());
+ }
+ };
+
+ let command = BalloonControlCommand::Adjust { num_bytes };
+ vms_request(&VmRequest::BalloonCommand(command), args)
+}
+
+fn create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()> {
+ if args.len() != 2 {
+ print_help("crosvm create_qcow2", "PATH SIZE", &[]);
+ println!("Create a new QCOW2 image at `PATH` of the specified `SIZE` in bytes.");
+ return Err(());
+ }
+ let file_path = args.nth(0).unwrap();
+ let size: u64 = match args.nth(0).unwrap().parse::<u64>() {
+ Ok(n) => n,
+ Err(_) => {
+ error!("Failed to parse size of the disk.");
+ return Err(());
+ }
+ };
+
+ let file = OpenOptions::new()
+ .create(true)
+ .read(true)
+ .write(true)
+ .open(&file_path)
+ .map_err(|e| {
+ error!("Failed opening qcow file at '{}': {}", file_path, e);
+ })?;
+
+ QcowFile::new(file, size).map_err(|e| {
+ error!("Failed to create qcow file at '{}': {}", file_path, e);
+ })?;
+
+ Ok(())
+}
+
+fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
+ if args.len() < 2 {
+ print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
+ println!("Manage attached virtual disk devices.");
+ println!("Subcommands:");
+ println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
+ return Err(());
+ }
+ let subcommand: &str = &args.nth(0).unwrap();
+
+ let request = match subcommand {
+ "resize" => {
+ let disk_index = match args.nth(0).unwrap().parse::<usize>() {
+ Ok(n) => n,
+ Err(_) => {
+ error!("Failed to parse disk index");
+ return Err(());
+ }
+ };
+
+ let new_size = match args.nth(0).unwrap().parse::<u64>() {
+ Ok(n) => n,
+ Err(_) => {
+ error!("Failed to parse disk size");
+ return Err(());
+ }
+ };
+
+ VmRequest::DiskCommand {
+ disk_index,
+ command: DiskControlCommand::Resize { new_size },
+ }
+ }
+ _ => {
+ error!("Unknown disk subcommand '{}'", subcommand);
+ return Err(());
+ }
+ };
+
+ vms_request(&request, args)
+}
+
+enum ModifyUsbError {
+ ArgMissing(&'static str),
+ ArgParse(&'static str, String),
+ ArgParseInt(&'static str, String, ParseIntError),
+ FailedFdValidate(sys_util::Error),
+ PathDoesNotExist(PathBuf),
+ SocketFailed,
+ UnexpectedResponse(VmResponse),
+ UnknownCommand(String),
+ UsbControl(UsbControlResult),
+}
+
+impl fmt::Display for ModifyUsbError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::ModifyUsbError::*;
+
+ match self {
+ ArgMissing(a) => write!(f, "argument missing: {}", a),
+ ArgParse(name, value) => {
+ write!(f, "failed to parse argument {} value `{}`", name, value)
+ }
+ ArgParseInt(name, value, e) => write!(
+ f,
+ "failed to parse integer argument {} value `{}`: {}",
+ name, value, e
+ ),
+ FailedFdValidate(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),
+ UnknownCommand(c) => write!(f, "unknown command: `{}`", c),
+ UsbControl(e) => write!(f, "{}", e),
+ }
+ }
+}
+
+type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
+
+fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
+ debug!("parse_bus_id_addr: {}", v);
+ let mut ids = v.split(":");
+ match (ids.next(), ids.next(), ids.next(), ids.next()) {
+ (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
+ let bus_id = bus_id
+ .parse::<u8>()
+ .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
+ let addr = addr
+ .parse::<u8>()
+ .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
+ let vid = u16::from_str_radix(&vid, 16)
+ .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
+ let pid = u16::from_str_radix(&pid, 16)
+ .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
+ Ok((bus_id, addr, vid, pid))
+ }
+ _ => Err(ModifyUsbError::ArgParse(
+ "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
+ v.to_owned(),
+ )),
+ }
+}
+
+fn raw_fd_from_path(path: &Path) -> ModifyUsbResult<RawFd> {
+ if !path.exists() {
+ return Err(ModifyUsbError::PathDoesNotExist(path.to_owned()));
+ }
+ let raw_fd = path
+ .file_name()
+ .and_then(|fd_osstr| fd_osstr.to_str())
+ .map_or(
+ Err(ModifyUsbError::ArgParse(
+ "USB_DEVICE_PATH",
+ path.to_string_lossy().into_owned(),
+ )),
+ |fd_str| {
+ fd_str.parse::<libc::c_int>().map_err(|e| {
+ ModifyUsbError::ArgParseInt("USB_DEVICE_PATH", fd_str.to_owned(), e)
+ })
+ },
+ )?;
+ validate_raw_fd(raw_fd).map_err(ModifyUsbError::FailedFdValidate)
+}
+
+fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
+ let val = args
+ .next()
+ .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
+ let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
+ let dev_path = PathBuf::from(
+ args.next()
+ .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
+ );
+ let usb_file: Option<File> = if dev_path == Path::new("-") {
+ None
+ } 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)?) })
+ } else {
+ Some(
+ OpenOptions::new()
+ .read(true)
+ .write(true)
+ .open(&dev_path)
+ .map_err(|_| ModifyUsbError::UsbControl(UsbControlResult::FailedToOpenDevice))?,
+ )
+ };
+
+ let request = VmRequest::UsbCommand(UsbControlCommand::AttachDevice {
+ bus,
+ addr,
+ vid,
+ pid,
+ fd: usb_file.map(MaybeOwnedFd::Owned),
+ });
+ let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
+ match response {
+ VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
+ r => Err(ModifyUsbError::UnexpectedResponse(r)),
+ }
+}
+
+fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
+ let port: u8 = args
+ .next()
+ .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
+ p.parse::<u8>()
+ .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
+ })?;
+ let request = VmRequest::UsbCommand(UsbControlCommand::DetachDevice { port });
+ let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
+ match response {
+ VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
+ r => Err(ModifyUsbError::UnexpectedResponse(r)),
+ }
+}
+
+fn usb_list(args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
+ let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default();
+ for (index, port) in ports.iter_mut().enumerate() {
+ *port = index as u8
+ }
+ let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports });
+ let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
+ match response {
+ VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
+ r => Err(ModifyUsbError::UnexpectedResponse(r)),
+ }
+}
+
+fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
+ if args.len() < 2 {
+ print_help("crosvm usb",
+ "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
+ return Err(());
+ }
+
+ // This unwrap will not panic because of the above length check.
+ let command = args.next().unwrap();
+ let result = match command.as_ref() {
+ "attach" => usb_attach(args),
+ "detach" => usb_detach(args),
+ "list" => usb_list(args),
+ other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
+ };
+ match result {
+ Ok(response) => {
+ println!("{}", response);
+ Ok(())
+ }
+ Err(e) => {
+ println!("error {}", e);
+ Err(())
+ }
+ }
+}
+
+fn print_usage() {
+ print_help("crosvm", "[stop|run]", &[]);
+ println!("Commands:");
+ println!(" stop - Stops crosvm instances via their control sockets.");
+ println!(" run - Start a new crosvm instance.");
+ println!(" create_qcow2 - Create a new qcow2 disk image file.");
+ println!(" disk - Manage attached virtual disk devices.");
+ println!(" usb - Manage attached virtual USB devices.");
+}
+
+fn crosvm_main() -> std::result::Result<(), ()> {
+ if let Err(e) = syslog::init() {
+ println!("failed to initialize syslog: {}", e);
+ return Err(());
+ }
+
+ panic_hook::set_panic_hook();
+
+ let mut args = std::env::args();
+ if args.next().is_none() {
+ error!("expected executable name");
+ return Err(());
+ }
+
+ // Past this point, usage of exit is in danger of leaking zombie processes.
+ let ret = match args.next().as_ref().map(|a| a.as_ref()) {
+ None => {
+ print_usage();
+ Ok(())
+ }
+ Some("stop") => stop_vms(args),
+ Some("suspend") => suspend_vms(args),
+ Some("resume") => resume_vms(args),
+ Some("run") => run_vm(args),
+ Some("balloon") => balloon_vms(args),
+ Some("create_qcow2") => create_qcow2(args),
+ Some("disk") => disk_cmd(args),
+ Some("usb") => modify_usb(args),
+ Some(c) => {
+ println!("invalid subcommand: {:?}", c);
+ print_usage();
+ Err(())
+ }
+ };
+
+ // Reap exit status from any child device processes. At this point, all devices should have been
+ // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
+ // take some time for the processes to shut down.
+ if !wait_all_children() {
+ // We gave them a chance, and it's too late.
+ warn!("not all child processes have exited; sending SIGKILL");
+ if let Err(e) = kill_process_group() {
+ // We're now at the mercy of the OS to clean up after us.
+ warn!("unable to kill all child processes: {}", e);
+ }
+ }
+
+ // WARNING: Any code added after this point is not guaranteed to run
+ // since we may forcibly kill this process (and its children) above.
+ ret
+}
+
+fn main() {
+ std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn parse_cpu_set_single() {
+ assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
+ }
+
+ #[test]
+ fn parse_cpu_set_list() {
+ assert_eq!(
+ parse_cpu_set("0,1,2,3").expect("parse failed"),
+ vec![0, 1, 2, 3]
+ );
+ }
+
+ #[test]
+ fn parse_cpu_set_range() {
+ assert_eq!(
+ parse_cpu_set("0-3").expect("parse failed"),
+ vec![0, 1, 2, 3]
+ );
+ }
+
+ #[test]
+ fn parse_cpu_set_list_of_ranges() {
+ assert_eq!(
+ parse_cpu_set("3-4,7-9,18").expect("parse failed"),
+ vec![3, 4, 7, 8, 9, 18]
+ );
+ }
+
+ #[test]
+ fn parse_cpu_set_repeated() {
+ // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
+ assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
+ }
+
+ #[test]
+ fn parse_cpu_set_negative() {
+ // Negative CPU numbers are not allowed.
+ parse_cpu_set("-3").expect_err("parse should have failed");
+ }
+
+ #[test]
+ fn parse_cpu_set_reverse_range() {
+ // Ranges must be from low to high.
+ parse_cpu_set("5-2").expect_err("parse should have failed");
+ }
+
+ #[test]
+ fn parse_cpu_set_open_range() {
+ parse_cpu_set("3-").expect_err("parse should have failed");
+ }
+
+ #[test]
+ fn parse_cpu_set_extra_comma() {
+ parse_cpu_set("0,1,2,").expect_err("parse should have failed");
+ }
+
+ #[test]
+ fn parse_serial_vaild() {
+ parse_serial_options("type=syslog,num=1,console=true").expect("parse should have succeded");
+ }
+
+ #[test]
+ fn parse_serial_valid_no_num() {
+ parse_serial_options("type=syslog").expect("parse should have succeded");
+ }
+
+ #[test]
+ fn parse_serial_invalid_type() {
+ parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
+ }
+
+ #[test]
+ fn parse_serial_invalid_num_upper() {
+ parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
+ }
+
+ #[test]
+ fn parse_serial_invalid_num_lower() {
+ parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
+ }
+
+ #[test]
+ fn parse_serial_invalid_num_string() {
+ parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
+ }
+
+ #[test]
+ fn parse_serial_invalid_option() {
+ parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
+ }
+}
diff --git a/src/panic_hook.rs b/src/panic_hook.rs
new file mode 100644
index 0000000..48cf0c5
--- /dev/null
+++ b/src/panic_hook.rs
@@ -0,0 +1,103 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::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 libc::{close, dup, dup2, pipe2, O_NONBLOCK, STDERR_FILENO};
+use sys_util::error;
+
+// Opens a pipe and puts the write end into the stderr FD slot. On success, returns the read end of
+// the pipe and the old stderr as a pair of files.
+fn redirect_stderr() -> Option<(File, File)> {
+ let mut fds = [-1, -1];
+ unsafe {
+ // Trivially safe because the return value is checked.
+ let old_stderr = dup(STDERR_FILENO);
+ if old_stderr == -1 {
+ return None;
+ }
+ // Safe because pipe2 will only ever write two integers to our array and we check output.
+ let mut ret = pipe2(fds.as_mut_ptr(), O_NONBLOCK);
+ if ret != 0 {
+ // Leaks FDs, but not important right before abort.
+ return None;
+ }
+ // Safe because the FD we are duplicating is owned by us.
+ ret = dup2(fds[1], STDERR_FILENO);
+ if ret == -1 {
+ // Leaks FDs, but not important right before abort.
+ return None;
+ }
+ // 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)))
+ }
+}
+
+// Sets stderr to the given file. Returns true on success.
+fn restore_stderr(stderr: File) -> bool {
+ let fd = stderr.into_raw_fd();
+
+ // Safe because fd is guaranteed to be valid and replacing stderr should be an atomic operation.
+ unsafe { dup2(fd, STDERR_FILENO) != -1 }
+}
+
+// Sends as much information about the panic as possible to syslog.
+fn log_panic_info(default_panic: &(dyn Fn(&PanicInfo) + Sync + Send + 'static), info: &PanicInfo) {
+ // Grab a lock of stderr to prevent concurrent threads from trampling on our stderr capturing
+ // procedure. The default_panic procedure likely uses stderr.lock as well, but the mutex inside
+ // stderr is reentrant, so it will not dead-lock on this thread.
+ let stderr = stderr();
+ let _stderr_lock = stderr.lock();
+
+ // Redirect stderr to a pipe we can read from later.
+ let (mut read_file, old_stderr) = match redirect_stderr() {
+ Some(f) => f,
+ None => {
+ error!("failed to capture stderr during panic");
+ return;
+ }
+ };
+ // Only through the default panic handler can we get a stacktrace. It only ever prints to
+ // stderr, hence all the previous code to redirect it to a pipe we can read.
+ env::set_var("RUST_BACKTRACE", "1");
+ default_panic(info);
+
+ // Closes the write end of the pipe so that we can reach EOF in read_to_string. Also allows
+ // others to write to stderr without failure.
+ if !restore_stderr(old_stderr) {
+ error!("failed to restore stderr during panic");
+ return;
+ }
+ drop(_stderr_lock);
+
+ let mut panic_output = String::new();
+ // Ignore errors and print what we got.
+ let _ = read_file.read_to_string(&mut panic_output);
+ // Split by line because the logging facilities do not handle embedded new lines well.
+ for line in panic_output.lines() {
+ error!("{}", line);
+ }
+}
+
+/// The intent of our panic hook is to get panic info and a stacktrace into the syslog, even for
+/// jailed subprocesses. It will always abort on panic to ensure a minidump is generated.
+///
+/// Note that jailed processes will usually have a stacktrace of <unknown> because the backtrace
+/// routines attempt to open this binary and are unable to do so in a jail.
+pub fn set_panic_hook() {
+ let default_panic = panic::take_hook();
+ panic::set_hook(Box::new(move |info| {
+ log_panic_info(default_panic.as_ref(), info);
+ // Abort to trigger the crash reporter so that a minidump is generated.
+ abort();
+ }));
+}
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
new file mode 100644
index 0000000..1cb3e57
--- /dev/null
+++ b/src/plugin/mod.rs
@@ -0,0 +1,801 @@
+// Copyright 2018 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.
+
+mod process;
+mod vcpu;
+
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
+use std::os::unix::net::UnixDatagram;
+use std::path::Path;
+use std::result;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::{Arc, Barrier};
+use std::thread;
+use std::time::{Duration, Instant};
+
+use libc::{
+ c_int, c_ulong, fcntl, ioctl, socketpair, AF_UNIX, EAGAIN, EBADF, EDEADLK, EEXIST, EINTR,
+ EINVAL, ENOENT, EOVERFLOW, EPERM, FIOCLEX, F_SETPIPE_SZ, MS_NODEV, MS_NOEXEC, MS_NOSUID,
+ SIGCHLD, SOCK_SEQPACKET,
+};
+
+use protobuf::ProtobufError;
+use remain::sorted;
+
+use io_jail::{self, Minijail};
+use kvm::{Datamatch, IoeventAddress, Kvm, Vcpu, VcpuExit, Vm};
+use net_util::{Error as TapError, Tap, TapT};
+use sys_util::{
+ block_signal, clear_signal, drop_capabilities, error, getegid, geteuid, info, pipe,
+ register_signal_handler, validate_raw_fd, warn, Error as SysError, EventFd, GuestMemory,
+ Killable, MmapError, PollContext, PollToken, Result as SysResult, SignalFd, SignalFdError,
+ SIGRTMIN,
+};
+
+use self::process::*;
+use self::vcpu::*;
+use crate::{Config, Executable};
+
+const MAX_DATAGRAM_SIZE: usize = 4096;
+const MAX_VCPU_DATAGRAM_SIZE: usize = 0x40000;
+
+/// An error that occurs during the lifetime of a plugin process.
+#[sorted]
+pub enum Error {
+ CloneEventFd(SysError),
+ CloneVcpuPipe(io::Error),
+ CreateEventFd(SysError),
+ CreateIrqChip(SysError),
+ CreateJail(io_jail::Error),
+ CreateKvm(SysError),
+ CreateMainSocket(SysError),
+ CreatePIT(SysError),
+ CreatePollContext(SysError),
+ CreateSignalFd(SignalFdError),
+ CreateSocketPair(io::Error),
+ CreateTapFd(TapError),
+ CreateVcpu(SysError),
+ CreateVcpuSocket(SysError),
+ CreateVm(SysError),
+ DecodeRequest(ProtobufError),
+ DropCapabilities(SysError),
+ EncodeResponse(ProtobufError),
+ Mount(io_jail::Error),
+ MountDev(io_jail::Error),
+ MountLib(io_jail::Error),
+ MountLib64(io_jail::Error),
+ MountPlugin(io_jail::Error),
+ MountPluginLib(io_jail::Error),
+ MountRoot(io_jail::Error),
+ NoRootDir,
+ ParsePivotRoot(io_jail::Error),
+ ParseSeccomp(io_jail::Error),
+ PluginFailed(i32),
+ PluginKill(SysError),
+ PluginKilled(i32),
+ PluginRunJail(io_jail::Error),
+ PluginSocketHup,
+ PluginSocketPoll(SysError),
+ PluginSocketRecv(SysError),
+ PluginSocketSend(SysError),
+ PluginSpawn(io::Error),
+ PluginTimeout,
+ PluginWait(SysError),
+ Poll(SysError),
+ PollContextAdd(SysError),
+ RootNotAbsolute,
+ RootNotDir,
+ SetGidMap(io_jail::Error),
+ SetUidMap(io_jail::Error),
+ SigChild {
+ pid: u32,
+ signo: u32,
+ status: i32,
+ code: i32,
+ },
+ SignalFd(SignalFdError),
+ SpawnVcpu(io::Error),
+ TapEnable(TapError),
+ TapOpen(TapError),
+ TapSetIp(TapError),
+ TapSetMacAddress(TapError),
+ TapSetNetmask(TapError),
+ ValidateTapFd(SysError),
+}
+
+impl Display for Error {
+ #[remain::check]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ #[sorted]
+ match self {
+ CloneEventFd(e) => write!(f, "failed to clone eventfd: {}", e),
+ CloneVcpuPipe(e) => write!(f, "failed to clone vcpu pipe: {}", e),
+ CreateEventFd(e) => write!(f, "failed to create eventfd: {}", e),
+ CreateIrqChip(e) => write!(f, "failed to create kvm irqchip: {}", e),
+ CreateJail(e) => write!(f, "failed to create jail: {}", e),
+ 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),
+ 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),
+ Mount(e) | MountDev(e) | MountLib(e) | MountLib64(e) | MountPlugin(e)
+ | MountPluginLib(e) | MountRoot(e) => write!(f, "failed to mount: {}", e),
+ NoRootDir => write!(f, "no root directory for jailed process to pivot root into"),
+ ParsePivotRoot(e) => write!(f, "failed to set jail pivot root: {}", e),
+ ParseSeccomp(e) => write!(f, "failed to parse jail seccomp filter: {}", e),
+ PluginFailed(e) => write!(f, "plugin exited with error: {}", e),
+ PluginKill(e) => write!(f, "error sending kill signal to plugin: {}", e),
+ PluginKilled(e) => write!(f, "plugin exited with signal {}", e),
+ PluginRunJail(e) => write!(f, "failed to run jail: {}", e),
+ PluginSocketHup => write!(f, "plugin request socket has been hung up"),
+ PluginSocketPoll(e) => write!(f, "failed to poll plugin request sockets: {}", e),
+ PluginSocketRecv(e) => write!(f, "failed to recv from plugin request socket: {}", e),
+ PluginSocketSend(e) => write!(f, "failed to send to plugin request socket: {}", e),
+ PluginSpawn(e) => write!(f, "failed to spawn plugin: {}", e),
+ 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),
+ SetUidMap(e) => write!(f, "failed to set uidmap for jail: {}", e),
+ SigChild {
+ pid,
+ signo,
+ status,
+ code,
+ } => write!(
+ f,
+ "process {} died with signal {}, status {}, and code {}",
+ pid, signo, status, code
+ ),
+ SignalFd(e) => write!(f, "failed to read signal fd: {}", e),
+ SpawnVcpu(e) => write!(f, "error spawning vcpu thread: {}", e),
+ TapEnable(e) => write!(f, "error enabling tap device: {}", e),
+ TapOpen(e) => write!(f, "error opening tap device: {}", e),
+ TapSetIp(e) => write!(f, "error setting tap ip: {}", e),
+ 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),
+ }
+ }
+}
+
+type Result<T> = result::Result<T, Error>;
+
+fn downcast_file<F: IntoRawFd>(f: F) -> File {
+ unsafe { File::from_raw_fd(f.into_raw_fd()) }
+}
+
+fn new_seqpacket_pair() -> SysResult<(UnixDatagram, UnixDatagram)> {
+ let mut fds = [0, 0];
+ unsafe {
+ let ret = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds.as_mut_ptr());
+ if ret == 0 {
+ ioctl(fds[0], FIOCLEX);
+ Ok((
+ UnixDatagram::from_raw_fd(fds[0]),
+ UnixDatagram::from_raw_fd(fds[1]),
+ ))
+ } else {
+ Err(SysError::last())
+ }
+ }
+}
+
+struct VcpuPipe {
+ crosvm_read: File,
+ plugin_write: File,
+ plugin_read: File,
+ crosvm_write: File,
+}
+
+fn new_pipe_pair() -> SysResult<VcpuPipe> {
+ let to_crosvm = pipe(true)?;
+ let to_plugin = pipe(true)?;
+ // Increasing the pipe size can be a nice-to-have to make sure that
+ // messages get across atomically (and made sure that writes don't block),
+ // though it's not necessary a hard requirement for things to work.
+ let flags = unsafe {
+ fcntl(
+ to_crosvm.0.as_raw_fd(),
+ F_SETPIPE_SZ,
+ MAX_VCPU_DATAGRAM_SIZE as c_int,
+ )
+ };
+ if flags < 0 || flags != MAX_VCPU_DATAGRAM_SIZE as i32 {
+ warn!(
+ "Failed to adjust size of crosvm pipe (result {}): {}",
+ flags,
+ SysError::last()
+ );
+ }
+ let flags = unsafe {
+ fcntl(
+ to_plugin.0.as_raw_fd(),
+ F_SETPIPE_SZ,
+ MAX_VCPU_DATAGRAM_SIZE as c_int,
+ )
+ };
+ if flags < 0 || flags != MAX_VCPU_DATAGRAM_SIZE as i32 {
+ warn!(
+ "Failed to adjust size of plugin pipe (result {}): {}",
+ flags,
+ SysError::last()
+ );
+ }
+ Ok(VcpuPipe {
+ crosvm_read: to_crosvm.0,
+ plugin_write: to_crosvm.1,
+ plugin_read: to_plugin.0,
+ crosvm_write: to_plugin.1,
+ })
+}
+
+fn proto_to_sys_err(e: ProtobufError) -> SysError {
+ match e {
+ ProtobufError::IoError(e) => SysError::new(e.raw_os_error().unwrap_or(EINVAL)),
+ _ => SysError::new(EINVAL),
+ }
+}
+
+fn io_to_sys_err(e: io::Error) -> SysError {
+ SysError::new(e.raw_os_error().unwrap_or(EINVAL))
+}
+
+fn mmap_to_sys_err(e: MmapError) -> SysError {
+ match e {
+ MmapError::SystemCallFailed(e) => e,
+ _ => SysError::new(EINVAL),
+ }
+}
+
+fn create_plugin_jail(root: &Path, seccomp_policy: &Path) -> Result<Minijail> {
+ // All child jails run in a new user namespace without any users mapped,
+ // they run as nobody unless otherwise configured.
+ let mut j = Minijail::new().map_err(Error::CreateJail)?;
+ j.namespace_pids();
+ j.namespace_user();
+ j.uidmap(&format!("0 {0} 1", geteuid()))
+ .map_err(Error::SetUidMap)?;
+ j.gidmap(&format!("0 {0} 1", getegid()))
+ .map_err(Error::SetGidMap)?;
+ j.namespace_user_disable_setgroups();
+ // Don't need any capabilities.
+ j.use_caps(0);
+ // Create a new mount namespace with an empty root FS.
+ j.namespace_vfs();
+ j.enter_pivot_root(root).map_err(Error::ParsePivotRoot)?;
+ // Run in an empty network namespace.
+ j.namespace_net();
+ j.no_new_privs();
+ // Use TSYNC only for the side effect of it using SECCOMP_RET_TRAP, which will correctly kill
+ // the entire plugin process if a worker thread commits a seccomp violation.
+ j.set_seccomp_filter_tsync();
+ #[cfg(debug_assertions)]
+ j.log_seccomp_filter_failures();
+ j.parse_seccomp_filters(seccomp_policy)
+ .map_err(Error::ParseSeccomp)?;
+ j.use_seccomp_filter();
+ // Don't do init setup.
+ j.run_as_init();
+
+ // Create a tmpfs in the plugin's root directory so that we can bind mount it's executable
+ // file into it. The size=67108864 is size=64*1024*1024 or size=64MB.
+ j.mount_with_data(
+ Path::new("none"),
+ Path::new("/"),
+ "tmpfs",
+ (MS_NOSUID | MS_NODEV | MS_NOEXEC) as usize,
+ "size=67108864",
+ )
+ .map_err(Error::MountRoot)?;
+
+ Ok(j)
+}
+
+/// Each `PluginObject` represents one object that was instantiated by the guest using the `Create`
+/// request.
+///
+/// Each such object has an ID associated with it that exists in an ID space shared by every variant
+/// of `PluginObject`. This allows all the objects to be indexed in a single map, and allows for a
+/// common destroy method.
+///
+
+/// In addition to the destory method, each object may have methods specific to its variant type.
+/// These variant methods must be done by matching the variant to the expected type for that method.
+/// For example, getting the dirty log from a `Memory` object starting with an ID:
+///
+/// ```
+/// match objects.get(&request_id) {
+/// Some(&PluginObject::Memory { slot, length }) => vm.get_dirty_log(slot, &mut dirty_log[..])
+/// _ => return Err(SysError::new(ENOENT)),
+/// }
+/// ```
+enum PluginObject {
+ IoEvent {
+ evt: EventFd,
+ addr: IoeventAddress,
+ length: u32,
+ datamatch: u64,
+ },
+ Memory {
+ slot: u32,
+ length: usize,
+ },
+ IrqEvent {
+ irq_id: u32,
+ evt: EventFd,
+ },
+}
+
+impl PluginObject {
+ fn destroy(self, vm: &mut Vm) -> SysResult<()> {
+ match self {
+ PluginObject::IoEvent {
+ evt,
+ addr,
+ length,
+ datamatch,
+ } => match length {
+ 0 => vm.unregister_ioevent(&evt, addr, Datamatch::AnyLength),
+ 1 => vm.unregister_ioevent(&evt, addr, Datamatch::U8(Some(datamatch as u8))),
+ 2 => vm.unregister_ioevent(&evt, addr, Datamatch::U16(Some(datamatch as u16))),
+ 4 => vm.unregister_ioevent(&evt, addr, Datamatch::U32(Some(datamatch as u32))),
+ 8 => vm.unregister_ioevent(&evt, addr, Datamatch::U64(Some(datamatch as u64))),
+ _ => Err(SysError::new(EINVAL)),
+ },
+ PluginObject::Memory { slot, .. } => vm.remove_device_memory(slot).and(Ok(())),
+ PluginObject::IrqEvent { irq_id, evt } => vm.unregister_irqfd(&evt, irq_id),
+ }
+ }
+}
+
+pub fn run_vcpus(
+ kvm: &Kvm,
+ vm: &Vm,
+ plugin: &Process,
+ vcpu_count: u32,
+ kill_signaled: &Arc<AtomicBool>,
+ exit_evt: &EventFd,
+ vcpu_handles: &mut Vec<thread::JoinHandle<()>>,
+) -> Result<()> {
+ let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_count) as usize));
+ for cpu_id in 0..vcpu_count {
+ let kill_signaled = kill_signaled.clone();
+ let vcpu_thread_barrier = vcpu_thread_barrier.clone();
+ let vcpu_exit_evt = exit_evt.try_clone().map_err(Error::CloneEventFd)?;
+ let vcpu_plugin = plugin.create_vcpu(cpu_id)?;
+ let vcpu = Vcpu::new(cpu_id as c_ulong, kvm, vm).map_err(Error::CreateVcpu)?;
+
+ vcpu_handles.push(
+ thread::Builder::new()
+ .name(format!("crosvm_vcpu{}", cpu_id))
+ .spawn(move || {
+ unsafe {
+ extern "C" fn handle_signal() {}
+ // Our signal handler does nothing and is trivially async signal safe.
+ // We need to install this signal handler even though we do block
+ // the signal below, to ensure that this signal will interrupt
+ // execution of KVM_RUN (this is implementation issue).
+ register_signal_handler(SIGRTMIN() + 0, handle_signal)
+ .expect("failed to register vcpu signal handler");
+ }
+
+ // We do not really want the signal handler to run...
+ block_signal(SIGRTMIN() + 0).expect("failed to block signal");
+ // Tell KVM to not block anything when entering kvm run
+ // because we will be using first RT signal to kick the VCPU.
+ vcpu.set_signal_mask(&[])
+ .expect("failed to set up KVM VCPU signal mask");
+
+ let res = vcpu_plugin.init(&vcpu);
+ vcpu_thread_barrier.wait();
+ if let Err(e) = res {
+ error!("failed to initialize vcpu {}: {}", cpu_id, e);
+ } else {
+ loop {
+ let mut interrupted_by_signal = false;
+ let run_res = vcpu.run();
+ match run_res {
+ Ok(run) => match run {
+ VcpuExit::IoIn { port, mut size } => {
+ let mut data = [0; 256];
+ if size > data.len() {
+ error!("unsupported IoIn size of {} bytes", size);
+ size = data.len();
+ }
+ vcpu_plugin.io_read(port as u64, &mut data[..size], &vcpu);
+ if let Err(e) = vcpu.set_data(&data[..size]) {
+ error!("failed to set return data for IoIn: {}", e);
+ }
+ }
+ VcpuExit::IoOut {
+ port,
+ mut size,
+ data,
+ } => {
+ if size > data.len() {
+ error!("unsupported IoOut size of {} bytes", size);
+ size = data.len();
+ }
+ vcpu_plugin.io_write(port as u64, &data[..size], &vcpu);
+ }
+ VcpuExit::MmioRead { address, size } => {
+ let mut data = [0; 8];
+ vcpu_plugin.mmio_read(
+ address as u64,
+ &mut data[..size],
+ &vcpu,
+ );
+ // Setting data for mmio can not fail.
+ let _ = vcpu.set_data(&data[..size]);
+ }
+ VcpuExit::MmioWrite {
+ address,
+ size,
+ data,
+ } => {
+ vcpu_plugin.mmio_write(
+ address as u64,
+ &data[..size],
+ &vcpu,
+ );
+ }
+ VcpuExit::Hlt => break,
+ VcpuExit::Shutdown => break,
+ VcpuExit::InternalError => {
+ error!("vcpu {} has internal error", cpu_id);
+ break;
+ }
+ r => warn!("unexpected vcpu exit: {:?}", r),
+ },
+ Err(e) => match e.errno() {
+ EINTR => interrupted_by_signal = true,
+ EAGAIN => {}
+ _ => {
+ error!("vcpu hit unknown error: {}", e);
+ break;
+ }
+ },
+ }
+ if kill_signaled.load(Ordering::SeqCst) {
+ break;
+ }
+
+ // Try to clear the signal that we use to kick VCPU if it is
+ // pending before attempting to handle pause requests.
+ if interrupted_by_signal {
+ clear_signal(SIGRTMIN() + 0)
+ .expect("failed to clear pending signal");
+ }
+
+ if let Err(e) = vcpu_plugin.pre_run(&vcpu) {
+ error!("failed to process pause on vcpu {}: {}", cpu_id, e);
+ break;
+ }
+ }
+ }
+ vcpu_exit_evt
+ .write(1)
+ .expect("failed to signal vcpu exit eventfd");
+ })
+ .map_err(Error::SpawnVcpu)?,
+ );
+ }
+ Ok(())
+}
+
+#[derive(PollToken)]
+enum Token {
+ Exit,
+ ChildSignal,
+ Plugin { index: usize },
+}
+
+/// Run a VM with a plugin process specified by `cfg`.
+///
+/// Not every field of `cfg` will be used. In particular, most field that pertain to a specific
+/// device are ignored because the plugin is responsible for emulating hardware.
+pub fn run_config(cfg: Config) -> Result<()> {
+ info!("crosvm starting plugin process");
+
+ // Masking signals is inherently dangerous, since this can persist across clones/execs. Do this
+ // before any jailed devices have been spawned, so that we can catch any of them that fail very
+ // quickly.
+ let sigchld_fd = SignalFd::new(SIGCHLD).map_err(Error::CreateSignalFd)?;
+
+ let jail = if cfg.sandbox {
+ // An empty directory for jailed plugin pivot root.
+ let root_path = match &cfg.plugin_root {
+ Some(dir) => dir,
+ None => Path::new("/var/empty"),
+ };
+
+ if root_path.is_relative() {
+ return Err(Error::RootNotAbsolute);
+ }
+
+ if !root_path.exists() {
+ return Err(Error::NoRootDir);
+ }
+
+ if !root_path.is_dir() {
+ return Err(Error::RootNotDir);
+ }
+
+ let policy_path = cfg.seccomp_policy_dir.join("plugin.policy");
+ let mut jail = create_plugin_jail(root_path, &policy_path)?;
+
+ // Update gid map of the jail if caller provided supplemental groups.
+ if !cfg.plugin_gid_maps.is_empty() {
+ let map = format!("0 {} 1", getegid())
+ + &cfg
+ .plugin_gid_maps
+ .into_iter()
+ .map(|m| format!(",{} {} {}", m.inner, m.outer, m.count))
+ .collect::<String>();
+ jail.gidmap(&map).map_err(Error::SetGidMap)?;
+ }
+
+ // Mount minimal set of devices (full, zero, urandom, etc). We can not use
+ // jail.mount_dev() here because crosvm may not be running with CAP_SYS_ADMIN.
+ let device_names = ["full", "null", "urandom", "zero"];
+ for name in &device_names {
+ let device = Path::new("/dev").join(&name);
+ jail.mount_bind(&device, &device, true)
+ .map_err(Error::MountDev)?;
+ }
+
+ for bind_mount in &cfg.plugin_mounts {
+ jail.mount_bind(&bind_mount.src, &bind_mount.dst, bind_mount.writable)
+ .map_err(Error::Mount)?;
+ }
+
+ Some(jail)
+ } else {
+ None
+ };
+
+ let mut tap_interfaces: Vec<Tap> = Vec::new();
+ if let Some(host_ip) = cfg.host_ip {
+ if let Some(netmask) = cfg.netmask {
+ if let Some(mac_address) = cfg.mac_address {
+ let tap = Tap::new(false).map_err(Error::TapOpen)?;
+ tap.set_ip_addr(host_ip).map_err(Error::TapSetIp)?;
+ tap.set_netmask(netmask).map_err(Error::TapSetNetmask)?;
+ tap.set_mac_address(mac_address)
+ .map_err(Error::TapSetMacAddress)?;
+
+ tap.enable().map_err(Error::TapEnable)?;
+ tap_interfaces.push(tap);
+ }
+ }
+ }
+ 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)?)
+ .map_err(Error::CreateTapFd)?
+ };
+ tap_interfaces.push(tap);
+ }
+
+ let plugin_args: Vec<&str> = cfg.params.iter().map(|s| &s[..]).collect();
+
+ let plugin_path = match cfg.executable_path {
+ Some(Executable::Plugin(ref plugin_path)) => plugin_path.as_path(),
+ _ => panic!("Executable was not a plugin"),
+ };
+ let vcpu_count = cfg.vcpu_count.unwrap_or(1);
+ let mem = GuestMemory::new(&[]).unwrap();
+ let kvm = Kvm::new().map_err(Error::CreateKvm)?;
+ let mut vm = Vm::new(&kvm, mem).map_err(Error::CreateVm)?;
+ vm.create_irq_chip().map_err(Error::CreateIrqChip)?;
+ vm.create_pit().map_err(Error::CreatePIT)?;
+
+ let mut plugin = Process::new(vcpu_count, plugin_path, &plugin_args, jail)?;
+ // Now that the jail for the plugin has been created and we had a chance to adjust gids there,
+ // we can drop all our capabilities in case we had any.
+ drop_capabilities().map_err(Error::DropCapabilities)?;
+
+ let mut res = Ok(());
+ // If Some, we will exit after enough time is passed to shutdown cleanly.
+ let mut dying_instant: Option<Instant> = None;
+ let duration_to_die = Duration::from_millis(1000);
+
+ let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
+ let kill_signaled = Arc::new(AtomicBool::new(false));
+ let mut vcpu_handles = Vec::with_capacity(vcpu_count as usize);
+
+ let poll_ctx = PollContext::new().map_err(Error::CreatePollContext)?;
+ poll_ctx
+ .add(&exit_evt, Token::Exit)
+ .map_err(Error::PollContextAdd)?;
+ poll_ctx
+ .add(&sigchld_fd, Token::ChildSignal)
+ .map_err(Error::PollContextAdd)?;
+
+ let mut sockets_to_drop = Vec::new();
+ let mut redo_poll_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 {
+ // After we have waited long enough, it's time to give up and exit.
+ if dying_instant
+ .map(|i| i.elapsed() >= duration_to_die)
+ .unwrap_or(false)
+ {
+ break;
+ }
+
+ if redo_poll_ctx_sockets {
+ for (index, socket) in plugin.sockets().iter().enumerate() {
+ poll_ctx
+ .add(socket, Token::Plugin { index })
+ .map_err(Error::PollContextAdd)?;
+ }
+ }
+
+ 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(),
+ };
+ match poll_res {
+ Ok(v) => v,
+ Err(e) => {
+ // Polling no longer works, time to break and cleanup,
+ if res.is_ok() {
+ res = Err(Error::Poll(e));
+ }
+ break;
+ }
+ }
+ };
+ for event in events.iter_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);
+ dying_instant.get_or_insert(Instant::now());
+ let sig_res = plugin.signal_kill();
+ if res.is_ok() && sig_res.is_err() {
+ res = sig_res.map_err(Error::PluginKill);
+ }
+ }
+ Token::ChildSignal => {
+ // Print all available siginfo structs, then exit the loop.
+ loop {
+ match sigchld_fd.read() {
+ Ok(Some(siginfo)) => {
+ // 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;
+ }
+ // Because SIGCHLD is not expected from anything other than the
+ // plugin process, report it as an error.
+ if res.is_ok() {
+ res = Err(Error::SigChild {
+ pid: siginfo.ssi_pid,
+ signo: siginfo.ssi_signo,
+ status: siginfo.ssi_status,
+ code: siginfo.ssi_code,
+ })
+ }
+ }
+ Ok(None) => break, // No more signals to read.
+ Err(e) => {
+ // Something really must be messed up for this to happen, continue
+ // processing connections for a limited time.
+ if res.is_ok() {
+ res = Err(Error::SignalFd(e));
+ }
+ break;
+ }
+ }
+ }
+ // As we only spawn the plugin process, getting a SIGCHLD can only mean
+ // something went wrong.
+ dying_instant.get_or_insert(Instant::now());
+ let sig_res = plugin.signal_kill();
+ if res.is_ok() && sig_res.is_err() {
+ res = sig_res.map_err(Error::PluginKill);
+ }
+ }
+ Token::Plugin { index } => {
+ match plugin.handle_socket(index, &kvm, &mut vm, &vcpu_handles, &tap_interfaces)
+ {
+ Ok(_) => {}
+ // A HUP is an expected event for a socket, so don't bother warning about
+ // it.
+ Err(Error::PluginSocketHup) => sockets_to_drop.push(index),
+ // Only one connection out of potentially many is broken. Drop it, but don't
+ // start cleaning up. Because the error isn't returned, we will warn about
+ // it here.
+ Err(e) => {
+ warn!("error handling plugin socket: {}", e);
+ sockets_to_drop.push(index);
+ }
+ }
+ }
+ }
+ }
+
+ if vcpu_handles.is_empty() && dying_instant.is_none() && plugin.is_started() {
+ let res = run_vcpus(
+ &kvm,
+ &vm,
+ &plugin,
+ vcpu_count,
+ &kill_signaled,
+ &exit_evt,
+ &mut vcpu_handles,
+ );
+ if let Err(e) = res {
+ dying_instant.get_or_insert(Instant::now());
+ error!("failed to start vcpus: {}", e);
+ }
+ }
+
+ redo_poll_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
+ // other error.
+ plugin.drop_sockets(&mut sockets_to_drop);
+ sockets_to_drop.clear();
+
+ if redo_poll_ctx_sockets {
+ for socket in plugin.sockets() {
+ let _ = poll_ctx.delete(socket);
+ }
+ }
+ }
+
+ // vcpu threads MUST see the kill signaled flag, otherwise they may re-enter the VM.
+ kill_signaled.store(true, Ordering::SeqCst);
+ // Depending on how we ended up here, the plugin process, or a VCPU thread waiting for requests
+ // might be stuck. The `signal_kill` call will unstick all the VCPU threads by closing their
+ // blocked connections.
+ plugin.signal_kill().map_err(Error::PluginKill)?;
+ 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),
+ }
+ }
+
+ match plugin.try_wait() {
+ // The plugin has run out of time by now
+ Ok(ProcessStatus::Running) => Err(Error::PluginTimeout),
+ // Return an error discovered earlier in this function.
+ Ok(ProcessStatus::Success) => res,
+ Ok(ProcessStatus::Fail(code)) => Err(Error::PluginFailed(code)),
+ Ok(ProcessStatus::Signal(code)) => Err(Error::PluginKilled(code)),
+ Err(e) => Err(Error::PluginWait(e)),
+ }
+}
diff --git a/src/plugin/process.rs b/src/plugin/process.rs
new file mode 100644
index 0000000..50b4465
--- /dev/null
+++ b/src/plugin/process.rs
@@ -0,0 +1,705 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::collections::hash_map::{Entry, HashMap, VacantEntry};
+use std::env::set_var;
+use std::fs::File;
+use std::io::Write;
+use std::mem::transmute;
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::os::unix::net::UnixDatagram;
+use std::path::Path;
+use std::process::Command;
+use std::sync::{Arc, RwLock};
+use std::thread::JoinHandle;
+
+use net_util;
+use net_util::Error as NetError;
+
+use libc::{pid_t, waitpid, EINVAL, ENODATA, ENOTTY, WEXITSTATUS, WIFEXITED, WNOHANG, WTERMSIG};
+
+use protobuf;
+use protobuf::Message;
+
+use io_jail::Minijail;
+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};
+use protos::plugin::*;
+use sync::Mutex;
+use sys_util::{
+ error, Error as SysError, EventFd, GuestAddress, Killable, MemoryMapping, Result as SysResult,
+ ScmSocket, SharedMemory, SIGRTMIN,
+};
+
+use super::*;
+
+// Wrapper types to make the kvm state structs DataInit
+use data_model::DataInit;
+#[derive(Copy, Clone)]
+struct VmPicState(kvm_pic_state);
+unsafe impl DataInit for VmPicState {}
+#[derive(Copy, Clone)]
+struct VmIoapicState(kvm_ioapic_state);
+unsafe impl DataInit for VmIoapicState {}
+#[derive(Copy, Clone)]
+struct VmPitState(kvm_pit_state2);
+unsafe impl DataInit for VmPitState {}
+#[derive(Copy, Clone)]
+struct VmClockState(kvm_clock_data);
+unsafe impl DataInit for VmClockState {}
+
+fn get_vm_state(vm: &Vm, state_set: MainRequest_StateSet) -> SysResult<Vec<u8>> {
+ Ok(match state_set {
+ MainRequest_StateSet::PIC0 => VmPicState(vm.get_pic_state(PicId::Primary)?)
+ .as_slice()
+ .to_vec(),
+ MainRequest_StateSet::PIC1 => VmPicState(vm.get_pic_state(PicId::Secondary)?)
+ .as_slice()
+ .to_vec(),
+ MainRequest_StateSet::IOAPIC => VmIoapicState(vm.get_ioapic_state()?).as_slice().to_vec(),
+ MainRequest_StateSet::PIT => VmPitState(vm.get_pit_state()?).as_slice().to_vec(),
+ MainRequest_StateSet::CLOCK => VmClockState(vm.get_clock()?).as_slice().to_vec(),
+ })
+}
+
+fn set_vm_state(vm: &Vm, state_set: MainRequest_StateSet, state: &[u8]) -> SysResult<()> {
+ match state_set {
+ MainRequest_StateSet::PIC0 => vm.set_pic_state(
+ PicId::Primary,
+ &VmPicState::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ MainRequest_StateSet::PIC1 => vm.set_pic_state(
+ PicId::Secondary,
+ &VmPicState::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ MainRequest_StateSet::IOAPIC => vm.set_ioapic_state(
+ &VmIoapicState::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ MainRequest_StateSet::PIT => vm.set_pit_state(
+ &VmPitState::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ MainRequest_StateSet::CLOCK => vm.set_clock(
+ &VmClockState::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ }
+}
+
+/// The status of a process, either that it is running, or that it exited under some condition.
+pub enum ProcessStatus {
+ /// The process is running and therefore has no information about its result.
+ Running,
+ /// The process has exited with a successful code.
+ Success,
+ /// The process failed with the given exit code.
+ Fail(i32),
+ /// The process was terminated with the given signal code.
+ Signal(i32),
+}
+
+/// Creates, owns, and handles messages from a plugin process.
+///
+/// A plugin process has control over a single VM and a fixed number of VCPUs via a set of pipes & unix
+/// domain socket connections and a protocol defined in `protos::plugin`. The plugin process is run
+/// in an unprivileged manner as a child process spawned via a path to a arbitrary executable.
+pub struct Process {
+ started: bool,
+ plugin_pid: pid_t,
+ request_sockets: Vec<UnixDatagram>,
+ objects: HashMap<u32, PluginObject>,
+ shared_vcpu_state: Arc<RwLock<SharedVcpuState>>,
+ per_vcpu_states: Vec<Arc<Mutex<PerVcpuState>>>,
+
+ // Resource to sent to plugin
+ kill_evt: EventFd,
+ vcpu_pipes: Vec<VcpuPipe>,
+
+ // Socket Transmission
+ request_buffer: Vec<u8>,
+ response_buffer: Vec<u8>,
+}
+
+impl Process {
+ /// Creates a new plugin process for the given number of vcpus and VM.
+ ///
+ /// This will immediately spawn the plugin process and wait for the child to signal that it is
+ /// ready to start. This call may block indefinitely.
+ ///
+ /// Set the `jail` argument to spawn the plugin process within the preconfigured jail.
+ /// Due to an API limitation in libminijail necessitating that this function set an environment
+ /// variable, this function is not thread-safe.
+ pub fn new(
+ cpu_count: u32,
+ cmd: &Path,
+ args: &[&str],
+ jail: Option<Minijail>,
+ ) -> Result<Process> {
+ let (request_socket, child_socket) =
+ new_seqpacket_pair().map_err(Error::CreateMainSocket)?;
+
+ let mut vcpu_pipes: Vec<VcpuPipe> = Vec::with_capacity(cpu_count as usize);
+ for _ in 0..cpu_count {
+ vcpu_pipes.push(new_pipe_pair().map_err(Error::CreateVcpuSocket)?);
+ }
+ let mut per_vcpu_states: Vec<Arc<Mutex<PerVcpuState>>> =
+ Vec::with_capacity(cpu_count as usize);
+ // TODO(zachr): replace with `resize_default` when that stabilizes. Using a plain `resize`
+ // is incorrect because each element in the `Vec` will contain a shared reference to the
+ // same `PerVcpuState` instance. This happens because `resize` fills new slots using clones
+ // of the instance given to `resize`.
+ for _ in 0..cpu_count {
+ per_vcpu_states.push(Default::default());
+ }
+
+ 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)
+ .map_err(Error::PluginRunJail)?
+ }
+ None => Command::new(cmd)
+ .args(args)
+ .env("CROSVM_SOCKET", child_socket.as_raw_fd().to_string())
+ .spawn()
+ .map_err(Error::PluginSpawn)?
+ .id() as pid_t,
+ };
+
+ Ok(Process {
+ started: false,
+ plugin_pid,
+ request_sockets: vec![request_socket],
+ objects: Default::default(),
+ shared_vcpu_state: Default::default(),
+ per_vcpu_states,
+ kill_evt: EventFd::new().map_err(Error::CreateEventFd)?,
+ vcpu_pipes,
+ request_buffer: vec![0; MAX_DATAGRAM_SIZE],
+ response_buffer: Vec::new(),
+ })
+ }
+
+ /// Creates a VCPU plugin connection object, used by a VCPU run loop to communicate with the
+ /// plugin process.
+ ///
+ /// While each invocation of `create_vcpu` with the given `cpu_id` will return a unique
+ /// `PluginVcpu` object, the underlying resources are shared by each `PluginVcpu` resulting from
+ /// the same `cpu_id`.
+ pub fn create_vcpu(&self, cpu_id: u32) -> Result<PluginVcpu> {
+ let vcpu_pipe_read = self.vcpu_pipes[cpu_id as usize]
+ .crosvm_read
+ .try_clone()
+ .map_err(Error::CloneVcpuPipe)?;
+ let vcpu_pipe_write = self.vcpu_pipes[cpu_id as usize]
+ .crosvm_write
+ .try_clone()
+ .map_err(Error::CloneVcpuPipe)?;
+ Ok(PluginVcpu::new(
+ self.shared_vcpu_state.clone(),
+ self.per_vcpu_states[cpu_id as usize].clone(),
+ vcpu_pipe_read,
+ vcpu_pipe_write,
+ ))
+ }
+
+ /// Returns if the plugin process indicated the VM was ready to start.
+ pub fn is_started(&self) -> bool {
+ self.started
+ }
+
+ /// Returns the process ID of the plugin process.
+ pub fn pid(&self) -> pid_t {
+ self.plugin_pid
+ }
+
+ /// Returns a slice of each socket that should be polled.
+ ///
+ /// If any socket in this slice becomes readable, `handle_socket` should be called with the
+ /// index of that socket. If any socket becomes closed, its index should be passed to
+ /// `drop_sockets`.
+ pub fn sockets(&self) -> &[UnixDatagram] {
+ &self.request_sockets
+ }
+
+ /// Drops the each socket identified by its index in the slice returned by `sockets`.
+ ///
+ /// The given `socket_idxs` slice will be modified in an arbitrary way for efficient removal of
+ /// the sockets from internal data structures.
+ pub fn drop_sockets(&mut self, socket_idxs: &mut [usize]) {
+ // Takes a mutable slice so that the indices can be sorted for efficient removal in
+ // request_sockets..
+ socket_idxs.sort_unstable_by(|a, b| b.cmp(a));
+ let old_len = self.request_sockets.len();
+ for &socket_index in socket_idxs.iter() {
+ // swap_remove changes the index of the last element, but we already know that one
+ // doesn't need to be removed because we are removing sockets in descending order thanks
+ // to the above sort.
+ self.request_sockets.swap_remove(socket_index);
+ }
+ assert_eq!(old_len - socket_idxs.len(), self.request_sockets.len());
+ }
+
+ /// Gently requests that the plugin process exit cleanly, and ends handling of all VCPU
+ /// connections.
+ ///
+ /// The plugin process can ignore the given signal, and so some timeout should be used before
+ /// forcefully terminating the process.
+ ///
+ /// Any blocked VCPU connections will get interrupted so that the VCPU threads can exit cleanly.
+ /// Any subsequent attempt to use the VCPU connections will fail.
+ pub fn signal_kill(&mut self) -> SysResult<()> {
+ self.kill_evt.write(1)?;
+ // Normally we'd get any blocked recv() calls in the VCPU threads
+ // to unblock by calling shutdown(). However, we're using pipes
+ // (for improved performance), and pipes don't have shutdown so
+ // instead we'll write a shutdown message to ourselves using the
+ // the writable side of the pipe (normally used by the plugin).
+ for pipe in self.vcpu_pipes.iter_mut() {
+ let mut shutdown_request = VcpuRequest::new();
+ shutdown_request.set_shutdown(VcpuRequest_Shutdown::new());
+ let mut buffer = Vec::new();
+ shutdown_request
+ .write_to_vec(&mut buffer)
+ .map_err(proto_to_sys_err)?;
+ pipe.plugin_write
+ .write(&buffer[..])
+ .map_err(io_to_sys_err)?;
+ }
+ Ok(())
+ }
+
+ /// Waits without blocking for the plugin process to exit and returns the status.
+ pub fn try_wait(&mut self) -> SysResult<ProcessStatus> {
+ let mut status = 0;
+ // Safe because waitpid is given a valid pointer of correct size and mutability, and the
+ // return value is checked.
+ let ret = unsafe { waitpid(self.plugin_pid, &mut status, WNOHANG) };
+ match ret {
+ -1 => Err(SysError::last()),
+ 0 => Ok(ProcessStatus::Running),
+ _ => {
+ // Trivially safe
+ if unsafe { WIFEXITED(status) } {
+ match unsafe { WEXITSTATUS(status) } {
+ // Trivially safe
+ 0 => Ok(ProcessStatus::Success),
+ code => Ok(ProcessStatus::Fail(code)),
+ }
+ } else {
+ // Plugin terminated but has no exit status, so it must have been signaled.
+ Ok(ProcessStatus::Signal(unsafe { WTERMSIG(status) })) // Trivially safe
+ }
+ }
+ }
+ }
+
+ fn handle_io_event(
+ entry: VacantEntry<u32, PluginObject>,
+ vm: &mut Vm,
+ io_event: &MainRequest_Create_IoEvent,
+ ) -> SysResult<RawFd> {
+ let evt = EventFd::new()?;
+ let addr = match io_event.space {
+ AddressSpace::IOPORT => IoeventAddress::Pio(io_event.address),
+ AddressSpace::MMIO => IoeventAddress::Mmio(io_event.address),
+ };
+ match io_event.length {
+ 0 => vm.register_ioevent(&evt, addr, Datamatch::AnyLength)?,
+ 1 => vm.register_ioevent(&evt, addr, Datamatch::U8(Some(io_event.datamatch as u8)))?,
+ 2 => {
+ vm.register_ioevent(&evt, addr, Datamatch::U16(Some(io_event.datamatch as u16)))?
+ }
+ 4 => {
+ vm.register_ioevent(&evt, addr, Datamatch::U32(Some(io_event.datamatch as u32)))?
+ }
+ 8 => {
+ vm.register_ioevent(&evt, addr, Datamatch::U64(Some(io_event.datamatch as u64)))?
+ }
+ _ => return Err(SysError::new(EINVAL)),
+ };
+
+ let fd = evt.as_raw_fd();
+ entry.insert(PluginObject::IoEvent {
+ evt,
+ addr,
+ length: io_event.length,
+ datamatch: io_event.datamatch,
+ });
+ Ok(fd)
+ }
+
+ fn handle_memory(
+ entry: VacantEntry<u32, PluginObject>,
+ vm: &mut Vm,
+ memfd: File,
+ offset: u64,
+ start: u64,
+ length: u64,
+ read_only: bool,
+ dirty_log: bool,
+ ) -> SysResult<()> {
+ let shm = SharedMemory::from_raw_fd(memfd)?;
+ // Checking the seals ensures the plugin process won't shrink the mmapped file, causing us
+ // to SIGBUS in the future.
+ let seals = shm.get_seals()?;
+ if !seals.shrink_seal() {
+ return Err(SysError::new(EPERM));
+ }
+ // Check to make sure we don't mmap areas beyond the end of the memfd.
+ match length.checked_add(offset) {
+ Some(end) if end > shm.size() => return Err(SysError::new(EINVAL)),
+ None => return Err(SysError::new(EOVERFLOW)),
+ _ => {}
+ }
+ let mem = MemoryMapping::from_fd_offset(&shm, length as usize, offset as usize)
+ .map_err(mmap_to_sys_err)?;
+ let slot = vm.add_device_memory(GuestAddress(start), mem, read_only, dirty_log)?;
+ entry.insert(PluginObject::Memory {
+ slot,
+ length: length as usize,
+ });
+ Ok(())
+ }
+
+ fn handle_reserve_range(&mut self, reserve_range: &MainRequest_ReserveRange) -> SysResult<()> {
+ match self.shared_vcpu_state.write() {
+ Ok(mut lock) => {
+ let space = match reserve_range.space {
+ AddressSpace::IOPORT => IoSpace::Ioport,
+ AddressSpace::MMIO => IoSpace::Mmio,
+ };
+ match reserve_range.length {
+ 0 => lock.unreserve_range(space, reserve_range.start),
+ _ => lock.reserve_range(space, reserve_range.start, reserve_range.length),
+ }
+ }
+ Err(_) => Err(SysError::new(EDEADLK)),
+ }
+ }
+
+ fn handle_set_irq_routing(
+ vm: &mut Vm,
+ irq_routing: &MainRequest_SetIrqRouting,
+ ) -> SysResult<()> {
+ let mut routes = Vec::with_capacity(irq_routing.routes.len());
+ for route in &irq_routing.routes {
+ routes.push(IrqRoute {
+ gsi: route.irq_id,
+ source: if route.has_irqchip() {
+ let irqchip = route.get_irqchip();
+ IrqSource::Irqchip {
+ chip: irqchip.irqchip,
+ pin: irqchip.pin,
+ }
+ } else if route.has_msi() {
+ let msi = route.get_msi();
+ IrqSource::Msi {
+ address: msi.address,
+ data: msi.data,
+ }
+ } else {
+ // Because route is a oneof field in the proto definition, this should
+ // only happen if a new variant gets added without updating this chained
+ // if block.
+ return Err(SysError::new(EINVAL));
+ },
+ });
+ }
+ vm.set_gsi_routing(&routes[..])
+ }
+
+ fn handle_pause_vcpus(&self, vcpu_handles: &[JoinHandle<()>], cpu_mask: u64, user_data: u64) {
+ for (cpu_id, (handle, per_cpu_state)) in
+ vcpu_handles.iter().zip(&self.per_vcpu_states).enumerate()
+ {
+ if cpu_mask & (1 << cpu_id) != 0 {
+ per_cpu_state.lock().request_pause(user_data);
+ if let Err(e) = handle.kill(SIGRTMIN() + 0) {
+ error!("failed to interrupt vcpu {}: {}", cpu_id, e);
+ }
+ }
+ }
+ }
+
+ fn handle_get_net_config(
+ tap: &net_util::Tap,
+ config: &mut MainResponse_GetNetConfig,
+ ) -> SysResult<()> {
+ // Log any NetError so that the cause can be found later, but extract and return the
+ // underlying errno for the client as well.
+ fn map_net_error(s: &str, e: NetError) -> SysError {
+ error!("failed to get {}: {}", s, e);
+ e.sys_error()
+ }
+
+ let ip_addr = tap.ip_addr().map_err(|e| map_net_error("IP address", e))?;
+ config.set_host_ipv4_address(u32::from(ip_addr));
+
+ let netmask = tap.netmask().map_err(|e| map_net_error("netmask", e))?;
+ config.set_netmask(u32::from(netmask));
+
+ let result_mac_addr = config.mut_host_mac_address();
+ let mac_addr_octets = tap
+ .mac_address()
+ .map_err(|e| map_net_error("mac address", e))?
+ .octets();
+ result_mac_addr.resize(mac_addr_octets.len(), 0);
+ result_mac_addr.clone_from_slice(&mac_addr_octets);
+
+ Ok(())
+ }
+
+ /// Handles a request on a readable socket identified by its index in the slice returned by
+ /// `sockets`.
+ ///
+ /// The `vm` is used to service request that affect the VM. The `vcpu_handles` slice is used to
+ /// interrupt a VCPU thread currently running in the VM if the socket request it.
+ pub fn handle_socket(
+ &mut self,
+ index: usize,
+ kvm: &Kvm,
+ vm: &mut Vm,
+ vcpu_handles: &[JoinHandle<()>],
+ taps: &[Tap],
+ ) -> Result<()> {
+ let (msg_size, request_file) = self.request_sockets[index]
+ .recv_with_fd(&mut self.request_buffer)
+ .map_err(Error::PluginSocketRecv)?;
+
+ if msg_size == 0 {
+ return Err(Error::PluginSocketHup);
+ }
+
+ let request = protobuf::parse_from_bytes::<MainRequest>(&self.request_buffer[..msg_size])
+ .map_err(Error::DecodeRequest)?;
+
+ let mut response_files = Vec::new();
+ let mut response_fds = Vec::new();
+ let mut response = MainResponse::new();
+ let res = if request.has_create() {
+ response.mut_create();
+ let create = request.get_create();
+ match self.objects.entry(create.id) {
+ Entry::Vacant(entry) => {
+ if create.has_io_event() {
+ match Self::handle_io_event(entry, vm, create.get_io_event()) {
+ Ok(fd) => {
+ response_fds.push(fd);
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ } else if create.has_memory() {
+ let memory = create.get_memory();
+ match request_file {
+ Some(memfd) => Self::handle_memory(
+ entry,
+ vm,
+ memfd,
+ memory.offset,
+ memory.start,
+ memory.length,
+ memory.read_only,
+ memory.dirty_log,
+ ),
+ None => Err(SysError::new(EBADF)),
+ }
+ } else if create.has_irq_event() {
+ let irq_event = create.get_irq_event();
+ match (EventFd::new(), EventFd::new()) {
+ (Ok(evt), Ok(resample_evt)) => match vm.register_irqfd_resample(
+ &evt,
+ &resample_evt,
+ irq_event.irq_id,
+ ) {
+ Ok(()) => {
+ response_fds.push(evt.as_raw_fd());
+ response_fds.push(resample_evt.as_raw_fd());
+ response_files.push(downcast_file(resample_evt));
+ entry.insert(PluginObject::IrqEvent {
+ irq_id: irq_event.irq_id,
+ evt,
+ });
+ Ok(())
+ }
+ Err(e) => Err(e),
+ },
+ (Err(e), _) | (_, Err(e)) => Err(e),
+ }
+ } else {
+ Err(SysError::new(ENOTTY))
+ }
+ }
+ Entry::Occupied(_) => Err(SysError::new(EEXIST)),
+ }
+ } else if request.has_destroy() {
+ response.mut_destroy();
+ match self.objects.entry(request.get_destroy().id) {
+ Entry::Occupied(entry) => entry.remove().destroy(vm),
+ Entry::Vacant(_) => Err(SysError::new(ENOENT)),
+ }
+ } else if request.has_new_connection() {
+ response.mut_new_connection();
+ match new_seqpacket_pair() {
+ Ok((request_socket, child_socket)) => {
+ self.request_sockets.push(request_socket);
+ response_fds.push(child_socket.as_raw_fd());
+ response_files.push(downcast_file(child_socket));
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ } else if request.has_get_shutdown_eventfd() {
+ response.mut_get_shutdown_eventfd();
+ response_fds.push(self.kill_evt.as_raw_fd());
+ Ok(())
+ } else if request.has_check_extension() {
+ // Safe because the Cap enum is not read by the check_extension method. In that method,
+ // cap is cast back to an integer and fed to an ioctl. If the extension name is actually
+ // invalid, the kernel will safely reject the extension under the assumption that the
+ // capability is legitimately unsupported.
+ let cap = unsafe { transmute(request.get_check_extension().extension) };
+ response.mut_check_extension().has_extension = vm.check_extension(cap);
+ Ok(())
+ } else if request.has_reserve_range() {
+ response.mut_reserve_range();
+ self.handle_reserve_range(request.get_reserve_range())
+ } else if request.has_set_irq() {
+ response.mut_set_irq();
+ let irq = request.get_set_irq();
+ vm.set_irq_line(irq.irq_id, irq.active)
+ } else if request.has_set_irq_routing() {
+ response.mut_set_irq_routing();
+ Self::handle_set_irq_routing(vm, request.get_set_irq_routing())
+ } else if request.has_get_state() {
+ let response_state = response.mut_get_state();
+ match get_vm_state(vm, request.get_get_state().set) {
+ Ok(state) => {
+ response_state.state = state;
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ } else if request.has_set_state() {
+ response.mut_set_state();
+ let set_state = request.get_set_state();
+ set_vm_state(vm, set_state.set, set_state.get_state())
+ } else if request.has_set_identity_map_addr() {
+ response.mut_set_identity_map_addr();
+ let addr = request.get_set_identity_map_addr().address;
+ vm.set_identity_map_addr(GuestAddress(addr as u64))
+ } else if request.has_pause_vcpus() {
+ response.mut_pause_vcpus();
+ let pause_vcpus = request.get_pause_vcpus();
+ self.handle_pause_vcpus(vcpu_handles, pause_vcpus.cpu_mask, pause_vcpus.user);
+ Ok(())
+ } 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());
+ }
+ Ok(())
+ } else if request.has_start() {
+ response.mut_start();
+ if self.started {
+ Err(SysError::new(EINVAL))
+ } else {
+ self.started = true;
+ Ok(())
+ }
+ } else if request.has_get_net_config() {
+ match taps.first() {
+ Some(tap) => {
+ match Self::handle_get_net_config(tap, response.mut_get_net_config()) {
+ Ok(_) => {
+ response_fds.push(tap.as_raw_fd());
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ }
+ None => Err(SysError::new(ENODATA)),
+ }
+ } else if request.has_dirty_log() {
+ let dirty_log_response = response.mut_dirty_log();
+ match self.objects.get(&request.get_dirty_log().id) {
+ Some(&PluginObject::Memory { slot, length }) => {
+ let dirty_log = dirty_log_response.mut_bitmap();
+ dirty_log.resize(dirty_log_bitmap_size(length), 0);
+ vm.get_dirty_log(slot, &mut dirty_log[..])
+ }
+ _ => Err(SysError::new(ENOENT)),
+ }
+ } else if request.has_get_supported_cpuid() {
+ let cpuid_response = &mut response.mut_get_supported_cpuid().entries;
+ match kvm.get_supported_cpuid() {
+ Ok(mut cpuid) => {
+ for entry in cpuid.mut_entries_slice() {
+ cpuid_response.push(cpuid_kvm_to_proto(entry));
+ }
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ } else if request.has_get_emulated_cpuid() {
+ let cpuid_response = &mut response.mut_get_emulated_cpuid().entries;
+ match kvm.get_emulated_cpuid() {
+ Ok(mut cpuid) => {
+ for entry in cpuid.mut_entries_slice() {
+ cpuid_response.push(cpuid_kvm_to_proto(entry));
+ }
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ } else if request.has_get_msr_index_list() {
+ let msr_list_response = &mut response.mut_get_msr_index_list().indices;
+ match kvm.get_msr_index_list() {
+ Ok(indices) => {
+ for entry in indices {
+ msr_list_response.push(entry);
+ }
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ } else {
+ Err(SysError::new(ENOTTY))
+ };
+
+ if let Err(e) = res {
+ response.errno = e.errno();
+ }
+
+ self.response_buffer.clear();
+ response
+ .write_to_vec(&mut self.response_buffer)
+ .map_err(Error::EncodeResponse)?;
+ assert_ne!(self.response_buffer.len(), 0);
+ self.request_sockets[index]
+ .send_with_fds(&self.response_buffer[..], &response_fds)
+ .map_err(Error::PluginSocketSend)?;
+
+ Ok(())
+ }
+}
+
+impl Drop for Process {
+ fn drop(&mut self) {
+ // Ignore the result because there is nothing we can do about it.
+ if let Err(e) = self.signal_kill() {
+ error!("failed to signal kill event for plugin: {}", e);
+ }
+ }
+}
diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs
new file mode 100644
index 0000000..c9d811a
--- /dev/null
+++ b/src/plugin/vcpu.rs
@@ -0,0 +1,608 @@
+// Copyright 2018 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::alloc::Layout;
+use std::cell::{Cell, RefCell};
+use std::cmp::min;
+use std::cmp::{self, Ord, PartialEq, PartialOrd};
+use std::collections::btree_set::BTreeSet;
+use std::io::{Read, Write};
+use std::mem;
+use std::sync::{Arc, RwLock};
+
+use libc::{EINVAL, ENOENT, ENOTTY, EPERM, EPIPE, EPROTO};
+
+use protobuf;
+use protobuf::Message;
+
+use assertions::const_assert;
+use data_model::DataInit;
+use kvm::{CpuId, Vcpu};
+use kvm_sys::{
+ kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_msrs, kvm_regs,
+ kvm_sregs, kvm_vcpu_events, kvm_xcrs, KVM_CPUID_FLAG_SIGNIFCANT_INDEX,
+};
+use protos::plugin::*;
+use sync::Mutex;
+use sys_util::{error, LayoutAllocation};
+
+use super::*;
+
+/// Identifier for an address space in the VM.
+#[derive(Copy, Clone)]
+pub enum IoSpace {
+ Ioport,
+ Mmio,
+}
+
+#[derive(Debug, Copy, Clone)]
+struct Range(u64, u64);
+
+impl Eq for Range {}
+
+impl PartialEq for Range {
+ fn eq(&self, other: &Range) -> bool {
+ self.0 == other.0
+ }
+}
+
+impl Ord for Range {
+ fn cmp(&self, other: &Range) -> cmp::Ordering {
+ self.0.cmp(&other.0)
+ }
+}
+
+impl PartialOrd for Range {
+ fn partial_cmp(&self, other: &Range) -> Option<cmp::Ordering> {
+ self.0.partial_cmp(&other.0)
+ }
+}
+
+// Wrapper types to make the kvm register structs DataInit
+#[derive(Copy, Clone)]
+struct VcpuRegs(kvm_regs);
+unsafe impl DataInit for VcpuRegs {}
+#[derive(Copy, Clone)]
+struct VcpuSregs(kvm_sregs);
+unsafe impl DataInit for VcpuSregs {}
+#[derive(Copy, Clone)]
+struct VcpuFpu(kvm_fpu);
+unsafe impl DataInit for VcpuFpu {}
+#[derive(Copy, Clone)]
+struct VcpuDebugregs(kvm_debugregs);
+unsafe impl DataInit for VcpuDebugregs {}
+#[derive(Copy, Clone)]
+struct VcpuXcregs(kvm_xcrs);
+unsafe impl DataInit for VcpuXcregs {}
+#[derive(Copy, Clone)]
+struct VcpuLapicState(kvm_lapic_state);
+unsafe impl DataInit for VcpuLapicState {}
+#[derive(Copy, Clone)]
+struct VcpuMpState(kvm_mp_state);
+unsafe impl DataInit for VcpuMpState {}
+#[derive(Copy, Clone)]
+struct VcpuEvents(kvm_vcpu_events);
+unsafe impl DataInit for VcpuEvents {}
+
+fn get_vcpu_state(vcpu: &Vcpu, state_set: VcpuRequest_StateSet) -> SysResult<Vec<u8>> {
+ Ok(match state_set {
+ VcpuRequest_StateSet::REGS => VcpuRegs(vcpu.get_regs()?).as_slice().to_vec(),
+ VcpuRequest_StateSet::SREGS => VcpuSregs(vcpu.get_sregs()?).as_slice().to_vec(),
+ VcpuRequest_StateSet::FPU => VcpuFpu(vcpu.get_fpu()?).as_slice().to_vec(),
+ VcpuRequest_StateSet::DEBUGREGS => VcpuDebugregs(vcpu.get_debugregs()?).as_slice().to_vec(),
+ VcpuRequest_StateSet::XCREGS => VcpuXcregs(vcpu.get_xcrs()?).as_slice().to_vec(),
+ VcpuRequest_StateSet::LAPIC => VcpuLapicState(vcpu.get_lapic()?).as_slice().to_vec(),
+ VcpuRequest_StateSet::MP => VcpuMpState(vcpu.get_mp_state()?).as_slice().to_vec(),
+ VcpuRequest_StateSet::EVENTS => VcpuEvents(vcpu.get_vcpu_events()?).as_slice().to_vec(),
+ })
+}
+
+fn set_vcpu_state(vcpu: &Vcpu, state_set: VcpuRequest_StateSet, state: &[u8]) -> SysResult<()> {
+ match state_set {
+ VcpuRequest_StateSet::REGS => {
+ vcpu.set_regs(&VcpuRegs::from_slice(state).ok_or(SysError::new(EINVAL))?.0)
+ }
+ VcpuRequest_StateSet::SREGS => {
+ vcpu.set_sregs(&VcpuSregs::from_slice(state).ok_or(SysError::new(EINVAL))?.0)
+ }
+ VcpuRequest_StateSet::FPU => {
+ vcpu.set_fpu(&VcpuFpu::from_slice(state).ok_or(SysError::new(EINVAL))?.0)
+ }
+ VcpuRequest_StateSet::DEBUGREGS => vcpu.set_debugregs(
+ &VcpuDebugregs::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ VcpuRequest_StateSet::XCREGS => vcpu.set_xcrs(
+ &VcpuXcregs::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ VcpuRequest_StateSet::LAPIC => vcpu.set_lapic(
+ &VcpuLapicState::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ VcpuRequest_StateSet::MP => vcpu.set_mp_state(
+ &VcpuMpState::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ VcpuRequest_StateSet::EVENTS => vcpu.set_vcpu_events(
+ &VcpuEvents::from_slice(state)
+ .ok_or(SysError::new(EINVAL))?
+ .0,
+ ),
+ }
+}
+
+/// State shared by every VCPU, grouped together to make edits to the state coherent across VCPUs.
+#[derive(Default)]
+pub struct SharedVcpuState {
+ ioport_regions: BTreeSet<Range>,
+ mmio_regions: BTreeSet<Range>,
+}
+
+impl SharedVcpuState {
+ /// Reserves the given range for handling by the plugin process.
+ ///
+ /// This will reject any reservation that overlaps with an existing reservation.
+ pub fn reserve_range(&mut self, space: IoSpace, start: u64, length: u64) -> SysResult<()> {
+ if length == 0 {
+ return Err(SysError::new(EINVAL));
+ }
+
+ // Reject all cases where this reservation is part of another reservation.
+ if self.is_reserved(space, start) {
+ return Err(SysError::new(EPERM));
+ }
+
+ let last_address = match start.checked_add(length) {
+ Some(end) => end - 1,
+ None => return Err(SysError::new(EINVAL)),
+ };
+
+ let space = match space {
+ IoSpace::Ioport => &mut self.ioport_regions,
+ IoSpace::Mmio => &mut self.mmio_regions,
+ };
+
+ match space.range(..Range(last_address, 0)).next_back().cloned() {
+ Some(Range(existing_start, _)) if existing_start >= start => Err(SysError::new(EPERM)),
+ _ => {
+ space.insert(Range(start, length));
+ Ok(())
+ }
+ }
+ }
+
+ //// Releases a reservation previously made at `start` in the given `space`.
+ pub fn unreserve_range(&mut self, space: IoSpace, start: u64) -> SysResult<()> {
+ let range = Range(start, 0);
+ let space = match space {
+ IoSpace::Ioport => &mut self.ioport_regions,
+ IoSpace::Mmio => &mut self.mmio_regions,
+ };
+ if space.remove(&range) {
+ Ok(())
+ } else {
+ Err(SysError::new(ENOENT))
+ }
+ }
+
+ fn is_reserved(&self, space: IoSpace, addr: u64) -> bool {
+ if let Some(Range(start, len)) = self.first_before(space, addr) {
+ let offset = addr - start;
+ if offset < len {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn first_before(&self, io_space: IoSpace, addr: u64) -> Option<Range> {
+ let space = match io_space {
+ IoSpace::Ioport => &self.ioport_regions,
+ IoSpace::Mmio => &self.mmio_regions,
+ };
+
+ match addr.checked_add(1) {
+ Some(next_addr) => space.range(..Range(next_addr, 0)).next_back().cloned(),
+ None => None,
+ }
+ }
+}
+
+/// State specific to a VCPU, grouped so that each `PluginVcpu` object will share a canonical
+/// version.
+#[derive(Default)]
+pub struct PerVcpuState {
+ pause_request: Option<u64>,
+}
+
+impl PerVcpuState {
+ /// Indicates that a VCPU should wait until the plugin process resumes the VCPU.
+ ///
+ /// This method will not cause a VCPU to pause immediately. Instead, the VCPU thread will
+ /// continue running until a interrupted, at which point it will check for a pending pause. If
+ /// there is another call to `request_pause` for this VCPU before that happens, the last pause
+ /// request's `data` will be overwritten with the most recent `data.
+ ///
+ /// To get an immediate pause after calling `request_pause`, send a signal (with a registered
+ /// handler) to the thread handling the VCPU corresponding to this state. This should interrupt
+ /// the running VCPU, which should check for a pause with `PluginVcpu::pre_run`.
+ pub fn request_pause(&mut self, data: u64) {
+ self.pause_request = Some(data);
+ }
+}
+
+enum VcpuRunData<'a> {
+ Read(&'a mut [u8]),
+ Write(&'a [u8]),
+}
+
+impl<'a> VcpuRunData<'a> {
+ fn is_write(&self) -> bool {
+ match self {
+ VcpuRunData::Write(_) => true,
+ _ => false,
+ }
+ }
+
+ fn as_slice(&self) -> &[u8] {
+ match self {
+ VcpuRunData::Read(s) => s,
+ VcpuRunData::Write(s) => s,
+ }
+ }
+
+ fn copy_from_slice(&mut self, data: &[u8]) {
+ if let VcpuRunData::Read(s) = self {
+ let copy_size = min(s.len(), data.len());
+ s.copy_from_slice(&data[..copy_size]);
+ }
+ }
+}
+
+/// State object for a VCPU's connection with the plugin process.
+///
+/// This is used by a VCPU thread to allow the plugin process to handle vmexits. Each method may
+/// block indefinitely while the plugin process is handling requests. In order to cleanly shutdown
+/// during these blocking calls, the `connection` socket should be shutdown. This will end the
+/// blocking calls,
+pub struct PluginVcpu {
+ shared_vcpu_state: Arc<RwLock<SharedVcpuState>>,
+ per_vcpu_state: Arc<Mutex<PerVcpuState>>,
+ read_pipe: File,
+ write_pipe: File,
+ wait_reason: Cell<Option<VcpuResponse_Wait>>,
+ request_buffer: RefCell<Vec<u8>>,
+ response_buffer: RefCell<Vec<u8>>,
+}
+
+impl PluginVcpu {
+ /// Creates the plugin state and connection container for a VCPU thread.
+ pub fn new(
+ shared_vcpu_state: Arc<RwLock<SharedVcpuState>>,
+ per_vcpu_state: Arc<Mutex<PerVcpuState>>,
+ read_pipe: File,
+ write_pipe: File,
+ ) -> PluginVcpu {
+ PluginVcpu {
+ shared_vcpu_state,
+ per_vcpu_state,
+ read_pipe,
+ write_pipe,
+ wait_reason: Default::default(),
+ request_buffer: Default::default(),
+ response_buffer: Default::default(),
+ }
+ }
+
+ /// Tells the plugin process to initialize this VCPU.
+ ///
+ /// This should be called for each VCPU before the first run of any of the VCPUs in the VM.
+ pub fn init(&self, vcpu: &Vcpu) -> SysResult<()> {
+ let mut wait_reason = VcpuResponse_Wait::new();
+ wait_reason.mut_init();
+ self.wait_reason.set(Some(wait_reason));
+ self.handle_until_resume(vcpu)?;
+ Ok(())
+ }
+
+ /// The VCPU thread should call this before rerunning a VM in order to handle pending requests
+ /// to this VCPU.
+ pub fn pre_run(&self, vcpu: &Vcpu) -> SysResult<()> {
+ let request = {
+ let mut lock = self.per_vcpu_state.lock();
+ lock.pause_request.take()
+ };
+
+ if let Some(user_data) = request {
+ let mut wait_reason = VcpuResponse_Wait::new();
+ wait_reason.mut_user().user = user_data;
+ self.wait_reason.set(Some(wait_reason));
+ self.handle_until_resume(vcpu)?;
+ }
+ Ok(())
+ }
+
+ fn process(&self, io_space: IoSpace, addr: u64, mut data: VcpuRunData, vcpu: &Vcpu) -> bool {
+ let vcpu_state_lock = match self.shared_vcpu_state.read() {
+ Ok(l) => l,
+ Err(e) => {
+ error!("error read locking shared cpu state: {}", e);
+ return false;
+ }
+ };
+
+ let first_before_addr = vcpu_state_lock.first_before(io_space, addr);
+ // Drops the read lock as soon as possible, to prevent holding lock while blocked in
+ // `handle_until_resume`.
+ drop(vcpu_state_lock);
+
+ match first_before_addr {
+ Some(Range(start, len)) => {
+ let offset = addr - start;
+ if offset >= len {
+ return false;
+ }
+
+ let mut wait_reason = VcpuResponse_Wait::new();
+ let io = wait_reason.mut_io();
+ io.space = match io_space {
+ IoSpace::Ioport => AddressSpace::IOPORT,
+ IoSpace::Mmio => AddressSpace::MMIO,
+ };
+ io.address = addr;
+ io.is_write = data.is_write();
+ io.data = data.as_slice().to_vec();
+
+ self.wait_reason.set(Some(wait_reason));
+ match self.handle_until_resume(vcpu) {
+ Ok(resume_data) => data.copy_from_slice(&resume_data),
+ Err(e) if e.errno() == EPIPE => {}
+ Err(e) => error!("failed to process vcpu requests: {}", e),
+ }
+ true
+ }
+ None => false,
+ }
+ }
+
+ /// Has the plugin process handle a IO port read.
+ pub fn io_read(&self, addr: u64, data: &mut [u8], vcpu: &Vcpu) -> bool {
+ self.process(IoSpace::Ioport, addr, VcpuRunData::Read(data), vcpu)
+ }
+
+ /// Has the plugin process handle a IO port write.
+ pub fn io_write(&self, addr: u64, data: &[u8], vcpu: &Vcpu) -> bool {
+ self.process(IoSpace::Ioport, addr, VcpuRunData::Write(data), vcpu)
+ }
+
+ /// Has the plugin process handle a MMIO read.
+ pub fn mmio_read(&self, addr: u64, data: &mut [u8], vcpu: &Vcpu) -> bool {
+ self.process(IoSpace::Mmio, addr, VcpuRunData::Read(data), vcpu)
+ }
+
+ /// Has the plugin process handle a MMIO write.
+ pub fn mmio_write(&self, addr: u64, data: &[u8], vcpu: &Vcpu) -> bool {
+ self.process(IoSpace::Mmio, addr, VcpuRunData::Write(data), vcpu)
+ }
+
+ fn handle_request(&self, vcpu: &Vcpu) -> SysResult<Option<Vec<u8>>> {
+ let mut wait_reason = self.wait_reason.take();
+ let mut do_recv = true;
+ let mut resume_data = None;
+ let mut response = VcpuResponse::new();
+
+ // Typically a response is sent for every request received. The odd (yet common)
+ // case is when a resume request is received. This function will skip sending
+ // a resume reply, and instead we'll go run the VM and then later reply with a wait
+ // response message. This code block handles checking if a wait reason is pending (where
+ // the wait reason isn't the first-time init [first time init needs to first
+ // receive a wait request from the plugin]) to send it as a reply before doing a recv()
+ // for the next request. Note that if a wait reply is pending then this function
+ // will send the reply and do nothing else--the expectation is that handle_until_resume()
+ // is the only caller of this function, so the function will immediately get called again
+ // and this second call will no longer see a pending wait reason and do a recv() for the
+ // next message.
+ if let Some(reason) = wait_reason {
+ if reason.has_init() {
+ wait_reason = Some(reason);
+ } else {
+ response.set_wait(reason);
+ do_recv = false;
+ wait_reason = None;
+ }
+ }
+
+ if do_recv {
+ let mut request_buffer = self.request_buffer.borrow_mut();
+ request_buffer.resize(MAX_VCPU_DATAGRAM_SIZE, 0);
+
+ let mut read_pipe = &self.read_pipe;
+ let msg_size = read_pipe.read(&mut request_buffer).map_err(io_to_sys_err)?;
+
+ let mut request =
+ protobuf::parse_from_bytes::<VcpuRequest>(&request_buffer[..msg_size])
+ .map_err(proto_to_sys_err)?;
+
+ let res = if request.has_wait() {
+ match wait_reason {
+ Some(wait_reason) => {
+ response.set_wait(wait_reason);
+ Ok(())
+ }
+ None => Err(SysError::new(EPROTO)),
+ }
+ } else if wait_reason.is_some() {
+ // Any request other than getting the wait_reason while there is one pending is invalid.
+ self.wait_reason.set(wait_reason);
+ Err(SysError::new(EPROTO))
+ } else if request.has_resume() {
+ response.mut_resume();
+ resume_data = Some(request.take_resume().take_data());
+ Ok(())
+ } else if request.has_get_state() {
+ let response_state = response.mut_get_state();
+ match get_vcpu_state(vcpu, request.get_get_state().set) {
+ Ok(state) => {
+ response_state.state = state;
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ } else if request.has_set_state() {
+ response.mut_set_state();
+ let set_state = request.get_set_state();
+ set_vcpu_state(vcpu, set_state.set, set_state.get_state())
+ } else if request.has_get_msrs() {
+ let entry_data = &mut response.mut_get_msrs().entry_data;
+ let entry_indices = &request.get_get_msrs().entry_indices;
+ let mut msr_entries = Vec::with_capacity(entry_indices.len());
+ for &index in entry_indices {
+ msr_entries.push(kvm_msr_entry {
+ index,
+ ..Default::default()
+ });
+ }
+ match vcpu.get_msrs(&mut msr_entries) {
+ Ok(()) => {
+ for msr_entry in msr_entries {
+ entry_data.push(msr_entry.data);
+ }
+ Ok(())
+ }
+ Err(e) => Err(e),
+ }
+ } else if request.has_set_msrs() {
+ const SIZE_OF_MSRS: usize = mem::size_of::<kvm_msrs>();
+ const SIZE_OF_ENTRY: usize = mem::size_of::<kvm_msr_entry>();
+ const ALIGN_OF_MSRS: usize = mem::align_of::<kvm_msrs>();
+ const ALIGN_OF_ENTRY: usize = mem::align_of::<kvm_msr_entry>();
+ const_assert!(ALIGN_OF_MSRS >= ALIGN_OF_ENTRY);
+
+ response.mut_set_msrs();
+ let request_entries = &request.get_set_msrs().entries;
+
+ let size = SIZE_OF_MSRS + request_entries.len() * SIZE_OF_ENTRY;
+ let layout =
+ Layout::from_size_align(size, ALIGN_OF_MSRS).expect("impossible layout");
+ let mut allocation = LayoutAllocation::zeroed(layout);
+
+ // Safe to obtain an exclusive reference because there are no other
+ // references to the allocation yet and all-zero is a valid bit
+ // pattern.
+ let kvm_msrs = unsafe { allocation.as_mut::<kvm_msrs>() };
+
+ unsafe {
+ // Mapping the unsized array to a slice is unsafe becase the length isn't known.
+ // Providing the length used to create the struct guarantees the entire slice is
+ // valid.
+ let kvm_msr_entries: &mut [kvm_msr_entry] =
+ kvm_msrs.entries.as_mut_slice(request_entries.len());
+ for (msr_entry, entry) in kvm_msr_entries.iter_mut().zip(request_entries) {
+ msr_entry.index = entry.index;
+ msr_entry.data = entry.data;
+ }
+ }
+ kvm_msrs.nmsrs = request_entries.len() as u32;
+ vcpu.set_msrs(&kvm_msrs)
+ } else if request.has_set_cpuid() {
+ response.mut_set_cpuid();
+ let request_entries = &request.get_set_cpuid().entries;
+ let mut cpuid = CpuId::new(request_entries.len());
+ let cpuid_entries = cpuid.mut_entries_slice();
+ for (request_entry, cpuid_entry) in request_entries.iter().zip(cpuid_entries) {
+ cpuid_entry.function = request_entry.function;
+ if request_entry.has_index {
+ cpuid_entry.index = request_entry.index;
+ cpuid_entry.flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ }
+ cpuid_entry.eax = request_entry.eax;
+ cpuid_entry.ebx = request_entry.ebx;
+ cpuid_entry.ecx = request_entry.ecx;
+ cpuid_entry.edx = request_entry.edx;
+ }
+ vcpu.set_cpuid2(&cpuid)
+ } else if request.has_shutdown() {
+ return Err(SysError::new(EPIPE));
+ } else {
+ Err(SysError::new(ENOTTY))
+ };
+
+ if let Err(e) = res {
+ response.errno = e.errno();
+ }
+ }
+
+ // Send the response, except if it's a resume response (in which case
+ // we'll go run the VM and afterwards send a wait response message).
+ if !response.has_resume() {
+ let mut response_buffer = self.response_buffer.borrow_mut();
+ response_buffer.clear();
+ response
+ .write_to_vec(&mut response_buffer)
+ .map_err(proto_to_sys_err)?;
+ let mut write_pipe = &self.write_pipe;
+ write_pipe
+ .write(&response_buffer[..])
+ .map_err(io_to_sys_err)?;
+ }
+
+ Ok(resume_data)
+ }
+
+ fn handle_until_resume(&self, vcpu: &Vcpu) -> SysResult<Vec<u8>> {
+ loop {
+ if let Some(resume_data) = self.handle_request(vcpu)? {
+ return Ok(resume_data);
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn shared_vcpu_reserve() {
+ let mut shared_vcpu_state = SharedVcpuState::default();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x10, 0)
+ .unwrap_err();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x10, 0x10)
+ .unwrap();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x0f, 0x10)
+ .unwrap_err();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x10, 0x10)
+ .unwrap_err();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x10, 0x15)
+ .unwrap_err();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x12, 0x15)
+ .unwrap_err();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x12, 0x01)
+ .unwrap_err();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x0, 0x20)
+ .unwrap_err();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x20, 0x05)
+ .unwrap();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x25, 0x05)
+ .unwrap();
+ shared_vcpu_state
+ .reserve_range(IoSpace::Ioport, 0x0, 0x10)
+ .unwrap();
+ }
+}
diff --git a/sync/Cargo.toml b/sync/Cargo.toml
new file mode 100644
index 0000000..af38fd1
--- /dev/null
+++ b/sync/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "sync"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+include = ["src/**/*", "Cargo.toml"]
+
+[workspace]
diff --git a/sync/src/condvar.rs b/sync/src/condvar.rs
new file mode 100644
index 0000000..4a6e80d
--- /dev/null
+++ b/sync/src/condvar.rs
@@ -0,0 +1,45 @@
+// Copyright 2018 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::fmt::{self, Debug};
+use std::sync::{Condvar as StdCondvar, MutexGuard};
+
+/// A Condition Variable.
+#[derive(Default)]
+pub struct Condvar {
+ std: StdCondvar,
+}
+
+impl Condvar {
+ /// Creates a new condvar that is ready to be waited on.
+ pub fn new() -> Condvar {
+ Condvar {
+ std: StdCondvar::new(),
+ }
+ }
+
+ /// Waits on a condvar, blocking the current thread until it is notified.
+ pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
+ match self.std.wait(guard) {
+ Ok(guard) => guard,
+ Err(_) => panic!("condvar is poisoned"),
+ }
+ }
+
+ /// Notifies one thread blocked by this condvar.
+ pub fn notify_one(&self) {
+ self.std.notify_one();
+ }
+
+ /// Notifies all threads blocked by this condvar.
+ pub fn notify_all(&self) {
+ self.std.notify_all();
+ }
+}
+
+impl Debug for Condvar {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.std, formatter)
+ }
+}
diff --git a/sync/src/lib.rs b/sync/src/lib.rs
new file mode 100644
index 0000000..5a1e883
--- /dev/null
+++ b/sync/src/lib.rs
@@ -0,0 +1,28 @@
+// Copyright 2018 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.
+
+//! Sync primitive types whose methods panic rather than returning error in case of poison.
+//!
+//! The Mutex/Condvar type in this crates wraps the standard library versions and mirrors the same
+//! methods, except that they panic where the standard library would return an Error. This API
+//! codifies our error handling strategy around poisoned mutexes in crosvm.
+//!
+//! - Crosvm releases are built with panic=abort so poisoning never occurs. A panic while a mutex is
+//! held (or ever) takes down the entire process. Thus we would like for code not to have to
+//! consider the possibility of poison.
+//!
+//! - We could ask developers to always write `.lock().unwrap()` on a standard library mutex.
+//! However, we would like to stigmatize the use of unwrap. It is confusing to permit unwrap but
+//! only on mutex lock results. During code review it may not always be obvious whether a
+//! particular unwrap is unwrapping a mutex lock result or a different error that should be
+//! handled in a more principled way.
+//!
+//! Developers should feel free to use types defined in this crate anywhere in crosvm that they
+//! would otherwise be using the corresponding types in std::sync.
+
+mod condvar;
+mod mutex;
+
+pub use crate::condvar::Condvar;
+pub use crate::mutex::{Mutex, WouldBlock};
diff --git a/sync/src/mutex.rs b/sync/src/mutex.rs
new file mode 100644
index 0000000..c479a2e
--- /dev/null
+++ b/sync/src/mutex.rs
@@ -0,0 +1,121 @@
+// Copyright 2018 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.
+
+//! Mutex type whose methods panic rather than returning error in case of
+//! poison.
+//!
+//! The Mutex type in this module wraps the standard library Mutex and mirrors
+//! the same methods, except that they panic where the standard library would
+//! return a PoisonError. This API codifies our error handling strategy around
+//! poisoned mutexes in crosvm.
+//!
+//! - Crosvm releases are built with panic=abort so poisoning never occurs. A
+//! panic while a mutex is held (or ever) takes down the entire process. Thus
+//! we would like for code not to have to consider the possibility of poison.
+//!
+//! - We could ask developers to always write `.lock().unwrap()` on a standard
+//! library mutex. However, we would like to stigmatize the use of unwrap. It
+//! is confusing to permit unwrap but only on mutex lock results. During code
+//! review it may not always be obvious whether a particular unwrap is
+//! unwrapping a mutex lock result or a different error that should be handled
+//! in a more principled way.
+//!
+//! Developers should feel free to use sync::Mutex anywhere in crosvm that they
+//! would otherwise be using std::sync::Mutex.
+
+use std::fmt::{self, Debug, Display};
+use std::sync::{Mutex as StdMutex, MutexGuard, TryLockError};
+
+/// A mutual exclusion primitive useful for protecting shared data.
+#[derive(Default)]
+pub struct Mutex<T: ?Sized> {
+ std: StdMutex<T>,
+}
+
+impl<T> Mutex<T> {
+ /// Creates a new mutex in an unlocked state ready for use.
+ pub fn new(value: T) -> Mutex<T> {
+ Mutex {
+ std: StdMutex::new(value),
+ }
+ }
+
+ /// Consumes this mutex, returning the underlying data.
+ pub fn into_inner(self) -> T {
+ match self.std.into_inner() {
+ Ok(value) => value,
+ Err(_) => panic!("mutex is poisoned"),
+ }
+ }
+}
+
+impl<T: ?Sized> Mutex<T> {
+ /// Acquires a mutex, blocking the current thread until it is able to do so.
+ ///
+ /// This function will block the local thread until it is available to
+ /// acquire the mutex. Upon returning, the thread is the only thread with
+ /// the lock held. An RAII guard is returned to allow scoped unlock of the
+ /// lock. When the guard goes out of scope, the mutex will be unlocked.
+ pub fn lock(&self) -> MutexGuard<T> {
+ match self.std.lock() {
+ Ok(guard) => guard,
+ Err(_) => panic!("mutex is poisoned"),
+ }
+ }
+
+ /// Attempts to acquire this lock.
+ ///
+ /// If the lock could not be acquired at this time, then Err is returned.
+ /// Otherwise, an RAII guard is returned. The lock will be unlocked when the
+ /// guard is dropped.
+ ///
+ /// This function does not block.
+ pub fn try_lock(&self) -> Result<MutexGuard<T>, WouldBlock> {
+ match self.std.try_lock() {
+ Ok(guard) => Ok(guard),
+ Err(TryLockError::Poisoned(_)) => panic!("mutex is poisoned"),
+ Err(TryLockError::WouldBlock) => Err(WouldBlock),
+ }
+ }
+
+ /// Returns a mutable reference to the underlying data.
+ ///
+ /// Since this call borrows the Mutex mutably, no actual locking needs to
+ /// take place -- the mutable borrow statically guarantees no locks exist.
+ pub fn get_mut(&mut self) -> &mut T {
+ match self.std.get_mut() {
+ Ok(value) => value,
+ Err(_) => panic!("mutex is poisoned"),
+ }
+ }
+}
+
+impl<T> From<T> for Mutex<T> {
+ fn from(value: T) -> Self {
+ Mutex {
+ std: StdMutex::from(value),
+ }
+ }
+}
+
+impl<T: ?Sized + Debug> Debug for Mutex<T> {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.std, formatter)
+ }
+}
+
+/// The lock could not be acquired at this time because the operation would
+/// otherwise block.
+///
+/// Error returned by Mutex::try_lock.
+#[derive(Debug)]
+pub struct WouldBlock;
+
+impl Display for WouldBlock {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&TryLockError::WouldBlock::<()>, formatter)
+ }
+}
+
+impl std::error::Error for WouldBlock {}
diff --git a/sys_util/Cargo.toml b/sys_util/Cargo.toml
new file mode 100644
index 0000000..00e39fa
--- /dev/null
+++ b/sys_util/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "sys_util"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+include = ["src/**/*", "Cargo.toml"]
+
+[dependencies]
+data_model = { path = "../data_model" } # provided by ebuild
+libc = "*"
+poll_token_derive = { version = "*", path = "poll_token_derive" }
+sync = { path = "../sync" } # provided by ebuild
+syscall_defines = { path = "../syscall_defines" } # provided by ebuild
+
+[workspace]
diff --git a/sys_util/poll_token_derive/Cargo.toml b/sys_util/poll_token_derive/Cargo.toml
new file mode 100644
index 0000000..45c4552
--- /dev/null
+++ b/sys_util/poll_token_derive/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "poll_token_derive"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+include = ["*.rs", "Cargo.toml"]
+
+[lib]
+proc-macro = true
+path = "poll_token_derive.rs"
+
+[dependencies]
+proc-macro2 = "0.4"
+quote = "0.6"
+syn = "0.15"
diff --git a/sys_util/poll_token_derive/poll_token_derive.rs b/sys_util/poll_token_derive/poll_token_derive.rs
new file mode 100644
index 0000000..7b7baac
--- /dev/null
+++ b/sys_util/poll_token_derive/poll_token_derive.rs
@@ -0,0 +1,172 @@
+// Copyright 2018 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.
+
+#![recursion_limit = "128"]
+
+extern crate proc_macro;
+
+use proc_macro2::{Ident, TokenStream};
+use quote::quote;
+use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, Index, Member, Variant};
+
+#[cfg(test)]
+mod tests;
+
+// The method for packing an enum into a u64 is as follows:
+// 1) Reserve the lowest "ceil(log_2(x))" bits where x is the number of enum variants.
+// 2) Store the enum variant's index (0-based index based on order in the enum definition) in
+// reserved bits.
+// 3) If there is data in the enum variant, store the data in remaining bits.
+// The method for unpacking is as follows
+// 1) Mask the raw token to just the reserved bits
+// 2) Match the reserved bits to the enum variant token.
+// 3) If the indicated enum variant had data, extract it from the unreserved bits.
+
+// Calculates the number of bits needed to store the variant index. Essentially the log base 2
+// of the number of variants, rounded up.
+fn variant_bits(variants: &[Variant]) -> u32 {
+ if variants.is_empty() {
+ // The degenerate case of no variants.
+ 0
+ } else {
+ variants.len().next_power_of_two().trailing_zeros()
+ }
+}
+
+// Name of the field if it has one, otherwise 0 assuming this is the zeroth
+// field of a tuple variant.
+fn field_member(field: &Field) -> Member {
+ match &field.ident {
+ Some(name) => Member::Named(name.clone()),
+ None => Member::Unnamed(Index::from(0)),
+ }
+}
+
+// Generates the function body for `as_raw_token`.
+fn generate_as_raw_token(enum_name: &Ident, variants: &[Variant]) -> TokenStream {
+ let variant_bits = variant_bits(variants);
+
+ // Each iteration corresponds to one variant's match arm.
+ let cases = variants.iter().enumerate().map(|(index, variant)| {
+ let variant_name = &variant.ident;
+ let index = index as u64;
+
+ // The capture string is for everything between the variant identifier and the `=>` in
+ // the match arm: the variant's data capture.
+ let capture = variant.fields.iter().next().map(|field| {
+ let member = field_member(&field);
+ quote!({ #member: data })
+ });
+
+ // The modifier string ORs the variant index with extra bits from the variant data
+ // field.
+ let modifier = match variant.fields {
+ Fields::Named(_) | Fields::Unnamed(_) => Some(quote! {
+ | ((data as u64) << #variant_bits)
+ }),
+ Fields::Unit => None,
+ };
+
+ // Assembly of the match arm.
+ quote! {
+ #enum_name::#variant_name #capture => #index #modifier
+ }
+ });
+
+ quote! {
+ match *self {
+ #(
+ #cases,
+ )*
+ }
+ }
+}
+
+// Generates the function body for `from_raw_token`.
+fn generate_from_raw_token(enum_name: &Ident, variants: &[Variant]) -> TokenStream {
+ let variant_bits = variant_bits(variants);
+ let variant_mask = ((1 << variant_bits) - 1) as u64;
+
+ // Each iteration corresponds to one variant's match arm.
+ let cases = variants.iter().enumerate().map(|(index, variant)| {
+ let variant_name = &variant.ident;
+ let index = index as u64;
+
+ // The data string is for extracting the enum variant's data bits out of the raw token
+ // data, which includes both variant index and data bits.
+ let data = variant.fields.iter().next().map(|field| {
+ let member = field_member(&field);
+ let ty = &field.ty;
+ quote!({ #member: (data >> #variant_bits) as #ty })
+ });
+
+ // Assembly of the match arm.
+ quote! {
+ #index => #enum_name::#variant_name #data
+ }
+ });
+
+ quote! {
+ // The match expression only matches the bits for the variant index.
+ match data & #variant_mask {
+ #(
+ #cases,
+ )*
+ _ => unreachable!(),
+ }
+ }
+}
+
+// The proc_macro::TokenStream type can only be constructed from within a
+// procedural macro, meaning that unit tests are not able to invoke `fn
+// poll_token` below as an ordinary Rust function. We factor out the logic into
+// a signature that deals with Syn and proc-macro2 types only which are not
+// restricted to a procedural macro invocation.
+fn poll_token_inner(input: DeriveInput) -> TokenStream {
+ let variants: Vec<Variant> = match input.data {
+ Data::Enum(data) => data.variants.into_iter().collect(),
+ Data::Struct(_) | Data::Union(_) => panic!("input must be an enum"),
+ };
+
+ for variant in &variants {
+ assert!(variant.fields.iter().count() <= 1);
+ }
+
+ // Given our basic model of a user given enum that is suitable as a token, we generate the
+ // implementation. The implementation is NOT always well formed, such as when a variant's data
+ // type is not bit shiftable or castable to u64, but we let Rust generate such errors as it
+ // would be difficult to detect every kind of error. Importantly, every implementation that we
+ // generate here and goes on to compile succesfully is sound.
+
+ let enum_name = input.ident;
+ let as_raw_token = generate_as_raw_token(&enum_name, &variants);
+ let from_raw_token = generate_from_raw_token(&enum_name, &variants);
+
+ quote! {
+ impl PollToken for #enum_name {
+ fn as_raw_token(&self) -> u64 {
+ #as_raw_token
+ }
+
+ fn from_raw_token(data: u64) -> Self {
+ #from_raw_token
+ }
+ }
+ }
+}
+
+/// Implements the PollToken trait for a given `enum`.
+///
+/// There are limitations on what `enum`s this custom derive will work on:
+///
+/// * Each variant must be a unit variant (no data), or have a single (un)named data field.
+/// * If a variant has data, it must be a primitive type castable to and from a `u64`.
+/// * If a variant data has size greater than or equal to a `u64`, its most significant bits must be
+/// zero. The number of bits truncated is equal to the number of bits used to store the variant
+/// index plus the number of bits above 64.
+#[proc_macro_derive(PollToken)]
+pub fn poll_token(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ poll_token_inner(input).into()
+}
diff --git a/sys_util/poll_token_derive/tests.rs b/sys_util/poll_token_derive/tests.rs
new file mode 100644
index 0000000..935f91c
--- /dev/null
+++ b/sys_util/poll_token_derive/tests.rs
@@ -0,0 +1,65 @@
+// Copyright 2018 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 quote::quote;
+use syn::{parse_quote, DeriveInput};
+
+#[test]
+fn test_variant_bits() {
+ let mut variants = vec![parse_quote!(A)];
+ assert_eq!(crate::variant_bits(&variants), 0);
+
+ variants.push(parse_quote!(B));
+ variants.push(parse_quote!(C));
+ assert_eq!(crate::variant_bits(&variants), 2);
+
+ for _ in 0..1021 {
+ variants.push(parse_quote!(Dynamic));
+ }
+ assert_eq!(crate::variant_bits(&variants), 10);
+
+ variants.push(parse_quote!(OneMore));
+ assert_eq!(crate::variant_bits(&variants), 11);
+}
+
+#[test]
+fn poll_token_e2e() {
+ let input: DeriveInput = parse_quote! {
+ enum Token {
+ A,
+ B,
+ C,
+ D(usize),
+ E { foobaz: u32 },
+ }
+ };
+
+ let actual = crate::poll_token_inner(input);
+ let expected = quote! {
+ impl PollToken for Token {
+ fn as_raw_token(&self) -> u64 {
+ match *self {
+ Token::A => 0u64,
+ Token::B => 1u64,
+ Token::C => 2u64,
+ Token::D { 0: data } => 3u64 | ((data as u64) << 3u32),
+ Token::E { foobaz: data } => 4u64 | ((data as u64) << 3u32),
+ }
+ }
+
+ fn from_raw_token(data: u64) -> Self {
+ match data & 7u64 {
+ 0u64 => Token::A,
+ 1u64 => Token::B,
+ 2u64 => Token::C,
+ 3u64 => Token::D { 0: (data >> 3u32) as usize },
+ 4u64 => Token::E { foobaz: (data >> 3u32) as u32 },
+ _ => unreachable!(),
+ }
+ }
+ }
+ };
+
+ assert_eq!(actual.to_string(), expected.to_string());
+}
diff --git a/sys_util/src/affinity.rs b/sys_util/src/affinity.rs
new file mode 100644
index 0000000..d91d3da
--- /dev/null
+++ b/sys_util/src/affinity.rs
@@ -0,0 +1,64 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Wrappers for CPU affinity functions.
+
+use std::iter::FromIterator;
+use std::mem;
+
+use libc::{cpu_set_t, sched_setaffinity, CPU_SET, CPU_SETSIZE, CPU_ZERO, EINVAL};
+
+use crate::{errno_result, Error, Result};
+
+// This is needed because otherwise the compiler will complain that the
+// impl doesn't reference any types from inside this crate.
+struct CpuSet(cpu_set_t);
+
+impl FromIterator<usize> for CpuSet {
+ fn from_iter<I: IntoIterator<Item = usize>>(cpus: I) -> Self {
+ // cpu_set_t is a C struct and can be safely initialized with zeroed memory.
+ let mut cpuset: cpu_set_t = unsafe { mem::zeroed() };
+ // Safe because we pass a valid cpuset pointer.
+ unsafe { CPU_ZERO(&mut cpuset) };
+ for cpu in cpus {
+ // Safe because we pass a valid cpuset pointer and cpu index.
+ unsafe { CPU_SET(cpu, &mut cpuset) };
+ }
+ CpuSet(cpuset)
+ }
+}
+
+/// Set the CPU affinity of the current thread to a given set of CPUs.
+///
+/// # Examples
+///
+/// Set the calling thread's CPU affinity so it will run on only CPUs
+/// 0, 1, 5, and 6.
+///
+/// ```
+/// # use sys_util::set_cpu_affinity;
+/// set_cpu_affinity(vec![0, 1, 5, 6]).unwrap();
+/// ```
+pub fn set_cpu_affinity<I: IntoIterator<Item = usize>>(cpus: I) -> Result<()> {
+ let CpuSet(cpuset) = cpus
+ .into_iter()
+ .map(|cpu| {
+ if cpu < CPU_SETSIZE as usize {
+ Ok(cpu)
+ } else {
+ Err(Error::new(EINVAL))
+ }
+ })
+ .collect::<Result<CpuSet>>()?;
+
+ // Safe because we pass 0 for the current thread, and cpuset is a valid pointer and only
+ // used for the duration of this call.
+ let res = unsafe { sched_setaffinity(0, mem::size_of_val(&cpuset), &cpuset) };
+
+ if res != 0 {
+ errno_result()
+ } else {
+ Ok(())
+ }
+}
diff --git a/sys_util/src/alloc.rs b/sys_util/src/alloc.rs
new file mode 100644
index 0000000..8ac9100
--- /dev/null
+++ b/sys_util/src/alloc.rs
@@ -0,0 +1,123 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::alloc::{alloc, alloc_zeroed, dealloc, Layout};
+
+/// A contiguous memory allocation with a specified size and alignment, with a
+/// Drop impl to perform the deallocation.
+///
+/// Conceptually this is like a Box<[u8]> but for which we can select a minimum
+/// required alignment at the time of allocation.
+///
+/// # Example
+///
+/// ```
+/// use std::alloc::Layout;
+/// use std::mem;
+/// use sys_util::LayoutAllocation;
+///
+/// #[repr(C)]
+/// struct Header {
+/// q: usize,
+/// entries: [Entry; 0], // flexible array member
+/// }
+///
+/// #[repr(C)]
+/// struct Entry {
+/// e: usize,
+/// }
+///
+/// fn demo(num_entries: usize) {
+/// let size = mem::size_of::<Header>() + num_entries * mem::size_of::<Entry>();
+/// let layout = Layout::from_size_align(size, mem::align_of::<Header>()).unwrap();
+/// let mut allocation = LayoutAllocation::zeroed(layout);
+///
+/// // Safe to obtain an exclusive reference because there are no other
+/// // references to the allocation yet and all-zero is a valid bit pattern for
+/// // our header.
+/// let header = unsafe { allocation.as_mut::<Header>() };
+/// }
+/// ```
+pub struct LayoutAllocation {
+ ptr: *mut u8,
+ layout: Layout,
+}
+
+impl LayoutAllocation {
+ /// Allocates memory with the specified size and alignment. The content is
+ /// not initialized.
+ ///
+ /// Uninitialized data is not safe to read. Further, it is not safe to
+ /// obtain a reference to data potentially holding a bit pattern
+ /// incompatible with its type, for example an uninitialized bool or enum.
+ pub fn uninitialized(layout: Layout) -> Self {
+ let ptr = if layout.size() > 0 {
+ unsafe {
+ // Safe as long as we guarantee layout.size() > 0.
+ alloc(layout)
+ }
+ } else {
+ layout.align() as *mut u8
+ };
+ LayoutAllocation { ptr, layout }
+ }
+
+ /// Allocates memory with the specified size and alignment and initializes
+ /// the content to all zero-bytes.
+ ///
+ /// Note that zeroing the memory does not necessarily make it safe to obtain
+ /// a reference to the allocation. Depending on the intended type T,
+ /// all-zero may or may not be a legal bit pattern for that type. For
+ /// example obtaining a reference would immediately be undefined behavior if
+ /// one of the fields has type NonZeroUsize.
+ pub fn zeroed(layout: Layout) -> Self {
+ let ptr = if layout.size() > 0 {
+ unsafe {
+ // Safe as long as we guarantee layout.size() > 0.
+ alloc_zeroed(layout)
+ }
+ } else {
+ layout.align() as *mut u8
+ };
+ LayoutAllocation { ptr, layout }
+ }
+
+ /// Returns a raw pointer to the allocated data.
+ pub fn as_ptr<T>(&self) -> *mut T {
+ self.ptr as *mut T
+ }
+
+ /// Returns a shared reference to the allocated data.
+ ///
+ /// # Safety
+ ///
+ /// Caller is responsible for ensuring that the data behind this pointer has
+ /// been initialized as much as necessary and that there are no already
+ /// existing mutable references to any part of the data.
+ pub unsafe fn as_ref<T>(&self) -> &T {
+ &*self.as_ptr()
+ }
+
+ /// Returns an exclusive reference to the allocated data.
+ ///
+ /// # Safety
+ ///
+ /// Caller is responsible for ensuring that the data behind this pointer has
+ /// been initialized as much as necessary and that there are no already
+ /// existing references to any part of the data.
+ pub unsafe fn as_mut<T>(&mut self) -> &mut T {
+ &mut *self.as_ptr()
+ }
+}
+
+impl Drop for LayoutAllocation {
+ fn drop(&mut self) {
+ if self.layout.size() > 0 {
+ unsafe {
+ // Safe as long as we guarantee layout.size() > 0.
+ dealloc(self.ptr, self.layout);
+ }
+ }
+ }
+}
diff --git a/sys_util/src/capabilities.rs b/sys_util/src/capabilities.rs
new file mode 100644
index 0000000..90b8784
--- /dev/null
+++ b/sys_util/src/capabilities.rs
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use libc::{c_int, c_void};
+
+use crate::{errno_result, Result};
+
+#[allow(non_camel_case_types)]
+type cap_t = *mut c_void;
+
+#[link(name = "cap")]
+extern "C" {
+ fn cap_init() -> cap_t;
+ fn cap_free(ptr: *mut c_void) -> c_int;
+ fn cap_set_proc(cap: cap_t) -> c_int;
+}
+
+/// Drops all capabilities (permitted, inheritable, and effective) from the current process.
+pub fn drop_capabilities() -> Result<()> {
+ unsafe {
+ // Safe because we do not actually manipulate any memory handled by libcap
+ // and we check errors.
+ let caps = cap_init();
+ if caps.is_null() {
+ return errno_result();
+ }
+
+ // Freshly initialized capabilities do not have any bits set, so applying them
+ // will drop all capabilities from the process.
+ // Safe because we will check the result and otherwise do not touch the memory.
+ let ret = cap_set_proc(caps);
+ // We need to free capabilities regardless of success of the operation above.
+ cap_free(caps);
+ // Now check if we managed to apply (drop) capabilities.
+ if ret < 0 {
+ return errno_result();
+ }
+ }
+ Ok(())
+}
diff --git a/sys_util/src/clock.rs b/sys_util/src/clock.rs
new file mode 100644
index 0000000..878014d
--- /dev/null
+++ b/sys_util/src/clock.rs
@@ -0,0 +1,96 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Utility file to provide a fake clock object representing current time, and a timerfd driven by
+// that time.
+
+use std::os::unix::io::AsRawFd;
+use std::time::{Duration, Instant};
+
+use crate::EventFd;
+
+#[derive(Debug, Copy, Clone)]
+pub struct Clock(Instant);
+impl Clock {
+ pub fn new() -> Self {
+ Clock(Instant::now())
+ }
+
+ pub fn now(&self) -> Self {
+ Clock(Instant::now())
+ }
+
+ pub fn duration_since(&self, earlier: &Self) -> Duration {
+ self.0.duration_since(earlier.0)
+ }
+}
+
+impl Default for Clock {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+const NS_PER_SEC: u64 = 1_000_000_000;
+/// A fake clock that can be used in tests to give exact control over the time.
+/// For a code example, see the tests in sys_util/src/timerfd.rs.
+#[derive(Debug)]
+pub struct FakeClock {
+ ns_since_epoch: u64,
+ deadlines: Vec<(u64, EventFd)>,
+}
+
+impl FakeClock {
+ pub fn new() -> Self {
+ FakeClock {
+ ns_since_epoch: 1_547_163_599 * NS_PER_SEC,
+ deadlines: Vec::new(),
+ }
+ }
+
+ /// Get the current time, according to this clock.
+ pub fn now(&self) -> Self {
+ FakeClock {
+ ns_since_epoch: self.ns_since_epoch,
+ deadlines: Vec::new(),
+ }
+ }
+
+ /// Get the current time in ns, according to this clock.
+ pub fn nanos(&self) -> u64 {
+ self.ns_since_epoch
+ }
+
+ /// Get the duration since |earlier|, assuming that earlier < self.
+ pub fn duration_since(&self, earlier: &Self) -> Duration {
+ let ns_diff = self.ns_since_epoch - earlier.ns_since_epoch;
+ Duration::new(ns_diff / NS_PER_SEC, (ns_diff % NS_PER_SEC) as u32)
+ }
+
+ /// Register the event fd for a notification when self's time is |deadline_ns|.
+ /// Drop any existing events registered to the same raw fd.
+ pub fn add_event_fd(&mut self, deadline_ns: u64, fd: EventFd) {
+ self.deadlines
+ .retain(|(_, old_fd)| fd.as_raw_fd() != old_fd.as_raw_fd());
+ self.deadlines.push((deadline_ns, fd));
+ }
+
+ pub fn add_ns(&mut self, ns: u64) {
+ self.ns_since_epoch += ns;
+ let time = self.ns_since_epoch;
+ self.deadlines.retain(|(ns, fd)| {
+ let expired = *ns <= time;
+ if expired {
+ fd.write(1).unwrap();
+ }
+ !expired
+ });
+ }
+}
+
+impl Default for FakeClock {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/sys_util/src/errno.rs b/sys_util/src/errno.rs
new file mode 100644
index 0000000..cf54aae
--- /dev/null
+++ b/sys_util/src/errno.rs
@@ -0,0 +1,64 @@
+// Copyright 2017 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::fmt::{self, Display};
+use std::io;
+use std::result;
+
+use libc::__errno_location;
+
+/// An error number, retrieved from errno (man 3 errno), set by a libc
+/// function that returned an error.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct Error(i32);
+pub type Result<T> = result::Result<T, Error>;
+
+impl Error {
+ /// Constructs a new error with the given errno.
+ pub fn new(e: i32) -> Error {
+ Error(e)
+ }
+
+ /// Constructs an error from the current errno.
+ ///
+ /// The result of this only has any meaning just after a libc call that returned a value
+ /// indicating errno was set.
+ pub fn last() -> Error {
+ Error(unsafe { *__errno_location() })
+ }
+
+ /// Gets the errno for this error
+ pub fn errno(self) -> i32 {
+ self.0
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(e: io::Error) -> Self {
+ Error::new(e.raw_os_error().unwrap_or_default())
+ }
+}
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ io::Error::from_raw_os_error(self.0).fmt(f)
+ }
+}
+
+/// Returns the last errno as a Result that is always an error.
+pub fn errno_result<T>() -> Result<T> {
+ Err(Error::last())
+}
+
+/// Sets errno to given error code.
+/// Only defined when we compile tests as normal code does not
+/// normally need set errno.
+#[cfg(test)]
+pub fn set_errno(e: i32) {
+ unsafe {
+ *__errno_location() = e;
+ }
+}
diff --git a/sys_util/src/eventfd.rs b/sys_util/src/eventfd.rs
new file mode 100644
index 0000000..fc1ead2
--- /dev/null
+++ b/sys_util/src/eventfd.rs
@@ -0,0 +1,132 @@
+// Copyright 2017 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::mem;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+
+use libc::{c_void, dup, eventfd, read, write};
+
+use crate::{errno_result, Result};
+
+/// A safe wrapper around a Linux eventfd (man 2 eventfd).
+///
+/// An eventfd is useful because it is sendable across processes and can be used for signaling in
+/// and out of the KVM API. They can also be polled like any other file descriptor.
+#[derive(Debug)]
+pub struct EventFd {
+ eventfd: File,
+}
+
+impl EventFd {
+ /// Creates a new blocking EventFd with an initial value of 0.
+ pub fn new() -> Result<EventFd> {
+ // This is safe because eventfd merely allocated an eventfd for our process and we handle
+ // the error case.
+ let ret = unsafe { eventfd(0, 0) };
+ if ret < 0 {
+ return errno_result();
+ }
+ // This is safe because we checked ret for success and know the kernel gave us an fd that we
+ // own.
+ Ok(EventFd {
+ eventfd: unsafe { File::from_raw_fd(ret) },
+ })
+ }
+
+ /// Adds `v` to the eventfd's count, blocking until this won't overflow the count.
+ pub fn write(&self, v: u64) -> Result<()> {
+ // This is safe because we made this fd and the pointer we pass can not overflow because we
+ // give the syscall's size parameter properly.
+ let ret = unsafe {
+ write(
+ self.as_raw_fd(),
+ &v as *const u64 as *const c_void,
+ mem::size_of::<u64>(),
+ )
+ };
+ if ret <= 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Blocks until the the eventfd's count is non-zero, then resets the count to zero.
+ pub fn read(&self) -> Result<u64> {
+ let mut buf: u64 = 0;
+ let ret = unsafe {
+ // This is safe because we made this fd and the pointer we pass can not overflow because
+ // we give the syscall's size parameter properly.
+ read(
+ self.as_raw_fd(),
+ &mut buf as *mut u64 as *mut c_void,
+ mem::size_of::<u64>(),
+ )
+ };
+ if ret <= 0 {
+ return errno_result();
+ }
+ Ok(buf)
+ }
+
+ /// Clones this EventFd, internally creating a new file descriptor. The new EventFd will share
+ /// the same underlying count within the kernel.
+ pub fn try_clone(&self) -> Result<EventFd> {
+ // This is safe because we made this fd and properly check that it returns without error.
+ let ret = unsafe { dup(self.as_raw_fd()) };
+ if ret < 0 {
+ return errno_result();
+ }
+ // This is safe because we checked ret for success and know the kernel gave us an fd that we
+ // own.
+ Ok(EventFd {
+ eventfd: unsafe { File::from_raw_fd(ret) },
+ })
+ }
+}
+
+impl AsRawFd for EventFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.eventfd.as_raw_fd()
+ }
+}
+
+impl FromRawFd for EventFd {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ EventFd {
+ eventfd: File::from_raw_fd(fd),
+ }
+ }
+}
+
+impl IntoRawFd for EventFd {
+ fn into_raw_fd(self) -> RawFd {
+ self.eventfd.into_raw_fd()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn new() {
+ EventFd::new().unwrap();
+ }
+
+ #[test]
+ fn read_write() {
+ let evt = EventFd::new().unwrap();
+ evt.write(55).unwrap();
+ assert_eq!(evt.read(), Ok(55));
+ }
+
+ #[test]
+ fn clone() {
+ let evt = EventFd::new().unwrap();
+ let evt_clone = evt.try_clone().unwrap();
+ evt.write(923).unwrap();
+ assert_eq!(evt_clone.read(), Ok(923));
+ }
+}
diff --git a/sys_util/src/file_flags.rs b/sys_util/src/file_flags.rs
new file mode 100644
index 0000000..7a2ad1b
--- /dev/null
+++ b/sys_util/src/file_flags.rs
@@ -0,0 +1,53 @@
+// Copyright 2018 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 libc::{fcntl, EINVAL, F_GETFL, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY};
+
+use crate::{errno_result, Error, Result};
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum FileFlags {
+ Read,
+ Write,
+ ReadWrite,
+}
+
+impl FileFlags {
+ pub fn from_file(file: &dyn AsRawFd) -> Result<FileFlags> {
+ // Trivially safe because fcntl with the F_GETFL command is totally safe and we check for
+ // error.
+ let flags = unsafe { fcntl(file.as_raw_fd(), F_GETFL) };
+ if flags == -1 {
+ errno_result()
+ } else {
+ match flags & O_ACCMODE {
+ O_RDONLY => Ok(FileFlags::Read),
+ O_WRONLY => Ok(FileFlags::Write),
+ O_RDWR => Ok(FileFlags::ReadWrite),
+ _ => Err(Error::new(EINVAL)),
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::{pipe, EventFd};
+
+ #[test]
+ fn pipe_pair() {
+ let (read_pipe, write_pipe) = pipe(true).unwrap();
+ assert_eq!(FileFlags::from_file(&read_pipe).unwrap(), FileFlags::Read);
+ assert_eq!(FileFlags::from_file(&write_pipe).unwrap(), FileFlags::Write);
+ }
+
+ #[test]
+ fn eventfd() {
+ let evt = EventFd::new().unwrap();
+ assert_eq!(FileFlags::from_file(&evt).unwrap(), FileFlags::ReadWrite);
+ }
+}
diff --git a/sys_util/src/file_traits.rs b/sys_util/src/file_traits.rs
new file mode 100644
index 0000000..584ac9a
--- /dev/null
+++ b/sys_util/src/file_traits.rs
@@ -0,0 +1,119 @@
+// Copyright 2018 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::{Error, ErrorKind, Result};
+use std::os::unix::io::AsRawFd;
+
+use data_model::VolatileSlice;
+
+use libc::{c_void, read, write};
+
+/// A trait for flushing the contents of a file to disk.
+/// This is equivalent to File's `sync_all` method, but
+/// wrapped in a trait so that it can be implemented for
+/// other types.
+pub trait FileSync {
+ // Flush buffers related to this file to disk.
+ fn fsync(&mut self) -> Result<()>;
+}
+
+impl FileSync for File {
+ fn fsync(&mut self) -> Result<()> {
+ self.sync_all()
+ }
+}
+
+/// A trait for setting the size of a file.
+/// This is equivalent to File's `set_len` method, but
+/// wrapped in a trait so that it can be implemented for
+/// other types.
+pub trait FileSetLen {
+ // Set the size of this file.
+ // This is the moral equivalent of `ftruncate()`.
+ fn set_len(&self, _len: u64) -> Result<()>;
+}
+
+impl FileSetLen for File {
+ fn set_len(&self, len: u64) -> Result<()> {
+ File::set_len(self, len)
+ }
+}
+
+/// A trait similar to `Read` and `Write`, but uses volatile memory as buffers.
+pub trait FileReadWriteVolatile {
+ /// Read bytes from this file into the given slice, returning the number of bytes read on
+ /// success.
+ fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize>;
+
+ /// Reads bytes from this into the given slice until all bytes in the slice are written, or an
+ /// error is returned.
+ fn read_exact_volatile(&mut self, mut slice: VolatileSlice) -> Result<()> {
+ while slice.size() > 0 {
+ let bytes_read = self.read_volatile(slice)?;
+ if bytes_read == 0 {
+ return Err(Error::from(ErrorKind::UnexpectedEof));
+ }
+ // Will panic if read_volatile read more bytes than we gave it, which would be worthy of
+ // a panic.
+ slice = slice.offset(bytes_read as u64).unwrap();
+ }
+ Ok(())
+ }
+
+ /// Write bytes from the slice to the given file, returning the number of bytes written on
+ /// success.
+ fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize>;
+
+ /// Write bytes from the slice to the given file until all the bytes from the slice have been
+ /// written, or an error is returned.
+ fn write_all_volatile(&mut self, mut slice: VolatileSlice) -> Result<()> {
+ while slice.size() > 0 {
+ let bytes_written = self.write_volatile(slice)?;
+ if bytes_written == 0 {
+ return Err(Error::from(ErrorKind::WriteZero));
+ }
+ // Will panic if read_volatile read more bytes than we gave it, which would be worthy of
+ // a panic.
+ slice = slice.offset(bytes_written as u64).unwrap();
+ }
+ Ok(())
+ }
+}
+
+impl FileReadWriteVolatile for File {
+ fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
+ // Safe because only bytes inside the slice are accessed and the kernel is expected to
+ // handle arbitrary memory for I/O.
+ let ret = unsafe {
+ read(
+ self.as_raw_fd(),
+ slice.as_ptr() as *mut c_void,
+ slice.size() as usize,
+ )
+ };
+ if ret >= 0 {
+ Ok(ret as usize)
+ } else {
+ Err(Error::last_os_error())
+ }
+ }
+
+ fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
+ // Safe because only bytes inside the slice are accessed and the kernel is expected to
+ // handle arbitrary memory for I/O.
+ let ret = unsafe {
+ write(
+ self.as_raw_fd(),
+ slice.as_ptr() as *const c_void,
+ slice.size() as usize,
+ )
+ };
+ if ret >= 0 {
+ Ok(ret as usize)
+ } else {
+ Err(Error::last_os_error())
+ }
+ }
+}
diff --git a/sys_util/src/fork.rs b/sys_util/src/fork.rs
new file mode 100644
index 0000000..98a22f0
--- /dev/null
+++ b/sys_util/src/fork.rs
@@ -0,0 +1,148 @@
+// Copyright 2017 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;
+use std::io;
+use std::path::Path;
+use std::process;
+use std::result;
+
+use libc::{c_long, pid_t, syscall, CLONE_NEWPID, CLONE_NEWUSER, SIGCHLD};
+use syscall_defines::linux::LinuxSyscall::SYS_clone;
+
+use crate::errno_result;
+
+/// Controls what namespace `clone_process` will have. See NAMESPACES(7).
+#[repr(u32)]
+pub enum CloneNamespace {
+ /// The new process will inherit the namespace from the old process.
+ Inherit = 0,
+ /// The new process with be in a new user and PID namespace.
+ NewUserPid = CLONE_NEWUSER as u32 | CLONE_NEWPID as u32,
+}
+
+#[derive(Debug)]
+pub enum CloneError {
+ /// There was an error trying to iterate this process's threads.
+ IterateTasks(io::Error),
+ /// There are multiple threads running. The `usize` indicates how many threads.
+ Multithreaded(usize),
+ /// There was an error while cloning.
+ Sys(crate::Error),
+}
+
+unsafe fn do_clone(flags: i32) -> crate::Result<pid_t> {
+ // Forking is unsafe, this function must be unsafe as there is no way to guarantee safety
+ // without more context about the state of the program.
+ let pid = syscall(SYS_clone as c_long, flags | SIGCHLD as i32, 0);
+ if pid < 0 {
+ errno_result()
+ } else {
+ Ok(pid as pid_t)
+ }
+}
+
+fn count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize> {
+ Ok(fs::read_dir(path)?.count())
+}
+
+/// Clones this process and calls a closure in the new process.
+///
+/// After `post_clone_cb` returns or panics, the new process exits. Similar to how a `fork` syscall
+/// works, the new process is the same as the current process with the exception of the namespace
+/// controlled with the `ns` argument.
+///
+/// # Arguments
+/// * `ns` - What namespace the new process will have (see NAMESPACES(7)).
+/// * `post_clone_cb` - Callback to run in the new process
+pub fn clone_process<F>(ns: CloneNamespace, post_clone_cb: F) -> result::Result<pid_t, CloneError>
+where
+ F: FnOnce(),
+{
+ match count_dir_entries("/proc/self/task") {
+ Ok(1) => {}
+ Ok(thread_count) => {
+ // Test cfg gets a free pass on this because tests generally have multiple independent
+ // test threads going.
+ let _ = thread_count;
+ #[cfg(not(test))]
+ return Err(CloneError::Multithreaded(thread_count));
+ }
+ Err(e) => return Err(CloneError::IterateTasks(e)),
+ }
+ // Forking is considered unsafe in mutlithreaded programs, but we just checked for other threads
+ // in this process. We also only allow valid flags from CloneNamespace and check the return
+ // result for errors. We also never let the cloned process return from this function.
+ let ret = unsafe { do_clone(ns as i32) }.map_err(CloneError::Sys)?;
+ if ret == 0 {
+ struct ExitGuard;
+ impl Drop for ExitGuard {
+ fn drop(&mut self) {
+ process::exit(101);
+ }
+ }
+ // Prevents a panic in post_clone_cb from bypassing the process::exit.
+ #[allow(unused_variables)]
+ let exit_guard = ExitGuard {};
+ post_clone_cb();
+ // ! Never returns
+ process::exit(0);
+ }
+
+ Ok(ret)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::{getpid, EventFd};
+ use libc;
+
+ fn wait_process(pid: libc::pid_t) -> crate::Result<libc::c_int> {
+ let mut status: libc::c_int = 0;
+ unsafe {
+ if libc::waitpid(pid, &mut status as *mut libc::c_int, 0) < 0 {
+ errno_result()
+ } else {
+ Ok(libc::WEXITSTATUS(status))
+ }
+ }
+ }
+
+ #[test]
+ fn pid_diff() {
+ let evt_fd = EventFd::new().expect("failed to create EventFd");
+ let evt_fd_fork = evt_fd.try_clone().expect("failed to clone EventFd");
+ let pid = getpid();
+ clone_process(CloneNamespace::Inherit, || {
+ // checks that this is a genuine fork with a new PID
+ if pid != getpid() {
+ evt_fd_fork.write(1).unwrap()
+ } else {
+ evt_fd_fork.write(2).unwrap()
+ }
+ })
+ .expect("failed to clone");
+ assert_eq!(evt_fd.read(), Ok(1));
+ }
+
+ #[test]
+ fn panic_safe() {
+ let pid = getpid();
+ assert_ne!(pid, 0);
+
+ let clone_pid = clone_process(CloneNamespace::Inherit, || {
+ assert!(false);
+ })
+ .expect("failed to clone");
+
+ // This should never happen;
+ if pid != getpid() {
+ process::exit(2);
+ }
+
+ let status = wait_process(clone_pid).expect("wait_process failed");
+ assert!(status == 101 || status == 0);
+ }
+}
diff --git a/sys_util/src/guest_address.rs b/sys_util/src/guest_address.rs
new file mode 100644
index 0000000..1b78e71
--- /dev/null
+++ b/sys_util/src/guest_address.rs
@@ -0,0 +1,144 @@
+// Copyright 2017 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.
+
+//! Represents an address in the guest's memory space.
+
+use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
+use std::fmt::{self, Display};
+use std::ops::{BitAnd, BitOr};
+
+/// Represents an Address in the guest's memory.
+#[derive(Clone, Copy, Debug)]
+pub struct GuestAddress(pub u64);
+
+impl GuestAddress {
+ /// Returns the offset from this address to the given base address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use sys_util::GuestAddress;
+ /// let base = GuestAddress(0x100);
+ /// let addr = GuestAddress(0x150);
+ /// assert_eq!(addr.offset_from(base), 0x50u64);
+ /// ```
+ pub fn offset_from(self, base: GuestAddress) -> u64 {
+ self.0 - base.0
+ }
+
+ /// Returns the address as a u64 offset from 0x0.
+ /// Use this when a raw number is needed to pass to the kernel.
+ pub fn offset(self) -> u64 {
+ self.0
+ }
+
+ /// Returns the result of the add or None if there is overflow.
+ pub fn checked_add(self, other: u64) -> Option<GuestAddress> {
+ self.0.checked_add(other).map(GuestAddress)
+ }
+
+ /// Returns the result of the base address + the size.
+ /// Only use this when `offset` is guaranteed not to overflow.
+ pub fn unchecked_add(self, offset: u64) -> GuestAddress {
+ GuestAddress(self.0 + offset)
+ }
+
+ /// Returns the result of the subtraction of None if there is underflow.
+ pub fn checked_sub(self, other: u64) -> Option<GuestAddress> {
+ self.0.checked_sub(other).map(GuestAddress)
+ }
+
+ /// Returns the bitwise and of the address with the given mask.
+ pub fn mask(self, mask: u64) -> GuestAddress {
+ GuestAddress(self.0 & mask as u64)
+ }
+}
+
+impl BitAnd<u64> for GuestAddress {
+ type Output = GuestAddress;
+
+ fn bitand(self, other: u64) -> GuestAddress {
+ GuestAddress(self.0 & other as u64)
+ }
+}
+
+impl BitOr<u64> for GuestAddress {
+ type Output = GuestAddress;
+
+ fn bitor(self, other: u64) -> GuestAddress {
+ GuestAddress(self.0 | other as u64)
+ }
+}
+
+impl PartialEq for GuestAddress {
+ fn eq(&self, other: &GuestAddress) -> bool {
+ self.0 == other.0
+ }
+}
+impl Eq for GuestAddress {}
+
+impl Ord for GuestAddress {
+ fn cmp(&self, other: &GuestAddress) -> Ordering {
+ self.0.cmp(&other.0)
+ }
+}
+
+impl PartialOrd for GuestAddress {
+ fn partial_cmp(&self, other: &GuestAddress) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Display for GuestAddress {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:#x}", self.0)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn equals() {
+ let a = GuestAddress(0x300);
+ let b = GuestAddress(0x300);
+ let c = GuestAddress(0x301);
+ assert_eq!(a, b);
+ assert_eq!(b, a);
+ assert_ne!(a, c);
+ assert_ne!(c, a);
+ }
+
+ #[test]
+ fn cmp() {
+ let a = GuestAddress(0x300);
+ let b = GuestAddress(0x301);
+ assert!(a < b);
+ assert!(b > a);
+ assert!(!(a < a));
+ }
+
+ #[test]
+ fn mask() {
+ let a = GuestAddress(0x5050);
+ assert_eq!(GuestAddress(0x5000), a & 0xff00u64);
+ assert_eq!(GuestAddress(0x5055), a | 0x0005u64);
+ }
+
+ #[test]
+ fn add_sub() {
+ let a = GuestAddress(0x50);
+ let b = GuestAddress(0x60);
+ assert_eq!(Some(GuestAddress(0xb0)), a.checked_add(0x60));
+ assert_eq!(0x10, b.offset_from(a));
+ }
+
+ #[test]
+ fn checked_add_overflow() {
+ let a = GuestAddress(0xffffffffffffff55);
+ assert_eq!(Some(GuestAddress(0xffffffffffffff57)), a.checked_add(2));
+ assert!(a.checked_add(0xf0).is_none());
+ }
+}
diff --git a/sys_util/src/guest_memory.rs b/sys_util/src/guest_memory.rs
new file mode 100644
index 0000000..40ae925
--- /dev/null
+++ b/sys_util/src/guest_memory.rs
@@ -0,0 +1,719 @@
+// Copyright 2017 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.
+
+//! Track memory regions that are mapped to the guest VM.
+
+use std::ffi::CStr;
+use std::fmt::{self, Display};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::result;
+use std::sync::Arc;
+
+use crate::guest_address::GuestAddress;
+use crate::mmap::{self, MemoryMapping};
+use crate::shm::{MemfdSeals, SharedMemory};
+use crate::{errno, pagesize};
+use data_model::volatile_memory::*;
+use data_model::DataInit;
+
+#[derive(Debug)]
+pub enum Error {
+ InvalidGuestAddress(GuestAddress),
+ MemoryAccess(GuestAddress, mmap::Error),
+ MemoryMappingFailed(mmap::Error),
+ MemoryRegionOverlap,
+ MemoryNotAligned,
+ MemoryCreationFailed(errno::Error),
+ MemorySetSizeFailed(errno::Error),
+ MemoryAddSealsFailed(errno::Error),
+ ShortWrite { expected: usize, completed: usize },
+ ShortRead { expected: usize, completed: usize },
+}
+pub type Result<T> = result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ InvalidGuestAddress(addr) => write!(f, "invalid guest address {}", addr),
+ MemoryAccess(addr, e) => {
+ write!(f, "invalid guest memory access at addr={}: {}", addr, e)
+ }
+ MemoryMappingFailed(e) => write!(f, "failed to map guest memory: {}", e),
+ MemoryRegionOverlap => write!(f, "memory regions overlap"),
+ MemoryNotAligned => write!(f, "memfd regions must be page aligned"),
+ MemoryCreationFailed(_) => write!(f, "failed to create memfd region"),
+ MemorySetSizeFailed(e) => write!(f, "failed to set memfd region size: {}", e),
+ MemoryAddSealsFailed(e) => write!(f, "failed to set seals on memfd region: {}", e),
+ ShortWrite {
+ expected,
+ completed,
+ } => write!(
+ f,
+ "incomplete write of {} instead of {} bytes",
+ completed, expected,
+ ),
+ ShortRead {
+ expected,
+ completed,
+ } => write!(
+ f,
+ "incomplete read of {} instead of {} bytes",
+ completed, expected,
+ ),
+ }
+ }
+}
+
+struct MemoryRegion {
+ mapping: MemoryMapping,
+ guest_base: GuestAddress,
+ memfd_offset: usize,
+}
+
+fn region_end(region: &MemoryRegion) -> GuestAddress {
+ // unchecked_add is safe as the region bounds were checked when it was created.
+ region
+ .guest_base
+ .unchecked_add(region.mapping.size() as u64)
+}
+
+/// Tracks a memory region and where it is mapped in the guest, along with a shm
+/// fd of the underlying memory regions.
+#[derive(Clone)]
+pub struct GuestMemory {
+ regions: Arc<Vec<MemoryRegion>>,
+ memfd: Option<Arc<SharedMemory>>,
+}
+
+impl AsRawFd for GuestMemory {
+ fn as_raw_fd(&self) -> RawFd {
+ match &self.memfd {
+ Some(memfd) => memfd.as_raw_fd(),
+ None => panic!("GuestMemory is not backed by a memfd"),
+ }
+ }
+}
+
+impl GuestMemory {
+ /// Creates backing memfd for GuestMemory regions
+ fn create_memfd(ranges: &[(GuestAddress, u64)]) -> Result<SharedMemory> {
+ let mut aligned_size = 0;
+ let pg_size = pagesize();
+ for range in ranges {
+ if range.1 % pg_size as u64 != 0 {
+ return Err(Error::MemoryNotAligned);
+ }
+
+ aligned_size += range.1;
+ }
+
+ let mut seals = MemfdSeals::new();
+
+ seals.set_shrink_seal();
+ seals.set_grow_seal();
+ seals.set_seal_seal();
+
+ let mut memfd =
+ SharedMemory::new(Some(CStr::from_bytes_with_nul(b"crosvm_guest\0").unwrap()))
+ .map_err(Error::MemoryCreationFailed)?;
+ memfd
+ .set_size(aligned_size)
+ .map_err(Error::MemorySetSizeFailed)?;
+ memfd
+ .add_seals(seals)
+ .map_err(Error::MemoryAddSealsFailed)?;
+
+ Ok(memfd)
+ }
+
+ /// Creates a container for guest memory regions.
+ /// Valid memory regions are specified as a Vec of (Address, Size) tuples sorted by Address.
+ pub fn new(ranges: &[(GuestAddress, u64)]) -> Result<GuestMemory> {
+ // Create memfd
+
+ // TODO(prilik) remove optional memfd once parallel CQ lands (crbug.com/942183).
+ // Many classic CQ builders run old kernels without memfd support, resulting in test
+ // failures. It's less effort to introduce this temporary optional path than to
+ // manually mark all affected tests as ignore.
+ let memfd = match GuestMemory::create_memfd(ranges) {
+ Err(Error::MemoryCreationFailed { .. }) => {
+ warn!("GuestMemory is not backed by a memfd");
+ None
+ }
+ Err(e) => return Err(e),
+ Ok(memfd) => Some(memfd),
+ };
+
+ // Create memory regions
+ let mut regions = Vec::<MemoryRegion>::new();
+ let mut offset = 0;
+
+ for range in ranges {
+ if let Some(last) = regions.last() {
+ if last
+ .guest_base
+ .checked_add(last.mapping.size() as u64)
+ .map_or(true, |a| a > range.0)
+ {
+ return Err(Error::MemoryRegionOverlap);
+ }
+ }
+
+ let mapping = match &memfd {
+ Some(memfd) => MemoryMapping::from_fd_offset(memfd, range.1 as usize, offset),
+ None => MemoryMapping::new(range.1 as usize),
+ }
+ .map_err(Error::MemoryMappingFailed)?;
+
+ regions.push(MemoryRegion {
+ mapping,
+ guest_base: range.0,
+ memfd_offset: offset,
+ });
+
+ offset += range.1 as usize;
+ }
+
+ Ok(GuestMemory {
+ regions: Arc::new(regions),
+ memfd: match memfd {
+ Some(memfd) => Some(Arc::new(memfd)),
+ None => None,
+ },
+ })
+ }
+
+ /// Returns the end address of memory.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use sys_util::{GuestAddress, GuestMemory, MemoryMapping};
+ /// # fn test_end_addr() -> Result<(), ()> {
+ /// let start_addr = GuestAddress(0x1000);
+ /// let mut gm = GuestMemory::new(&vec![(start_addr, 0x400)]).map_err(|_| ())?;
+ /// assert_eq!(start_addr.checked_add(0x400), Some(gm.end_addr()));
+ /// Ok(())
+ /// # }
+ /// ```
+ pub fn end_addr(&self) -> GuestAddress {
+ self.regions
+ .iter()
+ .max_by_key(|region| region.guest_base)
+ .map_or(GuestAddress(0), |region| region_end(region))
+ }
+
+ /// Returns the total size of memory in bytes.
+ pub fn memory_size(&self) -> u64 {
+ self.regions
+ .iter()
+ .map(|region| region.mapping.size() as u64)
+ .sum()
+ }
+
+ /// Returns true if the given address is within the memory range available to the guest.
+ pub fn address_in_range(&self, addr: GuestAddress) -> bool {
+ addr < self.end_addr()
+ }
+
+ /// Returns the address plus the offset if it is in range.
+ pub fn checked_offset(&self, addr: GuestAddress, offset: u64) -> Option<GuestAddress> {
+ addr.checked_add(offset)
+ .and_then(|a| if a < self.end_addr() { Some(a) } else { None })
+ }
+
+ /// Returns the size of the memory region in bytes.
+ pub fn num_regions(&self) -> u64 {
+ self.regions.len() as u64
+ }
+
+ /// Madvise away the address range in the host that is associated with the given guest range.
+ pub fn remove_range(&self, addr: GuestAddress, count: u64) -> Result<()> {
+ self.do_in_region(addr, move |mapping, offset| {
+ mapping
+ .remove_range(offset, count as usize)
+ .map_err(|e| Error::MemoryAccess(addr, e))
+ })
+ }
+
+ /// Perform the specified action on each region's addresses.
+ ///
+ /// Callback is called with arguments:
+ /// * index: usize
+ /// * guest_addr : GuestAddress
+ /// * size: usize
+ /// * host_addr: usize
+ /// * memfd_offset: usize
+ pub fn with_regions<F, E>(&self, mut cb: F) -> result::Result<(), E>
+ where
+ F: FnMut(usize, GuestAddress, usize, usize, usize) -> result::Result<(), E>,
+ {
+ for (index, region) in self.regions.iter().enumerate() {
+ cb(
+ index,
+ region.guest_base,
+ region.mapping.size(),
+ region.mapping.as_ptr() as usize,
+ region.memfd_offset,
+ )?;
+ }
+ Ok(())
+ }
+
+ /// Writes a slice to guest memory at the specified guest address.
+ /// Returns the number of bytes written. The number of bytes written can
+ /// be less than the length of the slice if there isn't enough room in the
+ /// memory region.
+ ///
+ /// # Examples
+ /// * Write a slice at guestaddress 0x200.
+ ///
+ /// ```
+ /// # use sys_util::{GuestAddress, GuestMemory, MemoryMapping};
+ /// # fn test_write_u64() -> Result<(), ()> {
+ /// # let start_addr = GuestAddress(0x1000);
+ /// # let mut gm = GuestMemory::new(&vec![(start_addr, 0x400)]).map_err(|_| ())?;
+ /// let res = gm.write_at_addr(&[1,2,3,4,5], GuestAddress(0x200)).map_err(|_| ())?;
+ /// assert_eq!(5, res);
+ /// Ok(())
+ /// # }
+ /// ```
+ pub fn write_at_addr(&self, buf: &[u8], guest_addr: GuestAddress) -> Result<usize> {
+ self.do_in_region(guest_addr, move |mapping, offset| {
+ mapping
+ .write_slice(buf, offset)
+ .map_err(|e| Error::MemoryAccess(guest_addr, e))
+ })
+ }
+
+ /// Writes the entire contents of a slice to guest memory at the specified
+ /// guest address.
+ ///
+ /// Returns an error if there isn't enough room in the memory region to
+ /// complete the entire write. Part of the data may have been written
+ /// nevertheless.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sys_util::{guest_memory, GuestAddress, GuestMemory};
+ ///
+ /// fn test_write_all() -> guest_memory::Result<()> {
+ /// let ranges = &[(GuestAddress(0x1000), 0x400)];
+ /// let gm = GuestMemory::new(ranges)?;
+ /// gm.write_all_at_addr(b"zyxwvut", GuestAddress(0x1200))
+ /// }
+ /// ```
+ pub fn write_all_at_addr(&self, buf: &[u8], guest_addr: GuestAddress) -> Result<()> {
+ let expected = buf.len();
+ let completed = self.write_at_addr(buf, guest_addr)?;
+ if expected == completed {
+ Ok(())
+ } else {
+ Err(Error::ShortWrite {
+ expected,
+ completed,
+ })
+ }
+ }
+
+ /// Reads to a slice from guest memory at the specified guest address.
+ /// Returns the number of bytes read. The number of bytes read can
+ /// be less than the length of the slice if there isn't enough room in the
+ /// memory region.
+ ///
+ /// # Examples
+ /// * Read a slice of length 16 at guestaddress 0x200.
+ ///
+ /// ```
+ /// # use sys_util::{GuestAddress, GuestMemory, MemoryMapping};
+ /// # fn test_write_u64() -> Result<(), ()> {
+ /// # let start_addr = GuestAddress(0x1000);
+ /// # let mut gm = GuestMemory::new(&vec![(start_addr, 0x400)]).map_err(|_| ())?;
+ /// let buf = &mut [0u8; 16];
+ /// let res = gm.read_at_addr(buf, GuestAddress(0x200)).map_err(|_| ())?;
+ /// assert_eq!(16, res);
+ /// Ok(())
+ /// # }
+ /// ```
+ pub fn read_at_addr(&self, buf: &mut [u8], guest_addr: GuestAddress) -> Result<usize> {
+ self.do_in_region(guest_addr, move |mapping, offset| {
+ mapping
+ .read_slice(buf, offset)
+ .map_err(|e| Error::MemoryAccess(guest_addr, e))
+ })
+ }
+
+ /// Reads from guest memory at the specified address to fill the entire
+ /// buffer.
+ ///
+ /// Returns an error if there isn't enough room in the memory region to fill
+ /// the entire buffer. Part of the buffer may have been filled nevertheless.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sys_util::{guest_memory, GuestAddress, GuestMemory, MemoryMapping};
+ ///
+ /// fn test_read_exact() -> guest_memory::Result<()> {
+ /// let ranges = &[(GuestAddress(0x1000), 0x400)];
+ /// let gm = GuestMemory::new(ranges)?;
+ /// let mut buffer = [0u8; 0x200];
+ /// gm.read_exact_at_addr(&mut buffer, GuestAddress(0x1200))
+ /// }
+ /// ```
+ pub fn read_exact_at_addr(&self, buf: &mut [u8], guest_addr: GuestAddress) -> Result<()> {
+ let expected = buf.len();
+ let completed = self.read_at_addr(buf, guest_addr)?;
+ if expected == completed {
+ Ok(())
+ } else {
+ Err(Error::ShortRead {
+ expected,
+ completed,
+ })
+ }
+ }
+
+ /// Reads an object from guest memory at the given guest address.
+ /// Reading from a volatile area isn't strictly safe as it could change
+ /// mid-read. However, as long as the type T is plain old data and can
+ /// handle random initialization, everything will be OK.
+ ///
+ /// # Examples
+ /// * Read a u64 from two areas of guest memory backed by separate mappings.
+ ///
+ /// ```
+ /// # use sys_util::{GuestAddress, GuestMemory, MemoryMapping};
+ /// # fn test_read_u64() -> Result<u64, ()> {
+ /// # let start_addr1 = GuestAddress(0x0);
+ /// # let start_addr2 = GuestAddress(0x400);
+ /// # let mut gm = GuestMemory::new(&vec![(start_addr1, 0x400), (start_addr2, 0x400)])
+ /// # .map_err(|_| ())?;
+ /// let num1: u64 = gm.read_obj_from_addr(GuestAddress(32)).map_err(|_| ())?;
+ /// let num2: u64 = gm.read_obj_from_addr(GuestAddress(0x400+32)).map_err(|_| ())?;
+ /// # Ok(num1 + num2)
+ /// # }
+ /// ```
+ pub fn read_obj_from_addr<T: DataInit>(&self, guest_addr: GuestAddress) -> Result<T> {
+ self.do_in_region(guest_addr, |mapping, offset| {
+ mapping
+ .read_obj(offset)
+ .map_err(|e| Error::MemoryAccess(guest_addr, e))
+ })
+ }
+
+ /// Writes an object to the memory region at the specified guest address.
+ /// Returns Ok(()) if the object fits, or Err if it extends past the end.
+ ///
+ /// # Examples
+ /// * Write a u64 at guest address 0x1100.
+ ///
+ /// ```
+ /// # use sys_util::{GuestAddress, GuestMemory, MemoryMapping};
+ /// # fn test_write_u64() -> Result<(), ()> {
+ /// # let start_addr = GuestAddress(0x1000);
+ /// # let mut gm = GuestMemory::new(&vec![(start_addr, 0x400)]).map_err(|_| ())?;
+ /// gm.write_obj_at_addr(55u64, GuestAddress(0x1100))
+ /// .map_err(|_| ())
+ /// # }
+ /// ```
+ pub fn write_obj_at_addr<T: DataInit>(&self, val: T, guest_addr: GuestAddress) -> Result<()> {
+ self.do_in_region(guest_addr, move |mapping, offset| {
+ mapping
+ .write_obj(val, offset)
+ .map_err(|e| Error::MemoryAccess(guest_addr, e))
+ })
+ }
+
+ /// Reads data from a file descriptor and writes it to guest memory.
+ ///
+ /// # Arguments
+ /// * `guest_addr` - Begin writing memory at this offset.
+ /// * `src` - Read from `src` to memory.
+ /// * `count` - Read `count` bytes from `src` to memory.
+ ///
+ /// # Examples
+ ///
+ /// * Read bytes from /dev/urandom
+ ///
+ /// ```
+ /// # use sys_util::{GuestAddress, GuestMemory, MemoryMapping};
+ /// # use std::fs::File;
+ /// # use std::path::Path;
+ /// # fn test_read_random() -> Result<u32, ()> {
+ /// # let start_addr = GuestAddress(0x1000);
+ /// # let gm = GuestMemory::new(&vec![(start_addr, 0x400)]).map_err(|_| ())?;
+ /// let mut file = File::open(Path::new("/dev/urandom")).map_err(|_| ())?;
+ /// let addr = GuestAddress(0x1010);
+ /// gm.read_to_memory(addr, &mut file, 128).map_err(|_| ())?;
+ /// let read_addr = addr.checked_add(8).ok_or(())?;
+ /// let rand_val: u32 = gm.read_obj_from_addr(read_addr).map_err(|_| ())?;
+ /// # Ok(rand_val)
+ /// # }
+ /// ```
+ pub fn read_to_memory(
+ &self,
+ guest_addr: GuestAddress,
+ src: &AsRawFd,
+ count: usize,
+ ) -> Result<()> {
+ self.do_in_region(guest_addr, move |mapping, offset| {
+ mapping
+ .read_to_memory(offset, src, count)
+ .map_err(|e| Error::MemoryAccess(guest_addr, e))
+ })
+ }
+
+ /// Writes data from memory to a file descriptor.
+ ///
+ /// # Arguments
+ /// * `guest_addr` - Begin reading memory from this offset.
+ /// * `dst` - Write from memory to `dst`.
+ /// * `count` - Read `count` bytes from memory to `src`.
+ ///
+ /// # Examples
+ ///
+ /// * Write 128 bytes to /dev/null
+ ///
+ /// ```
+ /// # use sys_util::{GuestAddress, GuestMemory, MemoryMapping};
+ /// # use std::fs::File;
+ /// # use std::path::Path;
+ /// # fn test_write_null() -> Result<(), ()> {
+ /// # let start_addr = GuestAddress(0x1000);
+ /// # let gm = GuestMemory::new(&vec![(start_addr, 0x400)]).map_err(|_| ())?;
+ /// let mut file = File::open(Path::new("/dev/null")).map_err(|_| ())?;
+ /// let addr = GuestAddress(0x1010);
+ /// gm.write_from_memory(addr, &mut file, 128).map_err(|_| ())?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn write_from_memory(
+ &self,
+ guest_addr: GuestAddress,
+ dst: &AsRawFd,
+ count: usize,
+ ) -> Result<()> {
+ self.do_in_region(guest_addr, move |mapping, offset| {
+ mapping
+ .write_from_memory(offset, dst, count)
+ .map_err(|e| Error::MemoryAccess(guest_addr, e))
+ })
+ }
+
+ /// Convert a GuestAddress into a pointer in the address space of this
+ /// process. This should only be necessary for giving addresses to the
+ /// kernel, as with vhost ioctls. Normal reads/writes to guest memory should
+ /// be done through `write_from_memory`, `read_obj_from_addr`, etc.
+ ///
+ /// # Arguments
+ /// * `guest_addr` - Guest address to convert.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use sys_util::{GuestAddress, GuestMemory};
+ /// # fn test_host_addr() -> Result<(), ()> {
+ /// let start_addr = GuestAddress(0x1000);
+ /// let mut gm = GuestMemory::new(&vec![(start_addr, 0x500)]).map_err(|_| ())?;
+ /// let addr = gm.get_host_address(GuestAddress(0x1200)).unwrap();
+ /// println!("Host address is {:p}", addr);
+ /// Ok(())
+ /// # }
+ /// ```
+ pub fn get_host_address(&self, guest_addr: GuestAddress) -> Result<*const u8> {
+ self.do_in_region(guest_addr, |mapping, offset| {
+ // This is safe; `do_in_region` already checks that offset is in
+ // bounds.
+ Ok(unsafe { mapping.as_ptr().add(offset) } as *const u8)
+ })
+ }
+
+ pub fn do_in_region<F, T>(&self, guest_addr: GuestAddress, cb: F) -> Result<T>
+ where
+ F: FnOnce(&MemoryMapping, usize) -> Result<T>,
+ {
+ for region in self.regions.iter() {
+ if guest_addr >= region.guest_base && guest_addr < region_end(region) {
+ return cb(
+ ®ion.mapping,
+ guest_addr.offset_from(region.guest_base) as usize,
+ );
+ }
+ }
+ Err(Error::InvalidGuestAddress(guest_addr))
+ }
+}
+
+impl VolatileMemory for GuestMemory {
+ fn get_slice(&self, offset: u64, count: u64) -> VolatileMemoryResult<VolatileSlice> {
+ for region in self.regions.iter() {
+ if offset >= region.guest_base.0 && offset < region_end(region).0 {
+ return region
+ .mapping
+ .get_slice(offset - region.guest_base.0, count);
+ }
+ }
+ Err(VolatileMemoryError::OutOfBounds { addr: offset })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::kernel_has_memfd;
+
+ #[test]
+ fn test_alignment() {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x1000);
+
+ assert!(GuestMemory::new(&vec![(start_addr1, 0x100), (start_addr2, 0x400)]).is_err());
+ assert!(GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x1000)]).is_ok());
+ }
+
+ #[test]
+ fn two_regions() {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x4000);
+ assert!(GuestMemory::new(&vec![(start_addr1, 0x4000), (start_addr2, 0x4000)]).is_ok());
+ }
+
+ #[test]
+ fn overlap_memory() {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x1000);
+ assert!(GuestMemory::new(&vec![(start_addr1, 0x2000), (start_addr2, 0x2000)]).is_err());
+ }
+
+ #[test]
+ fn test_read_u64() {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x1000);
+ let gm = GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
+
+ let val1: u64 = 0xaa55aa55aa55aa55;
+ let val2: u64 = 0x55aa55aa55aa55aa;
+ gm.write_obj_at_addr(val1, GuestAddress(0x500)).unwrap();
+ gm.write_obj_at_addr(val2, GuestAddress(0x1000 + 32))
+ .unwrap();
+ let num1: u64 = gm.read_obj_from_addr(GuestAddress(0x500)).unwrap();
+ let num2: u64 = gm.read_obj_from_addr(GuestAddress(0x1000 + 32)).unwrap();
+ assert_eq!(val1, num1);
+ assert_eq!(val2, num2);
+ }
+
+ #[test]
+ fn test_ref_load_u64() {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x1000);
+ let gm = GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
+
+ let val1: u64 = 0xaa55aa55aa55aa55;
+ let val2: u64 = 0x55aa55aa55aa55aa;
+ gm.write_obj_at_addr(val1, GuestAddress(0x500)).unwrap();
+ gm.write_obj_at_addr(val2, GuestAddress(0x1000 + 32))
+ .unwrap();
+ let num1: u64 = gm.get_ref(0x500).unwrap().load();
+ let num2: u64 = gm.get_ref(0x1000 + 32).unwrap().load();
+ assert_eq!(val1, num1);
+ assert_eq!(val2, num2);
+ }
+
+ #[test]
+ fn test_ref_store_u64() {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x1000);
+ let gm = GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
+
+ let val1: u64 = 0xaa55aa55aa55aa55;
+ let val2: u64 = 0x55aa55aa55aa55aa;
+ gm.get_ref(0x500).unwrap().store(val1);
+ gm.get_ref(0x1000 + 32).unwrap().store(val2);
+ let num1: u64 = gm.read_obj_from_addr(GuestAddress(0x500)).unwrap();
+ let num2: u64 = gm.read_obj_from_addr(GuestAddress(0x1000 + 32)).unwrap();
+ assert_eq!(val1, num1);
+ assert_eq!(val2, num2);
+ }
+
+ #[test]
+ fn test_memory_size() {
+ let start_region1 = GuestAddress(0x0);
+ let size_region1 = 0x1000;
+ let start_region2 = GuestAddress(0x10000);
+ let size_region2 = 0x2000;
+ let gm = GuestMemory::new(&vec![
+ (start_region1, size_region1),
+ (start_region2, size_region2),
+ ])
+ .unwrap();
+
+ let mem_size = gm.memory_size();
+ assert_eq!(mem_size, size_region1 + size_region2);
+ }
+
+ // Get the base address of the mapping for a GuestAddress.
+ fn get_mapping(mem: &GuestMemory, addr: GuestAddress) -> Result<*const u8> {
+ mem.do_in_region(addr, |mapping, _| Ok(mapping.as_ptr() as *const u8))
+ }
+
+ #[test]
+ fn guest_to_host() {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x1000);
+ let mem = GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x4000)]).unwrap();
+
+ // Verify the host addresses match what we expect from the mappings.
+ let addr1_base = get_mapping(&mem, start_addr1).unwrap();
+ let addr2_base = get_mapping(&mem, start_addr2).unwrap();
+ let host_addr1 = mem.get_host_address(start_addr1).unwrap();
+ let host_addr2 = mem.get_host_address(start_addr2).unwrap();
+ assert_eq!(host_addr1, addr1_base);
+ assert_eq!(host_addr2, addr2_base);
+
+ // Check that a bad address returns an error.
+ let bad_addr = GuestAddress(0x123456);
+ assert!(mem.get_host_address(bad_addr).is_err());
+ }
+
+ #[test]
+ fn memfd_offset() {
+ if !kernel_has_memfd() {
+ return;
+ }
+
+ let start_region1 = GuestAddress(0x0);
+ let size_region1 = 0x1000;
+ let start_region2 = GuestAddress(0x10000);
+ let size_region2 = 0x2000;
+ let gm = GuestMemory::new(&vec![
+ (start_region1, size_region1),
+ (start_region2, size_region2),
+ ])
+ .unwrap();
+
+ gm.write_obj_at_addr(0x1337u16, GuestAddress(0x0)).unwrap();
+ gm.write_obj_at_addr(0x0420u16, GuestAddress(0x10000))
+ .unwrap();
+
+ let _ = gm.with_regions::<_, ()>(|index, _, size, _, memfd_offset| {
+ let mmap = MemoryMapping::from_fd_offset(&gm, size, memfd_offset).unwrap();
+
+ if index == 0 {
+ assert!(mmap.read_obj::<u16>(0x0).unwrap() == 0x1337u16);
+ }
+
+ if index == 1 {
+ assert!(mmap.read_obj::<u16>(0x0).unwrap() == 0x0420u16);
+ }
+
+ Ok(())
+ });
+ }
+}
diff --git a/sys_util/src/handle_eintr.rs b/sys_util/src/handle_eintr.rs
new file mode 100644
index 0000000..d8cc0b3
--- /dev/null
+++ b/sys_util/src/handle_eintr.rs
@@ -0,0 +1,251 @@
+// Copyright 2017 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.
+
+//! Macro and helper trait for handling interrupted routines.
+
+use std::io;
+
+use libc::EINTR;
+
+/// Trait for determining if a result indicates the operation was interrupted.
+pub trait InterruptibleResult {
+ /// Returns `true` if this result indicates the operation was interrupted and should be retried,
+ /// and `false` in all other cases.
+ fn is_interrupted(&self) -> bool;
+}
+
+impl<T> InterruptibleResult for crate::Result<T> {
+ fn is_interrupted(&self) -> bool {
+ match self {
+ Err(e) if e.errno() == EINTR => true,
+ _ => false,
+ }
+ }
+}
+
+impl<T> InterruptibleResult for io::Result<T> {
+ fn is_interrupted(&self) -> bool {
+ match self {
+ Err(e) if e.kind() == io::ErrorKind::Interrupted => true,
+ _ => false,
+ }
+ }
+}
+
+/// Macro that retries the given expression every time its result indicates it was interrupted (i.e.
+/// returned `EINTR`). This is useful for operations that are prone to being interrupted by
+/// signals, such as blocking syscalls.
+///
+/// The given expression `$x` can return
+///
+/// * `sys_util::Result` in which case the expression is retried if the `Error::errno()` is `EINTR`.
+/// * `std::io::Result` in which case the expression is retried if the `ErrorKind` is `ErrorKind::Interrupted`.
+///
+/// Note that if expression returns i32 (i.e. either -1 or error code), then handle_eintr_errno()
+/// or handle_eintr_rc() should be used instead.
+///
+/// In all cases where the result does not indicate that the expression was interrupted, the result
+/// is returned verbatim to the caller of this macro.
+///
+/// See the section titled _Interruption of system calls and library functions by signal handlers_
+/// on the man page for `signal(7)` to see more information about interruptible syscalls.
+///
+/// To summarize, routines that use one of these syscalls _might_ need to handle `EINTR`:
+///
+/// * `accept(2)`
+/// * `clock_nanosleep(2)`
+/// * `connect(2)`
+/// * `epoll_pwait(2)`
+/// * `epoll_wait(2)`
+/// * `fcntl(2)`
+/// * `fifo(7)`
+/// * `flock(2)`
+/// * `futex(2)`
+/// * `getrandom(2)`
+/// * `inotify(7)`
+/// * `io_getevents(2)`
+/// * `ioctl(2)`
+/// * `mq_receive(3)`
+/// * `mq_send(3)`
+/// * `mq_timedreceive(3)`
+/// * `mq_timedsend(3)`
+/// * `msgrcv(2)`
+/// * `msgsnd(2)`
+/// * `nanosleep(2)`
+/// * `open(2)`
+/// * `pause(2)`
+/// * `poll(2)`
+/// * `ppoll(2)`
+/// * `pselect(2)`
+/// * `pthread_cond_wait(3)`
+/// * `pthread_mutex_lock(3)`
+/// * `read(2)`
+/// * `readv(2)`
+/// * `recv(2)`
+/// * `recvfrom(2)`
+/// * `recvmmsg(2)`
+/// * `recvmsg(2)`
+/// * `select(2)`
+/// * `sem_timedwait(3)`
+/// * `sem_wait(3)`
+/// * `semop(2)`
+/// * `semtimedop(2)`
+/// * `send(2)`
+/// * `sendmsg(2)`
+/// * `sendto(2)`
+/// * `setsockopt(2)`
+/// * `sigsuspend(2)`
+/// * `sigtimedwait(2)`
+/// * `sigwaitinfo(2)`
+/// * `sleep(3)`
+/// * `usleep(3)`
+/// * `wait(2)`
+/// * `wait3(2)`
+/// * `wait4(2)`
+/// * `waitid(2)`
+/// * `waitpid(2)`
+/// * `write(2)`
+/// * `writev(2)`
+///
+/// # Examples
+///
+/// ```
+/// # use sys_util::handle_eintr;
+/// # use std::io::stdin;
+/// # fn main() {
+/// let mut line = String::new();
+/// let res = handle_eintr!(stdin().read_line(&mut line));
+/// # }
+/// ```
+#[macro_export]
+macro_rules! handle_eintr {
+ ($x:expr) => {{
+ use $crate::handle_eintr::InterruptibleResult;
+ let res;
+ loop {
+ match $x {
+ ref v if v.is_interrupted() => continue,
+ v => {
+ res = v;
+ break;
+ }
+ }
+ }
+ res
+ }};
+}
+
+/// Macro that retries the given expression every time its result indicates it was interrupted.
+/// It is intended to use with system functions that return `EINTR` and other error codes
+/// directly as their result.
+/// Most of reentrant functions use this way of signalling errors.
+#[macro_export]
+macro_rules! handle_eintr_rc {
+ ($x:expr) => {{
+ use libc::EINTR;
+ let mut res;
+ loop {
+ res = $x;
+ if res != EINTR {
+ break;
+ }
+ }
+ res
+ }};
+}
+
+/// Macro that retries the given expression every time its result indicates it was interrupted.
+/// It is intended to use with system functions that signal error by returning `-1` and setting
+/// `errno` to appropriate error code (`EINTR`, `EINVAL`, etc.)
+/// Most of standard non-reentrant libc functions use this way of signalling errors.
+#[macro_export]
+macro_rules! handle_eintr_errno {
+ ($x:expr) => {{
+ use libc::EINTR;
+ use $crate::Error;
+ let mut res;
+ loop {
+ res = $x;
+ if res != -1 || Error::last() != Error::new(EINTR) {
+ break;
+ }
+ }
+ res
+ }};
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::errno::set_errno;
+ use crate::Error as SysError;
+
+ #[test]
+ fn i32_eintr_rc() {
+ let mut count = 3;
+ let mut dummy = || {
+ count -= 1;
+ if count > 0 {
+ EINTR
+ } else {
+ 0
+ }
+ };
+ let res = handle_eintr_rc!(dummy());
+ assert_eq!(res, 0);
+ assert_eq!(count, 0);
+ }
+
+ #[test]
+ fn i32_eintr_errno() {
+ let mut count = 3;
+ let mut dummy = || {
+ count -= 1;
+ if count > 0 {
+ set_errno(EINTR);
+ -1
+ } else {
+ 56
+ }
+ };
+ let res = handle_eintr_errno!(dummy());
+ assert_eq!(res, 56);
+ assert_eq!(count, 0);
+ }
+
+ #[test]
+ fn sys_eintr() {
+ let mut count = 7;
+ let mut dummy = || {
+ count -= 1;
+ if count > 1 {
+ Err(SysError::new(EINTR))
+ } else {
+ Ok(101)
+ }
+ };
+ let res = handle_eintr!(dummy());
+ assert_eq!(res, Ok(101));
+ assert_eq!(count, 1);
+ }
+
+ #[test]
+ fn io_eintr() {
+ let mut count = 108;
+ let mut dummy = || {
+ count -= 1;
+ if count > 99 {
+ Err(io::Error::new(
+ io::ErrorKind::Interrupted,
+ "interrupted again :(",
+ ))
+ } else {
+ Ok(32)
+ }
+ };
+ let res = handle_eintr!(dummy());
+ assert_eq!(res.unwrap(), 32);
+ assert_eq!(count, 99);
+ }
+}
diff --git a/sys_util/src/ioctl.rs b/sys_util/src/ioctl.rs
new file mode 100644
index 0000000..a0309ff
--- /dev/null
+++ b/sys_util/src/ioctl.rs
@@ -0,0 +1,198 @@
+// Copyright 2017 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.
+
+//! Macros and wrapper functions for dealing with ioctls.
+
+use std::os::raw::*;
+use std::os::unix::io::AsRawFd;
+
+use libc;
+
+/// Raw macro to declare the expression that calculates an ioctl number
+#[macro_export]
+macro_rules! ioctl_expr {
+ ($dir:expr, $ty:expr, $nr:expr, $size:expr) => {
+ (($dir << $crate::ioctl::_IOC_DIRSHIFT)
+ | ($ty << $crate::ioctl::_IOC_TYPESHIFT)
+ | ($nr << $crate::ioctl::_IOC_NRSHIFT)
+ | ($size << $crate::ioctl::_IOC_SIZESHIFT)) as ::std::os::raw::c_ulong
+ };
+}
+
+/// Raw macro to declare a function that returns an ioctl number.
+#[macro_export]
+macro_rules! ioctl_ioc_nr {
+ ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr) => {
+ #[allow(non_snake_case)]
+ pub fn $name() -> ::std::os::raw::c_ulong {
+ $crate::ioctl_expr!($dir, $ty, $nr, $size)
+ }
+ };
+ ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr, $($v:ident),+) => {
+ #[allow(non_snake_case)]
+ pub fn $name($($v: ::std::os::raw::c_uint),+) -> ::std::os::raw::c_ulong {
+ $crate::ioctl_expr!($dir, $ty, $nr, $size)
+ }
+ };
+}
+
+/// Declare an ioctl that transfers no data.
+#[macro_export]
+macro_rules! ioctl_io_nr {
+ ($name:ident, $ty:expr, $nr:expr) => {
+ $crate::ioctl_ioc_nr!($name, $crate::ioctl::_IOC_NONE, $ty, $nr, 0);
+ };
+ ($name:ident, $ty:expr, $nr:expr, $($v:ident),+) => {
+ $crate::ioctl_ioc_nr!($name, $crate::ioctl::_IOC_NONE, $ty, $nr, 0, $($v),+);
+ };
+}
+
+/// Declare an ioctl that reads data.
+#[macro_export]
+macro_rules! ioctl_ior_nr {
+ ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
+ $crate::ioctl_ioc_nr!(
+ $name,
+ $crate::ioctl::_IOC_READ,
+ $ty,
+ $nr,
+ ::std::mem::size_of::<$size>() as u32
+ );
+ };
+ ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
+ $crate::ioctl_ioc_nr!(
+ $name,
+ $crate::ioctl::_IOC_READ,
+ $ty,
+ $nr,
+ ::std::mem::size_of::<$size>() as u32,
+ $($v),+
+ );
+ };
+}
+
+/// Declare an ioctl that writes data.
+#[macro_export]
+macro_rules! ioctl_iow_nr {
+ ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
+ $crate::ioctl_ioc_nr!(
+ $name,
+ $crate::ioctl::_IOC_WRITE,
+ $ty,
+ $nr,
+ ::std::mem::size_of::<$size>() as u32
+ );
+ };
+ ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
+ $crate::ioctl_ioc_nr!(
+ $name,
+ $crate::ioctl::_IOC_WRITE,
+ $ty,
+ $nr,
+ ::std::mem::size_of::<$size>() as u32,
+ $($v),+
+ );
+ };
+}
+
+/// Declare an ioctl that reads and writes data.
+#[macro_export]
+macro_rules! ioctl_iowr_nr {
+ ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
+ $crate::ioctl_ioc_nr!(
+ $name,
+ $crate::ioctl::_IOC_READ | $crate::ioctl::_IOC_WRITE,
+ $ty,
+ $nr,
+ ::std::mem::size_of::<$size>() as u32
+ );
+ };
+ ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
+ $crate::ioctl_ioc_nr!(
+ $name,
+ $crate::ioctl::_IOC_READ | $crate::ioctl::_IOC_WRITE,
+ $ty,
+ $nr,
+ ::std::mem::size_of::<$size>() as u32,
+ $($v),+
+ );
+ };
+}
+
+pub const _IOC_NRBITS: c_uint = 8;
+pub const _IOC_TYPEBITS: c_uint = 8;
+pub const _IOC_SIZEBITS: c_uint = 14;
+pub const _IOC_DIRBITS: c_uint = 2;
+pub const _IOC_NRMASK: c_uint = 255;
+pub const _IOC_TYPEMASK: c_uint = 255;
+pub const _IOC_SIZEMASK: c_uint = 16383;
+pub const _IOC_DIRMASK: c_uint = 3;
+pub const _IOC_NRSHIFT: c_uint = 0;
+pub const _IOC_TYPESHIFT: c_uint = 8;
+pub const _IOC_SIZESHIFT: c_uint = 16;
+pub const _IOC_DIRSHIFT: c_uint = 30;
+pub const _IOC_NONE: c_uint = 0;
+pub const _IOC_WRITE: c_uint = 1;
+pub const _IOC_READ: c_uint = 2;
+pub const IOC_IN: c_uint = 1_073_741_824;
+pub const IOC_OUT: c_uint = 2_147_483_648;
+pub const IOC_INOUT: c_uint = 3_221_225_472;
+pub const IOCSIZE_MASK: c_uint = 1_073_676_288;
+pub const IOCSIZE_SHIFT: c_uint = 16;
+
+/// Run an ioctl with no arguments.
+pub unsafe fn ioctl<F: AsRawFd>(fd: &F, nr: c_ulong) -> c_int {
+ libc::ioctl(fd.as_raw_fd(), nr, 0)
+}
+
+/// Run an ioctl with a single value argument.
+pub unsafe fn ioctl_with_val<F: AsRawFd>(fd: &F, nr: c_ulong, arg: c_ulong) -> c_int {
+ libc::ioctl(fd.as_raw_fd(), nr, arg)
+}
+
+/// Run an ioctl with an immutable reference.
+pub unsafe fn ioctl_with_ref<F: AsRawFd, T>(fd: &F, nr: c_ulong, arg: &T) -> c_int {
+ libc::ioctl(fd.as_raw_fd(), nr, arg as *const T as *const c_void)
+}
+
+/// Run an ioctl with a mutable reference.
+pub unsafe fn ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, nr: c_ulong, arg: &mut T) -> c_int {
+ libc::ioctl(fd.as_raw_fd(), nr, arg as *mut T as *mut c_void)
+}
+
+/// Run an ioctl with a raw pointer.
+pub unsafe fn ioctl_with_ptr<F: AsRawFd, T>(fd: &F, nr: c_ulong, arg: *const T) -> c_int {
+ libc::ioctl(fd.as_raw_fd(), nr, arg as *const c_void)
+}
+
+/// Run an ioctl with a mutable raw pointer.
+pub unsafe fn ioctl_with_mut_ptr<F: AsRawFd, T>(fd: &F, nr: c_ulong, arg: *mut T) -> c_int {
+ libc::ioctl(fd.as_raw_fd(), nr, arg as *mut c_void)
+}
+
+#[cfg(test)]
+mod tests {
+ const TUNTAP: ::std::os::raw::c_uint = 0x54;
+ const VHOST: ::std::os::raw::c_uint = 0xaf;
+ const EVDEV: ::std::os::raw::c_uint = 0x45;
+
+ ioctl_io_nr!(VHOST_SET_OWNER, VHOST, 0x01);
+ ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 0xcf, ::std::os::raw::c_uint);
+ ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 0xd9, ::std::os::raw::c_int);
+ ioctl_iowr_nr!(VHOST_GET_VRING_BASE, VHOST, 0x12, ::std::os::raw::c_int);
+
+ ioctl_ior_nr!(EVIOCGBIT, EVDEV, 0x20 + evt, [u8; 128], evt);
+ ioctl_io_nr!(FAKE_IOCTL_2_ARG, EVDEV, 0x01 + x + y, x, y);
+
+ #[test]
+ fn ioctl_macros() {
+ assert_eq!(0x0000af01, VHOST_SET_OWNER());
+ assert_eq!(0x800454cf, TUNGETFEATURES());
+ assert_eq!(0x400454d9, TUNSETQUEUE());
+ assert_eq!(0xc004af12, VHOST_GET_VRING_BASE());
+
+ assert_eq!(0x80804522, EVIOCGBIT(2));
+ assert_eq!(0x00004509, FAKE_IOCTL_2_ARG(3, 5));
+ }
+}
diff --git a/sys_util/src/lib.rs b/sys_util/src/lib.rs
new file mode 100644
index 0000000..9e2e623
--- /dev/null
+++ b/sys_util/src/lib.rs
@@ -0,0 +1,323 @@
+// Copyright 2017 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.
+
+//! Small system utility modules for usage by other modules.
+
+pub mod affinity;
+mod alloc;
+#[macro_use]
+pub mod handle_eintr;
+#[macro_use]
+pub mod ioctl;
+#[macro_use]
+pub mod syslog;
+mod capabilities;
+mod clock;
+mod errno;
+mod eventfd;
+mod file_flags;
+mod file_traits;
+mod fork;
+mod guest_address;
+pub mod guest_memory;
+mod mmap;
+pub mod net;
+mod passwd;
+mod poll;
+mod priority;
+mod raw_fd;
+mod seek_hole;
+mod shm;
+pub mod signal;
+mod signalfd;
+mod sock_ctrl_msg;
+mod struct_util;
+mod tempdir;
+mod terminal;
+mod timerfd;
+mod write_zeroes;
+
+pub use crate::affinity::*;
+pub use crate::alloc::LayoutAllocation;
+pub use crate::capabilities::drop_capabilities;
+pub use crate::clock::{Clock, FakeClock};
+use crate::errno::errno_result;
+pub use crate::errno::{Error, Result};
+pub use crate::eventfd::*;
+pub use crate::file_flags::*;
+pub use crate::fork::*;
+pub use crate::guest_address::*;
+pub use crate::guest_memory::*;
+pub use crate::ioctl::*;
+pub use crate::mmap::*;
+pub use crate::passwd::*;
+pub use crate::poll::*;
+pub use crate::priority::*;
+pub use crate::raw_fd::*;
+pub use crate::shm::*;
+pub use crate::signal::*;
+pub use crate::signalfd::*;
+pub use crate::sock_ctrl_msg::*;
+pub use crate::struct_util::*;
+pub use crate::tempdir::*;
+pub use crate::terminal::*;
+pub use crate::timerfd::*;
+pub use poll_token_derive::*;
+
+pub use crate::file_traits::{FileReadWriteVolatile, FileSetLen, FileSync};
+pub use crate::guest_memory::Error as GuestMemoryError;
+pub use crate::mmap::Error as MmapError;
+pub use crate::seek_hole::SeekHole;
+pub use crate::signalfd::Error as SignalFdError;
+pub use crate::write_zeroes::{PunchHole, WriteZeroes};
+
+use std::ffi::CStr;
+use std::fs::{remove_file, File};
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::os::unix::net::UnixDatagram;
+use std::ptr;
+
+use libc::{
+ c_long, gid_t, kill, pid_t, pipe2, syscall, sysconf, uid_t, waitpid, O_CLOEXEC, SIGKILL,
+ WNOHANG, _SC_PAGESIZE,
+};
+
+use syscall_defines::linux::LinuxSyscall::SYS_getpid;
+
+/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
+#[inline(always)]
+pub fn pagesize() -> usize {
+ // Trivially safe
+ unsafe { sysconf(_SC_PAGESIZE) as usize }
+}
+
+/// Uses the system's page size in bytes to round the given value up to the nearest page boundary.
+#[inline(always)]
+pub fn round_up_to_page_size(v: usize) -> usize {
+ let page_mask = pagesize() - 1;
+ (v + page_mask) & !page_mask
+}
+
+/// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used
+/// elsewhere.
+#[inline(always)]
+pub fn getpid() -> pid_t {
+ // Safe because this syscall can never fail and we give it a valid syscall number.
+ unsafe { syscall(SYS_getpid as c_long) as pid_t }
+}
+
+/// Safe wrapper for `geteuid(2)`.
+#[inline(always)]
+pub fn geteuid() -> uid_t {
+ // trivially safe
+ unsafe { libc::geteuid() }
+}
+
+/// Safe wrapper for `getegid(2)`.
+#[inline(always)]
+pub fn getegid() -> gid_t {
+ // trivially safe
+ unsafe { libc::getegid() }
+}
+
+/// Safe wrapper for chown(2).
+#[inline(always)]
+pub fn chown(path: &CStr, uid: uid_t, gid: gid_t) -> Result<()> {
+ // Safe since we pass in a valid string pointer and check the return value.
+ let ret = unsafe { libc::chown(path.as_ptr(), uid, gid) };
+
+ if ret < 0 {
+ errno_result()
+ } else {
+ Ok(())
+ }
+}
+
+/// The operation to perform with `flock`.
+pub enum FlockOperation {
+ LockShared,
+ LockExclusive,
+ Unlock,
+}
+
+/// Safe wrapper for flock(2) with the operation `op` and optionally `nonblocking`. The lock will be
+/// dropped automatically when `file` is dropped.
+#[inline(always)]
+pub fn flock(file: &dyn AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()> {
+ let mut operation = match op {
+ FlockOperation::LockShared => libc::LOCK_SH,
+ FlockOperation::LockExclusive => libc::LOCK_EX,
+ FlockOperation::Unlock => libc::LOCK_UN,
+ };
+
+ if nonblocking {
+ operation |= libc::LOCK_NB;
+ }
+
+ // Safe since we pass in a valid fd and flock operation, and check the return value.
+ let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };
+
+ if ret < 0 {
+ errno_result()
+ } else {
+ Ok(())
+ }
+}
+
+/// The operation to perform with `fallocate`.
+pub enum FallocateMode {
+ PunchHole,
+ ZeroRange,
+}
+
+/// Safe wrapper for `fallocate()`.
+pub fn fallocate(
+ file: &dyn AsRawFd,
+ mode: FallocateMode,
+ keep_size: bool,
+ offset: u64,
+ len: u64,
+) -> Result<()> {
+ let offset = if offset > libc::off64_t::max_value() as u64 {
+ return Err(Error::new(libc::EINVAL));
+ } else {
+ offset as libc::off64_t
+ };
+
+ let len = if len > libc::off64_t::max_value() as u64 {
+ return Err(Error::new(libc::EINVAL));
+ } else {
+ len as libc::off64_t
+ };
+
+ let mut mode = match mode {
+ FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE,
+ FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE,
+ };
+
+ if keep_size {
+ mode |= libc::FALLOC_FL_KEEP_SIZE;
+ }
+
+ // Safe since we pass in a valid fd and fallocate mode, validate offset and len,
+ // and check the return value.
+ let ret = unsafe { libc::fallocate64(file.as_raw_fd(), mode, offset, len) };
+ if ret < 0 {
+ errno_result()
+ } else {
+ Ok(())
+ }
+}
+
+/// Reaps a child process that has terminated.
+///
+/// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children
+/// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap.
+///
+/// # Examples
+///
+/// Reaps all child processes until there are no terminated children to reap.
+///
+/// ```
+/// fn reap_children() {
+/// loop {
+/// match sys_util::reap_child() {
+/// Ok(0) => println!("no children ready to reap"),
+/// Ok(pid) => {
+/// println!("reaped {}", pid);
+/// continue
+/// },
+/// Err(e) if e.errno() == libc::ECHILD => println!("no children left"),
+/// Err(e) => println!("error reaping children: {}", e),
+/// }
+/// break
+/// }
+/// }
+/// ```
+pub fn reap_child() -> Result<pid_t> {
+ // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
+ let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
+ if ret == -1 {
+ errno_result()
+ } else {
+ Ok(ret)
+ }
+}
+
+/// Kill all processes in the current process group.
+///
+/// On success, this kills all processes in the current process group, including the current
+/// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
+pub fn kill_process_group() -> Result<()> {
+ let ret = unsafe { kill(0, SIGKILL) };
+ if ret == -1 {
+ errno_result()
+ } else {
+ // Kill succeeded, so this process never reaches here.
+ unreachable!();
+ }
+}
+
+/// Spawns a pipe pair where the first pipe is the read end and the second pipe is the write end.
+///
+/// If `close_on_exec` is true, the `O_CLOEXEC` flag will be set during pipe creation.
+pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
+ let flags = if close_on_exec { O_CLOEXEC } else { 0 };
+ let mut pipe_fds = [-1; 2];
+ // Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
+ // for error.
+ let ret = unsafe { pipe2(&mut pipe_fds[0], flags) };
+ if ret == -1 {
+ errno_result()
+ } else {
+ // Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
+ // exclusive ownership of them.
+ Ok(unsafe {
+ (
+ File::from_raw_fd(pipe_fds[0]),
+ File::from_raw_fd(pipe_fds[1]),
+ )
+ })
+ }
+}
+
+/// Used to attempt to clean up a named pipe after it is no longer used.
+pub struct UnlinkUnixDatagram(pub UnixDatagram);
+impl AsRef<UnixDatagram> for UnlinkUnixDatagram {
+ fn as_ref(&self) -> &UnixDatagram {
+ &self.0
+ }
+}
+impl Drop for UnlinkUnixDatagram {
+ fn drop(&mut self) {
+ if let Ok(addr) = self.0.local_addr() {
+ if let Some(path) = addr.as_pathname() {
+ if let Err(e) = remove_file(path) {
+ warn!("failed to remove control socket file: {}", e);
+ }
+ }
+ }
+ }
+}
+
+/// Verifies that |raw_fd| is actually owned by this process and duplicates it to ensure that
+/// we have a unique handle to it.
+pub fn validate_raw_fd(raw_fd: RawFd) -> Result<RawFd> {
+ // Checking that close-on-exec isn't set helps filter out FDs that were opened by
+ // crosvm as all crosvm FDs are close on exec.
+ // Safe because this doesn't modify any memory and we check the return value.
+ let flags = unsafe { libc::fcntl(raw_fd, libc::F_GETFD) };
+ if flags < 0 || (flags & libc::FD_CLOEXEC) != 0 {
+ return Err(Error::new(libc::EBADF));
+ }
+
+ // Duplicate the fd to ensure that we don't accidentally close an fd previously
+ // opened by another subsystem. Safe because this doesn't modify any memory and
+ // we check the return value.
+ let dup_fd = unsafe { libc::fcntl(raw_fd, libc::F_DUPFD_CLOEXEC, 0) };
+ if dup_fd < 0 {
+ return Err(Error::last());
+ }
+ Ok(dup_fd as RawFd)
+}
diff --git a/sys_util/src/mmap.rs b/sys_util/src/mmap.rs
new file mode 100644
index 0000000..3d1e577
--- /dev/null
+++ b/sys_util/src/mmap.rs
@@ -0,0 +1,986 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! The mmap module provides a safe interface to mmap memory and ensures unmap is called when the
+//! mmap object leaves scope.
+
+use std::cmp::min;
+use std::collections::BTreeMap;
+use std::fmt::{self, Display};
+use std::io;
+use std::mem::{size_of, ManuallyDrop};
+use std::os::unix::io::AsRawFd;
+use std::ptr::{copy_nonoverlapping, null_mut, read_unaligned, write_unaligned};
+
+use libc::{self, c_int, c_void, read, write};
+
+use data_model::volatile_memory::*;
+use data_model::DataInit;
+
+use crate::{errno, pagesize};
+
+#[derive(Debug)]
+pub enum Error {
+ /// Requested memory out of range.
+ InvalidAddress,
+ /// Requested offset is out of range of `libc::off_t`.
+ InvalidOffset,
+ /// Requested mapping is not page aligned
+ NotPageAligned,
+ /// Overlapping regions
+ Overlapping(usize, usize),
+ /// Requested memory range spans past the end of the region.
+ InvalidRange(usize, usize, usize),
+ /// `mmap` returned the given error.
+ SystemCallFailed(errno::Error),
+ /// Writing to memory failed
+ ReadToMemory(io::Error),
+ /// Reading from memory failed
+ WriteFromMemory(io::Error),
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ InvalidAddress => write!(f, "requested memory out of range"),
+ InvalidOffset => write!(f, "requested offset is out of range of off_t"),
+ NotPageAligned => write!(f, "requested memory is not page aligned"),
+ Overlapping(offset, count) => write!(
+ f,
+ "requested memory range overlaps with existing region: offset={} size={}",
+ offset, count
+ ),
+ InvalidRange(offset, count, region_size) => write!(
+ f,
+ "requested memory range spans past the end of the region: offset={} count={} region_size={}",
+ offset, count, region_size,
+ ),
+ SystemCallFailed(e) => write!(f, "mmap system call failed: {}", e),
+ ReadToMemory(e) => write!(f, "failed to read from file to memory: {}", e),
+ WriteFromMemory(e) => write!(f, "failed to write from memory to file: {}", e),
+ }
+ }
+}
+
+/// Memory access type for anonymous shared memory mapping.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Protection(c_int);
+impl Protection {
+ /// Returns Protection allowing no access.
+ #[inline(always)]
+ pub fn none() -> Protection {
+ Protection(libc::PROT_NONE)
+ }
+
+ /// Returns Protection allowing read/write access.
+ #[inline(always)]
+ pub fn read_write() -> Protection {
+ Protection(libc::PROT_READ | libc::PROT_WRITE)
+ }
+
+ /// Returns Protection allowing read access.
+ #[inline(always)]
+ pub fn read() -> Protection {
+ Protection(libc::PROT_READ)
+ }
+
+ /// Set read events.
+ #[inline(always)]
+ pub fn set_read(self) -> Protection {
+ Protection(self.0 | libc::PROT_READ)
+ }
+
+ /// Set write events.
+ #[inline(always)]
+ pub fn set_write(self) -> Protection {
+ Protection(self.0 | libc::PROT_WRITE)
+ }
+}
+
+impl From<c_int> for Protection {
+ fn from(f: c_int) -> Self {
+ Protection(f)
+ }
+}
+
+impl Into<c_int> for Protection {
+ fn into(self) -> c_int {
+ self.0
+ }
+}
+
+/// Wraps an anonymous shared memory mapping in the current process.
+#[derive(Debug)]
+pub struct MemoryMapping {
+ addr: *mut u8,
+ size: usize,
+}
+
+// Send and Sync aren't automatically inherited for the raw address pointer.
+// Accessing that pointer is only done through the stateless interface which
+// allows the object to be shared by multiple threads without a decrease in
+// safety.
+unsafe impl Send for MemoryMapping {}
+unsafe impl Sync for MemoryMapping {}
+
+impl MemoryMapping {
+ /// Creates an anonymous shared, read/write mapping of `size` bytes.
+ ///
+ /// # Arguments
+ /// * `size` - Size of memory region in bytes.
+ pub fn new(size: usize) -> Result<MemoryMapping> {
+ MemoryMapping::new_protection(size, Protection::read_write())
+ }
+
+ /// Creates an anonymous shared mapping of `size` bytes with `prot` protection.
+ ///
+ /// # Arguments
+ /// * `size` - Size of memory region in bytes.
+ /// * `prot` - Protection (e.g. readable/writable) of the memory region.
+ pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> {
+ // This is safe because we are creating an anonymous mapping in a place not already used by
+ // any other area in this process.
+ unsafe {
+ MemoryMapping::try_mmap(
+ None,
+ size,
+ prot.into(),
+ libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE,
+ None,
+ )
+ }
+ }
+
+ /// Maps the first `size` bytes of the given `fd` as read/write.
+ ///
+ /// # Arguments
+ /// * `fd` - File descriptor to mmap from.
+ /// * `size` - Size of memory region in bytes.
+ pub fn from_fd(fd: &dyn AsRawFd, size: usize) -> Result<MemoryMapping> {
+ MemoryMapping::from_fd_offset(fd, size, 0)
+ }
+
+ pub fn from_fd_offset(fd: &dyn AsRawFd, size: usize, offset: usize) -> Result<MemoryMapping> {
+ MemoryMapping::from_fd_offset_protection(fd, size, offset, Protection::read_write())
+ }
+
+ /// Maps the `size` bytes starting at `offset` bytes of the given `fd` as read/write.
+ ///
+ /// # Arguments
+ /// * `fd` - File descriptor to mmap from.
+ /// * `size` - Size of memory region in bytes.
+ /// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap.
+ /// * `prot` - Protection (e.g. readable/writable) of the memory region.
+ pub fn from_fd_offset_protection(
+ fd: &dyn AsRawFd,
+ size: usize,
+ offset: usize,
+ prot: Protection,
+ ) -> Result<MemoryMapping> {
+ // This is safe because we are creating an anonymous mapping in a place not already used by
+ // any other area in this process.
+ unsafe {
+ MemoryMapping::try_mmap(
+ None,
+ size,
+ prot.into(),
+ libc::MAP_SHARED,
+ Some((fd, offset)),
+ )
+ }
+ }
+
+ /// Creates an anonymous shared mapping of `size` bytes with `prot` protection.
+ /// Unsafe: unmaps any mmap'd regions already present at (addr..addr+size).
+ ///
+ /// # Arguments
+ /// * `addr` - Memory address to mmap at.
+ /// * `size` - Size of memory region in bytes.
+ /// * `prot` - Protection (e.g. readable/writable) of the memory region.
+ pub unsafe fn new_protection_fixed(
+ addr: *mut u8,
+ size: usize,
+ prot: Protection,
+ ) -> Result<MemoryMapping> {
+ MemoryMapping::try_mmap(
+ Some(addr),
+ size,
+ prot.into(),
+ libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE,
+ None,
+ )
+ }
+
+ /// Maps the `size` bytes starting at `offset` bytes of the given `fd` with
+ /// `prot` protections.
+ /// Unsafe: unmaps any mmap'd regions already present at (addr..addr+size).
+ ///
+ /// # Arguments
+ /// * `addr` - Memory address to mmap at.
+ /// * `fd` - File descriptor to mmap from.
+ /// * `size` - Size of memory region in bytes.
+ /// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap.
+ /// * `prot` - Protection (e.g. readable/writable) of the memory region.
+ pub unsafe fn from_fd_offset_protection_fixed(
+ addr: *mut u8,
+ fd: &dyn AsRawFd,
+ size: usize,
+ offset: usize,
+ prot: Protection,
+ ) -> Result<MemoryMapping> {
+ MemoryMapping::try_mmap(
+ Some(addr),
+ size,
+ prot.into(),
+ libc::MAP_SHARED | libc::MAP_NORESERVE,
+ Some((fd, offset)),
+ )
+ }
+
+ /// Helper wrapper around libc::mmap that does some basic validation, and calls
+ /// madvise with MADV_DONTDUMP on the created mmap
+ unsafe fn try_mmap(
+ addr: Option<*mut u8>,
+ size: usize,
+ prot: c_int,
+ flags: c_int,
+ fd: Option<(&AsRawFd, usize)>,
+ ) -> Result<MemoryMapping> {
+ let mut flags = flags;
+ // If addr is provided, set the FIXED flag, and validate addr alignment
+ let addr = match addr {
+ Some(addr) => {
+ if (addr as usize) % pagesize() != 0 {
+ return Err(Error::NotPageAligned);
+ }
+ flags |= libc::MAP_FIXED;
+ addr as *mut libc::c_void
+ }
+ None => null_mut(),
+ };
+ // If fd is provided, validate fd offset is within bounds
+ let (fd, offset) = match fd {
+ Some((fd, offset)) => {
+ if offset > libc::off_t::max_value() as usize {
+ return Err(Error::InvalidOffset);
+ }
+ (fd.as_raw_fd(), offset as libc::off_t)
+ }
+ None => (-1, 0),
+ };
+ let addr = libc::mmap(addr, size, prot, flags, fd, offset);
+ if addr == libc::MAP_FAILED {
+ return Err(Error::SystemCallFailed(errno::Error::last()));
+ }
+ // This is safe because we call madvise with a valid address and size, and we check the
+ // return value. We only warn about an error because failure here is not fatal to the mmap.
+ if libc::madvise(addr, size, libc::MADV_DONTDUMP) == -1 {
+ warn!(
+ "failed madvise(MADV_DONTDUMP) on mmap: {}",
+ errno::Error::last()
+ );
+ }
+ Ok(MemoryMapping {
+ addr: addr as *mut u8,
+ size,
+ })
+ }
+
+ /// Returns a pointer to the beginning of the memory region. Should only be
+ /// used for passing this region to ioctls for setting guest memory.
+ pub fn as_ptr(&self) -> *mut u8 {
+ self.addr
+ }
+
+ /// Returns the size of the memory region in bytes.
+ pub fn size(&self) -> usize {
+ self.size
+ }
+
+ /// Calls msync with MS_SYNC on the mapping.
+ pub fn msync(&self) -> Result<()> {
+ // This is safe since we use the exact address and length of a known
+ // good memory mapping.
+ let ret = unsafe {
+ libc::msync(
+ self.as_ptr() as *mut libc::c_void,
+ self.size(),
+ libc::MS_SYNC,
+ )
+ };
+ if ret == -1 {
+ return Err(Error::SystemCallFailed(errno::Error::last()));
+ }
+ Ok(())
+ }
+
+ /// Writes a slice to the memory region at the specified offset.
+ /// Returns the number of bytes written. The number of bytes written can
+ /// be less than the length of the slice if there isn't enough room in the
+ /// memory region.
+ ///
+ /// # Examples
+ /// * Write a slice at offset 256.
+ ///
+ /// ```
+ /// # use sys_util::MemoryMapping;
+ /// # let mut mem_map = MemoryMapping::new(1024).unwrap();
+ /// let res = mem_map.write_slice(&[1,2,3,4,5], 256);
+ /// assert!(res.is_ok());
+ /// assert_eq!(res.unwrap(), 5);
+ /// ```
+ pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> {
+ match self.size.checked_sub(offset) {
+ Some(size_past_offset) => {
+ let bytes_copied = min(size_past_offset, buf.len());
+ // The bytes_copied equation above ensures we don't copy bytes out of range of
+ // either buf or this slice. We also know that the buffers do not overlap because
+ // slices can never occupy the same memory as a volatile slice.
+ unsafe {
+ copy_nonoverlapping(buf.as_ptr(), self.as_ptr().add(offset), bytes_copied);
+ }
+ Ok(bytes_copied)
+ }
+ None => Err(Error::InvalidAddress),
+ }
+ }
+
+ /// Reads to a slice from the memory region at the specified offset.
+ /// Returns the number of bytes read. The number of bytes read can
+ /// be less than the length of the slice if there isn't enough room in the
+ /// memory region.
+ ///
+ /// # Examples
+ /// * Read a slice of size 16 at offset 256.
+ ///
+ /// ```
+ /// # use sys_util::MemoryMapping;
+ /// # let mut mem_map = MemoryMapping::new(1024).unwrap();
+ /// let buf = &mut [0u8; 16];
+ /// let res = mem_map.read_slice(buf, 256);
+ /// assert!(res.is_ok());
+ /// assert_eq!(res.unwrap(), 16);
+ /// ```
+ pub fn read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize> {
+ match self.size.checked_sub(offset) {
+ Some(size_past_offset) => {
+ let bytes_copied = min(size_past_offset, buf.len());
+ // The bytes_copied equation above ensures we don't copy bytes out of range of
+ // either buf or this slice. We also know that the buffers do not overlap because
+ // slices can never occupy the same memory as a volatile slice.
+ unsafe {
+ copy_nonoverlapping(
+ self.as_ptr().add(offset) as *const u8,
+ buf.as_mut_ptr(),
+ bytes_copied,
+ );
+ }
+ Ok(bytes_copied)
+ }
+ None => Err(Error::InvalidAddress),
+ }
+ }
+
+ /// Writes an object to the memory region at the specified offset.
+ /// Returns Ok(()) if the object fits, or Err if it extends past the end.
+ ///
+ /// # Examples
+ /// * Write a u64 at offset 16.
+ ///
+ /// ```
+ /// # use sys_util::MemoryMapping;
+ /// # let mut mem_map = MemoryMapping::new(1024).unwrap();
+ /// let res = mem_map.write_obj(55u64, 16);
+ /// assert!(res.is_ok());
+ /// ```
+ pub fn write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()> {
+ self.range_end(offset, size_of::<T>())?;
+ // This is safe because we checked the bounds above.
+ unsafe {
+ write_unaligned(self.as_ptr().add(offset) as *mut T, val);
+ }
+ Ok(())
+ }
+
+ /// Reads on object from the memory region at the given offset.
+ /// Reading from a volatile area isn't strictly safe as it could change
+ /// mid-read. However, as long as the type T is plain old data and can
+ /// handle random initialization, everything will be OK.
+ ///
+ /// # Examples
+ /// * Read a u64 written to offset 32.
+ ///
+ /// ```
+ /// # use sys_util::MemoryMapping;
+ /// # let mut mem_map = MemoryMapping::new(1024).unwrap();
+ /// let res = mem_map.write_obj(55u64, 32);
+ /// assert!(res.is_ok());
+ /// let num: u64 = mem_map.read_obj(32).unwrap();
+ /// assert_eq!(55, num);
+ /// ```
+ pub fn read_obj<T: DataInit>(&self, offset: usize) -> Result<T> {
+ self.range_end(offset, size_of::<T>())?;
+ // This is safe because by definition Copy types can have their bits set arbitrarily and
+ // still be valid.
+ unsafe {
+ Ok(read_unaligned(
+ self.as_ptr().add(offset) as *const u8 as *const T
+ ))
+ }
+ }
+
+ /// Reads data from a file descriptor and writes it to guest memory.
+ ///
+ /// # Arguments
+ /// * `mem_offset` - Begin writing memory at this offset.
+ /// * `src` - Read from `src` to memory.
+ /// * `count` - Read `count` bytes from `src` to memory.
+ ///
+ /// # Examples
+ ///
+ /// * Read bytes from /dev/urandom
+ ///
+ /// ```
+ /// # use sys_util::MemoryMapping;
+ /// # use std::fs::File;
+ /// # use std::path::Path;
+ /// # fn test_read_random() -> Result<u32, ()> {
+ /// # let mut mem_map = MemoryMapping::new(1024).unwrap();
+ /// let mut file = File::open(Path::new("/dev/urandom")).map_err(|_| ())?;
+ /// mem_map.read_to_memory(32, &mut file, 128).map_err(|_| ())?;
+ /// let rand_val: u32 = mem_map.read_obj(40).map_err(|_| ())?;
+ /// # Ok(rand_val)
+ /// # }
+ /// ```
+ pub fn read_to_memory(
+ &self,
+ mut mem_offset: usize,
+ src: &AsRawFd,
+ mut count: usize,
+ ) -> Result<()> {
+ self.range_end(mem_offset, count)
+ .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
+ while count > 0 {
+ // The check above ensures that no memory outside this slice will get accessed by this
+ // read call.
+ match unsafe {
+ read(
+ src.as_raw_fd(),
+ self.as_ptr().add(mem_offset) as *mut c_void,
+ count,
+ )
+ } {
+ 0 => {
+ return Err(Error::ReadToMemory(io::Error::from(
+ io::ErrorKind::UnexpectedEof,
+ )))
+ }
+ r if r < 0 => return Err(Error::ReadToMemory(io::Error::last_os_error())),
+ ret => {
+ let bytes_read = ret as usize;
+ match count.checked_sub(bytes_read) {
+ Some(count_remaining) => count = count_remaining,
+ None => break,
+ }
+ mem_offset += ret as usize;
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Writes data from memory to a file descriptor.
+ ///
+ /// # Arguments
+ /// * `mem_offset` - Begin reading memory from this offset.
+ /// * `dst` - Write from memory to `dst`.
+ /// * `count` - Read `count` bytes from memory to `src`.
+ ///
+ /// # Examples
+ ///
+ /// * Write 128 bytes to /dev/null
+ ///
+ /// ```
+ /// # use sys_util::MemoryMapping;
+ /// # use std::fs::File;
+ /// # use std::path::Path;
+ /// # fn test_write_null() -> Result<(), ()> {
+ /// # let mut mem_map = MemoryMapping::new(1024).unwrap();
+ /// let mut file = File::open(Path::new("/dev/null")).map_err(|_| ())?;
+ /// mem_map.write_from_memory(32, &mut file, 128).map_err(|_| ())?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn write_from_memory(
+ &self,
+ mut mem_offset: usize,
+ dst: &AsRawFd,
+ mut count: usize,
+ ) -> Result<()> {
+ self.range_end(mem_offset, count)
+ .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
+ while count > 0 {
+ // The check above ensures that no memory outside this slice will get accessed by this
+ // write call.
+ match unsafe {
+ write(
+ dst.as_raw_fd(),
+ self.as_ptr().add(mem_offset) as *const c_void,
+ count,
+ )
+ } {
+ 0 => {
+ return Err(Error::WriteFromMemory(io::Error::from(
+ io::ErrorKind::WriteZero,
+ )))
+ }
+ ret if ret < 0 => return Err(Error::WriteFromMemory(io::Error::last_os_error())),
+ ret => {
+ let bytes_written = ret as usize;
+ match count.checked_sub(bytes_written) {
+ Some(count_remaining) => count = count_remaining,
+ None => break,
+ }
+ mem_offset += ret as usize;
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Uses madvise to tell the kernel to remove the specified range. Subsequent reads
+ /// to the pages in the range will return zero bytes.
+ pub fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()> {
+ self.range_end(mem_offset, count)
+ .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
+ let ret = unsafe {
+ // madvising away the region is the same as the guest changing it.
+ // Next time it is read, it may return zero pages.
+ libc::madvise(
+ (self.addr as usize + mem_offset) as *mut _,
+ count,
+ libc::MADV_REMOVE,
+ )
+ };
+ if ret < 0 {
+ Err(Error::InvalidRange(mem_offset, count, self.size()))
+ } else {
+ Ok(())
+ }
+ }
+
+ // Check that offset+count is valid and return the sum.
+ fn range_end(&self, offset: usize, count: usize) -> Result<usize> {
+ let mem_end = offset.checked_add(count).ok_or(Error::InvalidAddress)?;
+ if mem_end > self.size() {
+ return Err(Error::InvalidAddress);
+ }
+ Ok(mem_end)
+ }
+}
+
+impl VolatileMemory for MemoryMapping {
+ fn get_slice(&self, offset: u64, count: u64) -> VolatileMemoryResult<VolatileSlice> {
+ let mem_end = calc_offset(offset, count)?;
+ if mem_end > self.size as u64 {
+ return Err(VolatileMemoryError::OutOfBounds { addr: mem_end });
+ }
+
+ // Safe because we checked that offset + count was within our range and we only ever hand
+ // out volatile accessors.
+ Ok(unsafe { VolatileSlice::new((self.addr as usize + offset as usize) as *mut _, count) })
+ }
+}
+
+impl Drop for MemoryMapping {
+ fn drop(&mut self) {
+ // This is safe because we mmap the area at addr ourselves, and nobody
+ // else is holding a reference to it.
+ unsafe {
+ libc::munmap(self.addr as *mut libc::c_void, self.size);
+ }
+ }
+}
+
+/// Tracks Fixed Memory Maps within an anonymous memory-mapped fixed-sized arena
+/// in the current process.
+pub struct MemoryMappingArena {
+ addr: *mut u8,
+ size: usize,
+ // When doing in-place swaps of MemoryMappings, the BTreeMap returns a owned
+ // instance of the old MemoryMapping. When the old MemoryMapping falls out
+ // of scope, it calls munmap on the same region as the new MemoryMapping
+ // that was just mapped in. To avoid accidentally munmapping the new,
+ // MemoryMapping, all mappings are wrapped in a ManuallyDrop, and then
+ // "forgotten" when removed from the BTreeMap
+ maps: BTreeMap<usize, ManuallyDrop<MemoryMapping>>,
+}
+
+// Send and Sync aren't automatically inherited for the raw address pointer.
+// Accessing that pointer is only done through the stateless interface which
+// allows the object to be shared by multiple threads without a decrease in
+// safety.
+unsafe impl Send for MemoryMappingArena {}
+unsafe impl Sync for MemoryMappingArena {}
+
+impl MemoryMappingArena {
+ /// Creates an mmap arena of `size` bytes.
+ ///
+ /// # Arguments
+ /// * `size` - Size of memory region in bytes.
+ pub fn new(size: usize) -> Result<MemoryMappingArena> {
+ // Reserve the arena's memory using an anonymous read-only mmap.
+ // The actual MemoryMapping object is forgotten, with
+ // MemoryMappingArena manually calling munmap on drop.
+ let mmap = MemoryMapping::new_protection(size, Protection::none().set_read())?;
+ let addr = mmap.as_ptr();
+ let size = mmap.size();
+ std::mem::forget(mmap);
+ Ok(MemoryMappingArena {
+ addr,
+ size,
+ maps: BTreeMap::new(),
+ })
+ }
+
+ /// Anonymously maps `size` bytes at `offset` bytes from the start of the arena.
+ /// `offset` must be page aligned.
+ ///
+ /// # Arguments
+ /// * `offset` - Page aligned offset into the arena in bytes.
+ /// * `size` - Size of memory region in bytes.
+ /// * `fd` - File descriptor to mmap from.
+ pub fn add_anon(&mut self, offset: usize, size: usize) -> Result<()> {
+ self.try_add(offset, size, Protection::read_write(), None)
+ }
+
+ /// Maps `size` bytes from the start of the given `fd` at `offset` bytes from
+ /// the start of the arena. `offset` must be page aligned.
+ ///
+ /// # Arguments
+ /// * `offset` - Page aligned offset into the arena in bytes.
+ /// * `size` - Size of memory region in bytes.
+ /// * `fd` - File descriptor to mmap from.
+ pub fn add_fd(&mut self, offset: usize, size: usize, fd: &dyn AsRawFd) -> Result<()> {
+ self.add_fd_offset(offset, size, fd, 0)
+ }
+
+ /// Maps `size` bytes starting at `fs_offset` bytes from within the given `fd`
+ /// at `offset` bytes from the start of the arena. `offset` must be page aligned.
+ ///
+ /// # Arguments
+ /// * `offset` - Page aligned offset into the arena in bytes.
+ /// * `size` - Size of memory region in bytes.
+ /// * `fd` - File descriptor to mmap from.
+ /// * `fd_offset` - Offset in bytes from the beginning of `fd` to start the mmap.
+ pub fn add_fd_offset(
+ &mut self,
+ offset: usize,
+ size: usize,
+ fd: &dyn AsRawFd,
+ fd_offset: usize,
+ ) -> Result<()> {
+ self.add_fd_offset_protection(offset, size, fd, fd_offset, Protection::read_write())
+ }
+
+ /// Maps `size` bytes starting at `fs_offset` bytes from within the given `fd`
+ /// at `offset` bytes from the start of the arena with `prot` protections.
+ /// `offset` must be page aligned.
+ ///
+ /// # Arguments
+ /// * `offset` - Page aligned offset into the arena in bytes.
+ /// * `size` - Size of memory region in bytes.
+ /// * `fd` - File descriptor to mmap from.
+ /// * `fd_offset` - Offset in bytes from the beginning of `fd` to start the mmap.
+ /// * `prot` - Protection (e.g. readable/writable) of the memory region.
+ pub fn add_fd_offset_protection(
+ &mut self,
+ offset: usize,
+ size: usize,
+ fd: &dyn AsRawFd,
+ fd_offset: usize,
+ prot: Protection,
+ ) -> Result<()> {
+ self.try_add(offset, size, prot, Some((fd, fd_offset)))
+ }
+
+ /// Helper method that calls appropriate MemoryMapping constructor and adds
+ /// the resulting map into the arena.
+ fn try_add(
+ &mut self,
+ offset: usize,
+ size: usize,
+ prot: Protection,
+ fd: Option<(&AsRawFd, usize)>,
+ ) -> Result<()> {
+ self.validate_range(offset, size)?;
+
+ // This is safe since the range has been validated.
+ let mmap = unsafe {
+ match fd {
+ Some((fd, fd_offset)) => MemoryMapping::from_fd_offset_protection_fixed(
+ (self.addr as usize + offset) as *mut u8,
+ fd,
+ size,
+ fd_offset,
+ prot,
+ )?,
+ None => MemoryMapping::new_protection_fixed(
+ (self.addr as usize + offset) as *mut u8,
+ size,
+ prot,
+ )?,
+ }
+ };
+
+ self.maps.insert(offset, ManuallyDrop::new(mmap));
+ Ok(())
+ }
+
+ /// Removes a mapping at `offset` from the start of the arena.
+ /// Returns a boolean indicating if there was a mapping present at `offset`.
+ /// If none was present, this method is a noop.
+ pub fn remove(&mut self, offset: usize) -> Result<bool> {
+ if let Some(mmap) = self.maps.remove(&offset) {
+ // Instead of munmapping the memory map, leaving an unprotected hole
+ // in the arena, swap this mmap with an anonymous protection.
+ // This is safe since the memory mapping perfectly overlaps with an
+ // existing, known good memory mapping.
+ let mmap = unsafe {
+ MemoryMapping::new_protection_fixed(
+ mmap.as_ptr(),
+ mmap.size(),
+ Protection::none().set_read(),
+ )?
+ };
+ self.maps.insert(offset, ManuallyDrop::new(mmap));
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+ }
+
+ /// Calls msync with MS_SYNC on the mapping at `offset` from the start of
+ /// the arena.
+ /// Returns a boolean indicating if there was a mapping present at `offset`.
+ /// If none was present, this method is a noop.
+ pub fn msync(&self, offset: usize) -> Result<bool> {
+ if let Some(mmap) = self.maps.get(&offset) {
+ mmap.msync()?;
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+ }
+
+ /// Returns a pointer to the beginning of the memory region. Should only be
+ /// used for passing this region to ioctls for setting guest memory.
+ pub fn as_ptr(&self) -> *mut u8 {
+ self.addr
+ }
+
+ /// Returns the size of the memory region in bytes.
+ pub fn size(&self) -> usize {
+ self.size
+ }
+
+ /// Validates `offset` and `size`.
+ /// Checks that offset..offset+size doesn't overlap with existing mappings.
+ /// Also ensures correct alignment, and checks for any overflow.
+ /// Note: offset..offset+size is considered valid if it _perfectly_ overlaps
+ /// with single other region.
+ fn validate_range(&self, offset: usize, size: usize) -> Result<()> {
+ // Ensure offset is page-aligned
+ if offset % pagesize() != 0 {
+ return Err(Error::NotPageAligned);
+ }
+ // Ensure offset + size doesn't overflow
+ let end_offset = offset.checked_add(size).ok_or(Error::InvalidAddress)?;
+ // Ensure offset + size are within the arena bounds
+ if end_offset > self.size {
+ return Err(Error::InvalidAddress);
+ }
+ // Ensure offset..offset+size doesn't overlap with existing regions
+ // Find the offset + size of the first mapping before the desired offset
+ let (prev_offset, prev_size) = match self.maps.range(..offset).rev().next() {
+ Some((offset, mmap)) => (*offset, mmap.size()),
+ None => {
+ // Empty map
+ return Ok(());
+ }
+ };
+ if offset == prev_offset {
+ // Perfectly overlapping regions are allowed
+ if size != prev_size {
+ return Err(Error::Overlapping(offset, size));
+ }
+ } else if offset < (prev_offset + prev_size) {
+ return Err(Error::Overlapping(offset, size));
+ }
+
+ Ok(())
+ }
+}
+
+impl Drop for MemoryMappingArena {
+ fn drop(&mut self) {
+ // This is safe because we mmap the area at addr ourselves, and nobody
+ // else is holding a reference to it.
+ unsafe {
+ libc::munmap(self.addr as *mut libc::c_void, self.size);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use data_model::{VolatileMemory, VolatileMemoryError};
+ use std::os::unix::io::FromRawFd;
+
+ #[test]
+ fn basic_map() {
+ let m = MemoryMapping::new(1024).unwrap();
+ assert_eq!(1024, m.size());
+ }
+
+ #[test]
+ fn map_invalid_size() {
+ let res = MemoryMapping::new(0).unwrap_err();
+ if let Error::SystemCallFailed(e) = res {
+ assert_eq!(e.errno(), libc::EINVAL);
+ } else {
+ panic!("unexpected error: {}", res);
+ }
+ }
+
+ #[test]
+ fn map_invalid_fd() {
+ let fd = unsafe { std::fs::File::from_raw_fd(-1) };
+ let res = MemoryMapping::from_fd(&fd, 1024).unwrap_err();
+ if let Error::SystemCallFailed(e) = res {
+ assert_eq!(e.errno(), libc::EBADF);
+ } else {
+ panic!("unexpected error: {}", res);
+ }
+ }
+
+ #[test]
+ fn test_write_past_end() {
+ let m = MemoryMapping::new(5).unwrap();
+ let res = m.write_slice(&[1, 2, 3, 4, 5, 6], 0);
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), 5);
+ }
+
+ #[test]
+ fn slice_size() {
+ let m = MemoryMapping::new(5).unwrap();
+ let s = m.get_slice(2, 3).unwrap();
+ assert_eq!(s.size(), 3);
+ }
+
+ #[test]
+ fn slice_addr() {
+ let m = MemoryMapping::new(5).unwrap();
+ let s = m.get_slice(2, 3).unwrap();
+ assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) });
+ }
+
+ #[test]
+ fn slice_store() {
+ let m = MemoryMapping::new(5).unwrap();
+ let r = m.get_ref(2).unwrap();
+ r.store(9u16);
+ assert_eq!(m.read_obj::<u16>(2).unwrap(), 9);
+ }
+
+ #[test]
+ fn slice_overflow_error() {
+ let m = MemoryMapping::new(5).unwrap();
+ let res = m.get_slice(std::u64::MAX, 3).unwrap_err();
+ assert_eq!(
+ res,
+ VolatileMemoryError::Overflow {
+ base: std::u64::MAX,
+ offset: 3,
+ }
+ );
+ }
+ #[test]
+ fn slice_oob_error() {
+ let m = MemoryMapping::new(5).unwrap();
+ let res = m.get_slice(3, 3).unwrap_err();
+ assert_eq!(res, VolatileMemoryError::OutOfBounds { addr: 6 });
+ }
+
+ #[test]
+ fn from_fd_offset_invalid() {
+ let fd = unsafe { std::fs::File::from_raw_fd(-1) };
+ let res = MemoryMapping::from_fd_offset(&fd, 4096, (libc::off_t::max_value() as usize) + 1)
+ .unwrap_err();
+ match res {
+ Error::InvalidOffset => {}
+ e => panic!("unexpected error: {}", e),
+ }
+ }
+
+ #[test]
+ fn arena_new() {
+ let m = MemoryMappingArena::new(0x40000).unwrap();
+ assert_eq!(m.size(), 0x40000);
+ }
+
+ #[test]
+ fn arena_add() {
+ let mut m = MemoryMappingArena::new(0x40000).unwrap();
+ assert!(m.add_anon(0, pagesize() * 4).is_ok());
+ }
+
+ #[test]
+ fn arena_remove() {
+ let mut m = MemoryMappingArena::new(0x40000).unwrap();
+ assert!(m.add_anon(0, pagesize() * 4).is_ok());
+ assert!(m.remove(0).unwrap(), true);
+ assert!(m.remove(0).unwrap(), false);
+ }
+
+ #[test]
+ fn arena_add_overlap_error() {
+ let page = pagesize();
+ let mut m = MemoryMappingArena::new(page * 4).unwrap();
+ assert!(m.add_anon(0, page * 4).is_ok());
+ let res = m.add_anon(page, page).unwrap_err();
+ match res {
+ Error::Overlapping(a, o) => {
+ assert_eq!((a, o), (page, page));
+ }
+ e => panic!("unexpected error: {}", e),
+ }
+ }
+
+ #[test]
+ fn arena_add_alignment_error() {
+ let mut m = MemoryMappingArena::new(pagesize() * 2).unwrap();
+ assert!(m.add_anon(0, 0x100).is_ok());
+ let res = m.add_anon(pagesize() + 1, 0x100).unwrap_err();
+ match res {
+ Error::NotPageAligned => {}
+ e => panic!("unexpected error: {}", e),
+ }
+ }
+
+ #[test]
+ fn arena_add_oob_error() {
+ let mut m = MemoryMappingArena::new(pagesize()).unwrap();
+ let res = m.add_anon(0, pagesize() + 1).unwrap_err();
+ match res {
+ Error::InvalidAddress => {}
+ e => panic!("unexpected error: {}", e),
+ }
+ }
+}
diff --git a/sys_util/src/net.rs b/sys_util/src/net.rs
new file mode 100644
index 0000000..70f975b
--- /dev/null
+++ b/sys_util/src/net.rs
@@ -0,0 +1,587 @@
+// Copyright 2018 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::OsString;
+use std::fs::remove_file;
+use std::io;
+use std::mem;
+use std::ops::Deref;
+use std::os::unix::{
+ ffi::{OsStrExt, OsStringExt},
+ io::{AsRawFd, FromRawFd, RawFd},
+};
+use std::path::Path;
+use std::path::PathBuf;
+use std::ptr::null_mut;
+use std::time::Duration;
+
+// Offset of sun_path in structure sockaddr_un.
+fn sun_path_offset() -> usize {
+ // Prefer 0 to null() so that we do not need to subtract from the `sub_path` pointer.
+ #[allow(clippy::zero_ptr)]
+ let addr = 0 as *const libc::sockaddr_un;
+ // Safe because we only use the dereference to create a pointer to the desired field in
+ // calculating the offset.
+ unsafe { &(*addr).sun_path as *const _ as usize }
+}
+
+// Return `sockaddr_un` for a given `path`
+fn sockaddr_un<P: AsRef<Path>>(path: P) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
+ let mut addr = libc::sockaddr_un {
+ sun_family: libc::AF_UNIX as libc::sa_family_t,
+ sun_path: [0; 108],
+ };
+
+ // Check if the input path is valid. Since
+ // * The pathname in sun_path should be null-terminated.
+ // * The length of the pathname, including the terminating null byte,
+ // should not exceed the size of sun_path.
+ //
+ // and our input is a `Path`, we only need to check
+ // * If the string size of `Path` should less than sizeof(sun_path)
+ // and make sure `sun_path` ends with '\0' by initialized the sun_path with zeros.
+ //
+ // Empty path name is valid since abstract socket address has sun_paht[0] = '\0'
+ let bytes = path.as_ref().as_os_str().as_bytes();
+ if bytes.len() >= addr.sun_path.len() {
+ return Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "Input path size should be less than the length of sun_path.",
+ ));
+ };
+
+ // Copy data from `path` to `addr.sun_path`
+ for (dst, src) in addr.sun_path.iter_mut().zip(bytes) {
+ *dst = *src as libc::c_char;
+ }
+
+ // The addrlen argument that describes the enclosing sockaddr_un structure
+ // should have a value of at least:
+ //
+ // offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path) + 1
+ //
+ // or, more simply, addrlen can be specified as sizeof(struct sockaddr_un).
+ let len = sun_path_offset() + bytes.len() + 1;
+ Ok((addr, len as libc::socklen_t))
+}
+
+/// A Unix `SOCK_SEQPACKET` socket point to given `path`
+pub struct UnixSeqpacket {
+ fd: RawFd,
+}
+
+impl UnixSeqpacket {
+ /// Open a `SOCK_SEQPACKET` connection to socket named by `path`.
+ ///
+ /// # Arguments
+ /// * `path` - Path to `SOCK_SEQPACKET` socket
+ ///
+ /// # Returns
+ /// A `UnixSeqpacket` structure point to the socket
+ ///
+ /// # Errors
+ /// Return `io::Error` when error occurs.
+ pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<Self> {
+ // Safe socket initialization since we handle the returned error.
+ let fd = unsafe {
+ match libc::socket(libc::AF_UNIX, libc::SOCK_SEQPACKET, 0) {
+ -1 => return Err(io::Error::last_os_error()),
+ fd => fd,
+ }
+ };
+
+ let (addr, len) = sockaddr_un(path.as_ref())?;
+ // Safe connect since we handle the error and use the right length generated from
+ // `sockaddr_un`.
+ unsafe {
+ let ret = libc::connect(fd, &addr as *const _ as *const _, len);
+ if ret < 0 {
+ return Err(io::Error::last_os_error());
+ }
+ }
+ Ok(UnixSeqpacket { fd })
+ }
+
+ /// Creates a pair of connected `SOCK_SEQPACKET` sockets.
+ ///
+ /// Both returned file descriptors have the `CLOEXEC` flag set.s
+ pub fn pair() -> io::Result<(UnixSeqpacket, UnixSeqpacket)> {
+ let mut fds = [0, 0];
+ unsafe {
+ // Safe because we give enough space to store all the fds and we check the return value.
+ let ret = libc::socketpair(
+ libc::AF_UNIX,
+ libc::SOCK_SEQPACKET | libc::SOCK_CLOEXEC,
+ 0,
+ &mut fds[0],
+ );
+ if ret == 0 {
+ Ok((
+ UnixSeqpacket::from_raw_fd(fds[0]),
+ UnixSeqpacket::from_raw_fd(fds[1]),
+ ))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Clone the underlying FD.
+ pub fn try_clone(&self) -> io::Result<Self> {
+ // Calling `dup` is safe as the kernel doesn't touch any user memory it the process.
+ let new_fd = unsafe { libc::dup(self.fd) };
+ if new_fd < 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(UnixSeqpacket { fd: new_fd })
+ }
+ }
+
+ /// Gets the number of bytes that can be read from this socket without blocking.
+ pub fn get_readable_bytes(&self) -> io::Result<usize> {
+ let mut byte_count = 0 as libc::c_int;
+ let ret = unsafe { libc::ioctl(self.fd, libc::FIONREAD, &mut byte_count) };
+ if ret < 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(byte_count as usize)
+ }
+ }
+
+ /// Write data from a given buffer to the socket fd
+ ///
+ /// # Arguments
+ /// * `buf` - A reference to the data buffer.
+ ///
+ /// # Returns
+ /// * `usize` - The size of bytes written to the buffer.
+ ///
+ /// # Errors
+ /// Returns error when `libc::write` failed.
+ pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
+ // Safe since we make sure the input `count` == `buf.len()` and handle the returned error.
+ unsafe {
+ let ret = libc::write(self.fd, buf.as_ptr() as *const _, buf.len());
+ if ret < 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(ret as usize)
+ }
+ }
+ }
+
+ /// Read data from the socket fd to a given buffer
+ ///
+ /// # Arguments
+ /// * `buf` - A mut reference to the data buffer.
+ ///
+ /// # Returns
+ /// * `usize` - The size of bytes read to the buffer.
+ ///
+ /// # Errors
+ /// Returns error when `libc::read` failed.
+ pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
+ // Safe since we make sure the input `count` == `buf.len()` and handle the returned error.
+ unsafe {
+ let ret = libc::read(self.fd, buf.as_mut_ptr() as *mut _, buf.len());
+ if ret < 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(ret as usize)
+ }
+ }
+ }
+
+ fn set_timeout(&self, timeout: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
+ let timeval = match timeout {
+ Some(t) => {
+ if t.as_secs() == 0 && t.subsec_micros() == 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "zero timeout duration is invalid",
+ ));
+ }
+ // subsec_micros fits in i32 because it is defined to be less than one million.
+ let nsec = t.subsec_micros() as i32;
+ libc::timeval {
+ tv_sec: t.as_secs() as libc::time_t,
+ tv_usec: libc::suseconds_t::from(nsec),
+ }
+ }
+ None => libc::timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ },
+ };
+ // Safe because we own the fd, and the length of the pointer's data is the same as the
+ // passed in length parameter. The level argument is valid, the kind is assumed to be valid,
+ // and the return value is checked.
+ let ret = unsafe {
+ libc::setsockopt(
+ self.fd,
+ libc::SOL_SOCKET,
+ kind,
+ &timeval as *const libc::timeval as *const libc::c_void,
+ mem::size_of::<libc::timeval>() as libc::socklen_t,
+ )
+ };
+ if ret < 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(())
+ }
+ }
+
+ /// Sets or removes the timeout for read/recv operations on this socket.
+ pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.set_timeout(timeout, libc::SO_RCVTIMEO)
+ }
+
+ /// Sets or removes the timeout for write/send operations on this socket.
+ pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.set_timeout(timeout, libc::SO_SNDTIMEO)
+ }
+}
+
+impl Drop for UnixSeqpacket {
+ fn drop(&mut self) {
+ // Safe if the UnixSeqpacket is created from Self::connect.
+ unsafe {
+ libc::close(self.fd);
+ }
+ }
+}
+
+impl FromRawFd for UnixSeqpacket {
+ // Unsafe in drop function
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ Self { fd }
+ }
+}
+
+impl AsRawFd for UnixSeqpacket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd
+ }
+}
+
+/// Like a `UnixListener` but for accepting `UnixSeqpacket` type sockets.
+pub struct UnixSeqpacketListener {
+ fd: RawFd,
+}
+
+impl UnixSeqpacketListener {
+ /// Creates a new `UnixSeqpacketListener` bound to the given path.
+ pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<Self> {
+ // Safe socket initialization since we handle the returned error.
+ let fd = unsafe {
+ match libc::socket(libc::AF_UNIX, libc::SOCK_SEQPACKET, 0) {
+ -1 => return Err(io::Error::last_os_error()),
+ fd => fd,
+ }
+ };
+
+ let (addr, len) = sockaddr_un(path.as_ref())?;
+ // Safe connect since we handle the error and use the right length generated from
+ // `sockaddr_un`.
+ unsafe {
+ let ret = handle_eintr_errno!(libc::bind(fd, &addr as *const _ as *const _, len));
+ if ret < 0 {
+ return Err(io::Error::last_os_error());
+ }
+ let ret = handle_eintr_errno!(libc::listen(fd, 128));
+ if ret < 0 {
+ return Err(io::Error::last_os_error());
+ }
+ }
+ Ok(UnixSeqpacketListener { fd })
+ }
+
+ /// Blocks for and accepts a new incoming connection and returns the socket associated with that
+ /// connection.
+ ///
+ /// The returned socket has the close-on-exec flag set.
+ pub fn accept(&self) -> io::Result<UnixSeqpacket> {
+ // Safe because we own this fd and the kernel will not write to null pointers.
+ let ret = unsafe { libc::accept4(self.fd, null_mut(), null_mut(), libc::SOCK_CLOEXEC) };
+ if ret < 0 {
+ return Err(io::Error::last_os_error());
+ }
+ // Safe because we checked the return value of accept. Therefore, the return value must be a
+ // valid socket.
+ Ok(unsafe { UnixSeqpacket::from_raw_fd(ret) })
+ }
+
+ /// Gets the path that this listener is bound to.
+ pub fn path(&self) -> io::Result<PathBuf> {
+ let mut addr = libc::sockaddr_un {
+ sun_family: libc::AF_UNIX as libc::sa_family_t,
+ sun_path: [0; 108],
+ };
+ let sun_path_offset = (&addr.sun_path as *const _ as usize
+ - &addr.sun_family as *const _ as usize)
+ as libc::socklen_t;
+ let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t;
+ // Safe because the length given matches the length of the data of the given pointer, and we
+ // check the return value.
+ let ret = unsafe {
+ handle_eintr_errno!(libc::getsockname(
+ self.fd,
+ &mut addr as *mut libc::sockaddr_un as *mut libc::sockaddr,
+ &mut len
+ ))
+ };
+ if ret < 0 {
+ return Err(io::Error::last_os_error());
+ }
+ if addr.sun_family != libc::AF_UNIX as libc::sa_family_t
+ || addr.sun_path[0] == 0
+ || len < 1 + sun_path_offset
+ {
+ return Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "getsockname on socket returned invalid value",
+ ));
+ }
+
+ let path_os_str = OsString::from_vec(
+ addr.sun_path[..(len - sun_path_offset - 1) as usize]
+ .iter()
+ .map(|&c| c as _)
+ .collect(),
+ );
+ Ok(path_os_str.into())
+ }
+}
+
+impl Drop for UnixSeqpacketListener {
+ fn drop(&mut self) {
+ // Safe if the UnixSeqpacketListener is created from Self::listen.
+ unsafe {
+ libc::close(self.fd);
+ }
+ }
+}
+
+impl FromRawFd for UnixSeqpacketListener {
+ // Unsafe in drop function
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ Self { fd }
+ }
+}
+
+impl AsRawFd for UnixSeqpacketListener {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd
+ }
+}
+
+/// Used to attempt to clean up a `UnixSeqpacketListener` after it is dropped.
+pub struct UnlinkUnixSeqpacketListener(pub UnixSeqpacketListener);
+impl AsRef<UnixSeqpacketListener> for UnlinkUnixSeqpacketListener {
+ fn as_ref(&self) -> &UnixSeqpacketListener {
+ &self.0
+ }
+}
+
+impl AsRawFd for UnlinkUnixSeqpacketListener {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl Deref for UnlinkUnixSeqpacketListener {
+ type Target = UnixSeqpacketListener;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl Drop for UnlinkUnixSeqpacketListener {
+ fn drop(&mut self) {
+ if let Ok(path) = self.0.path() {
+ if let Err(e) = remove_file(path) {
+ warn!("failed to remove control socket file: {:?}", e);
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::env;
+ use std::path::PathBuf;
+
+ fn tmpdir() -> PathBuf {
+ env::temp_dir()
+ }
+
+ #[test]
+ fn sockaddr_un_zero_length_input() {
+ let _res = sockaddr_un(Path::new("")).expect("sockaddr_un failed");
+ }
+
+ #[test]
+ fn sockaddr_un_long_input_err() {
+ let res = sockaddr_un(Path::new(&"a".repeat(108)));
+ assert!(res.is_err());
+ }
+
+ #[test]
+ fn sockaddr_un_long_input_pass() {
+ let _res = sockaddr_un(Path::new(&"a".repeat(107))).expect("sockaddr_un failed");
+ }
+
+ #[test]
+ fn sockaddr_un_len_check() {
+ let (_addr, len) = sockaddr_un(Path::new(&"a".repeat(50))).expect("sockaddr_un failed");
+ assert_eq!(len, (sun_path_offset() + 50 + 1) as u32);
+ }
+
+ #[test]
+ fn sockaddr_un_pass() {
+ let path_size = 50;
+ let (addr, len) =
+ sockaddr_un(Path::new(&"a".repeat(path_size))).expect("sockaddr_un failed");
+ assert_eq!(len, (sun_path_offset() + path_size + 1) as u32);
+ assert_eq!(addr.sun_family, libc::AF_UNIX as libc::sa_family_t);
+
+ // Check `sun_path` in returned `sockaddr_un`
+ let mut ref_sun_path = [0 as libc::c_char; 108];
+ for i in 0..path_size {
+ ref_sun_path[i] = 'a' as libc::c_char;
+ }
+
+ for (addr_char, ref_char) in addr.sun_path.iter().zip(ref_sun_path.iter()) {
+ assert_eq!(addr_char, ref_char);
+ }
+ }
+
+ #[test]
+ fn unix_seqpacket_path_not_exists() {
+ let res = UnixSeqpacket::connect("/path/not/exists");
+ assert!(res.is_err());
+ }
+
+ #[test]
+ fn unix_seqpacket_listener_path() {
+ let mut socket_path = tmpdir();
+ socket_path.push("unix_seqpacket_listener_path");
+ let listener = UnlinkUnixSeqpacketListener(
+ UnixSeqpacketListener::bind(&socket_path)
+ .expect("failed to create UnixSeqpacketListener"),
+ );
+ let listener_path = listener.path().expect("failed to get socket listener path");
+ assert_eq!(socket_path, listener_path);
+ }
+
+ #[test]
+ fn unix_seqpacket_path_exists_pass() {
+ let mut socket_path = tmpdir();
+ socket_path.push("path_to_socket");
+ let _listener = UnlinkUnixSeqpacketListener(
+ UnixSeqpacketListener::bind(&socket_path)
+ .expect("failed to create UnixSeqpacketListener"),
+ );
+ let _res =
+ UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
+ }
+
+ #[test]
+ fn unix_seqpacket_path_listener_accept() {
+ let mut socket_path = tmpdir();
+ socket_path.push("path_listerner_accept");
+ let listener = UnlinkUnixSeqpacketListener(
+ UnixSeqpacketListener::bind(&socket_path)
+ .expect("failed to create UnixSeqpacketListener"),
+ );
+ let s1 =
+ UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
+
+ let s2 = listener.accept().expect("UnixSeqpacket::accept failed");
+
+ let data1 = &[0, 1, 2, 3, 4];
+ let data2 = &[10, 11, 12, 13, 14];
+ s2.send(data2).expect("failed to send data2");
+ s1.send(data1).expect("failed to send data1");
+ let recv_data = &mut [0; 5];
+ s2.recv(recv_data).expect("failed to recv data");
+ assert_eq!(data1, recv_data);
+ s1.recv(recv_data).expect("failed to recv data");
+ assert_eq!(data2, recv_data);
+ }
+
+ #[test]
+ fn unix_seqpacket_zero_timeout() {
+ let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
+ // Timeouts less than a microsecond are too small and round to zero.
+ s1.set_read_timeout(Some(Duration::from_nanos(10)))
+ .expect_err("successfully set zero timeout");
+ }
+
+ #[test]
+ fn unix_seqpacket_read_timeout() {
+ let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
+ s1.set_read_timeout(Some(Duration::from_millis(1)))
+ .expect("failed to set read timeout for socket");
+ let _ = s1.recv(&mut [0]);
+ }
+
+ #[test]
+ fn unix_seqpacket_write_timeout() {
+ let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
+ s1.set_write_timeout(Some(Duration::from_millis(1)))
+ .expect("failed to set write timeout for socket");
+ }
+
+ #[test]
+ fn unix_seqpacket_send_recv() {
+ let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
+ let data1 = &[0, 1, 2, 3, 4];
+ let data2 = &[10, 11, 12, 13, 14];
+ s2.send(data2).expect("failed to send data2");
+ s1.send(data1).expect("failed to send data1");
+ let recv_data = &mut [0; 5];
+ s2.recv(recv_data).expect("failed to recv data");
+ assert_eq!(data1, recv_data);
+ s1.recv(recv_data).expect("failed to recv data");
+ assert_eq!(data2, recv_data);
+ }
+
+ #[test]
+ fn unix_seqpacket_send_fragments() {
+ let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
+ let data1 = &[0, 1, 2, 3, 4];
+ let data2 = &[10, 11, 12, 13, 14, 15, 16];
+ s1.send(data1).expect("failed to send data1");
+ s1.send(data2).expect("failed to send data2");
+
+ let recv_data = &mut [0; 32];
+ let size = s2.recv(recv_data).expect("failed to recv data");
+ assert_eq!(size, data1.len());
+ assert_eq!(data1, &recv_data[0..size]);
+
+ let size = s2.recv(recv_data).expect("failed to recv data");
+ assert_eq!(size, data2.len());
+ assert_eq!(data2, &recv_data[0..size]);
+ }
+
+ #[test]
+ fn unix_seqpacket_get_readable_bytes() {
+ let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
+ assert_eq!(s1.get_readable_bytes().unwrap(), 0);
+ assert_eq!(s2.get_readable_bytes().unwrap(), 0);
+ let data1 = &[0, 1, 2, 3, 4];
+ s1.send(data1).expect("failed to send data");
+
+ assert_eq!(s1.get_readable_bytes().unwrap(), 0);
+ assert_eq!(s2.get_readable_bytes().unwrap(), data1.len());
+
+ let recv_data = &mut [0; 5];
+ s2.recv(recv_data).expect("failed to recv data");
+ assert_eq!(s1.get_readable_bytes().unwrap(), 0);
+ assert_eq!(s2.get_readable_bytes().unwrap(), 0);
+ }
+}
diff --git a/sys_util/src/passwd.rs b/sys_util/src/passwd.rs
new file mode 100644
index 0000000..19a72cb
--- /dev/null
+++ b/sys_util/src/passwd.rs
@@ -0,0 +1,122 @@
+// Copyright 2017 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.
+
+//! Wrappers for passwd and group file access.
+
+use std::ffi::CStr;
+use std::mem;
+use std::ptr;
+
+use libc::{self, c_char, getgrnam_r, getpwnam_r, gid_t, uid_t};
+
+use crate::{errno_result, Result};
+
+/// Safe wrapper for getting a uid from a user name with `getpwnam_r(3)`.
+#[inline(always)]
+pub fn get_user_id(user_name: &CStr) -> Result<uid_t> {
+ // libc::passwd is a C struct and can be safely initialized with zeroed memory.
+ let mut passwd: libc::passwd = unsafe { mem::zeroed() };
+ let mut passwd_result: *mut libc::passwd = ptr::null_mut();
+ let mut buf = [0 as c_char; 256];
+
+ // For thread-safety, use the reentrant version of this function. This allows us to give it a
+ // buffer on the stack (instead of a global buffer). Unlike most libc functions, the return
+ // value of this doesn't really need to be checked, since the extra result pointer that is
+ // passed in indicates whether or not the function succeeded.
+ //
+ // This call is safe as long as it behaves as described in the man page. We pass in valid
+ // pointers to stack-allocated buffers, and the length check for the scratch buffer is correct.
+ unsafe {
+ handle_eintr_rc!(getpwnam_r(
+ user_name.as_ptr(),
+ &mut passwd,
+ buf.as_mut_ptr(),
+ buf.len(),
+ &mut passwd_result
+ ))
+ };
+
+ if passwd_result.is_null() {
+ errno_result()
+ } else {
+ Ok(passwd.pw_uid)
+ }
+}
+
+/// Safe wrapper for getting a gid from a group name with `getgrnam_r(3)`.
+#[inline(always)]
+pub fn get_group_id(group_name: &CStr) -> Result<gid_t> {
+ // libc::group is a C struct and can be safely initialized with zeroed memory.
+ let mut group: libc::group = unsafe { mem::zeroed() };
+ let mut group_result: *mut libc::group = ptr::null_mut();
+ let mut buf = [0 as c_char; 256];
+
+ // For thread-safety, use the reentrant version of this function. This allows us to give it a
+ // buffer on the stack (instead of a global buffer). Unlike most libc functions, the return
+ // value of this doesn't really need to be checked, since the extra result pointer that is
+ // passed in indicates whether or not the function succeeded.
+ //
+ // This call is safe as long as it behaves as described in the man page. We pass in valid
+ // pointers to stack-allocated buffers, and the length check for the scratch buffer is correct.
+ unsafe {
+ handle_eintr_rc!(getgrnam_r(
+ group_name.as_ptr(),
+ &mut group,
+ buf.as_mut_ptr(),
+ buf.len(),
+ &mut group_result
+ ))
+ };
+
+ if group_result.is_null() {
+ errno_result()
+ } else {
+ Ok(group.gr_gid)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn get_good_uid() {
+ let root_name = CStr::from_bytes_with_nul(b"root\0").unwrap();
+
+ // root's uid should always exist, and should be 0.
+ let root_uid = get_user_id(root_name).unwrap();
+ assert_eq!(root_uid, 0);
+ }
+
+ #[test]
+ fn get_bad_uid() {
+ let bad_name = CStr::from_bytes_with_nul(b"this better not be a user\0").unwrap();
+
+ // This user should give us an error. As a cruel joke, the getpwnam(3) man page allows
+ // ENOENT, ESRCH, EBADF, EPERM, or even 0 to be set in errno if a user isn't found. So
+ // instead of checking which error we got, just see that we did get one.
+ let bad_uid_result = get_user_id(bad_name);
+ assert!(bad_uid_result.is_err());
+ }
+
+ #[test]
+ fn get_good_gid() {
+ let root_name = CStr::from_bytes_with_nul(b"root\0").unwrap();
+
+ // root's gid should always exist, and should be 0.
+ let root_gid = get_group_id(root_name).unwrap();
+ assert_eq!(root_gid, 0);
+ }
+
+ #[test]
+ fn get_bad_gid() {
+ let bad_name = CStr::from_bytes_with_nul(b"this better not be a group\0").unwrap();
+
+ // This group should give us an error. As a cruel joke, the getgrnam(3) man page allows
+ // ENOENT, ESRCH, EBADF, EPERM, or even 0 to be set in errno if a group isn't found. So
+ // instead of checking which error we got, just see that we did get one.
+ let bad_gid_result = get_group_id(bad_name);
+ assert!(bad_gid_result.is_err());
+ }
+}
diff --git a/sys_util/src/poll.rs b/sys_util/src/poll.rs
new file mode 100644
index 0000000..0da0831
--- /dev/null
+++ b/sys_util/src/poll.rs
@@ -0,0 +1,753 @@
+// Copyright 2017 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::cell::{Cell, Ref, RefCell};
+use std::cmp::min;
+use std::fs::File;
+use std::i32;
+use std::i64;
+use std::marker::PhantomData;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::ptr::null_mut;
+use std::slice;
+use std::thread;
+use std::time::Duration;
+
+use libc::{
+ c_int, epoll_create1, epoll_ctl, epoll_event, epoll_wait, EPOLLHUP, EPOLLIN, EPOLLOUT,
+ EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD,
+};
+
+use crate::{errno_result, Result};
+
+const POLL_CONTEXT_MAX_EVENTS: usize = 16;
+
+/// EpollEvents wraps raw epoll_events, it should only be used with EpollContext.
+pub struct EpollEvents(RefCell<[epoll_event; POLL_CONTEXT_MAX_EVENTS]>);
+
+impl EpollEvents {
+ pub fn new() -> EpollEvents {
+ EpollEvents(RefCell::new(
+ [epoll_event { events: 0, u64: 0 }; POLL_CONTEXT_MAX_EVENTS],
+ ))
+ }
+}
+
+impl Default for EpollEvents {
+ fn default() -> EpollEvents {
+ Self::new()
+ }
+}
+
+/// Trait for a token that can be associated with an `fd` in a `PollContext`.
+///
+/// Simple enums that have no or primitive variant data data can use the `#[derive(PollToken)]`
+/// custom derive to implement this trait. See
+/// [poll_token_derive::poll_token](../poll_token_derive/fn.poll_token.html) for details.
+pub trait PollToken {
+ /// Converts this token into a u64 that can be turned back into a token via `from_raw_token`.
+ fn as_raw_token(&self) -> u64;
+
+ /// Converts a raw token as returned from `as_raw_token` back into a token.
+ ///
+ /// It is invalid to give a raw token that was not returned via `as_raw_token` from the same
+ /// `Self`. The implementation can expect that this will never happen as a result of its usage
+ /// in `PollContext`.
+ fn from_raw_token(data: u64) -> Self;
+}
+
+impl PollToken for usize {
+ fn as_raw_token(&self) -> u64 {
+ *self as u64
+ }
+
+ fn from_raw_token(data: u64) -> Self {
+ data as Self
+ }
+}
+
+impl PollToken for u64 {
+ fn as_raw_token(&self) -> u64 {
+ *self as u64
+ }
+
+ fn from_raw_token(data: u64) -> Self {
+ data as Self
+ }
+}
+
+impl PollToken for u32 {
+ fn as_raw_token(&self) -> u64 {
+ u64::from(*self)
+ }
+
+ fn from_raw_token(data: u64) -> Self {
+ data as Self
+ }
+}
+
+impl PollToken for u16 {
+ fn as_raw_token(&self) -> u64 {
+ u64::from(*self)
+ }
+
+ fn from_raw_token(data: u64) -> Self {
+ data as Self
+ }
+}
+
+impl PollToken for u8 {
+ fn as_raw_token(&self) -> u64 {
+ u64::from(*self)
+ }
+
+ fn from_raw_token(data: u64) -> Self {
+ data as Self
+ }
+}
+
+impl PollToken for () {
+ fn as_raw_token(&self) -> u64 {
+ 0
+ }
+
+ fn from_raw_token(_data: u64) -> Self {}
+}
+
+/// An event returned by `PollContext::wait`.
+pub struct PollEvent<'a, T> {
+ event: &'a epoll_event,
+ token: PhantomData<T>, // Needed to satisfy usage of T
+}
+
+impl<'a, T: PollToken> PollEvent<'a, T> {
+ /// Gets the token associated in `PollContext::add` with this event.
+ pub fn token(&self) -> T {
+ T::from_raw_token(self.event.u64)
+ }
+
+ /// True if the `fd` associated with this token in `PollContext::add` is readable.
+ pub fn readable(&self) -> bool {
+ self.event.events & (EPOLLIN 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
+ }
+}
+
+/// An iterator over some (sub)set of events returned by `PollContext::wait`.
+pub struct PollEventIter<'a, I, T>
+where
+ I: Iterator<Item = &'a epoll_event>,
+{
+ mask: u32,
+ iter: I,
+ tokens: PhantomData<[T]>, // Needed to satisfy usage of T
+}
+
+impl<'a, I, T> Iterator for PollEventIter<'a, I, T>
+where
+ I: Iterator<Item = &'a epoll_event>,
+ T: PollToken,
+{
+ type Item = PollEvent<'a, T>;
+ fn next(&mut self) -> Option<Self::Item> {
+ let mask = self.mask;
+ self.iter
+ .find(|event| (event.events & mask) != 0)
+ .map(|event| PollEvent {
+ event,
+ token: PhantomData,
+ })
+ }
+}
+
+/// The list of event returned by `PollContext::wait`.
+pub struct PollEvents<'a, T> {
+ count: usize,
+ events: Ref<'a, [epoll_event; POLL_CONTEXT_MAX_EVENTS]>,
+ tokens: PhantomData<[T]>, // Needed to satisfy usage of T
+}
+
+impl<'a, T: PollToken> PollEvents<'a, T> {
+ /// Copies the events to an owned structure so the reference to this (and by extension
+ /// `PollContext`) can be dropped.
+ pub fn to_owned(&self) -> PollEventsOwned<T> {
+ PollEventsOwned {
+ count: self.count,
+ events: RefCell::new(*self.events),
+ tokens: PhantomData,
+ }
+ }
+
+ /// Iterates over each event.
+ pub fn iter(&self) -> PollEventIter<slice::Iter<epoll_event>, T> {
+ PollEventIter {
+ mask: 0xffff_ffff,
+ iter: self.events[..self.count].iter(),
+ tokens: PhantomData,
+ }
+ }
+
+ /// Iterates over each readable event.
+ pub fn iter_readable(&self) -> PollEventIter<slice::Iter<epoll_event>, T> {
+ PollEventIter {
+ mask: EPOLLIN as u32,
+ iter: self.events[..self.count].iter(),
+ tokens: PhantomData,
+ }
+ }
+
+ /// Iterates over each hungup event.
+ pub fn iter_hungup(&self) -> PollEventIter<slice::Iter<epoll_event>, T> {
+ PollEventIter {
+ mask: EPOLLHUP as u32,
+ iter: self.events[..self.count].iter(),
+ tokens: PhantomData,
+ }
+ }
+}
+
+impl<'a, T: PollToken> IntoIterator for &'a PollEvents<'_, T> {
+ type Item = PollEvent<'a, T>;
+ type IntoIter = PollEventIter<'a, slice::Iter<'a, epoll_event>, T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+/// A deep copy of the event records from `PollEvents`.
+pub struct PollEventsOwned<T> {
+ count: usize,
+ events: RefCell<[epoll_event; POLL_CONTEXT_MAX_EVENTS]>,
+ tokens: PhantomData<T>, // Needed to satisfy usage of T
+}
+
+impl<T: PollToken> PollEventsOwned<T> {
+ /// Takes a reference to the events so that they can be iterated via methods in `PollEvents`.
+ pub fn as_ref(&self) -> PollEvents<T> {
+ PollEvents {
+ count: self.count,
+ events: self.events.borrow(),
+ tokens: PhantomData,
+ }
+ }
+}
+
+/// Watching events taken by PollContext.
+pub struct WatchingEvents(u32);
+
+impl WatchingEvents {
+ /// Returns empty Events.
+ #[inline(always)]
+ pub fn empty() -> WatchingEvents {
+ WatchingEvents(0)
+ }
+
+ /// Build Events from raw epoll events (defined in epoll_ctl(2)).
+ #[inline(always)]
+ pub fn new(raw: u32) -> WatchingEvents {
+ WatchingEvents(raw)
+ }
+
+ /// Set read events.
+ #[inline(always)]
+ pub fn set_read(self) -> WatchingEvents {
+ WatchingEvents(self.0 | EPOLLIN as u32)
+ }
+
+ /// Set write events.
+ #[inline(always)]
+ pub fn set_write(self) -> WatchingEvents {
+ WatchingEvents(self.0 | EPOLLOUT as u32)
+ }
+
+ /// Get the underlying epoll events.
+ pub fn get_raw(&self) -> u32 {
+ self.0
+ }
+}
+
+/// EpollContext wraps linux epoll. It provides similar interface to PollContext.
+/// It is thread safe while PollContext is not. It requires user to pass in a reference of
+/// EpollEvents while PollContext does not. Always use PollContext if you don't need to access the
+/// same epoll from different threads.
+pub struct EpollContext<T> {
+ epoll_ctx: File,
+ // Needed to satisfy usage of T
+ tokens: PhantomData<[T]>,
+}
+
+impl<T: PollToken> EpollContext<T> {
+ /// Creates a new `EpollContext`.
+ pub fn new() -> Result<EpollContext<T>> {
+ // Safe because we check the return value.
+ let epoll_fd = unsafe { epoll_create1(EPOLL_CLOEXEC) };
+ if epoll_fd < 0 {
+ return errno_result();
+ }
+ Ok(EpollContext {
+ epoll_ctx: unsafe { File::from_raw_fd(epoll_fd) },
+ tokens: PhantomData,
+ })
+ }
+
+ /// Adds the given `fd` to this context and associates the given `token` with the `fd`'s
+ /// readable events.
+ ///
+ /// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
+ /// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
+ /// FD number) added to this context, events will not be reported by `wait` anymore.
+ pub fn add(&self, fd: &dyn AsRawFd, token: T) -> Result<()> {
+ self.add_fd_with_events(fd, WatchingEvents::empty().set_read(), token)
+ }
+
+ /// Adds the given `fd` to this context, watching for the specified events and associates the
+ /// given 'token' with those events.
+ ///
+ /// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
+ /// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
+ /// FD number) added to this context, events will not be reported by `wait` anymore.
+ pub fn add_fd_with_events(
+ &self,
+ fd: &dyn AsRawFd,
+ events: WatchingEvents,
+ token: T,
+ ) -> Result<()> {
+ let mut evt = epoll_event {
+ events: events.get_raw(),
+ u64: token.as_raw_token(),
+ };
+ // Safe because we give a valid epoll FD and FD to watch, as well as a valid epoll_event
+ // structure. Then we check the return value.
+ let ret = unsafe {
+ epoll_ctl(
+ self.epoll_ctx.as_raw_fd(),
+ EPOLL_CTL_ADD,
+ fd.as_raw_fd(),
+ &mut evt,
+ )
+ };
+ if ret < 0 {
+ return errno_result();
+ };
+ Ok(())
+ }
+
+ /// If `fd` was previously added to this context, the watched events will be replaced with
+ /// `events` and the token associated with it will be replaced with the given `token`.
+ pub fn modify(&self, fd: &dyn AsRawFd, events: WatchingEvents, token: T) -> Result<()> {
+ let mut evt = epoll_event {
+ events: events.0,
+ u64: token.as_raw_token(),
+ };
+ // Safe because we give a valid epoll FD and FD to modify, as well as a valid epoll_event
+ // structure. Then we check the return value.
+ let ret = unsafe {
+ epoll_ctl(
+ self.epoll_ctx.as_raw_fd(),
+ EPOLL_CTL_MOD,
+ fd.as_raw_fd(),
+ &mut evt,
+ )
+ };
+ if ret < 0 {
+ return errno_result();
+ };
+ Ok(())
+ }
+
+ /// Deletes the given `fd` from this context.
+ ///
+ /// If an `fd`'s token shows up in the list of hangup events, it should be removed using this
+ /// method or by closing/dropping (if and only if the fd was never dup()'d/fork()'d) the `fd`.
+ /// Failure to do so will cause the `wait` method to always return immediately, causing ~100%
+ /// CPU load.
+ pub fn delete(&self, fd: &dyn AsRawFd) -> Result<()> {
+ // Safe because we give a valid epoll FD and FD to stop watching. Then we check the return
+ // value.
+ let ret = unsafe {
+ epoll_ctl(
+ self.epoll_ctx.as_raw_fd(),
+ EPOLL_CTL_DEL,
+ fd.as_raw_fd(),
+ null_mut(),
+ )
+ };
+ if ret < 0 {
+ return errno_result();
+ };
+ Ok(())
+ }
+
+ /// Waits for any events to occur in FDs that were previously added to this context.
+ ///
+ /// The events are level-triggered, meaning that if any events are unhandled (i.e. not reading
+ /// for readable events and not closing for hungup events), subsequent calls to `wait` will
+ /// return immediately. The consequence of not handling an event perpetually while calling
+ /// `wait` is that the callers loop will degenerated to busy loop polling, pinning a CPU to
+ /// ~100% usage.
+ pub fn wait<'a>(&self, events: &'a EpollEvents) -> Result<PollEvents<'a, T>> {
+ self.wait_timeout(events, Duration::new(i64::MAX as u64, 0))
+ }
+
+ /// Like `wait` except will only block for a maximum of the given `timeout`.
+ ///
+ /// This may return earlier than `timeout` with zero events if the duration indicated exceeds
+ /// system limits.
+ pub fn wait_timeout<'a>(
+ &self,
+ events: &'a EpollEvents,
+ timeout: Duration,
+ ) -> Result<PollEvents<'a, T>> {
+ let timeout_millis = if timeout.as_secs() as i64 == i64::max_value() {
+ // We make the convenient assumption that 2^63 seconds is an effectively unbounded time
+ // frame. This is meant to mesh with `wait` calling us with no timeout.
+ -1
+ } else {
+ // In cases where we the number of milliseconds would overflow an i32, we substitute the
+ // maximum timeout which is ~24.8 days.
+ let millis = timeout
+ .as_secs()
+ .checked_mul(1_000)
+ .and_then(|ms| ms.checked_add(u64::from(timeout.subsec_nanos()) / 1_000_000))
+ .unwrap_or(i32::max_value() as u64);
+ min(i32::max_value() as u64, millis) as i32
+ };
+ let ret = {
+ let mut epoll_events = events.0.borrow_mut();
+ let max_events = epoll_events.len() as c_int;
+ // Safe because we give an epoll context and a properly sized epoll_events array
+ // pointer, which we trust the kernel to fill in properly.
+ unsafe {
+ handle_eintr_errno!(epoll_wait(
+ self.epoll_ctx.as_raw_fd(),
+ &mut epoll_events[0],
+ max_events,
+ timeout_millis
+ ))
+ }
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+ let epoll_events = events.0.borrow();
+ let events = PollEvents {
+ count: ret as usize,
+ events: epoll_events,
+ tokens: PhantomData,
+ };
+ Ok(events)
+ }
+}
+
+impl<T: PollToken> AsRawFd for EpollContext<T> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.epoll_ctx.as_raw_fd()
+ }
+}
+
+impl<T: PollToken> IntoRawFd for EpollContext<T> {
+ fn into_raw_fd(self) -> RawFd {
+ self.epoll_ctx.into_raw_fd()
+ }
+}
+
+/// Used to poll multiple objects that have file descriptors.
+///
+/// # Example
+///
+/// ```
+/// # use sys_util::{Result, EventFd, PollContext, PollEvents};
+/// # fn test() -> Result<()> {
+/// let evt1 = EventFd::new()?;
+/// let evt2 = EventFd::new()?;
+/// evt2.write(1)?;
+///
+/// let ctx: PollContext<u32> = PollContext::new()?;
+/// ctx.add(&evt1, 1)?;
+/// ctx.add(&evt2, 2)?;
+///
+/// let pollevents: PollEvents<u32> = ctx.wait()?;
+/// let tokens: Vec<u32> = pollevents.iter_readable().map(|e| e.token()).collect();
+/// assert_eq!(&tokens[..], &[2]);
+/// # Ok(())
+/// # }
+/// ```
+pub struct PollContext<T> {
+ epoll_ctx: EpollContext<T>,
+
+ // We use a RefCell here so that the `wait` method only requires an immutable self reference
+ // while returning the events (encapsulated by PollEvents). Without the RefCell, `wait` would
+ // hold a mutable reference that lives as long as its returned reference (i.e. the PollEvents),
+ // even though that reference is immutable. This is terribly inconvenient for the caller because
+ // the borrow checking would prevent them from using `delete` and `add` while the events are in
+ // scope.
+ events: EpollEvents,
+
+ // Hangup busy loop detection variables. See `check_for_hungup_busy_loop`.
+ hangups: Cell<usize>,
+ max_hangups: Cell<usize>,
+}
+
+impl<T: PollToken> PollContext<T> {
+ /// Creates a new `PollContext`.
+ pub fn new() -> Result<PollContext<T>> {
+ Ok(PollContext {
+ epoll_ctx: EpollContext::new()?,
+ events: EpollEvents::new(),
+ hangups: Cell::new(0),
+ max_hangups: Cell::new(0),
+ })
+ }
+
+ /// Adds the given `fd` to this context and associates the given `token` with the `fd`'s
+ /// readable events.
+ ///
+ /// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
+ /// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
+ /// FD number) added to this context, events will not be reported by `wait` anymore.
+ pub fn add(&self, fd: &dyn AsRawFd, token: T) -> Result<()> {
+ self.add_fd_with_events(fd, WatchingEvents::empty().set_read(), token)
+ }
+
+ /// Adds the given `fd` to this context, watching for the specified events and associates the
+ /// given 'token' with those events.
+ ///
+ /// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
+ /// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
+ /// FD number) added to this context, events will not be reported by `wait` anymore.
+ pub fn add_fd_with_events(
+ &self,
+ fd: &dyn AsRawFd,
+ events: WatchingEvents,
+ token: T,
+ ) -> Result<()> {
+ self.epoll_ctx.add_fd_with_events(fd, events, token)?;
+ self.hangups.set(0);
+ self.max_hangups.set(self.max_hangups.get() + 1);
+ Ok(())
+ }
+
+ /// If `fd` was previously added to this context, the watched events will be replaced with
+ /// `events` and the token associated with it will be replaced with the given `token`.
+ pub fn modify(&self, fd: &dyn AsRawFd, events: WatchingEvents, token: T) -> Result<()> {
+ self.epoll_ctx.modify(fd, events, token)
+ }
+
+ /// Deletes the given `fd` from this context.
+ ///
+ /// If an `fd`'s token shows up in the list of hangup events, it should be removed using this
+ /// method or by closing/dropping (if and only if the fd was never dup()'d/fork()'d) the `fd`.
+ /// Failure to do so will cause the `wait` method to always return immediately, causing ~100%
+ /// CPU load.
+ pub fn delete(&self, fd: &dyn AsRawFd) -> Result<()> {
+ self.epoll_ctx.delete(fd)?;
+ self.hangups.set(0);
+ self.max_hangups.set(self.max_hangups.get() - 1);
+ Ok(())
+ }
+
+ // This method determines if the the user of wait is misusing the `PollContext` by leaving FDs
+ // in this `PollContext` that have been shutdown or hungup on. Such an FD will cause `wait` to
+ // return instantly with a hungup event. If that FD is perpetually left in this context, a busy
+ // loop burning ~100% of one CPU will silently occur with no human visible malfunction.
+ //
+ // How do we know if the client of this context is ignoring hangups? A naive implementation
+ // would trigger if consecutive wait calls yield hangup events, but there are legitimate cases
+ // for this, such as two distinct sockets becoming hungup across two consecutive wait calls. A
+ // smarter implementation would only trigger if `delete` wasn't called between waits that
+ // yielded hangups. Sadly `delete` isn't the only way to remove an FD from this context. The
+ // other way is for the client to close the hungup FD, which automatically removes it from this
+ // context. Assuming that the client always uses close, this implementation would too eagerly
+ // trigger.
+ //
+ // The implementation used here keeps an upper bound of FDs in this context using a counter
+ // hooked into add/delete (which is imprecise because close can also remove FDs without us
+ // knowing). The number of consecutive (no add or delete in between) hangups yielded by wait
+ // calls is counted and compared to the upper bound. If the upper bound is exceeded by the
+ // consecutive hangups, the implementation triggers the check and logs.
+ //
+ // This implementation has false negatives because the upper bound can be completely too high,
+ // in the worst case caused by only using close instead of delete. However, this method has the
+ // advantage of always triggering eventually genuine busy loop cases, requires no dynamic
+ // allocations, is fast and constant time to compute, and has no false positives.
+ fn check_for_hungup_busy_loop(&self, new_hangups: usize) {
+ let old_hangups = self.hangups.get();
+ let max_hangups = self.max_hangups.get();
+ if old_hangups <= max_hangups && old_hangups + new_hangups > max_hangups {
+ warn!(
+ "busy poll wait loop with hungup FDs detected on thread {}",
+ thread::current().name().unwrap_or("")
+ );
+ // This panic is helpful for tests of this functionality.
+ #[cfg(test)]
+ panic!("hungup busy loop detected");
+ }
+ self.hangups.set(old_hangups + new_hangups);
+ }
+
+ /// Waits for any events to occur in FDs that were previously added to this context.
+ ///
+ /// The events are level-triggered, meaning that if any events are unhandled (i.e. not reading
+ /// for readable events and not closing for hungup events), subsequent calls to `wait` will
+ /// return immediately. The consequence of not handling an event perpetually while calling
+ /// `wait` is that the callers loop will degenerated to busy loop polling, pinning a CPU to
+ /// ~100% usage.
+ ///
+ /// # Panics
+ /// Panics if the returned `PollEvents` structure is not dropped before subsequent `wait` calls.
+ pub fn wait(&self) -> Result<PollEvents<T>> {
+ self.wait_timeout(Duration::new(i64::MAX as u64, 0))
+ }
+
+ /// Like `wait` except will only block for a maximum of the given `timeout`.
+ ///
+ /// This may return earlier than `timeout` with zero events if the duration indicated exceeds
+ /// system limits.
+ pub fn wait_timeout(&self, timeout: Duration) -> Result<PollEvents<T>> {
+ let events = self.epoll_ctx.wait_timeout(&self.events, timeout)?;
+ let hangups = events.iter_hungup().count();
+ self.check_for_hungup_busy_loop(hangups);
+ Ok(events)
+ }
+}
+
+impl<T: PollToken> AsRawFd for PollContext<T> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.epoll_ctx.as_raw_fd()
+ }
+}
+
+impl<T: PollToken> IntoRawFd for PollContext<T> {
+ fn into_raw_fd(self) -> RawFd {
+ self.epoll_ctx.into_raw_fd()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::EventFd;
+ use poll_token_derive::PollToken;
+ use std::os::unix::net::UnixStream;
+ use std::time::Instant;
+
+ #[test]
+ fn poll_context() {
+ let evt1 = EventFd::new().unwrap();
+ let evt2 = EventFd::new().unwrap();
+ evt1.write(1).unwrap();
+ evt2.write(1).unwrap();
+ let ctx: PollContext<u32> = PollContext::new().unwrap();
+ ctx.add(&evt1, 1).unwrap();
+ ctx.add(&evt2, 2).unwrap();
+
+ let mut evt_count = 0;
+ while evt_count < 2 {
+ for event in ctx.wait().unwrap().iter_readable() {
+ evt_count += 1;
+ match event.token() {
+ 1 => {
+ evt1.read().unwrap();
+ ctx.delete(&evt1).unwrap();
+ }
+ 2 => {
+ evt2.read().unwrap();
+ ctx.delete(&evt2).unwrap();
+ }
+ _ => panic!("unexpected token"),
+ };
+ }
+ }
+ assert_eq!(evt_count, 2);
+ }
+
+ #[test]
+ fn poll_context_overflow() {
+ const EVT_COUNT: usize = POLL_CONTEXT_MAX_EVENTS * 2 + 1;
+ let ctx: PollContext<usize> = PollContext::new().unwrap();
+ let mut evts = Vec::with_capacity(EVT_COUNT);
+ for i in 0..EVT_COUNT {
+ let evt = EventFd::new().unwrap();
+ evt.write(1).unwrap();
+ ctx.add(&evt, i).unwrap();
+ evts.push(evt);
+ }
+ let mut evt_count = 0;
+ while evt_count < EVT_COUNT {
+ for event in ctx.wait().unwrap().iter_readable() {
+ evts[event.token()].read().unwrap();
+ evt_count += 1;
+ }
+ }
+ }
+
+ #[test]
+ #[should_panic]
+ fn poll_context_hungup() {
+ let (s1, s2) = UnixStream::pair().unwrap();
+ let ctx: PollContext<u32> = PollContext::new().unwrap();
+ ctx.add(&s1, 1).unwrap();
+
+ // Causes s1 to receive hangup events, which we purposefully ignore to trip the detection
+ // logic in `PollContext`.
+ drop(s2);
+
+ // Should easily panic within this many iterations.
+ for _ in 0..1000 {
+ ctx.wait().unwrap();
+ }
+ }
+
+ #[test]
+ fn poll_context_timeout() {
+ let ctx: PollContext<u32> = PollContext::new().unwrap();
+ let dur = Duration::from_millis(10);
+ let start_inst = Instant::now();
+ ctx.wait_timeout(dur).unwrap();
+ assert!(start_inst.elapsed() >= dur);
+ }
+
+ #[test]
+ #[allow(dead_code)]
+ fn poll_token_derive() {
+ #[derive(PollToken)]
+ enum EmptyToken {}
+
+ #[derive(PartialEq, Debug, PollToken)]
+ enum Token {
+ Alpha,
+ Beta,
+ // comments
+ Gamma(u32),
+ Delta { index: usize },
+ Omega,
+ }
+
+ assert_eq!(
+ Token::from_raw_token(Token::Alpha.as_raw_token()),
+ Token::Alpha
+ );
+ assert_eq!(
+ Token::from_raw_token(Token::Beta.as_raw_token()),
+ Token::Beta
+ );
+ assert_eq!(
+ Token::from_raw_token(Token::Gamma(55).as_raw_token()),
+ Token::Gamma(55)
+ );
+ assert_eq!(
+ Token::from_raw_token(Token::Delta { index: 100 }.as_raw_token()),
+ Token::Delta { index: 100 }
+ );
+ assert_eq!(
+ Token::from_raw_token(Token::Omega.as_raw_token()),
+ Token::Omega
+ );
+ }
+}
diff --git a/sys_util/src/priority.rs b/sys_util/src/priority.rs
new file mode 100644
index 0000000..77dfe29
--- /dev/null
+++ b/sys_util/src/priority.rs
@@ -0,0 +1,38 @@
+// Copyright 2018 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::{errno_result, Result};
+
+/// Enables real time thread priorities in the current thread up to `limit`.
+pub fn set_rt_prio_limit(limit: u64) -> Result<()> {
+ let rt_limit_arg = libc::rlimit {
+ rlim_cur: limit as libc::rlim_t,
+ rlim_max: limit as libc::rlim_t,
+ };
+ // Safe because the kernel doesn't modify memory that is accessible to the process here.
+ let res = unsafe { libc::setrlimit(libc::RLIMIT_RTPRIO, &rt_limit_arg) };
+
+ if res != 0 {
+ errno_result()
+ } else {
+ Ok(())
+ }
+}
+
+/// Sets the current thread to be scheduled using the round robin real time class with `priority`.
+pub fn set_rt_round_robin(priority: i32) -> Result<()> {
+ let sched_param = libc::sched_param {
+ sched_priority: priority,
+ };
+
+ // Safe because the kernel doesn't modify memory that is accessible to the process here.
+ let res =
+ unsafe { libc::pthread_setschedparam(libc::pthread_self(), libc::SCHED_RR, &sched_param) };
+
+ if res != 0 {
+ errno_result()
+ } else {
+ Ok(())
+ }
+}
diff --git a/sys_util/src/raw_fd.rs b/sys_util/src/raw_fd.rs
new file mode 100644
index 0000000..05a762f
--- /dev/null
+++ b/sys_util/src/raw_fd.rs
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Utility file to provide a slightly safer Fd type that cannot be confused with c_int.
+// Also useful for situations that require something that is `AsRawFd` but
+// where we don't want to store more than the fd.
+
+use std::os::unix::io::{AsRawFd, RawFd};
+
+pub struct Fd(pub RawFd);
+impl AsRawFd for Fd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0
+ }
+}
diff --git a/sys_util/src/seek_hole.rs b/sys_util/src/seek_hole.rs
new file mode 100644
index 0000000..8215a8d
--- /dev/null
+++ b/sys_util/src/seek_hole.rs
@@ -0,0 +1,203 @@
+// Copyright 2018 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::{Error, Result};
+use std::os::unix::io::AsRawFd;
+
+use libc::{lseek64, ENXIO, SEEK_DATA, SEEK_HOLE};
+
+/// A trait for seeking to the next hole or non-hole position in a file.
+pub trait SeekHole {
+ /// Seek to the first hole in a file at a position greater than or equal to `offset`.
+ /// If no holes exist after `offset`, the seek position will be set to the end of the file.
+ /// If `offset` is at or after the end of the file, the seek position is unchanged, and None is returned.
+ /// Returns the current seek position after the seek or an error.
+ fn seek_hole(&mut self, offset: u64) -> Result<Option<u64>>;
+
+ /// Seek to the first data in a file at a position greater than or equal to `offset`.
+ /// If no data exists after `offset`, the seek position is unchanged, and None is returned.
+ /// Returns the current offset after the seek or an error.
+ fn seek_data(&mut self, offset: u64) -> Result<Option<u64>>;
+}
+
+/// Safe wrapper for `libc::lseek64()`
+fn lseek(file: &mut File, offset: i64, whence: i32) -> Result<Option<u64>> {
+ // This is safe because we pass a known-good file descriptor.
+ let res = unsafe { lseek64(file.as_raw_fd(), offset, whence) };
+
+ if res < 0 {
+ // Convert ENXIO into None; pass any other error as-is.
+ let err = Error::last_os_error();
+ if let Some(errno) = Error::raw_os_error(&err) {
+ if errno == ENXIO {
+ return Ok(None);
+ }
+ }
+ Err(err)
+ } else {
+ Ok(Some(res as u64))
+ }
+}
+
+impl SeekHole for File {
+ fn seek_hole(&mut self, offset: u64) -> Result<Option<u64>> {
+ lseek(self, offset as i64, SEEK_HOLE)
+ }
+
+ fn seek_data(&mut self, offset: u64) -> Result<Option<u64>> {
+ lseek(self, offset as i64, SEEK_DATA)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::TempDir;
+ use std::fs::File;
+ use std::io::{Seek, SeekFrom, Write};
+ use std::path::PathBuf;
+
+ fn seek_cur(file: &mut File) -> u64 {
+ file.seek(SeekFrom::Current(0)).unwrap()
+ }
+
+ #[test]
+ fn seek_data() {
+ let tempdir = TempDir::new("/tmp/seek_data_test").unwrap();
+ let mut path = PathBuf::from(tempdir.as_path().unwrap());
+ path.push("test_file");
+ let mut file = File::create(&path).unwrap();
+
+ // Empty file
+ assert_eq!(file.seek_data(0).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // File with non-zero length consisting entirely of a hole
+ file.set_len(0x10000).unwrap();
+ assert_eq!(file.seek_data(0).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // seek_data at or after the end of the file should return None
+ assert_eq!(file.seek_data(0x10000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_data(0x10001).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // Write some data to [0x10000, 0x20000)
+ let b = [0x55u8; 0x10000];
+ file.seek(SeekFrom::Start(0x10000)).unwrap();
+ file.write_all(&b).unwrap();
+ assert_eq!(file.seek_data(0).unwrap(), Some(0x10000));
+ assert_eq!(seek_cur(&mut file), 0x10000);
+
+ // seek_data within data should return the same offset
+ assert_eq!(file.seek_data(0x10000).unwrap(), Some(0x10000));
+ assert_eq!(seek_cur(&mut file), 0x10000);
+ assert_eq!(file.seek_data(0x10001).unwrap(), Some(0x10001));
+ assert_eq!(seek_cur(&mut file), 0x10001);
+ assert_eq!(file.seek_data(0x1FFFF).unwrap(), Some(0x1FFFF));
+ assert_eq!(seek_cur(&mut file), 0x1FFFF);
+
+ // Extend the file to add another hole after the data
+ file.set_len(0x30000).unwrap();
+ assert_eq!(file.seek_data(0).unwrap(), Some(0x10000));
+ assert_eq!(seek_cur(&mut file), 0x10000);
+ assert_eq!(file.seek_data(0x1FFFF).unwrap(), Some(0x1FFFF));
+ assert_eq!(seek_cur(&mut file), 0x1FFFF);
+ assert_eq!(file.seek_data(0x20000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0x1FFFF);
+ }
+
+ #[test]
+ fn seek_hole() {
+ let tempdir = TempDir::new("/tmp/seek_hole_test").unwrap();
+ let mut path = PathBuf::from(tempdir.as_path().unwrap());
+ path.push("test_file");
+ let mut file = File::create(&path).unwrap();
+
+ // Empty file
+ assert_eq!(file.seek_hole(0).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // File with non-zero length consisting entirely of a hole
+ file.set_len(0x10000).unwrap();
+ assert_eq!(file.seek_hole(0).unwrap(), Some(0));
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
+ assert_eq!(seek_cur(&mut file), 0xFFFF);
+
+ // seek_hole at or after the end of the file should return None
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x10000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_hole(0x10001).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // Write some data to [0x10000, 0x20000)
+ let b = [0x55u8; 0x10000];
+ file.seek(SeekFrom::Start(0x10000)).unwrap();
+ file.write_all(&b).unwrap();
+
+ // seek_hole within a hole should return the same offset
+ assert_eq!(file.seek_hole(0).unwrap(), Some(0));
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
+ assert_eq!(seek_cur(&mut file), 0xFFFF);
+
+ // seek_hole within data should return the next hole (EOF)
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x10001).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+
+ // seek_hole at EOF after data should return None
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x20000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // Extend the file to add another hole after the data
+ file.set_len(0x30000).unwrap();
+ assert_eq!(file.seek_hole(0).unwrap(), Some(0));
+ assert_eq!(seek_cur(&mut file), 0);
+ assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
+ assert_eq!(seek_cur(&mut file), 0xFFFF);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x20000));
+ assert_eq!(seek_cur(&mut file), 0x20000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x20001));
+ assert_eq!(seek_cur(&mut file), 0x20001);
+
+ // seek_hole at EOF after a hole should return None
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x30000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+
+ // Write some data to [0x20000, 0x30000)
+ file.seek(SeekFrom::Start(0x20000)).unwrap();
+ file.write_all(&b).unwrap();
+
+ // seek_hole within [0x20000, 0x30000) should now find the hole at EOF
+ assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x30000));
+ assert_eq!(seek_cur(&mut file), 0x30000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x30000));
+ assert_eq!(seek_cur(&mut file), 0x30000);
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(file.seek_hole(0x30000).unwrap(), None);
+ assert_eq!(seek_cur(&mut file), 0);
+ }
+}
diff --git a/sys_util/src/shm.rs b/sys_util/src/shm.rs
new file mode 100644
index 0000000..bb008d9
--- /dev/null
+++ b/sys_util/src/shm.rs
@@ -0,0 +1,397 @@
+// Copyright 2017 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::CStr;
+use std::fs::File;
+use std::io::{self, Read, Seek, SeekFrom, Write};
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+
+use libc::{
+ self, c_char, c_int, c_long, c_uint, close, fcntl, ftruncate64, off64_t, syscall, F_ADD_SEALS,
+ F_GET_SEALS, F_SEAL_GROW, F_SEAL_SEAL, F_SEAL_SHRINK, F_SEAL_WRITE, MFD_ALLOW_SEALING,
+};
+use syscall_defines::linux::LinuxSyscall::SYS_memfd_create;
+
+use crate::{errno, errno_result, Result};
+
+/// A shared memory file descriptor and its size.
+pub struct SharedMemory {
+ fd: File,
+ size: u64,
+}
+
+// from <sys/memfd.h>
+const MFD_CLOEXEC: c_uint = 0x0001;
+
+unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int {
+ syscall(SYS_memfd_create as c_long, name, flags) as c_int
+}
+
+/// A set of memfd seals.
+///
+/// An enumeration of each bit can be found at `fcntl(2)`.
+#[derive(Copy, Clone, Default)]
+pub struct MemfdSeals(i32);
+
+impl MemfdSeals {
+ /// Returns an empty set of memfd seals.
+ #[inline]
+ pub fn new() -> MemfdSeals {
+ MemfdSeals(0)
+ }
+
+ /// Gets the raw bitmask of seals enumerated in `fcntl(2)`.
+ #[inline]
+ pub fn bitmask(self) -> i32 {
+ self.0
+ }
+
+ /// True of the grow seal bit is present.
+ #[inline]
+ pub fn grow_seal(self) -> bool {
+ self.0 & F_SEAL_GROW != 0
+ }
+
+ /// Sets the grow seal bit.
+ #[inline]
+ pub fn set_grow_seal(&mut self) {
+ self.0 |= F_SEAL_GROW;
+ }
+
+ /// True of the shrink seal bit is present.
+ #[inline]
+ pub fn shrink_seal(self) -> bool {
+ self.0 & F_SEAL_SHRINK != 0
+ }
+
+ /// Sets the shrink seal bit.
+ #[inline]
+ pub fn set_shrink_seal(&mut self) {
+ self.0 |= F_SEAL_SHRINK;
+ }
+
+ /// True of the write seal bit is present.
+ #[inline]
+ pub fn write_seal(self) -> bool {
+ self.0 & F_SEAL_WRITE != 0
+ }
+
+ /// Sets the write seal bit.
+ #[inline]
+ pub fn set_write_seal(&mut self) {
+ self.0 |= F_SEAL_WRITE;
+ }
+
+ /// True of the seal seal bit is present.
+ #[inline]
+ pub fn seal_seal(self) -> bool {
+ self.0 & F_SEAL_SEAL != 0
+ }
+
+ /// Sets the seal seal bit.
+ #[inline]
+ pub fn set_seal_seal(&mut self) {
+ self.0 |= F_SEAL_SEAL;
+ }
+}
+
+impl SharedMemory {
+ /// Creates a new shared memory file descriptor with zero size.
+ ///
+ /// If a name is given, it will appear in `/proc/self/fd/<shm fd>` for the purposes of
+ /// debugging. The name does not need to be unique.
+ ///
+ /// The file descriptor is opened with the close on exec flag and allows memfd sealing.
+ pub fn new(name: Option<&CStr>) -> Result<SharedMemory> {
+ let shm_name = name
+ .map(|n| n.as_ptr())
+ .unwrap_or(b"/crosvm_shm\0".as_ptr() as *const c_char);
+ // The following are safe because we give a valid C string and check the
+ // results of the memfd_create call.
+ let fd = unsafe { memfd_create(shm_name, MFD_CLOEXEC | MFD_ALLOW_SEALING) };
+ if fd < 0 {
+ return errno_result();
+ }
+
+ let file = unsafe { File::from_raw_fd(fd) };
+
+ Ok(SharedMemory { fd: file, size: 0 })
+ }
+
+ /// Constructs a `SharedMemory` instance from a file descriptor that represents shared memory.
+ ///
+ /// The size of the resulting shared memory will be determined using `File::seek`. If the given
+ /// file's size can not be determined this way, this will return an error.
+ pub fn from_raw_fd<T: IntoRawFd>(fd: T) -> Result<SharedMemory> {
+ // Safe because the IntoRawFd trait indicates fd has unique ownership.
+ let mut file = unsafe { File::from_raw_fd(fd.into_raw_fd()) };
+ let file_size = file.seek(SeekFrom::End(0))?;
+ Ok(SharedMemory {
+ fd: file,
+ size: file_size as u64,
+ })
+ }
+
+ /// Gets the memfd seals that have already been added to this.
+ ///
+ /// This may fail if this instance was not constructed from a memfd.
+ pub fn get_seals(&self) -> Result<MemfdSeals> {
+ let ret = unsafe { fcntl(self.fd.as_raw_fd(), F_GET_SEALS) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(MemfdSeals(ret))
+ }
+
+ /// Adds the given set of memfd seals.
+ ///
+ /// This may fail if this instance was not constructed from a memfd with sealing allowed or if
+ /// the seal seal (`F_SEAL_SEAL`) bit was already added.
+ pub fn add_seals(&mut self, seals: MemfdSeals) -> Result<()> {
+ let ret = unsafe { fcntl(self.fd.as_raw_fd(), F_ADD_SEALS, seals) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+
+ /// Gets the size in bytes of the shared memory.
+ ///
+ /// The size returned here does not reflect changes by other interfaces or users of the shared
+ /// memory file descriptor..
+ pub fn size(&self) -> u64 {
+ self.size
+ }
+
+ /// Sets the size in bytes of the shared memory.
+ ///
+ /// Note that if some process has already mapped this shared memory and the new size is smaller,
+ /// that process may get signaled with SIGBUS if they access any page past the new size.
+ pub fn set_size(&mut self, size: u64) -> Result<()> {
+ let ret = unsafe { ftruncate64(self.fd.as_raw_fd(), size as off64_t) };
+ if ret < 0 {
+ return errno_result();
+ }
+ self.size = size;
+ Ok(())
+ }
+}
+
+impl Read for SharedMemory {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.fd.read(buf)
+ }
+}
+
+impl Read for &SharedMemory {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (&self.fd).read(buf)
+ }
+}
+
+impl Write for SharedMemory {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.fd.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.fd.flush()
+ }
+}
+
+impl Write for &SharedMemory {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&self.fd).write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ (&self.fd).flush()
+ }
+}
+
+impl Seek for SharedMemory {
+ fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+ self.fd.seek(pos)
+ }
+}
+
+impl Seek for &SharedMemory {
+ fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+ (&self.fd).seek(pos)
+ }
+}
+
+impl AsRawFd for SharedMemory {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl AsRawFd for &SharedMemory {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl Into<File> for SharedMemory {
+ fn into(self) -> File {
+ self.fd
+ }
+}
+
+/// Checks if the kernel we are running on has memfd_create. It was introduced in 3.17.
+/// Only to be used from tests to prevent running on ancient kernels that won't
+/// support the functionality anyways.
+pub fn kernel_has_memfd() -> bool {
+ unsafe {
+ let fd = memfd_create(b"/test_memfd_create\0".as_ptr() as *const c_char, 0);
+ if fd < 0 {
+ if errno::Error::last().errno() == libc::ENOSYS {
+ return false;
+ }
+ return true;
+ }
+ close(fd);
+ }
+ true
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use std::ffi::CString;
+ use std::fs::read_link;
+
+ use data_model::VolatileMemory;
+
+ use crate::MemoryMapping;
+
+ #[test]
+ fn new() {
+ if !kernel_has_memfd() {
+ return;
+ }
+ let shm = SharedMemory::new(None).expect("failed to create shared memory");
+ assert_eq!(shm.size(), 0);
+ }
+
+ #[test]
+ fn new_sized() {
+ if !kernel_has_memfd() {
+ return;
+ }
+ let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
+ shm.set_size(1024)
+ .expect("failed to set shared memory size");
+ assert_eq!(shm.size(), 1024);
+ }
+
+ #[test]
+ fn new_huge() {
+ if !kernel_has_memfd() {
+ return;
+ }
+ let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
+ shm.set_size(0x7fff_ffff_ffff_ffff)
+ .expect("failed to set shared memory size");
+ assert_eq!(shm.size(), 0x7fff_ffff_ffff_ffff);
+ }
+
+ #[test]
+ fn new_too_huge() {
+ if !kernel_has_memfd() {
+ return;
+ }
+ let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
+ shm.set_size(0x8000_0000_0000_0000).unwrap_err();
+ assert_eq!(shm.size(), 0);
+ }
+
+ #[test]
+ fn new_named() {
+ if !kernel_has_memfd() {
+ return;
+ }
+ let name = "very unique name";
+ let cname = CString::new(name).unwrap();
+ let shm = SharedMemory::new(Some(&cname)).expect("failed to create shared memory");
+ let fd_path = format!("/proc/self/fd/{}", shm.as_raw_fd());
+ let link_name =
+ read_link(fd_path).expect("failed to read link of shared memory /proc/self/fd entry");
+ assert!(link_name.to_str().unwrap().contains(name));
+ }
+
+ #[test]
+ fn new_sealed() {
+ if !kernel_has_memfd() {
+ return;
+ }
+ let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
+ let mut seals = shm.get_seals().expect("failed to get seals");
+ assert_eq!(seals.bitmask(), 0);
+ seals.set_seal_seal();
+ shm.add_seals(seals).expect("failed to add seals");
+ seals = shm.get_seals().expect("failed to get seals");
+ assert!(seals.seal_seal());
+ // Adding more seals should be rejected by the kernel.
+ shm.add_seals(seals).unwrap_err();
+ }
+
+ #[test]
+ fn mmap_page() {
+ if !kernel_has_memfd() {
+ return;
+ }
+ let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
+ shm.set_size(4096)
+ .expect("failed to set shared memory size");
+
+ let mmap1 =
+ MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
+ let mmap2 =
+ MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
+
+ assert_ne!(
+ mmap1.get_slice(0, 1).unwrap().as_ptr(),
+ mmap2.get_slice(0, 1).unwrap().as_ptr()
+ );
+
+ mmap1
+ .get_slice(0, 4096)
+ .expect("failed to get mmap slice")
+ .write_bytes(0x45);
+
+ for i in 0..4096 {
+ assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0x45u8);
+ }
+ }
+
+ #[test]
+ fn mmap_page_offset() {
+ if !kernel_has_memfd() {
+ return;
+ }
+ let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
+ shm.set_size(8092)
+ .expect("failed to set shared memory size");
+
+ let mmap1 = MemoryMapping::from_fd_offset(&shm, shm.size() as usize, 4096)
+ .expect("failed to map shared memory");
+ let mmap2 =
+ MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
+
+ mmap1
+ .get_slice(0, 4096)
+ .expect("failed to get mmap slice")
+ .write_bytes(0x45);
+
+ for i in 0..4096 {
+ assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0);
+ }
+ for i in 4096..8092 {
+ assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0x45u8);
+ }
+ }
+}
diff --git a/sys_util/src/signal.rs b/sys_util/src/signal.rs
new file mode 100644
index 0000000..882da90
--- /dev/null
+++ b/sys_util/src/signal.rs
@@ -0,0 +1,285 @@
+// Copyright 2017 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 libc::{
+ c_int, pthread_kill, pthread_sigmask, pthread_t, sigaction, sigaddset, sigemptyset, siginfo_t,
+ sigismember, sigpending, sigset_t, sigtimedwait, timespec, EAGAIN, EINTR, EINVAL, SA_RESTART,
+ SIG_BLOCK, SIG_UNBLOCK,
+};
+
+use std::fmt::{self, Display};
+use std::io;
+use std::mem;
+use std::os::unix::thread::JoinHandleExt;
+use std::ptr::{null, null_mut};
+use std::result;
+use std::thread::JoinHandle;
+
+use crate::{errno, errno_result};
+
+#[derive(Debug)]
+pub enum Error {
+ /// Couldn't create a sigset.
+ CreateSigset(errno::Error),
+ /// The wrapped signal has already been blocked.
+ SignalAlreadyBlocked(c_int),
+ /// Failed to check if the requested signal is in the blocked set already.
+ CompareBlockedSignals(errno::Error),
+ /// The signal could not be blocked.
+ BlockSignal(errno::Error),
+ /// The signal mask could not be retrieved.
+ RetrieveSignalMask(i32),
+ /// The signal could not be unblocked.
+ UnblockSignal(errno::Error),
+ /// Failed to wait for given signal.
+ ClearWaitPending(errno::Error),
+ /// Failed to get pending signals.
+ ClearGetPending(errno::Error),
+ /// Failed to check if given signal is in the set of pending signals.
+ ClearCheckPending(errno::Error),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ CreateSigset(e) => write!(f, "couldn't create a sigset: {}", e),
+ SignalAlreadyBlocked(num) => write!(f, "signal {} already blocked", num),
+ CompareBlockedSignals(e) => write!(
+ f,
+ "failed to check whether requested signal is in the blocked set: {}",
+ e,
+ ),
+ BlockSignal(e) => write!(f, "signal could not be blocked: {}", e),
+ RetrieveSignalMask(errno) => write!(
+ f,
+ "failed to retrieve signal mask: {}",
+ io::Error::from_raw_os_error(*errno),
+ ),
+ UnblockSignal(e) => write!(f, "signal could not be unblocked: {}", e),
+ ClearWaitPending(e) => write!(f, "failed to wait for given signal: {}", e),
+ ClearGetPending(e) => write!(f, "failed to get pending signals: {}", e),
+ ClearCheckPending(e) => write!(
+ f,
+ "failed to check whether given signal is in the pending set: {}",
+ e,
+ ),
+ }
+ }
+}
+
+pub type SignalResult<T> = result::Result<T, Error>;
+
+#[link(name = "c")]
+extern "C" {
+ fn __libc_current_sigrtmin() -> c_int;
+ fn __libc_current_sigrtmax() -> c_int;
+}
+
+/// Returns the minimum (inclusive) real-time signal number.
+#[allow(non_snake_case)]
+pub fn SIGRTMIN() -> c_int {
+ unsafe { __libc_current_sigrtmin() }
+}
+
+/// Returns the maximum (inclusive) real-time signal number.
+#[allow(non_snake_case)]
+pub fn SIGRTMAX() -> c_int {
+ unsafe { __libc_current_sigrtmax() }
+}
+
+fn valid_signal_num(num: c_int) -> bool {
+ num >= SIGRTMIN() && num <= SIGRTMAX()
+}
+
+/// Registers `handler` as the signal handler of signum `num`.
+///
+/// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range.
+///
+/// This is considered unsafe because the given handler will be called asynchronously, interrupting
+/// whatever the thread was doing and therefore must only do async-signal-safe operations.
+pub unsafe fn register_signal_handler(
+ num: c_int,
+ handler: extern "C" fn() -> (),
+) -> errno::Result<()> {
+ if !valid_signal_num(num) {
+ return Err(errno::Error::new(EINVAL));
+ }
+
+ let mut sigact: sigaction = mem::zeroed();
+ sigact.sa_flags = SA_RESTART;
+ sigact.sa_sigaction = handler as *const () as usize;
+
+ let ret = sigaction(num, &sigact, null_mut());
+ if ret < 0 {
+ return errno_result();
+ }
+
+ Ok(())
+}
+
+/// Creates `sigset` from an array of signal numbers.
+///
+/// This is a helper function used when we want to manipulate signals.
+pub fn create_sigset(signals: &[c_int]) -> errno::Result<sigset_t> {
+ // sigset will actually be initialized by sigemptyset below.
+ let mut sigset: sigset_t = unsafe { mem::zeroed() };
+
+ // Safe - return value is checked.
+ let ret = unsafe { sigemptyset(&mut sigset) };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ for signal in signals {
+ // Safe - return value is checked.
+ let ret = unsafe { sigaddset(&mut sigset, *signal) };
+ if ret < 0 {
+ return errno_result();
+ }
+ }
+
+ Ok(sigset)
+}
+
+/// Retrieves the signal mask of the current thread as a vector of c_ints.
+pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
+ let mut mask = Vec::new();
+
+ // Safe - return values are checked.
+ unsafe {
+ let mut old_sigset: sigset_t = mem::zeroed();
+ let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t);
+ if ret < 0 {
+ return Err(Error::RetrieveSignalMask(ret));
+ }
+
+ for num in 0..=SIGRTMAX() {
+ if sigismember(&old_sigset, num) > 0 {
+ mask.push(num);
+ }
+ }
+ }
+
+ Ok(mask)
+}
+
+/// Masks given signal.
+///
+/// If signal is already blocked the call will fail with Error::SignalAlreadyBlocked
+/// result.
+pub fn block_signal(num: c_int) -> SignalResult<()> {
+ let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
+
+ // Safe - return values are checked.
+ unsafe {
+ let mut old_sigset: sigset_t = mem::zeroed();
+ let ret = pthread_sigmask(SIG_BLOCK, &sigset, &mut old_sigset as *mut sigset_t);
+ if ret < 0 {
+ return Err(Error::BlockSignal(errno::Error::last()));
+ }
+ let ret = sigismember(&old_sigset, num);
+ if ret < 0 {
+ return Err(Error::CompareBlockedSignals(errno::Error::last()));
+ } else if ret > 0 {
+ return Err(Error::SignalAlreadyBlocked(num));
+ }
+ }
+ Ok(())
+}
+
+/// Unmasks given signal.
+pub fn unblock_signal(num: c_int) -> SignalResult<()> {
+ let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
+
+ // Safe - return value is checked.
+ let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
+ if ret < 0 {
+ return Err(Error::UnblockSignal(errno::Error::last()));
+ }
+ Ok(())
+}
+
+/// Clears pending signal.
+pub fn clear_signal(num: c_int) -> SignalResult<()> {
+ let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
+
+ while {
+ // This is safe as we are rigorously checking return values
+ // of libc calls.
+ unsafe {
+ let mut siginfo: siginfo_t = mem::zeroed();
+ let ts = timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ };
+ // Attempt to consume one instance of pending signal. If signal
+ // is not pending, the call will fail with EAGAIN or EINTR.
+ let ret = sigtimedwait(&sigset, &mut siginfo, &ts);
+ if ret < 0 {
+ let e = errno::Error::last();
+ match e.errno() {
+ EAGAIN | EINTR => {}
+ _ => {
+ return Err(Error::ClearWaitPending(errno::Error::last()));
+ }
+ }
+ }
+
+ // This sigset will be actually filled with `sigpending` call.
+ let mut chkset: sigset_t = mem::zeroed();
+ // See if more instances of the signal are pending.
+ let ret = sigpending(&mut chkset);
+ if ret < 0 {
+ return Err(Error::ClearGetPending(errno::Error::last()));
+ }
+
+ let ret = sigismember(&chkset, num);
+ if ret < 0 {
+ return Err(Error::ClearCheckPending(errno::Error::last()));
+ }
+
+ // This is do-while loop condition.
+ ret != 0
+ }
+ } {}
+
+ Ok(())
+}
+
+/// Trait for threads that can be signalled via `pthread_kill`.
+///
+/// Note that this is only useful for signals between SIGRTMIN and SIGRTMAX because these are
+/// guaranteed to not be used by the C runtime.
+///
+/// This is marked unsafe because the implementation of this trait must guarantee that the returned
+/// pthread_t is valid and has a lifetime at least that of the trait object.
+pub unsafe trait Killable {
+ fn pthread_handle(&self) -> pthread_t;
+
+ /// Sends the signal `num` to this killable thread.
+ ///
+ /// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range.
+ fn kill(&self, num: c_int) -> errno::Result<()> {
+ if !valid_signal_num(num) {
+ return Err(errno::Error::new(EINVAL));
+ }
+
+ // Safe because we ensure we are using a valid pthread handle, a valid signal number, and
+ // check the return result.
+ let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+ }
+}
+
+// Safe because we fulfill our contract of returning a genuine pthread handle.
+unsafe impl<T> Killable for JoinHandle<T> {
+ fn pthread_handle(&self) -> pthread_t {
+ self.as_pthread_t()
+ }
+}
diff --git a/sys_util/src/signalfd.rs b/sys_util/src/signalfd.rs
new file mode 100644
index 0000000..0b50ea4
--- /dev/null
+++ b/sys_util/src/signalfd.rs
@@ -0,0 +1,195 @@
+// Copyright 2017 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::fmt::{self, Display};
+use std::fs::File;
+use std::mem;
+use std::os::raw::c_int;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::result;
+
+use libc::{c_void, read, signalfd, signalfd_siginfo};
+use libc::{EAGAIN, SFD_CLOEXEC, SFD_NONBLOCK};
+
+use crate::errno;
+use crate::signal;
+
+#[derive(Debug)]
+pub enum Error {
+ /// Failed to construct sigset when creating signalfd.
+ CreateSigset(errno::Error),
+ /// Failed to create a new signalfd.
+ CreateSignalFd(errno::Error),
+ /// Failed to block the signal when creating signalfd.
+ CreateBlockSignal(signal::Error),
+ /// Unable to read from signalfd.
+ SignalFdRead(errno::Error),
+ /// Signalfd could be read, but didn't return a full siginfo struct.
+ /// This wraps the number of bytes that were actually read.
+ SignalFdPartialRead(usize),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ CreateSigset(e) => write!(
+ f,
+ "failed to construct sigset when creating signalfd: {}",
+ e,
+ ),
+ CreateSignalFd(e) => write!(f, "failed to create a new signalfd: {}", e),
+ CreateBlockSignal(e) => write!(
+ f,
+ "failed to block the signal when creating signalfd: {}",
+ e,
+ ),
+ SignalFdRead(e) => write!(f, "unable to read from signalfd: {}", e),
+ SignalFdPartialRead(read) => write!(
+ f,
+ "signalfd failed to return a full siginfo struct, read only {} bytes",
+ read,
+ ),
+ }
+ }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+/// A safe wrapper around a Linux signalfd (man 2 signalfd).
+///
+/// A signalfd can be used for non-synchronous signals (such as SIGCHLD) so that
+/// signals can be processed without the use of a signal handler.
+pub struct SignalFd {
+ signalfd: File,
+ signal: c_int,
+}
+
+impl SignalFd {
+ /// Creates a new SignalFd for the given signal, blocking the normal handler
+ /// for the signal as well. Since we mask out the normal handler, this is
+ /// a risky operation - signal masking will persist across fork and even
+ /// **exec** so the user of SignalFd should think long and hard about
+ /// when to mask signals.
+ pub fn new(signal: c_int) -> Result<SignalFd> {
+ let sigset = signal::create_sigset(&[signal]).map_err(Error::CreateSigset)?;
+
+ // This is safe as we check the return value and know that fd is valid.
+ let fd = unsafe { signalfd(-1, &sigset, SFD_CLOEXEC | SFD_NONBLOCK) };
+ if fd < 0 {
+ return Err(Error::CreateSignalFd(errno::Error::last()));
+ }
+
+ // Mask out the normal handler for the signal.
+ signal::block_signal(signal).map_err(Error::CreateBlockSignal)?;
+
+ // This is safe because we checked fd for success and know the
+ // kernel gave us an fd that we own.
+ unsafe {
+ Ok(SignalFd {
+ signalfd: File::from_raw_fd(fd),
+ signal,
+ })
+ }
+ }
+
+ /// Read a siginfo struct from the signalfd, if available.
+ pub fn read(&self) -> Result<Option<signalfd_siginfo>> {
+ // signalfd_siginfo doesn't have a default, so just zero it.
+ let mut siginfo: signalfd_siginfo = unsafe { mem::zeroed() };
+ let siginfo_size = mem::size_of::<signalfd_siginfo>();
+
+ // This read is safe since we've got the space allocated for a
+ // single signalfd_siginfo, and that's exactly how much we're
+ // reading. Handling of EINTR is not required since SFD_NONBLOCK
+ // was specified. signalfds will always read in increments of
+ // sizeof(signalfd_siginfo); see man 2 signalfd.
+ let ret = unsafe {
+ read(
+ self.signalfd.as_raw_fd(),
+ &mut siginfo as *mut signalfd_siginfo as *mut c_void,
+ siginfo_size,
+ )
+ };
+
+ if ret < 0 {
+ let err = errno::Error::last();
+ if err.errno() == EAGAIN {
+ Ok(None)
+ } else {
+ Err(Error::SignalFdRead(err))
+ }
+ } else if ret == (siginfo_size as isize) {
+ Ok(Some(siginfo))
+ } else {
+ Err(Error::SignalFdPartialRead(ret as usize))
+ }
+ }
+}
+
+impl AsRawFd for SignalFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.signalfd.as_raw_fd()
+ }
+}
+
+impl Drop for SignalFd {
+ fn drop(&mut self) {
+ // This is thread-safe and safe in the sense that we're doing what
+ // was promised - unmasking the signal when we go out of scope.
+ let res = signal::unblock_signal(self.signal);
+ if let Err(e) = res {
+ error!("signalfd failed to unblock signal {}: {}", self.signal, e);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::signal::SIGRTMIN;
+ use libc::{pthread_sigmask, raise, sigismember, sigset_t};
+ use std::mem;
+ use std::ptr::null;
+
+ #[test]
+ fn new() {
+ SignalFd::new(SIGRTMIN()).unwrap();
+ }
+
+ #[test]
+ fn read() {
+ let sigid = SIGRTMIN() + 1;
+ let sigrt_fd = SignalFd::new(sigid).unwrap();
+
+ let ret = unsafe { raise(sigid) };
+ assert_eq!(ret, 0);
+
+ let siginfo = sigrt_fd.read().unwrap().unwrap();
+ assert_eq!(siginfo.ssi_signo, sigid as u32);
+ }
+
+ #[test]
+ fn drop() {
+ let sigid = SIGRTMIN() + 2;
+
+ let sigrt_fd = SignalFd::new(sigid).unwrap();
+ unsafe {
+ let mut sigset: sigset_t = mem::zeroed();
+ pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
+ assert_eq!(sigismember(&sigset, sigid), 1);
+ }
+
+ mem::drop(sigrt_fd);
+
+ // The signal should no longer be masked.
+ unsafe {
+ let mut sigset: sigset_t = mem::zeroed();
+ pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
+ assert_eq!(sigismember(&sigset, sigid), 0);
+ }
+ }
+}
diff --git a/sys_util/src/sock_ctrl_msg.rs b/sys_util/src/sock_ctrl_msg.rs
new file mode 100644
index 0000000..96a072d
--- /dev/null
+++ b/sys_util/src/sock_ctrl_msg.rs
@@ -0,0 +1,473 @@
+// Copyright 2017 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.
+
+//! Used to send and receive messages with file descriptors on sockets that accept control messages
+//! (e.g. Unix domain sockets).
+
+use std::fs::File;
+use std::mem::size_of;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::os::unix::net::{UnixDatagram, UnixStream};
+use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned};
+
+use libc::{
+ c_long, c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, MSG_NOSIGNAL, SCM_RIGHTS, SOL_SOCKET,
+};
+
+use data_model::VolatileSlice;
+
+use crate::net::UnixSeqpacket;
+use crate::{Error, Result};
+
+// Each of the following macros performs the same function as their C counterparts. They are each
+// macros because they are used to size statically allocated arrays.
+
+macro_rules! CMSG_ALIGN {
+ ($len:expr) => {
+ (($len) + size_of::<c_long>() - 1) & !(size_of::<c_long>() - 1)
+ };
+}
+
+macro_rules! CMSG_SPACE {
+ ($len:expr) => {
+ size_of::<cmsghdr>() + CMSG_ALIGN!($len)
+ };
+}
+
+macro_rules! CMSG_LEN {
+ ($len:expr) => {
+ size_of::<cmsghdr>() + ($len)
+ };
+}
+
+// This function (macro in the C version) is not used in any compile time constant slots, so is just
+// an ordinary function. The returned pointer is hard coded to be RawFd because that's all that this
+// module supports.
+#[allow(non_snake_case)]
+#[inline(always)]
+fn CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd {
+ // Essentially returns a pointer to just past the header.
+ cmsg_buffer.wrapping_offset(1) as *mut RawFd
+}
+
+// This function is like CMSG_NEXT, but safer because it reads only from references, although it
+// does some pointer arithmetic on cmsg_ptr.
+#[allow(clippy::cast_ptr_alignment)]
+fn get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr {
+ let next_cmsg = (cmsg_ptr as *mut u8).wrapping_add(CMSG_ALIGN!(cmsg.cmsg_len)) as *mut cmsghdr;
+ if next_cmsg
+ .wrapping_offset(1)
+ .wrapping_sub(msghdr.msg_control as usize) as usize
+ > msghdr.msg_controllen
+ {
+ null_mut()
+ } else {
+ next_cmsg
+ }
+}
+
+const CMSG_BUFFER_INLINE_CAPACITY: usize = CMSG_SPACE!(size_of::<RawFd>() * 32);
+
+enum CmsgBuffer {
+ Inline([u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8]),
+ Heap(Box<[cmsghdr]>),
+}
+
+impl CmsgBuffer {
+ fn with_capacity(capacity: usize) -> CmsgBuffer {
+ let cap_in_cmsghdr_units =
+ (capacity.checked_add(size_of::<cmsghdr>()).unwrap() - 1) / size_of::<cmsghdr>();
+ if capacity <= CMSG_BUFFER_INLINE_CAPACITY {
+ CmsgBuffer::Inline([0u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8])
+ } else {
+ CmsgBuffer::Heap(
+ vec![
+ cmsghdr {
+ cmsg_len: 0,
+ cmsg_level: 0,
+ cmsg_type: 0,
+ };
+ cap_in_cmsghdr_units
+ ]
+ .into_boxed_slice(),
+ )
+ }
+ }
+
+ fn as_mut_ptr(&mut self) -> *mut cmsghdr {
+ match self {
+ CmsgBuffer::Inline(a) => a.as_mut_ptr() as *mut cmsghdr,
+ CmsgBuffer::Heap(a) => a.as_mut_ptr(),
+ }
+ }
+}
+
+fn raw_sendmsg<D: IntoIovec>(fd: RawFd, out_data: D, out_fds: &[RawFd]) -> Result<usize> {
+ let cmsg_capacity = CMSG_SPACE!(size_of::<RawFd>() * out_fds.len());
+ let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
+
+ let mut iovec = iovec {
+ iov_base: out_data.as_ptr() as *mut c_void,
+ iov_len: out_data.size(),
+ };
+
+ let mut msg = msghdr {
+ msg_name: null_mut(),
+ msg_namelen: 0,
+ msg_iov: &mut iovec as *mut iovec,
+ msg_iovlen: 1,
+ msg_control: null_mut(),
+ msg_controllen: 0,
+ msg_flags: 0,
+ };
+
+ if !out_fds.is_empty() {
+ let cmsg = cmsghdr {
+ cmsg_len: CMSG_LEN!(size_of::<RawFd>() * out_fds.len()),
+ cmsg_level: SOL_SOCKET,
+ cmsg_type: SCM_RIGHTS,
+ };
+ unsafe {
+ // Safe because cmsg_buffer was allocated to be large enough to contain cmsghdr.
+ write_unaligned(cmsg_buffer.as_mut_ptr() as *mut cmsghdr, cmsg);
+ // Safe because the cmsg_buffer was allocated to be large enough to hold out_fds.len()
+ // file descriptors.
+ copy_nonoverlapping(
+ out_fds.as_ptr(),
+ CMSG_DATA(cmsg_buffer.as_mut_ptr()),
+ out_fds.len(),
+ );
+ }
+
+ msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
+ msg.msg_controllen = cmsg_capacity;
+ }
+
+ // Safe because the msghdr was properly constructed from valid (or null) pointers of the
+ // indicated length and we check the return value.
+ let write_count = unsafe { sendmsg(fd, &msg, MSG_NOSIGNAL) };
+
+ if write_count == -1 {
+ Err(Error::last())
+ } else {
+ Ok(write_count as usize)
+ }
+}
+
+fn raw_recvmsg(fd: RawFd, in_data: &mut [u8], in_fds: &mut [RawFd]) -> Result<(usize, usize)> {
+ let cmsg_capacity = CMSG_SPACE!(size_of::<RawFd>() * in_fds.len());
+ let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
+
+ let mut iovec = iovec {
+ iov_base: in_data.as_mut_ptr() as *mut c_void,
+ iov_len: in_data.len(),
+ };
+
+ let mut msg = msghdr {
+ msg_name: null_mut(),
+ msg_namelen: 0,
+ msg_iov: &mut iovec as *mut iovec,
+ msg_iovlen: 1,
+ msg_control: null_mut(),
+ msg_controllen: 0,
+ msg_flags: 0,
+ };
+
+ if !in_fds.is_empty() {
+ msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
+ msg.msg_controllen = cmsg_capacity;
+ }
+
+ // Safe because the msghdr was properly constructed from valid (or null) pointers of the
+ // indicated length and we check the return value.
+ let total_read = unsafe { recvmsg(fd, &mut msg, 0) };
+
+ if total_read == -1 {
+ return Err(Error::last());
+ }
+
+ if total_read == 0 && msg.msg_controllen < size_of::<cmsghdr>() {
+ return Ok((0, 0));
+ }
+
+ let mut cmsg_ptr = msg.msg_control as *mut cmsghdr;
+ let mut in_fds_count = 0;
+ while !cmsg_ptr.is_null() {
+ // Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such that
+ // that only happens when there is at least sizeof(cmsghdr) space after the pointer to read.
+ let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() };
+
+ if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS {
+ let fd_count = (cmsg.cmsg_len - CMSG_LEN!(0)) / size_of::<RawFd>();
+ unsafe {
+ copy_nonoverlapping(
+ CMSG_DATA(cmsg_ptr),
+ in_fds[in_fds_count..(in_fds_count + fd_count)].as_mut_ptr(),
+ fd_count,
+ );
+ }
+ in_fds_count += fd_count;
+ }
+
+ cmsg_ptr = get_next_cmsg(&msg, &cmsg, cmsg_ptr);
+ }
+
+ Ok((total_read as usize, in_fds_count))
+}
+
+/// Trait for file descriptors can send and receive socket control messages via `sendmsg` and
+/// `recvmsg`.
+pub trait ScmSocket {
+ /// Gets the file descriptor of this socket.
+ fn socket_fd(&self) -> RawFd;
+
+ /// Sends the given data and file descriptor over the socket.
+ ///
+ /// On success, returns the number of bytes sent.
+ ///
+ /// # Arguments
+ ///
+ /// * `buf` - A buffer of data to send on the `socket`.
+ /// * `fd` - A file descriptors to be sent.
+ fn send_with_fd<D: IntoIovec>(&self, buf: D, fd: RawFd) -> Result<usize> {
+ self.send_with_fds(buf, &[fd])
+ }
+
+ /// Sends the given data and file descriptors over the socket.
+ ///
+ /// On success, returns the number of bytes sent.
+ ///
+ /// # Arguments
+ ///
+ /// * `buf` - A buffer of data to send on the `socket`.
+ /// * `fds` - A list of file descriptors to be sent.
+ fn send_with_fds<D: IntoIovec>(&self, buf: D, fd: &[RawFd]) -> Result<usize> {
+ raw_sendmsg(self.socket_fd(), buf, fd)
+ }
+
+ /// Receives data and potentially a file descriptor from the socket.
+ ///
+ /// On success, returns the number of bytes and an optional file descriptor.
+ ///
+ /// # Arguments
+ ///
+ /// * `buf` - A buffer to receive data from the socket.vm
+ fn recv_with_fd(&self, buf: &mut [u8]) -> Result<(usize, Option<File>)> {
+ let mut fd = [0];
+ let (read_count, fd_count) = self.recv_with_fds(buf, &mut fd)?;
+ let file = if fd_count == 0 {
+ None
+ } else {
+ // Safe because the first fd from recv_with_fds is owned by us and valid because this
+ // branch was taken.
+ Some(unsafe { File::from_raw_fd(fd[0]) })
+ };
+ Ok((read_count, file))
+ }
+
+ /// Receives data and file descriptors from the socket.
+ ///
+ /// On success, returns the number of bytes and file descriptors received as a tuple
+ /// `(bytes count, files count)`.
+ ///
+ /// # Arguments
+ ///
+ /// * `buf` - A buffer to receive data from the socket.
+ /// * `fds` - A slice of `RawFd`s to put the received file descriptors into. On success, the
+ /// number of valid file descriptors is indicated by the second element of the
+ /// returned tuple. The caller owns these file descriptors, but they will not be
+ /// closed on drop like a `File`-like type would be. It is recommended that each valid
+ /// file descriptor gets wrapped in a drop type that closes it after this returns.
+ fn recv_with_fds(&self, buf: &mut [u8], fds: &mut [RawFd]) -> Result<(usize, usize)> {
+ raw_recvmsg(self.socket_fd(), buf, fds)
+ }
+}
+
+impl ScmSocket for UnixDatagram {
+ fn socket_fd(&self) -> RawFd {
+ self.as_raw_fd()
+ }
+}
+
+impl ScmSocket for UnixStream {
+ fn socket_fd(&self) -> RawFd {
+ self.as_raw_fd()
+ }
+}
+impl ScmSocket for UnixSeqpacket {
+ fn socket_fd(&self) -> RawFd {
+ self.as_raw_fd()
+ }
+}
+
+/// Trait for types that can be converted into an `iovec` that can be referenced by a syscall for
+/// the lifetime of this object.
+///
+/// This trait is unsafe because interfaces that use this trait depend on the base pointer and size
+/// being accurate.
+pub unsafe trait IntoIovec {
+ /// Gets the base pointer of this `iovec`.
+ fn as_ptr(&self) -> *const c_void;
+
+ /// Gets the size in bytes of this `iovec`.
+ fn size(&self) -> usize;
+}
+
+// Safe because this slice can not have another mutable reference and it's pointer and size are
+// guaranteed to be valid.
+unsafe impl<'a> IntoIovec for &'a [u8] {
+ // Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/3480
+ #[allow(clippy::useless_asref)]
+ fn as_ptr(&self) -> *const c_void {
+ self.as_ref().as_ptr() as *const c_void
+ }
+
+ fn size(&self) -> usize {
+ self.len()
+ }
+}
+
+// Safe because volatile slices are only ever accessed with other volatile interfaces and the
+// pointer and size are guaranteed to be accurate.
+unsafe impl<'a> IntoIovec for VolatileSlice<'a> {
+ fn as_ptr(&self) -> *const c_void {
+ self.as_ptr() as *const c_void
+ }
+
+ fn size(&self) -> usize {
+ self.size() as usize
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use std::io::Write;
+ use std::mem::size_of;
+ use std::os::raw::c_long;
+ use std::os::unix::net::UnixDatagram;
+ use std::slice::from_raw_parts;
+
+ use libc::cmsghdr;
+
+ use crate::EventFd;
+
+ #[test]
+ fn buffer_len() {
+ assert_eq!(CMSG_SPACE!(0 * size_of::<RawFd>()), size_of::<cmsghdr>());
+ assert_eq!(
+ CMSG_SPACE!(1 * size_of::<RawFd>()),
+ size_of::<cmsghdr>() + size_of::<c_long>()
+ );
+ if size_of::<RawFd>() == 4 {
+ assert_eq!(
+ CMSG_SPACE!(2 * size_of::<RawFd>()),
+ size_of::<cmsghdr>() + size_of::<c_long>()
+ );
+ assert_eq!(
+ CMSG_SPACE!(3 * size_of::<RawFd>()),
+ size_of::<cmsghdr>() + size_of::<c_long>() * 2
+ );
+ assert_eq!(
+ CMSG_SPACE!(4 * size_of::<RawFd>()),
+ size_of::<cmsghdr>() + size_of::<c_long>() * 2
+ );
+ } else if size_of::<RawFd>() == 8 {
+ assert_eq!(
+ CMSG_SPACE!(2 * size_of::<RawFd>()),
+ size_of::<cmsghdr>() + size_of::<c_long>() * 2
+ );
+ assert_eq!(
+ CMSG_SPACE!(3 * size_of::<RawFd>()),
+ size_of::<cmsghdr>() + size_of::<c_long>() * 3
+ );
+ assert_eq!(
+ CMSG_SPACE!(4 * size_of::<RawFd>()),
+ size_of::<cmsghdr>() + size_of::<c_long>() * 4
+ );
+ }
+ }
+
+ #[test]
+ fn send_recv_no_fd() {
+ let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
+
+ let write_count = s1
+ .send_with_fds([1u8, 1, 2, 21, 34, 55].as_ref(), &[])
+ .expect("failed to send data");
+
+ assert_eq!(write_count, 6);
+
+ let mut buf = [0; 6];
+ let mut files = [0; 1];
+ let (read_count, file_count) = s2
+ .recv_with_fds(&mut buf[..], &mut files)
+ .expect("failed to recv data");
+
+ assert_eq!(read_count, 6);
+ assert_eq!(file_count, 0);
+ assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
+ }
+
+ #[test]
+ fn send_recv_only_fd() {
+ let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
+
+ let evt = EventFd::new().expect("failed to create eventfd");
+ let write_count = s1
+ .send_with_fd([].as_ref(), evt.as_raw_fd())
+ .expect("failed to send fd");
+
+ assert_eq!(write_count, 0);
+
+ let (read_count, file_opt) = s2.recv_with_fd(&mut []).expect("failed to recv fd");
+
+ let mut file = file_opt.unwrap();
+
+ assert_eq!(read_count, 0);
+ assert!(file.as_raw_fd() >= 0);
+ assert_ne!(file.as_raw_fd(), s1.as_raw_fd());
+ assert_ne!(file.as_raw_fd(), s2.as_raw_fd());
+ assert_ne!(file.as_raw_fd(), evt.as_raw_fd());
+
+ file.write(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
+ .expect("failed to write to sent fd");
+
+ assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
+ }
+
+ #[test]
+ fn send_recv_with_fd() {
+ let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
+
+ let evt = EventFd::new().expect("failed to create eventfd");
+ let write_count = s1
+ .send_with_fds([237].as_ref(), &[evt.as_raw_fd()])
+ .expect("failed to send fd");
+
+ assert_eq!(write_count, 1);
+
+ let mut files = [0; 2];
+ let mut buf = [0u8];
+ let (read_count, file_count) = s2
+ .recv_with_fds(&mut buf, &mut files)
+ .expect("failed to recv fd");
+
+ assert_eq!(read_count, 1);
+ assert_eq!(buf[0], 237);
+ assert_eq!(file_count, 1);
+ assert!(files[0] >= 0);
+ assert_ne!(files[0], s1.as_raw_fd());
+ assert_ne!(files[0], s2.as_raw_fd());
+ assert_ne!(files[0], evt.as_raw_fd());
+
+ let mut file = unsafe { File::from_raw_fd(files[0]) };
+
+ file.write(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
+ .expect("failed to write to sent fd");
+
+ assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
+ }
+}
diff --git a/sys_util/src/struct_util.rs b/sys_util/src/struct_util.rs
new file mode 100644
index 0000000..3d8def2
--- /dev/null
+++ b/sys_util/src/struct_util.rs
@@ -0,0 +1,146 @@
+// Copyright 2017 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;
+use std::io::Read;
+use std::mem;
+
+#[derive(Debug)]
+pub enum Error {
+ ReadStruct,
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Reads a struct from an input buffer.
+/// This is unsafe because the struct is initialized to unverified data read from the input.
+/// `read_struct` should only be called to fill plain old data structs. It is not endian safe.
+///
+/// # Arguments
+///
+/// * `f` - The input to read from. Often this is a file.
+/// * `out` - The struct to fill with data read from `f`.
+pub unsafe fn read_struct<T: Copy, F: Read>(f: &mut F, out: &mut T) -> Result<()> {
+ let out_slice = std::slice::from_raw_parts_mut(out as *mut T as *mut u8, mem::size_of::<T>());
+ f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?;
+ Ok(())
+}
+
+/// Reads an array of structs from an input buffer. Returns a Vec of structs initialized with data
+/// from the specified input.
+/// This is unsafe because the structs are initialized to unverified data read from the input.
+/// `read_struct_slice` should only be called for plain old data structs. It is not endian safe.
+///
+/// # Arguments
+///
+/// * `f` - The input to read from. Often this is a file.
+/// * `len` - The number of structs to fill with data read from `f`.
+pub unsafe fn read_struct_slice<T: Copy, F: Read>(f: &mut F, len: usize) -> Result<Vec<T>> {
+ let mut out: Vec<T> = Vec::with_capacity(len);
+ out.set_len(len);
+ let out_slice = std::slice::from_raw_parts_mut(
+ out.as_ptr() as *mut T as *mut u8,
+ mem::size_of::<T>() * len,
+ );
+ f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?;
+ Ok(out)
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::io::Cursor;
+ use std::mem;
+
+ #[derive(Clone, Copy, Debug, Default, PartialEq)]
+ struct TestRead {
+ a: u64,
+ b: u8,
+ c: u8,
+ d: u8,
+ e: u8,
+ }
+
+ #[test]
+ fn struct_basic_read() {
+ let orig = TestRead {
+ a: 0x7766554433221100,
+ b: 0x88,
+ c: 0x99,
+ d: 0xaa,
+ e: 0xbb,
+ };
+ let source = unsafe {
+ // Don't worry it's a test
+ std::slice::from_raw_parts(
+ &orig as *const _ as *const u8,
+ std::mem::size_of::<TestRead>(),
+ )
+ };
+ assert_eq!(mem::size_of::<TestRead>(), mem::size_of_val(&source));
+ let mut tr: TestRead = Default::default();
+ unsafe {
+ read_struct(&mut Cursor::new(source), &mut tr).unwrap();
+ }
+ assert_eq!(orig, tr);
+ }
+
+ #[test]
+ fn struct_read_past_end() {
+ let orig = TestRead {
+ a: 0x7766554433221100,
+ b: 0x88,
+ c: 0x99,
+ d: 0xaa,
+ e: 0xbb,
+ };
+ let source = unsafe {
+ // Don't worry it's a test
+ std::slice::from_raw_parts(
+ &orig as *const _ as *const u8,
+ std::mem::size_of::<TestRead>() - 1,
+ )
+ };
+ let mut tr: TestRead = Default::default();
+ unsafe {
+ assert!(read_struct(&mut Cursor::new(source), &mut tr).is_err());
+ }
+ }
+
+ #[test]
+ fn struct_slice_read() {
+ let orig = vec![
+ TestRead {
+ a: 0x7766554433221100,
+ b: 0x88,
+ c: 0x99,
+ d: 0xaa,
+ e: 0xbb,
+ },
+ TestRead {
+ a: 0x7867564534231201,
+ b: 0x02,
+ c: 0x13,
+ d: 0x24,
+ e: 0x35,
+ },
+ TestRead {
+ a: 0x7a69584736251403,
+ b: 0x04,
+ c: 0x15,
+ d: 0x26,
+ e: 0x37,
+ },
+ ];
+ let source = unsafe {
+ // Don't worry it's a test
+ std::slice::from_raw_parts(
+ orig.as_ptr() as *const u8,
+ std::mem::size_of::<TestRead>() * 3,
+ )
+ };
+
+ let tr: Vec<TestRead> = unsafe { read_struct_slice(&mut Cursor::new(source), 3).unwrap() };
+ assert_eq!(orig, tr);
+ }
+}
diff --git a/sys_util/src/syslog.rs b/sys_util/src/syslog.rs
new file mode 100644
index 0000000..da23229
--- /dev/null
+++ b/sys_util/src/syslog.rs
@@ -0,0 +1,681 @@
+// Copyright 2017 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.
+
+//! Facilities for sending log message to syslog.
+//!
+//! Every function exported by this module is thread-safe. Each function will silently fail until
+//! `syslog::init()` is called and returns `Ok`.
+//!
+//! # Examples
+//!
+//! ```
+//! use sys_util::{error, syslog, warn};
+//!
+//! fn main() {
+//! if let Err(e) = syslog::init() {
+//! println!("failed to initiailize syslog: {}", e);
+//! return;
+//! }
+//! warn!("this is your {} warning", "final");
+//! error!("something went horribly wrong: {}", "out of RAMs");
+//! }
+//! ```
+
+use std::env;
+use std::ffi::{OsStr, OsString};
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io;
+use std::io::{stderr, Cursor, ErrorKind, Write};
+use std::mem;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::os::unix::net::UnixDatagram;
+use std::path::PathBuf;
+use std::ptr::null;
+use std::sync::{MutexGuard, Once, ONCE_INIT};
+
+use libc::{
+ closelog, fcntl, localtime_r, openlog, time, time_t, tm, F_GETFD, LOG_NDELAY, LOG_PERROR,
+ LOG_PID, LOG_USER,
+};
+
+use sync::Mutex;
+
+use crate::getpid;
+
+const SYSLOG_PATH: &str = "/dev/log";
+
+/// The priority (i.e. severity) of a syslog message.
+///
+/// See syslog man pages for information on their semantics.
+#[derive(Copy, Clone, Debug)]
+pub enum Priority {
+ Emergency = 0,
+ Alert = 1,
+ Critical = 2,
+ Error = 3,
+ Warning = 4,
+ Notice = 5,
+ Info = 6,
+ Debug = 7,
+}
+
+impl Display for Priority {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Priority::*;
+
+ let string = match self {
+ Emergency => "EMERGENCY",
+ Alert => "ALERT",
+ Critical => "CRITICAL",
+ Error => "ERROR",
+ Warning => "WARNING",
+ Notice => "NOTICE",
+ Info => "INFO",
+ Debug => "DEBUG",
+ };
+
+ write!(f, "{}", string)
+ }
+}
+
+/// The facility of a syslog message.
+///
+/// See syslog man pages for information on their semantics.
+#[derive(Copy, Clone)]
+pub enum Facility {
+ Kernel = 0,
+ User = 1 << 3,
+ Mail = 2 << 3,
+ Daemon = 3 << 3,
+ Auth = 4 << 3,
+ Syslog = 5 << 3,
+ Lpr = 6 << 3,
+ News = 7 << 3,
+ Uucp = 8 << 3,
+ Local0 = 16 << 3,
+ Local1 = 17 << 3,
+ Local2 = 18 << 3,
+ Local3 = 19 << 3,
+ Local4 = 20 << 3,
+ Local5 = 21 << 3,
+ Local6 = 22 << 3,
+ Local7 = 23 << 3,
+}
+
+/// Errors returned by `syslog::init()`.
+#[derive(Debug)]
+pub enum Error {
+ /// Initialization was never attempted.
+ NeverInitialized,
+ /// Initialization has previously failed and can not be retried.
+ Poisoned,
+ /// Error while creating socket.
+ Socket(io::Error),
+ /// Error while attempting to connect socket.
+ Connect(io::Error),
+ // There was an error using `open` to get the lowest file descriptor.
+ GetLowestFd(io::Error),
+ // The guess of libc's file descriptor for the syslog connection was invalid.
+ InvalidFd,
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ NeverInitialized => write!(f, "initialization was never attempted"),
+ Poisoned => write!(f, "initialization previously failed and cannot be retried"),
+ Socket(e) => write!(f, "failed to create socket: {}", e),
+ Connect(e) => write!(f, "failed to connect socket: {}", e),
+ GetLowestFd(e) => write!(f, "failed to get lowest file descriptor: {}", e),
+ InvalidFd => write!(f, "guess of fd for syslog connection was invalid"),
+ }
+ }
+}
+
+fn get_proc_name() -> Option<String> {
+ env::args_os()
+ .next()
+ .map(PathBuf::from)
+ .and_then(|s| s.file_name().map(OsStr::to_os_string))
+ .map(OsString::into_string)
+ .and_then(Result::ok)
+}
+
+// Uses libc's openlog function to get a socket to the syslogger. By getting the socket this way, as
+// opposed to connecting to the syslogger directly, libc's internal state gets initialized for other
+// libraries (e.g. minijail) that make use of libc's syslog function. Note that this function
+// depends on no other threads or signal handlers being active in this process because they might
+// create FDs.
+//
+// TODO(zachr): Once https://android-review.googlesource.com/470998 lands, there won't be any
+// libraries in use that hard depend on libc's syslogger. Remove this and go back to making the
+// connection directly once minjail is ready.
+fn openlog_and_get_socket() -> Result<UnixDatagram, Error> {
+ // closelog first in case there was already a file descriptor open. Safe because it takes no
+ // arguments and just closes an open file descriptor. Does nothing if the file descriptor
+ // was not already open.
+ unsafe {
+ closelog();
+ }
+
+ // Ordinarily libc's FD for the syslog connection can't be accessed, but we can guess that the
+ // FD that openlog will be getting is the lowest unused FD. To guarantee that an FD is opened in
+ // this function we use the LOG_NDELAY to tell openlog to connect to the syslog now. To get the
+ // lowest unused FD, we open a dummy file (which the manual says will always return the lowest
+ // fd), and then close that fd. Voilà, we now know the lowest numbered FD. The call to openlog
+ // will make use of that FD, and then we just wrap a `UnixDatagram` around it for ease of use.
+ let fd = File::open("/dev/null")
+ .map_err(Error::GetLowestFd)?
+ .as_raw_fd();
+
+ unsafe {
+ // Safe because openlog accesses no pointers because `ident` is null, only valid flags are
+ // used, and it returns no error.
+ openlog(null(), LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
+ // For safety, ensure the fd we guessed is valid. The `fcntl` call itself only reads the
+ // file descriptor table of the current process, which is trivially safe.
+ if fcntl(fd, F_GETFD) >= 0 {
+ Ok(UnixDatagram::from_raw_fd(fd))
+ } else {
+ Err(Error::InvalidFd)
+ }
+ }
+}
+
+struct State {
+ stderr: bool,
+ socket: Option<UnixDatagram>,
+ file: Option<File>,
+ proc_name: Option<String>,
+}
+
+impl State {
+ fn new() -> Result<State, Error> {
+ let s = openlog_and_get_socket()?;
+ Ok(State {
+ stderr: true,
+ socket: Some(s),
+ file: None,
+ proc_name: get_proc_name(),
+ })
+ }
+}
+
+static STATE_ONCE: Once = ONCE_INIT;
+static mut STATE: *const Mutex<State> = 0 as *const _;
+
+fn new_mutex_ptr<T>(inner: T) -> *const Mutex<T> {
+ Box::into_raw(Box::new(Mutex::new(inner)))
+}
+
+/// Initialize the syslog connection and internal variables.
+///
+/// This should only be called once per process before any other threads have been spawned or any
+/// signal handlers have been registered. Every call made after the first will have no effect
+/// besides return `Ok` or `Err` appropriately.
+pub fn init() -> Result<(), Error> {
+ let mut err = Error::Poisoned;
+ STATE_ONCE.call_once(|| match State::new() {
+ // Safe because STATE mutation is guarded by `Once`.
+ Ok(state) => unsafe { STATE = new_mutex_ptr(state) },
+ Err(e) => err = e,
+ });
+
+ if unsafe { STATE.is_null() } {
+ Err(err)
+ } else {
+ Ok(())
+ }
+}
+
+fn lock() -> Result<MutexGuard<'static, State>, Error> {
+ // Safe because we assume that STATE is always in either a valid or NULL state.
+ let state_ptr = unsafe { STATE };
+ if state_ptr.is_null() {
+ return Err(Error::NeverInitialized);
+ }
+ // Safe because STATE only mutates once and we checked for NULL.
+ let state = unsafe { &*state_ptr };
+ let guard = state.lock();
+ Ok(guard)
+}
+
+// Attempts to lock and retrieve the state. Returns from the function silently on failure.
+macro_rules! lock {
+ () => {
+ match lock() {
+ Ok(s) => s,
+ _ => return,
+ };
+ };
+}
+
+/// Replaces the process name reported in each syslog message.
+///
+/// The default process name is the _file name_ of `argv[0]`. For example, if this program was
+/// invoked as
+///
+/// ```bash
+/// $ path/to/app --delete everything
+/// ```
+///
+/// the default process name would be _app_.
+///
+/// Does nothing if syslog was never initialized.
+pub fn set_proc_name<T: Into<String>>(proc_name: T) {
+ let mut state = lock!();
+ state.proc_name = Some(proc_name.into());
+}
+
+/// Enables or disables echoing log messages to the syslog.
+///
+/// The default behavior is **enabled**.
+///
+/// If `enable` goes from `true` to `false`, the syslog connection is closed. The connection is
+/// reopened if `enable` is set to `true` after it became `false`.
+///
+/// Returns an error if syslog was never initialized or the syslog connection failed to be
+/// established.
+///
+/// # Arguments
+/// * `enable` - `true` to enable echoing to syslog, `false` to disable echoing to syslog.
+pub fn echo_syslog(enable: bool) -> Result<(), Error> {
+ let state_ptr = unsafe { STATE };
+ if state_ptr.is_null() {
+ return Err(Error::NeverInitialized);
+ }
+ let mut state = lock().map_err(|_| Error::Poisoned)?;
+
+ match state.socket.take() {
+ Some(_) if enable => {}
+ Some(s) => {
+ // Because `openlog_and_get_socket` actually just "borrows" the syslog FD, this module
+ // does not own the syslog connection and therefore should not destroy it.
+ mem::forget(s);
+ }
+ None if enable => {
+ let s = openlog_and_get_socket()?;
+ state.socket = Some(s);
+ }
+ _ => {}
+ }
+ Ok(())
+}
+
+/// Replaces the optional `File` to echo log messages to.
+///
+/// The default behavior is to not echo to a file. Passing `None` to this function restores that
+/// behavior.
+///
+/// Does nothing if syslog was never initialized.
+///
+/// # Arguments
+/// * `file` - `Some(file)` to echo to `file`, `None` to disable echoing to the file previously passed to `echo_file`.
+pub fn echo_file(file: Option<File>) {
+ let mut state = lock!();
+ state.file = file;
+}
+
+/// Enables or disables echoing log messages to the `std::io::stderr()`.
+///
+/// The default behavior is **enabled**.
+///
+/// Does nothing if syslog was never initialized.
+///
+/// # Arguments
+/// * `enable` - `true` to enable echoing to stderr, `false` to disable echoing to stderr.
+pub fn echo_stderr(enable: bool) {
+ let mut state = lock!();
+ state.stderr = enable;
+}
+
+/// Retrieves the file descriptors owned by the global syslogger.
+///
+/// Does nothing if syslog was never initialized. If their are any file descriptors, they will be
+/// pushed into `fds`.
+///
+/// Note that the `stderr` file descriptor is never added, as it is not owned by syslog.
+pub fn push_fds(fds: &mut Vec<RawFd>) {
+ let state = lock!();
+ fds.extend(state.socket.iter().map(|s| s.as_raw_fd()));
+ fds.extend(state.file.iter().map(|f| f.as_raw_fd()));
+}
+
+/// Should only be called after `init()` was called.
+fn send_buf(socket: &UnixDatagram, buf: &[u8]) {
+ const SEND_RETRY: usize = 2;
+
+ for _ in 0..SEND_RETRY {
+ match socket.send(&buf[..]) {
+ Ok(_) => break,
+ Err(e) => match e.kind() {
+ ErrorKind::ConnectionRefused
+ | ErrorKind::ConnectionReset
+ | ErrorKind::ConnectionAborted
+ | ErrorKind::NotConnected => {
+ let res = socket.connect(SYSLOG_PATH);
+ if res.is_err() {
+ break;
+ }
+ }
+ _ => {}
+ },
+ }
+ }
+}
+
+fn get_localtime() -> tm {
+ unsafe {
+ // Safe because tm is just a struct of plain data.
+ let mut tm: tm = mem::zeroed();
+ let mut now: time_t = 0;
+ // Safe because we give time a valid pointer and can never fail.
+ time(&mut now as *mut _);
+ // Safe because we give localtime_r valid pointers and can never fail.
+ localtime_r(&now, &mut tm as *mut _);
+ tm
+ }
+}
+
+/// Records a log message with the given details.
+///
+/// Note that this will fail silently if syslog was not initialized.
+///
+/// # Arguments
+/// * `pri` - The `Priority` (i.e. severity) of the log message.
+/// * `fac` - The `Facility` of the log message. Usually `Facility::User` should be used.
+/// * `file_line` - Optional tuple of the name of the file that generated the
+/// log and the line number within that file.
+/// * `args` - The log's message to record, in the form of `format_args!()` return value
+///
+/// # Examples
+///
+/// ```
+/// # use sys_util::syslog;
+/// # fn main() {
+/// # if let Err(e) = syslog::init() {
+/// # println!("failed to initiailize syslog: {}", e);
+/// # return;
+/// # }
+/// syslog::log(syslog::Priority::Error,
+/// syslog::Facility::User,
+/// Some((file!(), line!())),
+/// format_args!("hello syslog"));
+/// # }
+/// ```
+pub fn log(pri: Priority, fac: Facility, file_line: Option<(&str, u32)>, args: fmt::Arguments) {
+ const MONTHS: [&str; 12] = [
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ ];
+
+ let mut state = lock!();
+ let mut buf = [0u8; 1024];
+ if let Some(socket) = &state.socket {
+ let tm = get_localtime();
+ let prifac = (pri as u8) | (fac as u8);
+ let res = {
+ let mut buf_cursor = Cursor::new(&mut buf[..]);
+ write!(
+ &mut buf_cursor,
+ "<{}>{} {:02} {:02}:{:02}:{:02} {}[{}]: ",
+ prifac,
+ MONTHS[tm.tm_mon as usize],
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ state.proc_name.as_ref().map(|s| s.as_ref()).unwrap_or("-"),
+ getpid()
+ )
+ .and_then(|()| {
+ if let Some((file_name, line)) = &file_line {
+ write!(&mut buf_cursor, " [{}:{}] ", file_name, line)
+ } else {
+ Ok(())
+ }
+ })
+ .and_then(|()| write!(&mut buf_cursor, "{}", args))
+ .and_then(|()| Ok(buf_cursor.position() as usize))
+ };
+
+ if let Ok(len) = &res {
+ send_buf(&socket, &buf[..*len])
+ }
+ }
+
+ let res = {
+ let mut buf_cursor = Cursor::new(&mut buf[..]);
+ if let Some((file_name, line)) = &file_line {
+ write!(&mut buf_cursor, "[{}:{}:{}] ", pri, file_name, line)
+ } else {
+ Ok(())
+ }
+ .and_then(|()| writeln!(&mut buf_cursor, "{}", args))
+ .and_then(|()| Ok(buf_cursor.position() as usize))
+ };
+ if let Ok(len) = &res {
+ if let Some(file) = &mut state.file {
+ let _ = file.write_all(&buf[..*len]);
+ }
+ if state.stderr {
+ let _ = stderr().write_all(&buf[..*len]);
+ }
+ }
+}
+
+/// A macro for logging at an arbitrary priority level.
+///
+/// Note that this will fail silently if syslog was not initialized.
+#[macro_export]
+macro_rules! log {
+ ($pri:expr, $($args:tt)+) => ({
+ $crate::syslog::log($pri, $crate::syslog::Facility::User, Some((file!(), line!())), format_args!($($args)+))
+ })
+}
+
+/// A macro for logging an error.
+///
+/// Note that this will fail silently if syslog was not initialized.
+#[macro_export]
+macro_rules! error {
+ ($($args:tt)+) => ($crate::log!($crate::syslog::Priority::Error, $($args)*))
+}
+
+/// A macro for logging a warning.
+///
+/// Note that this will fail silently if syslog was not initialized.
+#[macro_export]
+macro_rules! warn {
+ ($($args:tt)+) => ($crate::log!($crate::syslog::Priority::Warning, $($args)*))
+}
+
+/// A macro for logging info.
+///
+/// Note that this will fail silently if syslog was not initialized.
+#[macro_export]
+macro_rules! info {
+ ($($args:tt)+) => ($crate::log!($crate::syslog::Priority::Info, $($args)*))
+}
+
+/// A macro for logging debug information.
+///
+/// Note that this will fail silently if syslog was not initialized.
+#[macro_export]
+macro_rules! debug {
+ ($($args:tt)+) => ($crate::log!($crate::syslog::Priority::Debug, $($args)*))
+}
+
+// Struct that implements io::Write to be used for writing directly to the syslog
+pub struct Syslogger {
+ buf: String,
+ priority: Priority,
+ facility: Facility,
+}
+
+impl Syslogger {
+ pub fn new(p: Priority, f: Facility) -> Syslogger {
+ Syslogger {
+ buf: String::new(),
+ priority: p,
+ facility: f,
+ }
+ }
+}
+
+impl io::Write for Syslogger {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let parsed_str = String::from_utf8_lossy(buf);
+ self.buf.push_str(&parsed_str);
+
+ if let Some(last_newline_idx) = self.buf.rfind('\n') {
+ for line in self.buf[..last_newline_idx].lines() {
+ log(self.priority, self.facility, None, format_args!("{}", line));
+ }
+
+ self.buf.drain(..=last_newline_idx);
+ }
+
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use libc::{shm_open, shm_unlink, O_CREAT, O_EXCL, O_RDWR};
+
+ use std::ffi::CStr;
+ use std::io::{Read, Seek, SeekFrom};
+ use std::os::unix::io::FromRawFd;
+
+ #[test]
+ fn init_syslog() {
+ init().unwrap();
+ }
+
+ #[test]
+ fn fds() {
+ init().unwrap();
+ let mut fds = Vec::new();
+ push_fds(&mut fds);
+ assert!(fds.len() >= 1);
+ for fd in fds {
+ assert!(fd >= 0);
+ }
+ }
+
+ #[test]
+ fn syslog_log() {
+ init().unwrap();
+ log(
+ Priority::Error,
+ Facility::User,
+ Some((file!(), line!())),
+ format_args!("hello syslog"),
+ );
+ }
+
+ #[test]
+ fn proc_name() {
+ init().unwrap();
+ log(
+ Priority::Error,
+ Facility::User,
+ Some((file!(), line!())),
+ format_args!("before proc name"),
+ );
+ set_proc_name("sys_util-test");
+ log(
+ Priority::Error,
+ Facility::User,
+ Some((file!(), line!())),
+ format_args!("after proc name"),
+ );
+ }
+
+ #[test]
+ fn syslog_file() {
+ init().unwrap();
+ let shm_name = CStr::from_bytes_with_nul(b"/crosvm_shm\0").unwrap();
+ let mut file = unsafe {
+ shm_unlink(shm_name.as_ptr());
+ let fd = shm_open(shm_name.as_ptr(), O_RDWR | O_CREAT | O_EXCL, 0666);
+ assert!(fd >= 0, "error creating shared memory;");
+ File::from_raw_fd(fd)
+ };
+
+ let syslog_file = file.try_clone().expect("error cloning shared memory file");
+ echo_file(Some(syslog_file));
+
+ const TEST_STR: &'static str = "hello shared memory file";
+ log(
+ Priority::Error,
+ Facility::User,
+ Some((file!(), line!())),
+ format_args!("{}", TEST_STR),
+ );
+
+ file.seek(SeekFrom::Start(0))
+ .expect("error seeking shared memory file");
+ let mut buf = String::new();
+ file.read_to_string(&mut buf)
+ .expect("error reading shared memory file");
+ assert!(buf.contains(TEST_STR));
+ }
+
+ #[test]
+ fn macros() {
+ init().unwrap();
+ error!("this is an error {}", 3);
+ warn!("this is a warning {}", "uh oh");
+ info!("this is info {}", true);
+ debug!("this is debug info {:?}", Some("helpful stuff"));
+ }
+
+ #[test]
+ fn syslogger_char() {
+ init().unwrap();
+ let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
+
+ let string = "Writing chars to syslog";
+ for c in string.chars() {
+ syslogger.write(&[c as u8]).expect("error writing char");
+ }
+
+ syslogger
+ .write(&['\n' as u8])
+ .expect("error writing newline char");
+ }
+
+ #[test]
+ fn syslogger_line() {
+ init().unwrap();
+ let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
+
+ let s = "Writing string to syslog\n";
+ syslogger
+ .write(&s.as_bytes())
+ .expect("error writing string");
+ }
+
+ #[test]
+ fn syslogger_partial() {
+ init().unwrap();
+ let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
+
+ let s = "Writing partial string";
+ // Should not log because there is no newline character
+ syslogger
+ .write(&s.as_bytes())
+ .expect("error writing string");
+ }
+}
diff --git a/sys_util/src/tempdir.rs b/sys_util/src/tempdir.rs
new file mode 100644
index 0000000..045f245
--- /dev/null
+++ b/sys_util/src/tempdir.rs
@@ -0,0 +1,102 @@
+// Copyright 2017 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;
+use std::ffi::OsStr;
+use std::ffi::OsString;
+use std::fs;
+use std::os::unix::ffi::OsStringExt;
+use std::path::Path;
+use std::path::PathBuf;
+
+use libc;
+
+use crate::{errno_result, Result};
+
+/// Create and remove a temporary directory. The directory will be maintained for the lifetime of
+/// the `TempDir` object.
+pub struct TempDir {
+ path: Option<PathBuf>,
+}
+
+impl TempDir {
+ /// Creates a new tempory directory.
+ /// The directory will be removed when the object goes out of scope.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use std::path::Path;
+ /// # use std::path::PathBuf;
+ /// # use sys_util::TempDir;
+ /// # fn test_create_temp_dir() -> Result<(), ()> {
+ /// let t = TempDir::new("/tmp/testdir").map_err(|_| ())?;
+ /// assert!(t.as_path().unwrap().exists());
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn new<P: AsRef<OsStr>>(prefix: P) -> Result<TempDir> {
+ let mut dir_string = prefix.as_ref().to_os_string();
+ dir_string.push("XXXXXX");
+ // unwrap this result as the internal bytes can't have a null with a valid path.
+ let dir_name = CString::new(dir_string.into_vec()).unwrap();
+ let mut dir_bytes = dir_name.into_bytes_with_nul();
+ let ret = unsafe {
+ // Creating the directory isn't unsafe. The fact that it modifies the guts of the path
+ // is also OK because it only overwrites the last 6 Xs added above.
+ libc::mkdtemp(dir_bytes.as_mut_ptr() as *mut libc::c_char)
+ };
+ if ret.is_null() {
+ return errno_result();
+ }
+ dir_bytes.pop(); // Remove the null becasue from_vec can't handle it.
+ Ok(TempDir {
+ path: Some(PathBuf::from(OsString::from_vec(dir_bytes))),
+ })
+ }
+
+ /// Removes the temporary directory. Calling this is optional as dropping a `TempDir` object
+ /// will also remove the directory. Calling remove explicitly allows for better error handling.
+ pub fn remove(mut self) -> Result<()> {
+ let path = self.path.take();
+ path.map_or(Ok(()), fs::remove_dir_all)?;
+ Ok(())
+ }
+
+ /// Returns the path to the tempdir if it is currently valid
+ pub fn as_path(&self) -> Option<&Path> {
+ self.path.as_ref().map(PathBuf::as_path)
+ }
+}
+
+impl Drop for TempDir {
+ fn drop(&mut self) {
+ if let Some(p) = &self.path {
+ // Nothing can be done here if this returns an error.
+ let _ = fs::remove_dir_all(p);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn create_dir() {
+ let t = TempDir::new("/tmp/asdf").unwrap();
+ let path = t.as_path().unwrap();
+ assert!(path.exists());
+ assert!(path.is_dir());
+ assert!(path.starts_with("/tmp/"));
+ }
+
+ #[test]
+ fn remove_dir() {
+ let t = TempDir::new("/tmp/asdf").unwrap();
+ let path = t.as_path().unwrap().to_owned();
+ assert!(t.remove().is_ok());
+ assert!(!path.exists());
+ }
+}
diff --git a/sys_util/src/terminal.rs b/sys_util/src/terminal.rs
new file mode 100644
index 0000000..fc90e45
--- /dev/null
+++ b/sys_util/src/terminal.rs
@@ -0,0 +1,114 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::io::StdinLock;
+use std::mem::zeroed;
+use std::os::unix::io::RawFd;
+
+use libc::{
+ c_int, fcntl, isatty, read, tcgetattr, tcsetattr, termios, ECHO, F_GETFL, F_SETFL, ICANON,
+ ISIG, O_NONBLOCK, STDIN_FILENO, TCSANOW,
+};
+
+use crate::{errno_result, Result};
+
+fn modify_mode<F: FnOnce(&mut termios)>(fd: RawFd, f: F) -> Result<()> {
+ // Safe because we check the return value of isatty.
+ if unsafe { isatty(fd) } != 1 {
+ return Ok(());
+ }
+
+ // The following pair are safe because termios gets totally overwritten by tcgetattr and we
+ // check the return result.
+ let mut termios: termios = unsafe { zeroed() };
+ let ret = unsafe { tcgetattr(fd, &mut termios as *mut _) };
+ if ret < 0 {
+ return errno_result();
+ }
+ let mut new_termios = termios;
+ f(&mut new_termios);
+ // Safe because the syscall will only read the extent of termios and we check the return result.
+ let ret = unsafe { tcsetattr(fd, TCSANOW, &new_termios as *const _) };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ Ok(())
+}
+
+fn get_flags(fd: RawFd) -> Result<c_int> {
+ // Safe because no third parameter is expected and we check the return result.
+ let ret = unsafe { fcntl(fd, F_GETFL) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(ret)
+}
+
+fn set_flags(fd: RawFd, flags: c_int) -> Result<()> {
+ // Safe because we supply the third parameter and we check the return result.
+ let ret = unsafe { fcntl(fd, F_SETFL, flags) };
+ if ret < 0 {
+ return errno_result();
+ }
+ Ok(())
+}
+
+/// Trait for file descriptors that are TTYs, according to `isatty(3)`.
+///
+/// This is marked unsafe because the implementation must promise that the returned RawFd is a valid
+/// fd and that the lifetime of the returned fd is at least that of the trait object.
+pub unsafe trait Terminal {
+ /// Gets the file descriptor of the TTY.
+ fn tty_fd(&self) -> RawFd;
+
+ /// Set this terminal's mode to canonical mode (`ICANON | ECHO | ISIG`).
+ fn set_canon_mode(&self) -> Result<()> {
+ modify_mode(self.tty_fd(), |t| t.c_lflag |= ICANON | ECHO | ISIG)
+ }
+
+ /// Set this terminal's mode to raw mode (`!(ICANON | ECHO | ISIG)`).
+ fn set_raw_mode(&self) -> Result<()> {
+ modify_mode(self.tty_fd(), |t| t.c_lflag &= !(ICANON | ECHO | ISIG))
+ }
+
+ /// Sets the non-blocking mode of this terminal's file descriptor.
+ ///
+ /// If `non_block` is `true`, then `read_raw` will not block. If `non_block` is `false`, then
+ /// `read_raw` may block if there is nothing to read.
+ fn set_non_block(&self, non_block: bool) -> Result<()> {
+ let old_flags = get_flags(self.tty_fd())?;
+ let new_flags = if non_block {
+ old_flags | O_NONBLOCK
+ } else {
+ old_flags & !O_NONBLOCK
+ };
+ if new_flags != old_flags {
+ set_flags(self.tty_fd(), new_flags)?
+ }
+ Ok(())
+ }
+
+ /// Reads up to `out.len()` bytes from this terminal without any buffering.
+ ///
+ /// This may block, depending on if non-blocking was enabled with `set_non_block` or if there
+ /// are any bytes to read. If there is at least one byte that is readable, this will not block.
+ fn read_raw(&self, out: &mut [u8]) -> Result<usize> {
+ // Safe because read will only modify the pointer up to the length we give it and we check
+ // the return result.
+ let ret = unsafe { read(self.tty_fd(), out.as_mut_ptr() as *mut _, out.len()) };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ Ok(ret as usize)
+ }
+}
+
+// Safe because we return a genuine terminal fd that never changes and shares our lifetime.
+unsafe impl<'a> Terminal for StdinLock<'a> {
+ fn tty_fd(&self) -> RawFd {
+ STDIN_FILENO
+ }
+}
diff --git a/sys_util/src/timerfd.rs b/sys_util/src/timerfd.rs
new file mode 100644
index 0000000..8e5d91b
--- /dev/null
+++ b/sys_util/src/timerfd.rs
@@ -0,0 +1,292 @@
+// Copyright 2018 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::mem;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::ptr;
+use std::sync::Arc;
+use std::time::Duration;
+use sync::Mutex;
+
+use libc::{self, timerfd_create, timerfd_gettime, timerfd_settime, CLOCK_MONOTONIC, TFD_CLOEXEC};
+
+use crate::{errno_result, EventFd, FakeClock, Result};
+
+/// A safe wrapper around a Linux timerfd (man 2 timerfd_create).
+pub struct TimerFd(File);
+
+impl TimerFd {
+ /// Creates a new timerfd. The timer is initally disarmed and must be armed by calling
+ /// `reset`.
+ pub fn new() -> Result<TimerFd> {
+ // Safe because this doesn't modify any memory and we check the return value.
+ let ret = unsafe { timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ // Safe because we uniquely own the file descriptor.
+ Ok(TimerFd(unsafe { File::from_raw_fd(ret) }))
+ }
+
+ /// Sets the timer to expire after `dur`. If `interval` is not `None` it represents
+ /// the period for repeated expirations after the initial expiration. Otherwise
+ /// the timer will expire just once. Cancels any existing duration and repeating interval.
+ pub fn reset(&mut self, dur: Duration, interval: Option<Duration>) -> Result<()> {
+ // Safe because we are zero-initializing a struct with only primitive member fields.
+ let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
+ spec.it_value.tv_sec = dur.as_secs() as libc::time_t;
+ // nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
+ let nsec = dur.subsec_nanos() as i32;
+ spec.it_value.tv_nsec = libc::c_long::from(nsec);
+
+ if let Some(int) = interval {
+ spec.it_interval.tv_sec = int.as_secs() as libc::time_t;
+ // nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
+ let nsec = int.subsec_nanos() as i32;
+ spec.it_interval.tv_nsec = libc::c_long::from(nsec);
+ }
+
+ // Safe because this doesn't modify any memory and we check the return value.
+ let ret = unsafe { timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ Ok(())
+ }
+
+ /// Waits until the timer expires. The return value represents the number of times the timer
+ /// has expired since the last time `wait` was called. If the timer has not yet expired once
+ /// this call will block until it does.
+ pub fn wait(&mut self) -> Result<u64> {
+ let mut count = 0u64;
+
+ // Safe because this will only modify |buf| and we check the return value.
+ let ret = unsafe {
+ libc::read(
+ self.as_raw_fd(),
+ &mut count as *mut _ as *mut libc::c_void,
+ mem::size_of_val(&count),
+ )
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ // The bytes in the buffer are guaranteed to be in native byte-order so we don't need to
+ // use from_le or from_be.
+ Ok(count)
+ }
+
+ /// Returns `true` if the timer is currently armed.
+ pub fn is_armed(&self) -> Result<bool> {
+ // Safe because we are zero-initializing a struct with only primitive member fields.
+ let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
+
+ // Safe because timerfd_gettime is trusted to only modify `spec`.
+ let ret = unsafe { timerfd_gettime(self.as_raw_fd(), &mut spec) };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ Ok(spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0)
+ }
+
+ /// Disarms the timer.
+ pub fn clear(&mut self) -> Result<()> {
+ // Safe because we are zero-initializing a struct with only primitive member fields.
+ let spec: libc::itimerspec = unsafe { mem::zeroed() };
+
+ // Safe because this doesn't modify any memory and we check the return value.
+ let ret = unsafe { timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ Ok(())
+ }
+}
+
+impl AsRawFd for TimerFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl FromRawFd for TimerFd {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ TimerFd(File::from_raw_fd(fd))
+ }
+}
+
+impl IntoRawFd for TimerFd {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+/// FakeTimerFd: For use in tests.
+pub struct FakeTimerFd {
+ clock: Arc<Mutex<FakeClock>>,
+ deadline_ns: Option<u64>,
+ interval: Option<Duration>,
+ fd: EventFd,
+}
+
+impl FakeTimerFd {
+ /// Creates a new fake timerfd. The timer is initally disarmed and must be armed by calling
+ /// `reset`.
+ pub fn new(clock: Arc<Mutex<FakeClock>>) -> Self {
+ FakeTimerFd {
+ clock,
+ deadline_ns: None,
+ interval: None,
+ fd: EventFd::new().unwrap(),
+ }
+ }
+
+ fn duration_to_nanos(d: Duration) -> u64 {
+ d.as_secs() * 1_000_000_000 + u64::from(d.subsec_nanos())
+ }
+
+ /// Sets the timer to expire after `dur`. If `interval` is not `None` it represents
+ /// the period for repeated expirations after the initial expiration. Otherwise
+ /// the timer will expire just once. Cancels any existing duration and repeating interval.
+ pub fn reset(&mut self, dur: Duration, interval: Option<Duration>) -> Result<()> {
+ let mut guard = self.clock.lock();
+ let deadline = guard.nanos() + FakeTimerFd::duration_to_nanos(dur);
+ self.deadline_ns = Some(deadline);
+ self.interval = interval;
+ guard.add_event_fd(deadline, self.fd.try_clone()?);
+ Ok(())
+ }
+
+ /// Waits until the timer expires. The return value represents the number of times the timer
+ /// has expired since the last time `wait` was called. If the timer has not yet expired once
+ /// this call will block until it does.
+ pub fn wait(&mut self) -> Result<u64> {
+ loop {
+ self.fd.read()?;
+ if let Some(deadline_ns) = &mut self.deadline_ns {
+ let mut guard = self.clock.lock();
+ let now = guard.nanos();
+ if now >= *deadline_ns {
+ let mut expirys = 0;
+ if let Some(interval) = self.interval {
+ let interval_ns = FakeTimerFd::duration_to_nanos(interval);
+ if interval_ns > 0 {
+ expirys += (now - *deadline_ns) / interval_ns;
+ *deadline_ns += (expirys + 1) * interval_ns;
+ guard.add_event_fd(*deadline_ns, self.fd.try_clone()?);
+ }
+ }
+ return Ok(expirys + 1);
+ }
+ }
+ }
+ }
+
+ /// Returns `true` if the timer is currently armed.
+ pub fn is_armed(&self) -> Result<bool> {
+ Ok(self.deadline_ns.is_some())
+ }
+
+ /// Disarms the timer.
+ pub fn clear(&mut self) -> Result<()> {
+ self.deadline_ns = None;
+ self.interval = None;
+ Ok(())
+ }
+}
+
+impl AsRawFd for FakeTimerFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for FakeTimerFd {
+ fn into_raw_fd(self) -> RawFd {
+ self.fd.into_raw_fd()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::thread::sleep;
+ use std::time::{Duration, Instant};
+
+ #[test]
+ fn one_shot() {
+ let mut tfd = TimerFd::new().expect("failed to create timerfd");
+ assert_eq!(tfd.is_armed().unwrap(), false);
+
+ let dur = Duration::from_millis(200);
+ let now = Instant::now();
+ tfd.reset(dur.clone(), None).expect("failed to arm timer");
+
+ assert_eq!(tfd.is_armed().unwrap(), true);
+
+ let count = tfd.wait().expect("unable to wait for timer");
+
+ assert_eq!(count, 1);
+ assert!(now.elapsed() >= dur);
+ }
+
+ #[test]
+ fn repeating() {
+ let mut tfd = TimerFd::new().expect("failed to create timerfd");
+
+ let dur = Duration::from_millis(200);
+ let interval = Duration::from_millis(100);
+ tfd.reset(dur.clone(), Some(interval))
+ .expect("failed to arm timer");
+
+ sleep(dur * 3);
+
+ let count = tfd.wait().expect("unable to wait for timer");
+ assert!(count >= 5, "count = {}", count);
+ }
+
+ #[test]
+ fn fake_one_shot() {
+ let clock = Arc::new(Mutex::new(FakeClock::new()));
+ let mut tfd = FakeTimerFd::new(clock.clone());
+ assert_eq!(tfd.is_armed().unwrap(), false);
+
+ let dur = Duration::from_nanos(200);
+ tfd.reset(dur.clone(), None).expect("failed to arm timer");
+
+ assert_eq!(tfd.is_armed().unwrap(), true);
+ clock.lock().add_ns(200);
+
+ let count = tfd.wait().expect("unable to wait for timer");
+
+ assert_eq!(count, 1);
+ }
+
+ #[test]
+ fn fake_repeating() {
+ let clock = Arc::new(Mutex::new(FakeClock::new()));
+ let mut tfd = FakeTimerFd::new(clock.clone());
+
+ let dur = Duration::from_nanos(200);
+ let interval = Duration::from_nanos(100);
+ tfd.reset(dur.clone(), Some(interval))
+ .expect("failed to arm timer");
+
+ clock.lock().add_ns(300);
+
+ let mut count = tfd.wait().expect("unable to wait for timer");
+ // An expiration from the initial expiry and from 1 repeat.
+ assert_eq!(count, 2);
+
+ clock.lock().add_ns(300);
+ count = tfd.wait().expect("unable to wait for timer");
+ assert_eq!(count, 3);
+ }
+}
diff --git a/sys_util/src/write_zeroes.rs b/sys_util/src/write_zeroes.rs
new file mode 100644
index 0000000..51f0dbe
--- /dev/null
+++ b/sys_util/src/write_zeroes.rs
@@ -0,0 +1,169 @@
+// Copyright 2018 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::cmp::min;
+use std::fs::File;
+use std::io::{self, Seek, SeekFrom, Write};
+
+use crate::fallocate;
+use crate::FallocateMode;
+
+/// A trait for deallocating space in a file.
+pub trait PunchHole {
+ /// Replace a range of bytes with a hole.
+ fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()>;
+}
+
+impl PunchHole for File {
+ fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()> {
+ fallocate(self, FallocateMode::PunchHole, true, offset, length as u64)
+ .map_err(|e| io::Error::from_raw_os_error(e.errno()))
+ }
+}
+
+/// A trait for writing zeroes to a stream.
+pub trait WriteZeroes {
+ /// Write `length` bytes of zeroes to the stream, returning how many bytes were written.
+ fn write_zeroes(&mut self, length: usize) -> io::Result<usize>;
+}
+
+impl<T: PunchHole + Seek + Write> WriteZeroes for T {
+ fn write_zeroes(&mut self, length: usize) -> io::Result<usize> {
+ // Try to punch a hole first.
+ let offset = self.seek(SeekFrom::Current(0))?;
+ if let Ok(()) = self.punch_hole(offset, length as u64) {
+ // Advance the seek cursor as if we had done a real write().
+ self.seek(SeekFrom::Current(length as i64))?;
+ return Ok(length);
+ }
+
+ // fall back to write()
+
+ // punch_hole() failed; fall back to writing a buffer of zeroes
+ // until we have written up to length.
+ let buf_size = min(length, 0x10000);
+ let buf = vec![0u8; buf_size];
+ let mut nwritten: usize = 0;
+ while nwritten < length {
+ let remaining = length - nwritten;
+ let write_size = min(remaining, buf_size);
+ nwritten += self.write(&buf[0..write_size])?;
+ }
+ Ok(length)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::fs::OpenOptions;
+ use std::io::{Read, Seek, SeekFrom};
+ use std::path::PathBuf;
+
+ use crate::TempDir;
+
+ #[test]
+ fn simple_test() {
+ let tempdir = TempDir::new("/tmp/write_zeroes_test").unwrap();
+ let mut path = PathBuf::from(tempdir.as_path().unwrap());
+ path.push("file");
+ let mut f = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .open(&path)
+ .unwrap();
+ f.set_len(16384).unwrap();
+
+ // Write buffer of non-zero bytes to offset 1234
+ let orig_data = [0x55u8; 5678];
+ f.seek(SeekFrom::Start(1234)).unwrap();
+ f.write(&orig_data).unwrap();
+
+ // Read back the data plus some overlap on each side
+ let mut readback = [0u8; 16384];
+ f.seek(SeekFrom::Start(0)).unwrap();
+ f.read(&mut readback).unwrap();
+ // Bytes before the write should still be 0
+ for read in &readback[0..1234] {
+ assert_eq!(*read, 0);
+ }
+ // Bytes that were just written should be 0x55
+ for read in &readback[1234..(1234 + 5678)] {
+ assert_eq!(*read, 0x55);
+ }
+ // Bytes after the written area should still be 0
+ for read in &readback[(1234 + 5678)..] {
+ assert_eq!(*read, 0);
+ }
+
+ // Overwrite some of the data with zeroes
+ f.seek(SeekFrom::Start(2345)).unwrap();
+ f.write_zeroes(4321).expect("write_zeroes failed");
+ // Verify seek position after write_zeroes()
+ assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 2345 + 4321);
+
+ // Read back the data and verify that it is now zero
+ f.seek(SeekFrom::Start(0)).unwrap();
+ f.read(&mut readback).unwrap();
+ // Bytes before the write should still be 0
+ for read in &readback[0..1234] {
+ assert_eq!(*read, 0);
+ }
+ // Original data should still exist before the write_zeroes region
+ for read in &readback[1234..2345] {
+ assert_eq!(*read, 0x55);
+ }
+ // The write_zeroes region should now be zero
+ for read in &readback[2345..(2345 + 4321)] {
+ assert_eq!(*read, 0);
+ }
+ // Original data should still exist after the write_zeroes region
+ for read in &readback[(2345 + 4321)..(1234 + 5678)] {
+ assert_eq!(*read, 0x55);
+ }
+ // The rest of the file should still be 0
+ for read in &readback[(1234 + 5678)..] {
+ assert_eq!(*read, 0);
+ }
+ }
+
+ #[test]
+ fn large_write_zeroes() {
+ let tempdir = TempDir::new("/tmp/write_zeroes_test").unwrap();
+ let mut path = PathBuf::from(tempdir.as_path().unwrap());
+ path.push("file");
+ let mut f = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .open(&path)
+ .unwrap();
+ f.set_len(16384).unwrap();
+
+ // Write buffer of non-zero bytes
+ let orig_data = [0x55u8; 0x20000];
+ f.seek(SeekFrom::Start(0)).unwrap();
+ f.write(&orig_data).unwrap();
+
+ // Overwrite some of the data with zeroes
+ f.seek(SeekFrom::Start(0)).unwrap();
+ f.write_zeroes(0x10001).expect("write_zeroes failed");
+ // Verify seek position after write_zeroes()
+ assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 0x10001);
+
+ // Read back the data and verify that it is now zero
+ let mut readback = [0u8; 0x20000];
+ f.seek(SeekFrom::Start(0)).unwrap();
+ f.read(&mut readback).unwrap();
+ // The write_zeroes region should now be zero
+ for read in &readback[0..0x10001] {
+ assert_eq!(*read, 0);
+ }
+ // Original data should still exist after the write_zeroes region
+ for read in &readback[0x10001..0x20000] {
+ assert_eq!(*read, 0x55);
+ }
+ }
+}
diff --git a/syscall_defines/Cargo.toml b/syscall_defines/Cargo.toml
new file mode 100644
index 0000000..d99cd2c
--- /dev/null
+++ b/syscall_defines/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "syscall_defines"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+include = ["src/**/*", "Cargo.toml"]
+
+[dependencies]
+
+[workspace]
diff --git a/syscall_defines/src/lib.rs b/syscall_defines/src/lib.rs
new file mode 100644
index 0000000..cf016ab
--- /dev/null
+++ b/syscall_defines/src/lib.rs
@@ -0,0 +1,19 @@
+// Copyright 2017 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")]
+#[path = "linux-x86_64/mod.rs"]
+pub mod linux;
+
+#[cfg(target_arch = "x86")]
+#[path = "linux-x86/mod.rs"]
+pub mod linux;
+
+#[cfg(target_arch = "aarch64")]
+#[path = "linux-aarch64/mod.rs"]
+pub mod linux;
+
+#[cfg(target_arch = "arm")]
+#[path = "linux-arm/mod.rs"]
+pub mod linux;
diff --git a/syscall_defines/src/linux-aarch64/mod.rs b/syscall_defines/src/linux-aarch64/mod.rs
new file mode 100644
index 0000000..8136d00
--- /dev/null
+++ b/syscall_defines/src/linux-aarch64/mod.rs
@@ -0,0 +1,330 @@
+// Copyright 2017 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.
+
+// Generated with: cat include/uapi/asm-generic/unistd.h |
+// awk ' { print "SYS_" $2 " = " $2"," } '
+#[allow(dead_code)]
+#[allow(non_camel_case_types)]
+pub enum LinuxSyscall {
+ SYS_io_setup = 0,
+ SYS_io_destroy = 1,
+ SYS_io_submit = 2,
+ SYS_io_cancel = 3,
+ SYS_io_getevents = 4,
+ SYS_setxattr = 5,
+ SYS_lsetxattr = 6,
+ SYS_fsetxattr = 7,
+ SYS_getxattr = 8,
+ SYS_lgetxattr = 9,
+ SYS_fgetxattr = 10,
+ SYS_listxattr = 11,
+ SYS_llistxattr = 12,
+ SYS_flistxattr = 13,
+ SYS_removexattr = 14,
+ SYS_lremovexattr = 15,
+ SYS_fremovexattr = 16,
+ SYS_getcwd = 17,
+ SYS_lookup_dcookie = 18,
+ SYS_eventfd2 = 19,
+ SYS_epoll_create1 = 20,
+ SYS_epoll_ctl = 21,
+ SYS_epoll_pwait = 22,
+ SYS_dup = 23,
+ SYS_dup3 = 24,
+ SYS_inotify_init1 = 26,
+ SYS_inotify_add_watch = 27,
+ SYS_inotify_rm_watch = 28,
+ SYS_ioctl = 29,
+ SYS_ioprio_set = 30,
+ SYS_ioprio_get = 31,
+ SYS_flock = 32,
+ SYS_mknodat = 33,
+ SYS_mkdirat = 34,
+ SYS_unlinkat = 35,
+ SYS_symlinkat = 36,
+ SYS_linkat = 37,
+ SYS_renameat = 38,
+ SYS_umount2 = 39,
+ SYS_mount = 40,
+ SYS_pivot_root = 41,
+ SYS_nfsservctl = 42,
+ SYS_fallocate = 47,
+ SYS_faccessat = 48,
+ SYS_chdir = 49,
+ SYS_fchdir = 50,
+ SYS_chroot = 51,
+ SYS_fchmod = 52,
+ SYS_fchmodat = 53,
+ SYS_fchownat = 54,
+ SYS_fchown = 55,
+ SYS_openat = 56,
+ SYS_close = 57,
+ SYS_vhangup = 58,
+ SYS_pipe2 = 59,
+ SYS_quotactl = 60,
+ SYS_getdents64 = 61,
+ SYS_read = 63,
+ SYS_write = 64,
+ SYS_readv = 65,
+ SYS_writev = 66,
+ SYS_pread64 = 67,
+ SYS_pwrite64 = 68,
+ SYS_preadv = 69,
+ SYS_pwritev = 70,
+ SYS_pselect6 = 72,
+ SYS_ppoll = 73,
+ SYS_signalfd4 = 74,
+ SYS_vmsplice = 75,
+ SYS_splice = 76,
+ SYS_tee = 77,
+ SYS_readlinkat = 78,
+ SYS_sync = 81,
+ SYS_fsync = 82,
+ SYS_fdatasync = 83,
+ SYS_sync_file_range = 84,
+ SYS_timerfd_create = 85,
+ SYS_timerfd_settime = 86,
+ SYS_timerfd_gettime = 87,
+ SYS_utimensat = 88,
+ SYS_acct = 89,
+ SYS_capget = 90,
+ SYS_capset = 91,
+ SYS_personality = 92,
+ SYS_exit = 93,
+ SYS_exit_group = 94,
+ SYS_waitid = 95,
+ SYS_set_tid_address = 96,
+ SYS_unshare = 97,
+ SYS_futex = 98,
+ SYS_set_robust_list = 99,
+ SYS_get_robust_list = 100,
+ SYS_nanosleep = 101,
+ SYS_getitimer = 102,
+ SYS_setitimer = 103,
+ SYS_kexec_load = 104,
+ SYS_init_module = 105,
+ SYS_delete_module = 106,
+ SYS_timer_create = 107,
+ SYS_timer_gettime = 108,
+ SYS_timer_getoverrun = 109,
+ SYS_timer_settime = 110,
+ SYS_timer_delete = 111,
+ SYS_clock_settime = 112,
+ SYS_clock_gettime = 113,
+ SYS_clock_getres = 114,
+ SYS_clock_nanosleep = 115,
+ SYS_syslog = 116,
+ SYS_ptrace = 117,
+ SYS_sched_setparam = 118,
+ SYS_sched_setscheduler = 119,
+ SYS_sched_getscheduler = 120,
+ SYS_sched_getparam = 121,
+ SYS_sched_setaffinity = 122,
+ SYS_sched_getaffinity = 123,
+ SYS_sched_yield = 124,
+ SYS_sched_get_priority_max = 125,
+ SYS_sched_get_priority_min = 126,
+ SYS_sched_rr_get_interval = 127,
+ SYS_restart_syscall = 128,
+ SYS_kill = 129,
+ SYS_tkill = 130,
+ SYS_tgkill = 131,
+ SYS_sigaltstack = 132,
+ SYS_rt_sigsuspend = 133,
+ SYS_rt_sigaction = 134,
+ SYS_rt_sigprocmask = 135,
+ SYS_rt_sigpending = 136,
+ SYS_rt_sigtimedwait = 137,
+ SYS_rt_sigqueueinfo = 138,
+ SYS_rt_sigreturn = 139,
+ SYS_setpriority = 140,
+ SYS_getpriority = 141,
+ SYS_reboot = 142,
+ SYS_setregid = 143,
+ SYS_setgid = 144,
+ SYS_setreuid = 145,
+ SYS_setuid = 146,
+ SYS_setresuid = 147,
+ SYS_getresuid = 148,
+ SYS_setresgid = 149,
+ SYS_getresgid = 150,
+ SYS_setfsuid = 151,
+ SYS_setfsgid = 152,
+ SYS_times = 153,
+ SYS_setpgid = 154,
+ SYS_getpgid = 155,
+ SYS_getsid = 156,
+ SYS_setsid = 157,
+ SYS_getgroups = 158,
+ SYS_setgroups = 159,
+ SYS_uname = 160,
+ SYS_sethostname = 161,
+ SYS_setdomainname = 162,
+ SYS_getrlimit = 163,
+ SYS_setrlimit = 164,
+ SYS_getrusage = 165,
+ SYS_umask = 166,
+ SYS_prctl = 167,
+ SYS_getcpu = 168,
+ SYS_gettimeofday = 169,
+ SYS_settimeofday = 170,
+ SYS_adjtimex = 171,
+ SYS_getpid = 172,
+ SYS_getppid = 173,
+ SYS_getuid = 174,
+ SYS_geteuid = 175,
+ SYS_getgid = 176,
+ SYS_getegid = 177,
+ SYS_gettid = 178,
+ SYS_sysinfo = 179,
+ SYS_mq_open = 180,
+ SYS_mq_unlink = 181,
+ SYS_mq_timedsend = 182,
+ SYS_mq_timedreceive = 183,
+ SYS_mq_notify = 184,
+ SYS_mq_getsetattr = 185,
+ SYS_msgget = 186,
+ SYS_msgctl = 187,
+ SYS_msgrcv = 188,
+ SYS_msgsnd = 189,
+ SYS_semget = 190,
+ SYS_semctl = 191,
+ SYS_semtimedop = 192,
+ SYS_semop = 193,
+ SYS_shmget = 194,
+ SYS_shmctl = 195,
+ SYS_shmat = 196,
+ SYS_shmdt = 197,
+ SYS_socket = 198,
+ SYS_socketpair = 199,
+ SYS_bind = 200,
+ SYS_listen = 201,
+ SYS_accept = 202,
+ SYS_connect = 203,
+ SYS_getsockname = 204,
+ SYS_getpeername = 205,
+ SYS_sendto = 206,
+ SYS_recvfrom = 207,
+ SYS_setsockopt = 208,
+ SYS_getsockopt = 209,
+ SYS_shutdown = 210,
+ SYS_sendmsg = 211,
+ SYS_recvmsg = 212,
+ SYS_readahead = 213,
+ SYS_brk = 214,
+ SYS_munmap = 215,
+ SYS_mremap = 216,
+ SYS_add_key = 217,
+ SYS_request_key = 218,
+ SYS_keyctl = 219,
+ SYS_clone = 220,
+ SYS_execve = 221,
+ SYS_swapon = 224,
+ SYS_swapoff = 225,
+ SYS_mprotect = 226,
+ SYS_msync = 227,
+ SYS_mlock = 228,
+ SYS_munlock = 229,
+ SYS_mlockall = 230,
+ SYS_munlockall = 231,
+ SYS_mincore = 232,
+ SYS_madvise = 233,
+ SYS_remap_file_pages = 234,
+ SYS_mbind = 235,
+ SYS_get_mempolicy = 236,
+ SYS_set_mempolicy = 237,
+ SYS_migrate_pages = 238,
+ SYS_move_pages = 239,
+ SYS_rt_tgsigqueueinfo = 240,
+ SYS_perf_event_open = 241,
+ SYS_accept4 = 242,
+ SYS_recvmmsg = 243,
+ SYS_arch_specific_syscall = 244,
+ SYS_wait4 = 260,
+ SYS_prlimit64 = 261,
+ SYS_fanotify_init = 262,
+ SYS_fanotify_mark = 263,
+ SYS_name_to_handle_at = 264,
+ SYS_open_by_handle_at = 265,
+ SYS_clock_adjtime = 266,
+ SYS_syncfs = 267,
+ SYS_setns = 268,
+ SYS_sendmmsg = 269,
+ SYS_process_vm_readv = 270,
+ SYS_process_vm_writev = 271,
+ SYS_kcmp = 272,
+ SYS_finit_module = 273,
+ SYS_sched_setattr = 274,
+ SYS_sched_getattr = 275,
+ SYS_renameat2 = 276,
+ SYS_seccomp = 277,
+ SYS_getrandom = 278,
+ SYS_memfd_create = 279,
+ SYS_bpf = 280,
+ SYS_execveat = 281,
+ SYS_userfaultfd = 282,
+ SYS_membarrier = 283,
+ SYS_mlock2 = 284,
+ SYS_copy_file_range = 285,
+ SYS_preadv2 = 286,
+ SYS_pwritev2 = 287,
+ SYS_pkey_mprotect = 288,
+ SYS_pkey_alloc = 289,
+ SYS_pkey_free = 290,
+ SYS_syscalls = 291,
+ SYS_open = 1024,
+ SYS_link = 1025,
+ SYS_unlink = 1026,
+ SYS_mknod = 1027,
+ SYS_chmod = 1028,
+ SYS_chown = 1029,
+ SYS_mkdir = 1030,
+ SYS_rmdir = 1031,
+ SYS_lchown = 1032,
+ SYS_access = 1033,
+ SYS_rename = 1034,
+ SYS_readlink = 1035,
+ SYS_symlink = 1036,
+ SYS_utimes = 1037,
+ SYS_pipe = 1040,
+ SYS_dup2 = 1041,
+ SYS_epoll_create = 1042,
+ SYS_inotify_init = 1043,
+ SYS_eventfd = 1044,
+ SYS_signalfd = 1045,
+ SYS_sendfile = 1046,
+ SYS_ftruncate = 1047,
+ SYS_truncate = 1048,
+ SYS_stat = 1049,
+ SYS_lstat = 1050,
+ SYS_fstat = 1051,
+ SYS_fcntl = 1052,
+ SYS_fadvise64 = 1053,
+ SYS_newfstatat = 1054,
+ SYS_fstatfs = 1055,
+ SYS_statfs = 1056,
+ SYS_lseek = 1057,
+ SYS_mmap = 1058,
+ SYS_alarm = 1059,
+ SYS_getpgrp = 1060,
+ SYS_pause = 1061,
+ SYS_time = 1062,
+ SYS_utime = 1063,
+ SYS_creat = 1064,
+ SYS_getdents = 1065,
+ SYS_futimesat = 1066,
+ SYS_select = 1067,
+ SYS_poll = 1068,
+ SYS_epoll_wait = 1069,
+ SYS_ustat = 1070,
+ SYS_vfork = 1071,
+ SYS_oldwait4 = 1072,
+ SYS_recv = 1073,
+ SYS_send = 1074,
+ SYS_bdflush = 1075,
+ SYS_umount = 1076,
+ SYS_uselib = 1077,
+ SYS__sysctl = 1078,
+ SYS_fork = 1079,
+}
diff --git a/syscall_defines/src/linux-arm/mod.rs b/syscall_defines/src/linux-arm/mod.rs
new file mode 100644
index 0000000..fbaf4a2
--- /dev/null
+++ b/syscall_defines/src/linux-arm/mod.rs
@@ -0,0 +1,364 @@
+// Copyright 2017 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.
+
+// Based on /usr/include/asm/unistd.h from the sysroot of an arm32 board.
+#[allow(dead_code)]
+#[allow(non_camel_case_types)]
+pub enum LinuxSyscall {
+ SYS_restart_syscall = 0,
+ SYS_exit = 1,
+ SYS_fork = 2,
+ SYS_read = 3,
+ SYS_write = 4,
+ SYS_open = 5,
+ SYS_close = 6,
+ SYS_creat = 8,
+ SYS_link = 9,
+ SYS_unlink = 10,
+ SYS_execve = 11,
+ SYS_chdir = 12,
+ SYS_time = 13,
+ SYS_mknod = 14,
+ SYS_chmod = 15,
+ SYS_lchown = 16,
+ SYS_lseek = 19,
+ SYS_getpid = 20,
+ SYS_mount = 21,
+ SYS_umount = 22,
+ SYS_setuid = 23,
+ SYS_getuid = 24,
+ SYS_stime = 25,
+ SYS_ptrace = 26,
+ SYS_alarm = 27,
+ SYS_pause = 29,
+ SYS_utime = 30,
+ SYS_access = 33,
+ SYS_nice = 34,
+ SYS_sync = 36,
+ SYS_kill = 37,
+ SYS_rename = 38,
+ SYS_mkdir = 39,
+ SYS_rmdir = 40,
+ SYS_dup = 41,
+ SYS_pipe = 42,
+ SYS_times = 43,
+ SYS_brk = 45,
+ SYS_setgid = 46,
+ SYS_getgid = 47,
+ SYS_geteuid = 49,
+ SYS_getegid = 50,
+ SYS_acct = 51,
+ SYS_umount2 = 52,
+ SYS_ioctl = 54,
+ SYS_fcntl = 55,
+ SYS_setpgid = 57,
+ SYS_umask = 60,
+ SYS_chroot = 61,
+ SYS_ustat = 62,
+ SYS_dup2 = 63,
+ SYS_getppid = 64,
+ SYS_getpgrp = 65,
+ SYS_setsid = 66,
+ SYS_sigaction = 67,
+ SYS_setreuid = 70,
+ SYS_setregid = 71,
+ SYS_sigsuspend = 72,
+ SYS_sigpending = 73,
+ SYS_sethostname = 74,
+ SYS_setrlimit = 75,
+ SYS_getrlimit = 76,
+ SYS_getrusage = 77,
+ SYS_gettimeofday = 78,
+ SYS_settimeofday = 79,
+ SYS_getgroups = 80,
+ SYS_setgroups = 81,
+ SYS_select = 82,
+ SYS_symlink = 83,
+ SYS_readlink = 85,
+ SYS_uselib = 86,
+ SYS_swapon = 87,
+ SYS_reboot = 88,
+ SYS_readdir = 89,
+ SYS_mmap = 90,
+ SYS_munmap = 91,
+ SYS_truncate = 92,
+ SYS_ftruncate = 93,
+ SYS_fchmod = 94,
+ SYS_fchown = 95,
+ SYS_getpriority = 96,
+ SYS_setpriority = 97,
+ SYS_statfs = 99,
+ SYS_fstatfs = 100,
+ SYS_socketcall = 102,
+ SYS_syslog = 103,
+ SYS_setitimer = 104,
+ SYS_getitimer = 105,
+ SYS_stat = 106,
+ SYS_lstat = 107,
+ SYS_fstat = 108,
+ SYS_vhangup = 111,
+ SYS_syscall = 113,
+ SYS_wait4 = 114,
+ SYS_swapoff = 115,
+ SYS_sysinfo = 116,
+ SYS_ipc = 117,
+ SYS_fsync = 118,
+ SYS_sigreturn = 119,
+ SYS_clone = 120,
+ SYS_setdomainname = 121,
+ SYS_uname = 122,
+ SYS_adjtimex = 124,
+ SYS_mprotect = 125,
+ SYS_sigprocmask = 126,
+ SYS_init_module = 128,
+ SYS_delete_module = 129,
+ SYS_quotactl = 131,
+ SYS_getpgid = 132,
+ SYS_fchdir = 133,
+ SYS_bdflush = 134,
+ SYS_sysfs = 135,
+ SYS_personality = 136,
+ SYS_setfsuid = 138,
+ SYS_setfsgid = 139,
+ SYS__llseek = 140,
+ SYS_getdents = 141,
+ SYS__newselect = 142,
+ SYS_flock = 143,
+ SYS_msync = 144,
+ SYS_readv = 145,
+ SYS_writev = 146,
+ SYS_getsid = 147,
+ SYS_fdatasync = 148,
+ SYS__sysctl = 149,
+ SYS_mlock = 150,
+ SYS_munlock = 151,
+ SYS_mlockall = 152,
+ SYS_munlockall = 153,
+ SYS_sched_setparam = 154,
+ SYS_sched_getparam = 155,
+ SYS_sched_setscheduler = 156,
+ SYS_sched_getscheduler = 157,
+ SYS_sched_yield = 158,
+ SYS_sched_get_priority_max = 159,
+ SYS_sched_get_priority_min = 160,
+ SYS_sched_rr_get_interval = 161,
+ SYS_nanosleep = 162,
+ SYS_mremap = 163,
+ SYS_setresuid = 164,
+ SYS_getresuid = 165,
+ SYS_poll = 168,
+ SYS_nfsservctl = 169,
+ SYS_setresgid = 170,
+ SYS_getresgid = 171,
+ SYS_prctl = 172,
+ SYS_rt_sigreturn = 173,
+ SYS_rt_sigaction = 174,
+ SYS_rt_sigprocmask = 175,
+ SYS_rt_sigpending = 176,
+ SYS_rt_sigtimedwait = 177,
+ SYS_rt_sigqueueinfo = 178,
+ SYS_rt_sigsuspend = 179,
+ SYS_pread64 = 180,
+ SYS_pwrite64 = 181,
+ SYS_chown = 182,
+ SYS_getcwd = 183,
+ SYS_capget = 184,
+ SYS_capset = 185,
+ SYS_sigaltstack = 186,
+ SYS_sendfile = 187,
+ SYS_vfork = 190,
+ SYS_ugetrlimit = 191,
+ SYS_mmap2 = 192,
+ SYS_truncate64 = 193,
+ SYS_ftruncate64 = 194,
+ SYS_stat64 = 195,
+ SYS_lstat64 = 196,
+ SYS_fstat64 = 197,
+ SYS_lchown32 = 198,
+ SYS_getuid32 = 199,
+ SYS_getgid32 = 200,
+ SYS_geteuid32 = 201,
+ SYS_getegid32 = 202,
+ SYS_setreuid32 = 203,
+ SYS_setregid32 = 204,
+ SYS_getgroups32 = 205,
+ SYS_setgroups32 = 206,
+ SYS_fchown32 = 207,
+ SYS_setresuid32 = 208,
+ SYS_getresuid32 = 209,
+ SYS_setresgid32 = 210,
+ SYS_getresgid32 = 211,
+ SYS_chown32 = 212,
+ SYS_setuid32 = 213,
+ SYS_setgid32 = 214,
+ SYS_setfsuid32 = 215,
+ SYS_setfsgid32 = 216,
+ SYS_getdents64 = 217,
+ SYS_pivot_root = 218,
+ SYS_mincore = 219,
+ SYS_madvise = 220,
+ SYS_fcntl64 = 221,
+ SYS_gettid = 224,
+ SYS_readahead = 225,
+ SYS_setxattr = 226,
+ SYS_lsetxattr = 227,
+ SYS_fsetxattr = 228,
+ SYS_getxattr = 229,
+ SYS_lgetxattr = 230,
+ SYS_fgetxattr = 231,
+ SYS_listxattr = 232,
+ SYS_llistxattr = 233,
+ SYS_flistxattr = 234,
+ SYS_removexattr = 235,
+ SYS_lremovexattr = 236,
+ SYS_fremovexattr = 237,
+ SYS_tkill = 238,
+ SYS_sendfile64 = 239,
+ SYS_futex = 240,
+ SYS_sched_setaffinity = 241,
+ SYS_sched_getaffinity = 242,
+ SYS_io_setup = 243,
+ SYS_io_destroy = 244,
+ SYS_io_getevents = 245,
+ SYS_io_submit = 246,
+ SYS_io_cancel = 247,
+ SYS_exit_group = 248,
+ SYS_lookup_dcookie = 249,
+ SYS_epoll_create = 250,
+ SYS_epoll_ctl = 251,
+ SYS_epoll_wait = 252,
+ SYS_remap_file_pages = 253,
+ SYS_set_tid_address = 256,
+ SYS_timer_create = 257,
+ SYS_timer_settime = 258,
+ SYS_timer_gettime = 259,
+ SYS_timer_getoverrun = 260,
+ SYS_timer_delete = 261,
+ SYS_clock_settime = 262,
+ SYS_clock_gettime = 263,
+ SYS_clock_getres = 264,
+ SYS_clock_nanosleep = 265,
+ SYS_statfs64 = 266,
+ SYS_fstatfs64 = 267,
+ SYS_tgkill = 268,
+ SYS_utimes = 269,
+ SYS_arm_fadvise64_64 = 270,
+ SYS_pciconfig_iobase = 271,
+ SYS_pciconfig_read = 272,
+ SYS_pciconfig_write = 273,
+ SYS_mq_open = 274,
+ SYS_mq_unlink = 275,
+ SYS_mq_timedsend = 276,
+ SYS_mq_timedreceive = 277,
+ SYS_mq_notify = 278,
+ SYS_mq_getsetattr = 279,
+ SYS_waitid = 280,
+ SYS_socket = 281,
+ SYS_bind = 282,
+ SYS_connect = 283,
+ SYS_listen = 284,
+ SYS_accept = 285,
+ SYS_getsockname = 286,
+ SYS_getpeername = 287,
+ SYS_socketpair = 288,
+ SYS_send = 289,
+ SYS_sendto = 290,
+ SYS_recv = 291,
+ SYS_recvfrom = 292,
+ SYS_shutdown = 293,
+ SYS_setsockopt = 294,
+ SYS_getsockopt = 295,
+ SYS_sendmsg = 296,
+ SYS_recvmsg = 297,
+ SYS_semop = 298,
+ SYS_semget = 299,
+ SYS_semctl = 300,
+ SYS_msgsnd = 301,
+ SYS_msgrcv = 302,
+ SYS_msgget = 303,
+ SYS_msgctl = 304,
+ SYS_shmat = 305,
+ SYS_shmdt = 306,
+ SYS_shmget = 307,
+ SYS_shmctl = 308,
+ SYS_add_key = 309,
+ SYS_request_key = 310,
+ SYS_keyctl = 311,
+ SYS_semtimedop = 312,
+ SYS_vserver = 313,
+ SYS_ioprio_set = 314,
+ SYS_ioprio_get = 315,
+ SYS_inotify_init = 316,
+ SYS_inotify_add_watch = 317,
+ SYS_inotify_rm_watch = 318,
+ SYS_mbind = 319,
+ SYS_get_mempolicy = 320,
+ SYS_set_mempolicy = 321,
+ SYS_openat = 322,
+ SYS_mkdirat = 323,
+ SYS_mknodat = 324,
+ SYS_fchownat = 325,
+ SYS_futimesat = 326,
+ SYS_fstatat64 = 327,
+ SYS_unlinkat = 328,
+ SYS_renameat = 329,
+ SYS_linkat = 330,
+ SYS_symlinkat = 331,
+ SYS_readlinkat = 332,
+ SYS_fchmodat = 333,
+ SYS_faccessat = 334,
+ SYS_pselect6 = 335,
+ SYS_ppoll = 336,
+ SYS_unshare = 337,
+ SYS_set_robust_list = 338,
+ SYS_get_robust_list = 339,
+ SYS_splice = 340,
+ SYS_arm_sync_file_range = 341,
+ SYS_tee = 342,
+ SYS_vmsplice = 343,
+ SYS_move_pages = 344,
+ SYS_getcpu = 345,
+ SYS_epoll_pwait = 346,
+ SYS_kexec_load = 347,
+ SYS_utimensat = 348,
+ SYS_signalfd = 349,
+ SYS_timerfd_create = 350,
+ SYS_eventfd = 351,
+ SYS_fallocate = 352,
+ SYS_timerfd_settime = 353,
+ SYS_timerfd_gettime = 354,
+ SYS_signalfd4 = 355,
+ SYS_eventfd2 = 356,
+ SYS_epoll_create1 = 357,
+ SYS_dup3 = 358,
+ SYS_pipe2 = 359,
+ SYS_inotify_init1 = 360,
+ SYS_preadv = 361,
+ SYS_pwritev = 362,
+ SYS_rt_tgsigqueueinfo = 363,
+ SYS_perf_event_open = 364,
+ SYS_recvmmsg = 365,
+ SYS_accept4 = 366,
+ SYS_fanotify_init = 367,
+ SYS_fanotify_mark = 368,
+ SYS_prlimit64 = 369,
+ SYS_name_to_handle_at = 370,
+ SYS_open_by_handle_at = 371,
+ SYS_clock_adjtime = 372,
+ SYS_syncfs = 373,
+ SYS_sendmmsg = 374,
+ SYS_setns = 375,
+ SYS_process_vm_readv = 376,
+ SYS_process_vm_writev = 377,
+ SYS_kcmp = 378,
+ SYS_finit_module = 379,
+ SYS_sched_setattr = 380,
+ SYS_sched_getattr = 381,
+ SYS_renameat2 = 382,
+ SYS_seccomp = 383,
+ SYS_getrandom = 384,
+ SYS_memfd_create = 385,
+ SYS_bpf = 386,
+ SYS_execveat = 387,
+}
diff --git a/syscall_defines/src/linux-x86/mod.rs b/syscall_defines/src/linux-x86/mod.rs
new file mode 100644
index 0000000..c6a3302
--- /dev/null
+++ b/syscall_defines/src/linux-x86/mod.rs
@@ -0,0 +1,384 @@
+// Copyright 2017 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.
+
+// Generated with: cat arch/x86/entry/syscalls/syscall_32.tbl |
+// awk ' { print "SYS_" $3 " = " $1"," } '
+#[allow(dead_code)]
+#[allow(non_camel_case_types)]
+pub enum LinuxSyscall {
+ SYS_restart_syscall = 0,
+ SYS_exit = 1,
+ SYS_fork = 2,
+ SYS_read = 3,
+ SYS_write = 4,
+ SYS_open = 5,
+ SYS_close = 6,
+ SYS_waitpid = 7,
+ SYS_creat = 8,
+ SYS_link = 9,
+ SYS_unlink = 10,
+ SYS_execve = 11,
+ SYS_chdir = 12,
+ SYS_time = 13,
+ SYS_mknod = 14,
+ SYS_chmod = 15,
+ SYS_lchown = 16,
+ SYS_break = 17,
+ SYS_oldstat = 18,
+ SYS_lseek = 19,
+ SYS_getpid = 20,
+ SYS_mount = 21,
+ SYS_umount = 22,
+ SYS_setuid = 23,
+ SYS_getuid = 24,
+ SYS_stime = 25,
+ SYS_ptrace = 26,
+ SYS_alarm = 27,
+ SYS_oldfstat = 28,
+ SYS_pause = 29,
+ SYS_utime = 30,
+ SYS_stty = 31,
+ SYS_gtty = 32,
+ SYS_access = 33,
+ SYS_nice = 34,
+ SYS_ftime = 35,
+ SYS_sync = 36,
+ SYS_kill = 37,
+ SYS_rename = 38,
+ SYS_mkdir = 39,
+ SYS_rmdir = 40,
+ SYS_dup = 41,
+ SYS_pipe = 42,
+ SYS_times = 43,
+ SYS_prof = 44,
+ SYS_brk = 45,
+ SYS_setgid = 46,
+ SYS_getgid = 47,
+ SYS_signal = 48,
+ SYS_geteuid = 49,
+ SYS_getegid = 50,
+ SYS_acct = 51,
+ SYS_umount2 = 52,
+ SYS_lock = 53,
+ SYS_ioctl = 54,
+ SYS_fcntl = 55,
+ SYS_mpx = 56,
+ SYS_setpgid = 57,
+ SYS_ulimit = 58,
+ SYS_oldolduname = 59,
+ SYS_umask = 60,
+ SYS_chroot = 61,
+ SYS_ustat = 62,
+ SYS_dup2 = 63,
+ SYS_getppid = 64,
+ SYS_getpgrp = 65,
+ SYS_setsid = 66,
+ SYS_sigaction = 67,
+ SYS_sgetmask = 68,
+ SYS_ssetmask = 69,
+ SYS_setreuid = 70,
+ SYS_setregid = 71,
+ SYS_sigsuspend = 72,
+ SYS_sigpending = 73,
+ SYS_sethostname = 74,
+ SYS_setrlimit = 75,
+ SYS_getrlimit = 76,
+ SYS_getrusage = 77,
+ SYS_gettimeofday = 78,
+ SYS_settimeofday = 79,
+ SYS_getgroups = 80,
+ SYS_setgroups = 81,
+ SYS_select = 82,
+ SYS_symlink = 83,
+ SYS_oldlstat = 84,
+ SYS_readlink = 85,
+ SYS_uselib = 86,
+ SYS_swapon = 87,
+ SYS_reboot = 88,
+ SYS_readdir = 89,
+ SYS_mmap = 90,
+ SYS_munmap = 91,
+ SYS_truncate = 92,
+ SYS_ftruncate = 93,
+ SYS_fchmod = 94,
+ SYS_fchown = 95,
+ SYS_getpriority = 96,
+ SYS_setpriority = 97,
+ SYS_profil = 98,
+ SYS_statfs = 99,
+ SYS_fstatfs = 100,
+ SYS_ioperm = 101,
+ SYS_socketcall = 102,
+ SYS_syslog = 103,
+ SYS_setitimer = 104,
+ SYS_getitimer = 105,
+ SYS_stat = 106,
+ SYS_lstat = 107,
+ SYS_fstat = 108,
+ SYS_olduname = 109,
+ SYS_iopl = 110,
+ SYS_vhangup = 111,
+ SYS_idle = 112,
+ SYS_vm86old = 113,
+ SYS_wait4 = 114,
+ SYS_swapoff = 115,
+ SYS_sysinfo = 116,
+ SYS_ipc = 117,
+ SYS_fsync = 118,
+ SYS_sigreturn = 119,
+ SYS_clone = 120,
+ SYS_setdomainname = 121,
+ SYS_uname = 122,
+ SYS_modify_ldt = 123,
+ SYS_adjtimex = 124,
+ SYS_mprotect = 125,
+ SYS_sigprocmask = 126,
+ SYS_create_module = 127,
+ SYS_init_module = 128,
+ SYS_delete_module = 129,
+ SYS_get_kernel_syms = 130,
+ SYS_quotactl = 131,
+ SYS_getpgid = 132,
+ SYS_fchdir = 133,
+ SYS_bdflush = 134,
+ SYS_sysfs = 135,
+ SYS_personality = 136,
+ SYS_afs_syscall = 137,
+ SYS_setfsuid = 138,
+ SYS_setfsgid = 139,
+ SYS__llseek = 140,
+ SYS_getdents = 141,
+ SYS__newselect = 142,
+ SYS_flock = 143,
+ SYS_msync = 144,
+ SYS_readv = 145,
+ SYS_writev = 146,
+ SYS_getsid = 147,
+ SYS_fdatasync = 148,
+ SYS__sysctl = 149,
+ SYS_mlock = 150,
+ SYS_munlock = 151,
+ SYS_mlockall = 152,
+ SYS_munlockall = 153,
+ SYS_sched_setparam = 154,
+ SYS_sched_getparam = 155,
+ SYS_sched_setscheduler = 156,
+ SYS_sched_getscheduler = 157,
+ SYS_sched_yield = 158,
+ SYS_sched_get_priority_max = 159,
+ SYS_sched_get_priority_min = 160,
+ SYS_sched_rr_get_interval = 161,
+ SYS_nanosleep = 162,
+ SYS_mremap = 163,
+ SYS_setresuid = 164,
+ SYS_getresuid = 165,
+ SYS_vm86 = 166,
+ SYS_query_module = 167,
+ SYS_poll = 168,
+ SYS_nfsservctl = 169,
+ SYS_setresgid = 170,
+ SYS_getresgid = 171,
+ SYS_prctl = 172,
+ SYS_rt_sigreturn = 173,
+ SYS_rt_sigaction = 174,
+ SYS_rt_sigprocmask = 175,
+ SYS_rt_sigpending = 176,
+ SYS_rt_sigtimedwait = 177,
+ SYS_rt_sigqueueinfo = 178,
+ SYS_rt_sigsuspend = 179,
+ SYS_pread64 = 180,
+ SYS_pwrite64 = 181,
+ SYS_chown = 182,
+ SYS_getcwd = 183,
+ SYS_capget = 184,
+ SYS_capset = 185,
+ SYS_sigaltstack = 186,
+ SYS_sendfile = 187,
+ SYS_getpmsg = 188,
+ SYS_putpmsg = 189,
+ SYS_vfork = 190,
+ SYS_ugetrlimit = 191,
+ SYS_mmap2 = 192,
+ SYS_truncate64 = 193,
+ SYS_ftruncate64 = 194,
+ SYS_stat64 = 195,
+ SYS_lstat64 = 196,
+ SYS_fstat64 = 197,
+ SYS_lchown32 = 198,
+ SYS_getuid32 = 199,
+ SYS_getgid32 = 200,
+ SYS_geteuid32 = 201,
+ SYS_getegid32 = 202,
+ SYS_setreuid32 = 203,
+ SYS_setregid32 = 204,
+ SYS_getgroups32 = 205,
+ SYS_setgroups32 = 206,
+ SYS_fchown32 = 207,
+ SYS_setresuid32 = 208,
+ SYS_getresuid32 = 209,
+ SYS_setresgid32 = 210,
+ SYS_getresgid32 = 211,
+ SYS_chown32 = 212,
+ SYS_setuid32 = 213,
+ SYS_setgid32 = 214,
+ SYS_setfsuid32 = 215,
+ SYS_setfsgid32 = 216,
+ SYS_pivot_root = 217,
+ SYS_mincore = 218,
+ SYS_madvise = 219,
+ SYS_getdents64 = 220,
+ SYS_fcntl64 = 221,
+ SYS_gettid = 224,
+ SYS_readahead = 225,
+ SYS_setxattr = 226,
+ SYS_lsetxattr = 227,
+ SYS_fsetxattr = 228,
+ SYS_getxattr = 229,
+ SYS_lgetxattr = 230,
+ SYS_fgetxattr = 231,
+ SYS_listxattr = 232,
+ SYS_llistxattr = 233,
+ SYS_flistxattr = 234,
+ SYS_removexattr = 235,
+ SYS_lremovexattr = 236,
+ SYS_fremovexattr = 237,
+ SYS_tkill = 238,
+ SYS_sendfile64 = 239,
+ SYS_futex = 240,
+ SYS_sched_setaffinity = 241,
+ SYS_sched_getaffinity = 242,
+ SYS_set_thread_area = 243,
+ SYS_get_thread_area = 244,
+ SYS_io_setup = 245,
+ SYS_io_destroy = 246,
+ SYS_io_getevents = 247,
+ SYS_io_submit = 248,
+ SYS_io_cancel = 249,
+ SYS_fadvise64 = 250,
+ SYS_exit_group = 252,
+ SYS_lookup_dcookie = 253,
+ SYS_epoll_create = 254,
+ SYS_epoll_ctl = 255,
+ SYS_epoll_wait = 256,
+ SYS_remap_file_pages = 257,
+ SYS_set_tid_address = 258,
+ SYS_timer_create = 259,
+ SYS_timer_settime = 260,
+ SYS_timer_gettime = 261,
+ SYS_timer_getoverrun = 262,
+ SYS_timer_delete = 263,
+ SYS_clock_settime = 264,
+ SYS_clock_gettime = 265,
+ SYS_clock_getres = 266,
+ SYS_clock_nanosleep = 267,
+ SYS_statfs64 = 268,
+ SYS_fstatfs64 = 269,
+ SYS_tgkill = 270,
+ SYS_utimes = 271,
+ SYS_fadvise64_64 = 272,
+ SYS_vserver = 273,
+ SYS_mbind = 274,
+ SYS_get_mempolicy = 275,
+ SYS_set_mempolicy = 276,
+ SYS_mq_open = 277,
+ SYS_mq_unlink = 278,
+ SYS_mq_timedsend = 279,
+ SYS_mq_timedreceive = 280,
+ SYS_mq_notify = 281,
+ SYS_mq_getsetattr = 282,
+ SYS_kexec_load = 283,
+ SYS_waitid = 284,
+ SYS_sys_setaltroot = 285,
+ SYS_add_key = 286,
+ SYS_request_key = 287,
+ SYS_keyctl = 288,
+ SYS_ioprio_set = 289,
+ SYS_ioprio_get = 290,
+ SYS_inotify_init = 291,
+ SYS_inotify_add_watch = 292,
+ SYS_inotify_rm_watch = 293,
+ SYS_migrate_pages = 294,
+ SYS_openat = 295,
+ SYS_mkdirat = 296,
+ SYS_mknodat = 297,
+ SYS_fchownat = 298,
+ SYS_futimesat = 299,
+ SYS_fstatat64 = 300,
+ SYS_unlinkat = 301,
+ SYS_renameat = 302,
+ SYS_linkat = 303,
+ SYS_symlinkat = 304,
+ SYS_readlinkat = 305,
+ SYS_fchmodat = 306,
+ SYS_faccessat = 307,
+ SYS_pselect6 = 308,
+ SYS_ppoll = 309,
+ SYS_unshare = 310,
+ SYS_set_robust_list = 311,
+ SYS_get_robust_list = 312,
+ SYS_splice = 313,
+ SYS_sync_file_range = 314,
+ SYS_tee = 315,
+ SYS_vmsplice = 316,
+ SYS_move_pages = 317,
+ SYS_getcpu = 318,
+ SYS_epoll_pwait = 319,
+ SYS_utimensat = 320,
+ SYS_signalfd = 321,
+ SYS_timerfd_create = 322,
+ SYS_eventfd = 323,
+ SYS_fallocate = 324,
+ SYS_timerfd_settime = 325,
+ SYS_timerfd_gettime = 326,
+ SYS_signalfd4 = 327,
+ SYS_eventfd2 = 328,
+ SYS_epoll_create1 = 329,
+ SYS_dup3 = 330,
+ SYS_pipe2 = 331,
+ SYS_inotify_init1 = 332,
+ SYS_preadv = 333,
+ SYS_pwritev = 334,
+ SYS_rt_tgsigqueueinfo = 335,
+ SYS_perf_event_open = 336,
+ SYS_recvmmsg = 337,
+ SYS_fanotify_init = 338,
+ SYS_fanotify_mark = 339,
+ SYS_prlimit64 = 340,
+ SYS_name_to_handle_at = 341,
+ SYS_open_by_handle_at = 342,
+ SYS_clock_adjtime = 343,
+ SYS_syncfs = 344,
+ SYS_sendmmsg = 345,
+ SYS_setns = 346,
+ SYS_process_vm_readv = 347,
+ SYS_process_vm_writev = 348,
+ SYS_kcmp = 349,
+ SYS_finit_module = 350,
+ SYS_sched_setattr = 351,
+ SYS_sched_getattr = 352,
+ SYS_renameat2 = 353,
+ SYS_seccomp = 354,
+ SYS_getrandom = 355,
+ SYS_memfd_create = 356,
+ SYS_bpf = 357,
+ SYS_execveat = 358,
+ SYS_socket = 359,
+ SYS_socketpair = 360,
+ SYS_bind = 361,
+ SYS_connect = 362,
+ SYS_listen = 363,
+ SYS_accept4 = 364,
+ SYS_getsockopt = 365,
+ SYS_setsockopt = 366,
+ SYS_getsockname = 367,
+ SYS_getpeername = 368,
+ SYS_sendto = 369,
+ SYS_sendmsg = 370,
+ SYS_recvfrom = 371,
+ SYS_recvmsg = 372,
+ SYS_shutdown = 373,
+ SYS_userfaultfd = 374,
+ SYS_membarrier = 375,
+ SYS_mlock2 = 376,
+}
diff --git a/syscall_defines/src/linux-x86_64/mod.rs b/syscall_defines/src/linux-x86_64/mod.rs
new file mode 100644
index 0000000..f58961a
--- /dev/null
+++ b/syscall_defines/src/linux-x86_64/mod.rs
@@ -0,0 +1,370 @@
+// Copyright 2017 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.
+
+// Generated with: cat arch/x86/entry/syscalls/syscall_64.tbl |
+// awk ' { print "SYS_" $3 " = " $1"," } '
+#[allow(dead_code)]
+#[allow(non_camel_case_types)]
+pub enum LinuxSyscall {
+ SYS_read = 0,
+ SYS_write = 1,
+ SYS_open = 2,
+ SYS_close = 3,
+ SYS_stat = 4,
+ SYS_fstat = 5,
+ SYS_lstat = 6,
+ SYS_poll = 7,
+ SYS_lseek = 8,
+ SYS_mmap = 9,
+ SYS_mprotect = 10,
+ SYS_munmap = 11,
+ SYS_brk = 12,
+ SYS_rt_sigaction = 13,
+ SYS_rt_sigprocmask = 14,
+ SYS_rt_sigreturn = 15,
+ SYS_ioctl = 16,
+ SYS_pread64 = 17,
+ SYS_pwrite64 = 18,
+ SYS_readv = 19,
+ SYS_writev = 20,
+ SYS_access = 21,
+ SYS_pipe = 22,
+ SYS_select = 23,
+ SYS_sched_yield = 24,
+ SYS_mremap = 25,
+ SYS_msync = 26,
+ SYS_mincore = 27,
+ SYS_madvise = 28,
+ SYS_shmget = 29,
+ SYS_shmat = 30,
+ SYS_shmctl = 31,
+ SYS_dup = 32,
+ SYS_dup2 = 33,
+ SYS_pause = 34,
+ SYS_nanosleep = 35,
+ SYS_getitimer = 36,
+ SYS_alarm = 37,
+ SYS_setitimer = 38,
+ SYS_getpid = 39,
+ SYS_sendfile = 40,
+ SYS_socket = 41,
+ SYS_connect = 42,
+ SYS_accept = 43,
+ SYS_sendto = 44,
+ SYS_recvfrom = 45,
+ SYS_sendmsg = 46,
+ SYS_recvmsg = 47,
+ SYS_shutdown = 48,
+ SYS_bind = 49,
+ SYS_listen = 50,
+ SYS_getsockname = 51,
+ SYS_getpeername = 52,
+ SYS_socketpair = 53,
+ SYS_setsockopt = 54,
+ SYS_getsockopt = 55,
+ SYS_clone = 56,
+ SYS_fork = 57,
+ SYS_vfork = 58,
+ SYS_execve = 59,
+ SYS_exit = 60,
+ SYS_wait4 = 61,
+ SYS_kill = 62,
+ SYS_uname = 63,
+ SYS_semget = 64,
+ SYS_semop = 65,
+ SYS_semctl = 66,
+ SYS_shmdt = 67,
+ SYS_msgget = 68,
+ SYS_msgsnd = 69,
+ SYS_msgrcv = 70,
+ SYS_msgctl = 71,
+ SYS_fcntl = 72,
+ SYS_flock = 73,
+ SYS_fsync = 74,
+ SYS_fdatasync = 75,
+ SYS_truncate = 76,
+ SYS_ftruncate = 77,
+ SYS_getdents = 78,
+ SYS_getcwd = 79,
+ SYS_chdir = 80,
+ SYS_fchdir = 81,
+ SYS_rename = 82,
+ SYS_mkdir = 83,
+ SYS_rmdir = 84,
+ SYS_creat = 85,
+ SYS_link = 86,
+ SYS_unlink = 87,
+ SYS_symlink = 88,
+ SYS_readlink = 89,
+ SYS_chmod = 90,
+ SYS_fchmod = 91,
+ SYS_chown = 92,
+ SYS_fchown = 93,
+ SYS_lchown = 94,
+ SYS_umask = 95,
+ SYS_gettimeofday = 96,
+ SYS_getrlimit = 97,
+ SYS_getrusage = 98,
+ SYS_sysinfo = 99,
+ SYS_times = 100,
+ SYS_ptrace = 101,
+ SYS_getuid = 102,
+ SYS_syslog = 103,
+ SYS_getgid = 104,
+ SYS_setuid = 105,
+ SYS_setgid = 106,
+ SYS_geteuid = 107,
+ SYS_getegid = 108,
+ SYS_setpgid = 109,
+ SYS_getppid = 110,
+ SYS_getpgrp = 111,
+ SYS_setsid = 112,
+ SYS_setreuid = 113,
+ SYS_setregid = 114,
+ SYS_getgroups = 115,
+ SYS_setgroups = 116,
+ SYS_setresuid = 117,
+ SYS_getresuid = 118,
+ SYS_setresgid = 119,
+ SYS_getresgid = 120,
+ SYS_getpgid = 121,
+ SYS_setfsuid = 122,
+ SYS_setfsgid = 123,
+ SYS_getsid = 124,
+ SYS_capget = 125,
+ SYS_capset = 126,
+ SYS_rt_sigpending = 127,
+ SYS_rt_sigtimedwait = 128,
+ SYS_rt_sigqueueinfo = 129,
+ SYS_rt_sigsuspend = 130,
+ SYS_sigaltstack = 131,
+ SYS_utime = 132,
+ SYS_mknod = 133,
+ SYS_uselib = 134,
+ SYS_personality = 135,
+ SYS_ustat = 136,
+ SYS_statfs = 137,
+ SYS_fstatfs = 138,
+ SYS_sysfs = 139,
+ SYS_getpriority = 140,
+ SYS_setpriority = 141,
+ SYS_sched_setparam = 142,
+ SYS_sched_getparam = 143,
+ SYS_sched_setscheduler = 144,
+ SYS_sched_getscheduler = 145,
+ SYS_sched_get_priority_max = 146,
+ SYS_sched_get_priority_min = 147,
+ SYS_sched_rr_get_interval = 148,
+ SYS_mlock = 149,
+ SYS_munlock = 150,
+ SYS_mlockall = 151,
+ SYS_munlockall = 152,
+ SYS_vhangup = 153,
+ SYS_modify_ldt = 154,
+ SYS_pivot_root = 155,
+ SYS__sysctl = 156,
+ SYS_prctl = 157,
+ SYS_arch_prctl = 158,
+ SYS_adjtimex = 159,
+ SYS_setrlimit = 160,
+ SYS_chroot = 161,
+ SYS_sync = 162,
+ SYS_acct = 163,
+ SYS_settimeofday = 164,
+ SYS_mount = 165,
+ SYS_umount2 = 166,
+ SYS_swapon = 167,
+ SYS_swapoff = 168,
+ SYS_reboot = 169,
+ SYS_sethostname = 170,
+ SYS_setdomainname = 171,
+ SYS_iopl = 172,
+ SYS_ioperm = 173,
+ SYS_create_module = 174,
+ SYS_init_module = 175,
+ SYS_delete_module = 176,
+ SYS_get_kernel_syms = 177,
+ SYS_query_module = 178,
+ SYS_quotactl = 179,
+ SYS_nfsservctl = 180,
+ SYS_getpmsg = 181,
+ SYS_putpmsg = 182,
+ SYS_afs_syscall = 183,
+ SYS_tuxcall = 184,
+ SYS_security = 185,
+ SYS_gettid = 186,
+ SYS_readahead = 187,
+ SYS_setxattr = 188,
+ SYS_lsetxattr = 189,
+ SYS_fsetxattr = 190,
+ SYS_getxattr = 191,
+ SYS_lgetxattr = 192,
+ SYS_fgetxattr = 193,
+ SYS_listxattr = 194,
+ SYS_llistxattr = 195,
+ SYS_flistxattr = 196,
+ SYS_removexattr = 197,
+ SYS_lremovexattr = 198,
+ SYS_fremovexattr = 199,
+ SYS_tkill = 200,
+ SYS_time = 201,
+ SYS_futex = 202,
+ SYS_sched_setaffinity = 203,
+ SYS_sched_getaffinity = 204,
+ SYS_set_thread_area = 205,
+ SYS_io_setup = 206,
+ SYS_io_destroy = 207,
+ SYS_io_getevents = 208,
+ SYS_io_submit = 209,
+ SYS_io_cancel = 210,
+ SYS_get_thread_area = 211,
+ SYS_lookup_dcookie = 212,
+ SYS_epoll_create = 213,
+ SYS_epoll_ctl_old = 214,
+ SYS_epoll_wait_old = 215,
+ SYS_remap_file_pages = 216,
+ SYS_getdents64 = 217,
+ SYS_set_tid_address = 218,
+ SYS_restart_syscall = 219,
+ SYS_semtimedop = 220,
+ SYS_fadvise64 = 221,
+ SYS_timer_create = 222,
+ SYS_timer_settime = 223,
+ SYS_timer_gettime = 224,
+ SYS_timer_getoverrun = 225,
+ SYS_timer_delete = 226,
+ SYS_clock_settime = 227,
+ SYS_clock_gettime = 228,
+ SYS_clock_getres = 229,
+ SYS_clock_nanosleep = 230,
+ SYS_exit_group = 231,
+ SYS_epoll_wait = 232,
+ SYS_epoll_ctl = 233,
+ SYS_tgkill = 234,
+ SYS_utimes = 235,
+ SYS_vserver = 236,
+ SYS_mbind = 237,
+ SYS_set_mempolicy = 238,
+ SYS_get_mempolicy = 239,
+ SYS_mq_open = 240,
+ SYS_mq_unlink = 241,
+ SYS_mq_timedsend = 242,
+ SYS_mq_timedreceive = 243,
+ SYS_mq_notify = 244,
+ SYS_mq_getsetattr = 245,
+ SYS_kexec_load = 246,
+ SYS_waitid = 247,
+ SYS_add_key = 248,
+ SYS_request_key = 249,
+ SYS_keyctl = 250,
+ SYS_ioprio_set = 251,
+ SYS_ioprio_get = 252,
+ SYS_inotify_init = 253,
+ SYS_inotify_add_watch = 254,
+ SYS_inotify_rm_watch = 255,
+ SYS_migrate_pages = 256,
+ SYS_openat = 257,
+ SYS_mkdirat = 258,
+ SYS_mknodat = 259,
+ SYS_fchownat = 260,
+ SYS_futimesat = 261,
+ SYS_newfstatat = 262,
+ SYS_unlinkat = 263,
+ SYS_renameat = 264,
+ SYS_linkat = 265,
+ SYS_symlinkat = 266,
+ SYS_readlinkat = 267,
+ SYS_fchmodat = 268,
+ SYS_faccessat = 269,
+ SYS_pselect6 = 270,
+ SYS_ppoll = 271,
+ SYS_unshare = 272,
+ SYS_set_robust_list = 273,
+ SYS_get_robust_list = 274,
+ SYS_splice = 275,
+ SYS_tee = 276,
+ SYS_sync_file_range = 277,
+ SYS_vmsplice = 278,
+ SYS_move_pages = 279,
+ SYS_utimensat = 280,
+ SYS_epoll_pwait = 281,
+ SYS_signalfd = 282,
+ SYS_timerfd_create = 283,
+ SYS_eventfd = 284,
+ SYS_fallocate = 285,
+ SYS_timerfd_settime = 286,
+ SYS_timerfd_gettime = 287,
+ SYS_accept4 = 288,
+ SYS_signalfd4 = 289,
+ SYS_eventfd2 = 290,
+ SYS_epoll_create1 = 291,
+ SYS_dup3 = 292,
+ SYS_pipe2 = 293,
+ SYS_inotify_init1 = 294,
+ SYS_preadv = 295,
+ SYS_pwritev = 296,
+ SYS_rt_tgsigqueueinfo = 297,
+ SYS_perf_event_open = 298,
+ SYS_recvmmsg = 299,
+ SYS_fanotify_init = 300,
+ SYS_fanotify_mark = 301,
+ SYS_prlimit64 = 302,
+ SYS_name_to_handle_at = 303,
+ SYS_open_by_handle_at = 304,
+ SYS_clock_adjtime = 305,
+ SYS_syncfs = 306,
+ SYS_sendmmsg = 307,
+ SYS_setns = 308,
+ SYS_getcpu = 309,
+ SYS_process_vm_readv = 310,
+ SYS_process_vm_writev = 311,
+ SYS_kcmp = 312,
+ SYS_finit_module = 313,
+ SYS_sched_setattr = 314,
+ SYS_sched_getattr = 315,
+ SYS_renameat2 = 316,
+ SYS_seccomp = 317,
+ SYS_getrandom = 318,
+ SYS_memfd_create = 319,
+ SYS_kexec_file_load = 320,
+ SYS_bpf = 321,
+ SYS_execveat = 322,
+ SYS_userfaultfd = 323,
+ SYS_membarrier = 324,
+ SYS_mlock2 = 325,
+ compat_SYS_rt_sigaction = 512,
+ compat_SYS_rt_sigreturn = 513,
+ compat_SYS_ioctl = 514,
+ compat_SYS_readv = 515,
+ compat_SYS_writev = 516,
+ compat_SYS_recvfrom = 517,
+ compat_SYS_sendmsg = 518,
+ compat_SYS_recvmsg = 519,
+ compat_SYS_execve = 520,
+ compat_SYS_ptrace = 521,
+ compat_SYS_rt_sigpending = 522,
+ compat_SYS_rt_sigtimedwait = 523,
+ compat_SYS_rt_sigqueueinfo = 524,
+ compat_SYS_sigaltstack = 525,
+ compat_SYS_timer_create = 526,
+ compat_SYS_mq_notify = 527,
+ compat_SYS_kexec_load = 528,
+ compat_SYS_waitid = 529,
+ compat_SYS_set_robust_list = 530,
+ compat_SYS_get_robust_list = 531,
+ compat_SYS_vmsplice = 532,
+ compat_SYS_move_pages = 533,
+ compat_SYS_preadv = 534,
+ compat_SYS_pwritev = 535,
+ compat_SYS_rt_tgsigqueueinfo = 536,
+ compat_SYS_recvmmsg = 537,
+ compat_SYS_sendmmsg = 538,
+ compat_SYS_process_vm_readv = 539,
+ compat_SYS_process_vm_writev = 540,
+ compat_SYS_setsockopt = 541,
+ compat_SYS_getsockopt = 542,
+ compat_SYS_io_setup = 543,
+ compat_SYS_io_submit = 544,
+ compat_SYS_execveat = 545,
+}
diff --git a/tempfile/Cargo.toml b/tempfile/Cargo.toml
new file mode 100644
index 0000000..b12e976
--- /dev/null
+++ b/tempfile/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "tempfile"
+version = "3.0.7"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+rand_ish = { path = "../rand_ish" }
diff --git a/tempfile/src/lib.rs b/tempfile/src/lib.rs
new file mode 100644
index 0000000..14f235b
--- /dev/null
+++ b/tempfile/src/lib.rs
@@ -0,0 +1,75 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Simplified tempfile which doesn't depend on the `rand` crate, instead using
+//! /dev/urandom as a source of entropy
+
+use rand_ish::urandom_str;
+use std::env;
+use std::fs;
+use std::io::{Error, ErrorKind, Result};
+use std::path::{Path, PathBuf};
+
+pub struct Builder {
+ prefix: String,
+}
+
+impl Builder {
+ pub fn new() -> Self {
+ Builder {
+ prefix: ".tmp".to_owned(),
+ }
+ }
+
+ /// Set a custom filename prefix.
+ ///
+ /// Default: `.tmp`
+ pub fn prefix(&mut self, prefix: &str) -> &mut Self {
+ self.prefix = prefix.to_owned();
+ self
+ }
+
+ /// Tries to make a tempdir inside of `env::temp_dir()` with a specified
+ /// prefix. The directory and it's content is destroyed when TempDir is
+ /// dropped.
+ /// If the directory can not be created, `Err` is returned.
+ pub fn tempdir(&self) -> Result<TempDir> {
+ for _ in 0..NUM_RETRIES {
+ let suffix = urandom_str(12)?;
+ let path = env::temp_dir().join(format!("{}.{}", self.prefix, suffix));
+
+ match fs::create_dir(&path) {
+ Ok(_) => return Ok(TempDir { path }),
+ Err(ref e) if e.kind() == ErrorKind::AlreadyExists => {}
+ Err(e) => return Err(e),
+ }
+ }
+
+ Err(Error::new(
+ ErrorKind::AlreadyExists,
+ "too many tempdirs exist",
+ ))
+ }
+}
+
+pub struct TempDir {
+ path: PathBuf,
+}
+
+const NUM_RETRIES: u32 = 4;
+
+impl TempDir {
+ /// Accesses the tempdir's [`Path`].
+ ///
+ /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
+ pub fn path(&self) -> &Path {
+ self.path.as_ref()
+ }
+}
+
+impl Drop for TempDir {
+ fn drop(&mut self) {
+ let _ = fs::remove_dir_all(&self.path);
+ }
+}
diff --git a/tests/mini_plugin_template.c b/tests/mini_plugin_template.c
new file mode 100644
index 0000000..684b473
--- /dev/null
+++ b/tests/mini_plugin_template.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2018 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 <errno.h>
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "crosvm.h"
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#endif
+
+#ifndef F_SEAL_SHRINK
+#define F_SEAL_SHRINK 0x0002
+#endif
+
+#define LOAD_ADDRESS {load_address}
+
+const uint8_t g_assembly_code[] = {{
+ {assembly_code}
+}};
+
+/* These get defined by the code inserted below. */
+int setup_vm(struct crosvm *, void *mem);
+int handle_vpcu_init(struct crosvm_vcpu *, struct kvm_regs *, struct kvm_sregs *);
+int handle_vpcu_evt(struct crosvm_vcpu *, struct crosvm_vcpu_event evt);
+int check_result(struct crosvm *, void *mem);
+{src}
+
+struct vcpu_context {{
+ struct crosvm_vcpu *vcpu;
+}};
+
+void *vcpu_thread(void *arg) {{
+ struct vcpu_context *ctx = arg;
+ struct crosvm_vcpu *vcpu = ctx->vcpu;
+ struct crosvm_vcpu_event evt;
+ int ret;
+ while (crosvm_vcpu_wait(vcpu, &evt) == 0) {{
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_INIT) {{
+ struct kvm_regs regs;
+ crosvm_vcpu_get_regs(vcpu, ®s);
+ regs.rflags = 2;
+ regs.rip = LOAD_ADDRESS;
+
+ struct kvm_sregs sregs;
+ crosvm_vcpu_get_sregs(vcpu, &sregs);
+ sregs.cs.base = 0;
+ sregs.cs.selector = 0;
+
+ handle_vpcu_init(vcpu, ®s, &sregs);
+ crosvm_vcpu_set_regs(vcpu, ®s);
+ crosvm_vcpu_set_sregs(vcpu, &sregs);
+ }} else {{
+ ret = handle_vpcu_evt(vcpu, evt);
+ if (ret)
+ return NULL;
+ }}
+
+ crosvm_vcpu_resume(vcpu);
+ }}
+
+ return NULL;
+}}
+
+int main(int argc, char** argv) {{
+ int i;
+ uint64_t dummy = 1;
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {{
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }}
+
+ int kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ if (kill_evt < 0) {{
+ fprintf(stderr, "failed to get kill eventfd: %d\n", kill_evt);
+ return 1;
+ }}
+
+ int mem_size = {mem_size};
+ int mem_fd = syscall(SYS_memfd_create, "guest_mem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (mem_fd < 0) {{
+ fprintf(stderr, "failed to create guest memfd: %d\n", errno);
+ return 1;
+ }}
+ ret = ftruncate(mem_fd, mem_size);
+ if (ret) {{
+ fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
+ return 1;
+ }}
+ uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0);
+ if (mem == MAP_FAILED) {{
+ fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
+ return 1;
+ }}
+ fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
+ memcpy(mem + LOAD_ADDRESS, g_assembly_code, sizeof(g_assembly_code));
+
+ struct crosvm_memory *mem_obj;
+ ret = crosvm_create_memory(crosvm, mem_fd, 0, mem_size, 0, false, false, &mem_obj);
+ if (ret) {{
+ fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
+ return 1;
+ }}
+
+ ret = setup_vm(crosvm, mem);
+ if (ret)
+ return ret;
+
+ struct crosvm_vcpu *vcpus[32];
+ struct vcpu_context ctxs[32];
+ pthread_t vcpu_threads[32];
+ uint32_t vcpu_count;
+ for (vcpu_count = 0; vcpu_count < 32; vcpu_count++) {{
+ ret = crosvm_get_vcpu(crosvm, vcpu_count, &vcpus[vcpu_count]);
+ if (ret == -ENOENT)
+ break;
+
+ if (ret) {{
+ fprintf(stderr, "error while getting all vcpus: %d\n", ret);
+ return 1;
+ }}
+ ctxs[vcpu_count].vcpu = vcpus[vcpu_count];
+ pthread_create(&vcpu_threads[vcpu_count], NULL, vcpu_thread, &ctxs[vcpu_count]);
+ }}
+
+ ret = crosvm_start(crosvm);
+ if (ret) {{
+ fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
+ return 1;
+ }}
+
+ ret = read(kill_evt, &dummy, sizeof(dummy));
+ if (ret == -1) {{
+ fprintf(stderr, "failed to read kill eventfd: %d\n", errno);
+ return 1;
+ }}
+
+ return check_result(crosvm, mem);
+}}
diff --git a/tests/plugin.policy b/tests/plugin.policy
new file mode 100644
index 0000000..a3ab2f7
--- /dev/null
+++ b/tests/plugin.policy
@@ -0,0 +1,51 @@
+# Copyright 2017 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.
+
+close: 1
+dup: 1
+dup2: 1
+execve: 1
+exit_group: 1
+futex: 1
+lseek: 1
+mprotect: 1
+munmap: 1
+read: 1
+recvfrom: 1
+sched_getaffinity: 1
+set_robust_list: 1
+sigaltstack: 1
+# Disallow clone's other than new threads.
+clone: arg0 & 0x00010000
+write: 1
+eventfd2: 1
+poll: 1
+getpid: 1
+# Allow PR_SET_NAME only.
+prctl: arg0 == 15
+access: 1
+arch_prctl: 1
+brk: 1
+exit: 1
+fcntl: 1
+fstat: 1
+ftruncate: 1
+getcwd: 1
+getrlimit: 1
+# TUNGETFEATURES
+ioctl: arg1 == 0x800454CF
+madvise: 1
+memfd_create: 1
+mmap: 1
+open: 1
+openat: 1
+prlimit64: arg2 == 0 && arg3 != 0
+recvmsg: 1
+restart_syscall: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+sendmsg: 1
+set_tid_address: 1
+stat: 1
+writev: 1
diff --git a/tests/plugin_adder.c b/tests/plugin_adder.c
new file mode 100644
index 0000000..8728614
--- /dev/null
+++ b/tests/plugin_adder.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2017 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 <errno.h>
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "crosvm.h"
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#endif
+
+#ifndef F_SEAL_SHRINK
+#define F_SEAL_SHRINK 0x0002
+#endif
+
+#define SERIAL_ADDRESS 0x3f8
+#define KILL_ADDRESS 0x3f9
+
+char g_serial_out[16];
+int g_kill_evt;
+
+void *vcpu_thread(void *arg) {
+ struct crosvm_vcpu *vcpu = arg;
+ struct crosvm_vcpu_event evt;
+ int i = 0;
+ while (crosvm_vcpu_wait(vcpu, &evt) == 0) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_INIT) {
+ struct kvm_sregs sregs;
+ crosvm_vcpu_get_sregs(vcpu, &sregs);
+ sregs.cs.base = 0;
+ sregs.cs.selector = 0;
+ sregs.es.base = KILL_ADDRESS;
+ sregs.es.selector = 0;
+ crosvm_vcpu_set_sregs(vcpu, &sregs);
+
+ struct kvm_regs regs;
+ crosvm_vcpu_get_regs(vcpu, ®s);
+ regs.rip = 0x1000;
+ regs.rax = 2;
+ regs.rbx = 7;
+ regs.rflags = 2;
+ crosvm_vcpu_set_regs(vcpu, ®s);
+ }
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS) {
+ if (evt.io_access.address_space == CROSVM_ADDRESS_SPACE_IOPORT &&
+ evt.io_access.address == SERIAL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1) {
+ g_serial_out[i] = evt.io_access.data[0];
+ i++;
+ }
+ if (evt.io_access.address_space == CROSVM_ADDRESS_SPACE_IOPORT &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ uint64_t dummy = 1;
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return NULL;
+ }
+ }
+
+ crosvm_vcpu_resume(vcpu);
+ }
+
+ return NULL;
+}
+
+int main(int argc, char** argv) {
+ const uint8_t code[] = {
+ /*
+ 0000 BAF803 mov dx,0x3f8
+ 0003 00D8 add al,bl
+ 0005 0430 add al,0x30
+ 0007 EE out dx,al
+ 0008 B05C mov al,0x0a
+ 000A EE out dx,al
+ 000B BAF903 mov dx,0x3f9
+ 000E B001 mov al,0x1
+ 0010 EE out dx,al
+ 0011 F4 hlt
+ */
+ 0xba, 0xf8, 0x03,
+ 0x00, 0xd8,
+ 0x04, '0',
+ 0xee,
+ 0xb0, '\n',
+ 0xee,
+ 0xba, 0xf9, 0x03,
+ 0xb0, 0x01,
+ 0xee,
+ 0xf4
+ };
+
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ /*
+ * Not strictly necessary, but demonstrates we can have as many connections
+ * as we please.
+ */
+ struct crosvm *extra_crosvm;
+ ret = crosvm_new_connection(crosvm, &extra_crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to make new socket: %d\n", ret);
+ return 1;
+ }
+
+ /* We needs this eventfd to know when to exit before being killed. */
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ if (g_kill_evt < 0) {
+ fprintf(stderr, "failed to get kill eventfd: %d\n", g_kill_evt);
+ return 1;
+ }
+
+ ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, SERIAL_ADDRESS, 1);
+ if (ret) {
+ fprintf(stderr, "failed to reserve ioport range: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, KILL_ADDRESS, 1);
+ if (ret) {
+ fprintf(stderr, "failed to reserve mmio range: %d\n", ret);
+ return 1;
+ }
+
+ int mem_size = 0x2000;
+ int mem_fd = syscall(SYS_memfd_create, "guest_mem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (mem_fd < 0) {
+ fprintf(stderr, "failed to create guest memfd: %d\n", errno);
+ return 1;
+ }
+ ret = ftruncate(mem_fd, mem_size);
+ if (ret) {
+ fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
+ return 1;
+ }
+ uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0x1000);
+ if (mem == MAP_FAILED) {
+ fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
+ return 1;
+ }
+ fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
+ memcpy(mem, code, sizeof(code));
+
+ struct crosvm_memory *mem_obj;
+ ret = crosvm_create_memory(crosvm, mem_fd, 0x1000, 0x1000, 0x1000, false, false, &mem_obj);
+ if (ret) {
+ fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
+ return 1;
+ }
+
+ /* get and creat a thread for each vcpu */
+ struct crosvm_vcpu *vcpus[32];
+ pthread_t vcpu_threads[32];
+ uint32_t vcpu_count;
+ for (vcpu_count = 0; vcpu_count < 32; vcpu_count++) {
+ ret = crosvm_get_vcpu(crosvm, vcpu_count, &vcpus[vcpu_count]);
+ if (ret == -ENOENT)
+ break;
+
+ if (ret) {
+ fprintf(stderr, "error while getting all vcpus: %d\n", ret);
+ return 1;
+ }
+ pthread_create(&vcpu_threads[vcpu_count], NULL, vcpu_thread, vcpus[vcpu_count]);
+ }
+
+ ret = crosvm_start(extra_crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
+ return 1;
+ }
+
+ /* Wait for crosvm to request that we exit otherwise we will be killed. */
+ uint64_t dummy;
+ read(g_kill_evt, &dummy, 8);
+
+ ret = crosvm_destroy_memory(crosvm, &mem_obj);
+ if (ret) {
+ fprintf(stderr, "failed to destroy memory in crosvm: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, SERIAL_ADDRESS, 0);
+ if (ret) {
+ fprintf(stderr, "failed to unreserve ioport range: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, KILL_ADDRESS, 0);
+ if (ret) {
+ fprintf(stderr, "failed to unreserve mmio range: %d\n", ret);
+ return 1;
+ }
+
+ return strcmp(g_serial_out, "9\n");
+}
diff --git a/tests/plugin_dirty_log.c b/tests/plugin_dirty_log.c
new file mode 100644
index 0000000..f266cdf
--- /dev/null
+++ b/tests/plugin_dirty_log.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2017 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 <errno.h>
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "crosvm.h"
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#endif
+
+#ifndef F_SEAL_SHRINK
+#define F_SEAL_SHRINK 0x0002
+#endif
+
+#define LOAD_ADDRESS 0x1000
+#define SI_ADDRESS 0x8000
+#define BL_VALUE 0x12
+#define KILL_ADDRESS 0x9000
+
+int g_kill_evt;
+
+void *vcpu_thread(void *arg) {
+ struct crosvm_vcpu *vcpu = arg;
+ struct crosvm_vcpu_event evt;
+ int i = 0;
+ while (crosvm_vcpu_wait(vcpu, &evt) == 0) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_INIT) {
+ struct kvm_sregs sregs;
+ crosvm_vcpu_get_sregs(vcpu, &sregs);
+ sregs.cs.base = 0;
+ sregs.cs.selector = 0;
+ sregs.es.base = KILL_ADDRESS;
+ sregs.es.selector = 0;
+ crosvm_vcpu_set_sregs(vcpu, &sregs);
+
+ struct kvm_regs regs;
+ crosvm_vcpu_get_regs(vcpu, ®s);
+ regs.rflags = 2;
+ regs.rip = LOAD_ADDRESS;
+ regs.rbx = BL_VALUE;
+ regs.rsi = SI_ADDRESS;
+ crosvm_vcpu_set_regs(vcpu, ®s);
+ }
+
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ uint64_t dummy = 1;
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return NULL;
+ }
+
+ crosvm_vcpu_resume(vcpu);
+ }
+
+ return NULL;
+}
+
+int main(int argc, char** argv) {
+ const uint8_t code[] = {
+ /*
+ 0000 881C mov [si],bl
+ 0014 26C606000001 mov byte [es:0x0],0x1
+ 0002 F4 hlt
+ */
+ 0x88, 0x1c, 0x26, 0xc6, 0x06, 0x00, 0x00, 0x01, 0xf4
+ };
+
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ if (g_kill_evt < 0) {
+ fprintf(stderr, "failed to get kill eventfd: %d\n", g_kill_evt);
+ return 1;
+ }
+
+ ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
+ if (ret) {
+ fprintf(stderr, "failed to reserve mmio range: %d\n", ret);
+ return 1;
+ }
+
+ int mem_size = 0x9000;
+ int mem_fd = syscall(SYS_memfd_create, "guest_mem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (mem_fd < 0) {
+ fprintf(stderr, "failed to create guest memfd: %d\n", errno);
+ return 1;
+ }
+ ret = ftruncate(mem_fd, mem_size);
+ if (ret) {
+ fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
+ return 1;
+ }
+ uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0);
+ if (mem == MAP_FAILED) {
+ fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
+ return 1;
+ }
+ fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
+ memcpy(mem + LOAD_ADDRESS, code, sizeof(code));
+
+ struct crosvm_memory *mem_obj;
+ ret = crosvm_create_memory(crosvm, mem_fd, 0, mem_size, 0, false, true, &mem_obj);
+ if (ret) {
+ fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
+ return 1;
+ }
+
+ struct crosvm_vcpu *vcpus[32];
+ pthread_t vcpu_threads[32];
+ uint32_t vcpu_count;
+ for (vcpu_count = 0; vcpu_count < 32; vcpu_count++) {
+ ret = crosvm_get_vcpu(crosvm, vcpu_count, &vcpus[vcpu_count]);
+ if (ret == -ENOENT)
+ break;
+
+ if (ret) {
+ fprintf(stderr, "error while getting all vcpus: %d\n", ret);
+ return 1;
+ }
+ pthread_create(&vcpu_threads[vcpu_count], NULL, vcpu_thread, vcpus[vcpu_count]);
+ }
+
+ ret = crosvm_start(crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
+ return 1;
+ }
+
+ uint64_t dummy;
+ read(g_kill_evt, &dummy, 8);
+
+ uint8_t dirty_log[2] = {0};
+ ret = crosvm_memory_get_dirty_log(crosvm, mem_obj, dirty_log);
+ if (ret) {
+ fprintf(stderr, "failed to get dirty log: %d\n", ret);
+ return 1;
+ }
+
+ if (dirty_log[1] != 0x1) {
+ fprintf(stderr, "dirty log does not have expected bits: %x\n", dirty_log[1]);
+ return 1;
+ }
+
+ uint64_t val = *(uint64_t*)(&mem[SI_ADDRESS]);
+ if (val != BL_VALUE) {
+ fprintf(stderr, "memory does not have expected value %d\n", val);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugin_extensions.c b/tests/plugin_extensions.c
new file mode 100644
index 0000000..4e3ea87
--- /dev/null
+++ b/tests/plugin_extensions.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018 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 <errno.h>
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "crosvm.h"
+
+int main(int argc, char** argv) {
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ bool supported;
+ ret = crosvm_check_extension(crosvm, KVM_CAP_IRQCHIP, &supported);
+ if (ret) {
+ fprintf(stderr, "failed to check for KVM extension: %d\n", ret);
+ return 1;
+ }
+ if (!supported) {
+ fprintf(stderr, "expected KVM extension to be supported\n");
+ return 1;
+ }
+
+ // Assume s390 extensions aren't supported because we shouldn't be running on one.
+ ret = crosvm_check_extension(crosvm, KVM_CAP_S390_PSW, &supported);
+ if (ret) {
+ fprintf(stderr, "failed to check for KVM extension: %d\n", ret);
+ return 1;
+ }
+ if (supported) {
+ fprintf(stderr, "unexpected KVM extension is supported\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugin_ioevent.c b/tests/plugin_ioevent.c
new file mode 100644
index 0000000..2a0eca9
--- /dev/null
+++ b/tests/plugin_ioevent.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2017 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 <errno.h>
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "crosvm.h"
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#endif
+
+#ifndef F_SEAL_SHRINK
+#define F_SEAL_SHRINK 0x0002
+#endif
+
+#define LOAD_ADDRESS 0x1000
+#define DATAMATCH_VAL 0x88
+#define KILL_ADDRESS 0x4000
+
+int g_kill_evt;
+
+void *vcpu_thread(void *arg) {
+ struct crosvm_vcpu *vcpu = arg;
+ struct crosvm_vcpu_event evt;
+ int i = 0;
+ while (crosvm_vcpu_wait(vcpu, &evt) == 0) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_INIT) {
+ struct kvm_sregs sregs;
+ crosvm_vcpu_get_sregs(vcpu, &sregs);
+ sregs.cs.base = 0;
+ sregs.cs.selector = 0;
+ sregs.es.base = KILL_ADDRESS;
+ sregs.es.selector = 0;
+ crosvm_vcpu_set_sregs(vcpu, &sregs);
+
+ struct kvm_regs regs;
+ crosvm_vcpu_get_regs(vcpu, ®s);
+ regs.rflags = 2;
+ regs.rip = LOAD_ADDRESS;
+ regs.rax = DATAMATCH_VAL;
+ regs.rbx = DATAMATCH_VAL - 1;
+ crosvm_vcpu_set_regs(vcpu, ®s);
+ }
+
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ uint64_t dummy = 1;
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return NULL;
+ }
+
+ crosvm_vcpu_resume(vcpu);
+ }
+
+ return NULL;
+}
+
+int main(int argc, char** argv) {
+ const uint8_t code[] = {
+ /*
+ 0000 BAF803 mov dx,0x3f8
+ 0003 88C3 mov bl,al
+ 0005 EE out dx,al
+ 0006 B000 mov al,0x0
+ 0008 EE out dx,al
+ 0009 88D8 mov al,bl
+ 000B EE out dx,al
+ 0014 26C606000001 mov byte [es:0x0],0x1
+ 000C F4 hlt
+ */
+ 0xba, 0xf8, 0x03,
+ 0x88, 0xc3,
+ 0xee,
+ 0xb0, 0x00,
+ 0xee,
+ 0x88, 0xd8,
+ 0xee,
+ 0x26, 0xc6, 0x06, 0x00, 0x00, 0x01,
+ 0xf4,
+ };
+
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ if (g_kill_evt < 0) {
+ fprintf(stderr, "failed to get kill eventfd: %d\n", g_kill_evt);
+ return 1;
+ }
+
+ ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
+ if (ret) {
+ fprintf(stderr, "failed to reserve mmio range: %d\n", ret);
+ return 1;
+ }
+
+ uint8_t datamatch = DATAMATCH_VAL;
+ struct crosvm_io *io;
+ ret = crosvm_create_io_event(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, 0x3f8, 1, &datamatch, &io);
+ if (ret) {
+ fprintf(stderr, "failed to create ioevent: %d\n", ret);
+ return 1;
+ }
+
+ int ioeventfd = crosvm_io_event_fd(io);
+
+ int mem_size = 0x4000;
+ int mem_fd = syscall(SYS_memfd_create, "guest_mem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (mem_fd < 0) {
+ fprintf(stderr, "failed to create guest memfd: %d\n", errno);
+ return 1;
+ }
+ ret = ftruncate(mem_fd, mem_size);
+ if (ret) {
+ fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
+ return 1;
+ }
+ uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0);
+ if (mem == MAP_FAILED) {
+ fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
+ return 1;
+ }
+ fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
+ memcpy(mem + LOAD_ADDRESS, code, sizeof(code));
+
+ struct crosvm_memory *mem_obj;
+ ret = crosvm_create_memory(crosvm, mem_fd, 0, mem_size, 0, false, false, &mem_obj);
+ if (ret) {
+ fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
+ return 1;
+ }
+
+ /* get and creat a thread for each vcpu */
+ struct crosvm_vcpu *vcpus[32];
+ pthread_t vcpu_threads[32];
+ uint32_t vcpu_count;
+ for (vcpu_count = 0; vcpu_count < 32; vcpu_count++) {
+ ret = crosvm_get_vcpu(crosvm, vcpu_count, &vcpus[vcpu_count]);
+ if (ret == -ENOENT)
+ break;
+
+ if (ret) {
+ fprintf(stderr, "error while getting all vcpus: %d\n", ret);
+ return 1;
+ }
+ pthread_create(&vcpu_threads[vcpu_count], NULL, vcpu_thread, vcpus[vcpu_count]);
+ }
+
+ ret = crosvm_start(crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
+ return 1;
+ }
+
+ uint64_t dummy;
+ read(g_kill_evt, &dummy, 8);
+
+ ret = read(ioeventfd, &dummy, sizeof(dummy));
+ if (ret == -1) {
+ fprintf(stderr, "failed to read ioeventfd: %d\n", errno);
+ return 1;
+ }
+
+ if (dummy != 2) {
+ fprintf(stderr, "ioeventfd was not triggered the expected number of times: %d\n", dummy);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugin_irqfd.c b/tests/plugin_irqfd.c
new file mode 100644
index 0000000..490ebab
--- /dev/null
+++ b/tests/plugin_irqfd.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2017 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 <errno.h>
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "crosvm.h"
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#endif
+
+#ifndef F_SEAL_SHRINK
+#define F_SEAL_SHRINK 0x0002
+#endif
+
+#define LOAD_ADDRESS 0x1000
+#define STACK_BASE (LOAD_ADDRESS + 0x1000)
+#define STACK_SIZE 0x1000
+#define SUCCESS_ADDRESS 0x3000
+#define KILL_ADDRESS 0x4000
+
+/*
+org 0x1000
+bits 16
+
+cli
+
+; Set entry 0x0 in the interrupt vector table
+mov word [0x0], handle
+mov word [0x2], 0x0
+
+sti
+
+; Loop until interrupt is handled
+loop:
+ cmp byte [si], 0x01
+ jne loop
+
+cli
+
+; Signal that we are ready to end
+end:
+ mov byte [es:0], 0x01
+ hlt
+
+; Handle the interrupt by halting
+handle:
+ mov byte [si], 0x01
+ iret
+*/
+const uint8_t g_code[] = {
+ 0xfa, 0xc7, 0x06, 0x00, 0x00, 0x1b, 0x10, 0xc7, 0x06, 0x02, 0x00, 0x00,
+ 0x00, 0xfb, 0x80, 0x3c, 0x01, 0x75, 0xfb, 0xfa, 0x26, 0xc6, 0x06, 0x00,
+ 0x00, 0x01, 0xf4, 0xc6, 0x04, 0x01, 0xcf
+};
+
+struct vcpu_context {
+ struct crosvm_vcpu *vcpu;
+ int irqeventfd;
+ int kill_evt;
+};
+
+void *vcpu_thread(void *arg) {
+ struct vcpu_context *ctx = arg;
+ struct crosvm_vcpu *vcpu = ctx->vcpu;
+ struct crosvm_vcpu_event evt;
+ uint64_t dummy = 1;
+ int i = 0;
+ int ret;
+ while (crosvm_vcpu_wait(vcpu, &evt) == 0) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_INIT) {
+ struct kvm_sregs sregs;
+ crosvm_vcpu_get_sregs(vcpu, &sregs);
+ sregs.cs.base = 0;
+ sregs.cs.selector = 0x0;
+ sregs.ss.base = 0;
+ sregs.ss.selector = 0x0;
+ sregs.es.base = KILL_ADDRESS;
+ sregs.es.selector = 0x0;
+ crosvm_vcpu_set_sregs(vcpu, &sregs);
+
+ struct kvm_regs regs;
+ crosvm_vcpu_get_regs(vcpu, ®s);
+ regs.rflags = 2;
+ regs.rip = LOAD_ADDRESS;
+ regs.rsp = STACK_BASE + STACK_SIZE;
+ regs.rsi = SUCCESS_ADDRESS;
+ crosvm_vcpu_set_regs(vcpu, ®s);
+
+ write(ctx->irqeventfd, &dummy, sizeof(dummy));
+ }
+
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ write(ctx->kill_evt, &dummy, sizeof(dummy));
+ return NULL;
+ }
+
+ crosvm_vcpu_resume(vcpu);
+ }
+
+ return NULL;
+}
+
+int main(int argc, char** argv) {
+ int i;
+ uint64_t dummy = 1;
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ int kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ if (kill_evt < 0) {
+ fprintf(stderr, "failed to get kill eventfd: %d\n", kill_evt);
+ return 1;
+ }
+
+ crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
+
+ struct crosvm_irq *irq;
+ ret = crosvm_create_irq_event(crosvm, 0, &irq);
+ if (ret) {
+ fprintf(stderr, "failed to create irq event: %d\n", ret);
+ return 1;
+ }
+
+ int irqeventfd = crosvm_irq_event_get_fd(irq);
+
+ int mem_size = 0x4000;
+ int mem_fd = syscall(SYS_memfd_create, "guest_mem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (mem_fd < 0) {
+ fprintf(stderr, "failed to create guest memfd: %d\n", errno);
+ return 1;
+ }
+ ret = ftruncate(mem_fd, mem_size);
+ if (ret) {
+ fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
+ return 1;
+ }
+ uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0);
+ if (mem == MAP_FAILED) {
+ fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
+ return 1;
+ }
+ fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
+ memcpy(mem + LOAD_ADDRESS, g_code, sizeof(g_code));
+
+ struct crosvm_memory *mem_obj;
+ ret = crosvm_create_memory(crosvm, mem_fd, 0, mem_size, 0, false, false, &mem_obj);
+ if (ret) {
+ fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
+ return 1;
+ }
+
+ struct crosvm_vcpu *vcpus[32];
+ struct vcpu_context ctxs[32];
+ pthread_t vcpu_threads[32];
+ uint32_t vcpu_count;
+ for (vcpu_count = 0; vcpu_count < 32; vcpu_count++) {
+ ret = crosvm_get_vcpu(crosvm, vcpu_count, &vcpus[vcpu_count]);
+ if (ret == -ENOENT)
+ break;
+
+ if (ret) {
+ fprintf(stderr, "error while getting all vcpus: %d\n", ret);
+ return 1;
+ }
+ ctxs[vcpu_count].vcpu = vcpus[vcpu_count];
+ ctxs[vcpu_count].irqeventfd = irqeventfd;
+ ctxs[vcpu_count].kill_evt = kill_evt;
+ pthread_create(&vcpu_threads[vcpu_count], NULL, vcpu_thread, &ctxs[vcpu_count]);
+ }
+
+ ret = crosvm_start(crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
+ return 1;
+ }
+
+ ret = read(kill_evt, &dummy, sizeof(dummy));
+ if (ret == -1) {
+ fprintf(stderr, "failed to read kill eventfd: %d\n", errno);
+ return 1;
+ }
+
+ if (mem[SUCCESS_ADDRESS] != 0x01) {
+ fprintf(stderr, "interrupt was not handled: 0x%x\n", mem[SUCCESS_ADDRESS]);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugin_msr_index_list.c b/tests/plugin_msr_index_list.c
new file mode 100644
index 0000000..0a940ae
--- /dev/null
+++ b/tests/plugin_msr_index_list.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 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 <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "crosvm.h"
+
+int main(int argc, char** argv) {
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ uint32_t msr_indices[256];
+ int n_entries;
+ ret = crosvm_get_msr_index_list(crosvm, 1, msr_indices, &n_entries);
+ if (ret >= 0) {
+ fprintf(stderr,
+ "expected crosvm_get_msr_index_list to fail with E2BIG\n");
+ return 1;
+ }
+
+
+ memset(msr_indices, 0, sizeof(msr_indices));
+ ret = crosvm_get_msr_index_list(crosvm, 256, msr_indices, &n_entries);
+ if (ret < 0) {
+ fprintf(stderr,
+ "unexpected failure of crosvm_get_msr_index_list: %d\n", ret);
+ return 1;
+ }
+
+ if (n_entries <= 1) {
+ fprintf(stderr,
+ "unexpected number of supported msr entries: %d\n",
+ n_entries);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugin_net_config.c b/tests/plugin_net_config.c
new file mode 100644
index 0000000..f11fa10
--- /dev/null
+++ b/tests/plugin_net_config.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2018 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 <arpa/inet.h>
+#include <linux/if_tun.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "crosvm.h"
+
+/*
+ * These must match the network arguments supplied to the plugin in plugins.rs.
+ * IPv4 addresses here are in host-native byte order.
+ */
+const uint32_t expected_ip = 0x64735c05; // 100.115.92.5
+const uint32_t expected_netmask = 0xfffffffc; // 255.255.255.252
+const uint8_t expected_mac[] = {0xde, 0x21, 0xe8, 0x47, 0x6b, 0x6a};
+
+int main(int argc, char** argv) {
+ struct crosvm *crosvm;
+ struct crosvm_net_config net_config;
+ int ret = crosvm_connect(&crosvm);
+
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_net_get_config(crosvm, &net_config);
+ if (ret) {
+ fprintf(stderr, "failed to get crosvm net config: %d\n", ret);
+ return 1;
+ }
+
+ if (net_config.tap_fd < 0) {
+ fprintf(stderr, "fd %d is < 0\n", net_config.tap_fd);
+ return 1;
+ }
+
+ unsigned int features;
+ if (ioctl(net_config.tap_fd, TUNGETFEATURES, &features) < 0) {
+ fprintf(stderr,
+ "failed to read tap features: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ if (net_config.host_ip != htonl(expected_ip)) {
+ char ip_addr[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &net_config.host_ip, ip_addr, sizeof(ip_addr));
+ fprintf(stderr, "ip %s != 100.115.92.5\n", ip_addr);
+ return 1;
+ }
+
+ if (net_config.netmask != htonl(expected_netmask)) {
+ char netmask[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &net_config.netmask, netmask, sizeof(netmask));
+ fprintf(stderr, "netmask %s != 255.255.255.252\n", netmask);
+ return 1;
+ }
+
+ if (memcmp(net_config.host_mac_address,
+ expected_mac,
+ sizeof(expected_mac)) != 0) {
+ fprintf(stderr,
+ "mac %02X:%02X:%02X:%02X:%02X:%02X != de:21:e8:47:6b:6a\n",
+ net_config.host_mac_address[0],
+ net_config.host_mac_address[1],
+ net_config.host_mac_address[2],
+ net_config.host_mac_address[3],
+ net_config.host_mac_address[4],
+ net_config.host_mac_address[5]);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugin_supported_cpuid.c b/tests/plugin_supported_cpuid.c
new file mode 100644
index 0000000..0acb134
--- /dev/null
+++ b/tests/plugin_supported_cpuid.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018 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 <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "crosvm.h"
+
+int main(int argc, char** argv) {
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ struct kvm_cpuid_entry2 cpuids[100];
+ int n_entries;
+ ret = crosvm_get_supported_cpuid(crosvm, 1, cpuids, &n_entries);
+ if (ret >= 0) {
+ fprintf(stderr,
+ "expected crosvm_get_supported_cpuids to fail with E2BIG\n");
+ return 1;
+ }
+
+ ret = crosvm_get_supported_cpuid(crosvm, 100, cpuids, &n_entries);
+ if (ret < 0) {
+ fprintf(stderr,
+ "unexpected failure of crosvm_get_supported_cpuids: %d\n", ret);
+ return 1;
+ }
+
+ if (n_entries <= 1) {
+ fprintf(stderr,
+ "unexpected number of supported cpuid entries: %d\n",
+ n_entries);
+ return 1;
+ }
+
+ ret = crosvm_get_emulated_cpuid(crosvm, 1, cpuids, &n_entries);
+ if (ret >= 0) {
+ fprintf(stderr,
+ "expected crosvm_get_emulated_cpuids to fail with E2BIG\n");
+ return 1;
+ }
+
+ ret = crosvm_get_emulated_cpuid(crosvm, 100, cpuids, &n_entries);
+ if (ret < 0) {
+ fprintf(stderr,
+ "unexpected failure of crosvm_get_emulated_cpuid: %d\n", ret);
+ return 1;
+ }
+
+ if (n_entries < 1) {
+ fprintf(stderr,
+ "unexpected number of emulated cpuid entries: %d\n", n_entries);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugin_vcpu_pause.c b/tests/plugin_vcpu_pause.c
new file mode 100644
index 0000000..ff69b04
--- /dev/null
+++ b/tests/plugin_vcpu_pause.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2017 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 <errno.h>
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "crosvm.h"
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#endif
+
+#ifndef F_SEAL_SHRINK
+#define F_SEAL_SHRINK 0x0002
+#endif
+
+#define SERIAL_ADDRESS 0x3f8
+#define KILL_ADDRESS 0x3f9
+
+static char g_serial_out[16];
+static int g_kill_evt;
+
+static bool g_paused;
+static bool g_exit_loop;
+static pthread_mutex_t g_pause_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t g_pause_cond = PTHREAD_COND_INITIALIZER;
+
+static volatile int count;
+
+static void *vcpu_thread_fn(void *arg) {
+ struct crosvm_vcpu *vcpu = arg;
+ struct crosvm_vcpu_event evt;
+ int i = 0;
+
+ while (crosvm_vcpu_wait(vcpu, &evt) == 0) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_INIT) {
+ struct kvm_sregs sregs;
+ crosvm_vcpu_get_sregs(vcpu, &sregs);
+ sregs.cs.base = 0;
+ sregs.cs.selector = 0;
+ sregs.es.base = KILL_ADDRESS;
+ sregs.es.selector = 0;
+ crosvm_vcpu_set_sregs(vcpu, &sregs);
+
+ struct kvm_regs regs;
+ crosvm_vcpu_get_regs(vcpu, ®s);
+ regs.rip = 0x1000;
+ regs.rax = 2;
+ regs.rbx = 7;
+ regs.rflags = 2;
+ crosvm_vcpu_set_regs(vcpu, ®s);
+
+ /* Signal the main thread that init is done */
+ uint64_t dummy = 1;
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ }
+ else if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_IOPORT &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1) {
+ uint64_t dummy = 1;
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return NULL;
+ }
+ else if (evt.kind == CROSVM_VCPU_EVENT_KIND_PAUSED) {
+ /* Signal that we paused */
+ uint64_t dummy = 1;
+ write(g_kill_evt, &dummy, sizeof(dummy));
+
+ /* Wait till we can continue again */
+ pthread_mutex_lock(&g_pause_mutex);
+ while (g_paused)
+ pthread_cond_wait(&g_pause_cond, &g_pause_mutex);
+
+ /* Kick the VM from infinite loop if requested */
+ if (g_exit_loop) {
+ struct kvm_regs regs;
+ crosvm_vcpu_get_regs(vcpu, ®s);
+ regs.rbx = 1;
+ crosvm_vcpu_set_regs(vcpu, ®s);
+ }
+
+ /* Signal that we are no longer paused */
+ write(g_kill_evt, &dummy, sizeof(dummy));
+
+ pthread_mutex_unlock(&g_pause_mutex);
+ }
+ crosvm_vcpu_resume(vcpu);
+ }
+
+ return NULL;
+}
+
+static int signal_pause(struct crosvm *crosvm) {
+ pthread_mutex_lock(&g_pause_mutex);
+ g_paused = true;
+ pthread_mutex_unlock(&g_pause_mutex);
+
+ return crosvm_pause_vcpus(crosvm, 1, NULL);
+}
+
+static void signal_unpause(struct crosvm *crosvm, bool exit_loop) {
+ pthread_mutex_lock(&g_pause_mutex);
+ g_paused = false;
+ g_exit_loop = exit_loop;
+ pthread_cond_broadcast(&g_pause_cond);
+ pthread_mutex_unlock(&g_pause_mutex);
+}
+
+int main(int argc, char** argv) {
+ const uint8_t code[] = {
+ /*
+ 0000 00D8 add al,bl
+ 0002 80FB01 cmp bl, 0x1
+ 0005 75F9 jne 0x0
+ 0007 BAF903 mov dx,0x3f8
+ 000A B001 mov al,0x1
+ 000C EE out dx,al
+ 000D F4 hlt
+ */
+ 0x00, 0xd8,
+ 0x80, 0xfb, 0x01,
+ 0x75, 0xf9,
+ 0xba, 0xf9, 0x03,
+ 0xb0, 0x01,
+ 0xee,
+ 0xf4
+ };
+
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ /* We needs this eventfd to know when to exit before being killed. */
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ if (g_kill_evt < 0) {
+ fprintf(stderr, "failed to get kill eventfd: %d\n", g_kill_evt);
+ return 1;
+ }
+
+ ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
+ KILL_ADDRESS, 1);
+ if (ret) {
+ fprintf(stderr, "failed to reserve mmio range: %d\n", ret);
+ return 1;
+ }
+
+ int mem_size = 0x2000;
+ int mem_fd = syscall(SYS_memfd_create,
+ "guest_mem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (mem_fd < 0) {
+ fprintf(stderr, "failed to create guest memfd: %d\n", errno);
+ return 1;
+ }
+ ret = ftruncate(mem_fd, mem_size);
+ if (ret) {
+ fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
+ return 1;
+ }
+ uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ mem_fd, 0x1000);
+ if (mem == MAP_FAILED) {
+ fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
+ return 1;
+ }
+ fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
+ memcpy(mem, code, sizeof(code));
+
+ struct crosvm_memory *mem_obj;
+ ret = crosvm_create_memory(crosvm, mem_fd, 0x1000, 0x1000, 0x1000,
+ false, false, &mem_obj);
+ if (ret) {
+ fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
+ return 1;
+ }
+
+ /* get and create a thread for the vcpu */
+ struct crosvm_vcpu *vcpu;
+ ret = crosvm_get_vcpu(crosvm, 0, &vcpu);
+ if (ret) {
+ fprintf(stderr, "error while getting vcpu: %d\n", ret);
+ return 1;
+ }
+
+ pthread_t vcpu_thread;
+ ret = pthread_create(&vcpu_thread, NULL, vcpu_thread_fn, vcpu);
+ if (ret) {
+ fprintf(stderr, "failed to createvcpu thread\n");
+ return 1;
+ }
+
+ ret = crosvm_start(crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
+ return 1;
+ }
+
+ /* Wait till VCPU thread tells us that its initialization is done */
+ uint64_t dummy;
+ read(g_kill_evt, &dummy, sizeof(dummy));
+
+ ret = signal_pause(crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to pause vcpus (1st time): %d\n", ret);
+ return 1;
+ }
+
+ /* Wait till VCPU thread tells us it is paused */
+ read(g_kill_evt, &dummy, sizeof(dummy));
+
+ /* Try pausing VCPUs 2nd time to make sure we do not deadlock */
+ ret = signal_pause(crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to pause vcpus (2nd time): %d\n", ret);
+ return 1;
+ }
+
+ signal_unpause(crosvm, false);
+
+ /* Wait until VCPU thread tells us that it is no longer paused */
+ read(g_kill_evt, &dummy, sizeof(dummy));
+
+ /*
+ * Try pausing VCPUs 3rd time to see if we will miss pause
+ * request as we are exiting previous pause.
+ */
+ ret = signal_pause(crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to pause vcpus (2nd time): %d\n", ret);
+ return 1;
+ }
+
+ signal_unpause(crosvm, true);
+
+ /* Wait until VCPU thread tells us that it is no longer paused */
+ read(g_kill_evt, &dummy, sizeof(dummy));
+
+ /* Wait for crosvm to request that we exit otherwise we will be killed. */
+ read(g_kill_evt, &dummy, sizeof(dummy));
+
+ ret = crosvm_destroy_memory(crosvm, &mem_obj);
+ if (ret) {
+ fprintf(stderr, "failed to destroy memory in crosvm: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
+ KILL_ADDRESS, 0);
+ if (ret) {
+ fprintf(stderr, "failed to unreserve mmio range: %d\n", ret);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugin_vm_state.c b/tests/plugin_vm_state.c
new file mode 100644
index 0000000..450d324
--- /dev/null
+++ b/tests/plugin_vm_state.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "crosvm.h"
+
+int main(int argc, char** argv) {
+ struct crosvm *crosvm;
+ int ret = crosvm_connect(&crosvm);
+ if (ret) {
+ fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
+ return 1;
+ }
+
+ struct kvm_pic_state pic_state;
+ ret = crosvm_get_pic_state(crosvm, false, &pic_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get initial PIC1 state: %d\n", ret);
+ return 1;
+ }
+
+ if (pic_state.auto_eoi) {
+ fprintf(stderr, "unexpected value of auto_eoi flag\n");
+ return 1;
+ }
+
+ pic_state.auto_eoi = true;
+ ret = crosvm_set_pic_state(crosvm, false, &pic_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to update PIC1 state: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_get_pic_state(crosvm, false, &pic_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get updated PIC1 state: %d\n", ret);
+ return 1;
+ }
+
+ if (!pic_state.auto_eoi) {
+ fprintf(stderr, "unexpected value of auto_eoi flag after update\n");
+ return 1;
+ }
+
+ // Test retrieving and setting IOAPIC state.
+ struct kvm_ioapic_state ioapic_state;
+ ret = crosvm_get_ioapic_state(crosvm, &ioapic_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get initial PIC1 state: %d\n", ret);
+ return 1;
+ }
+
+ fprintf(stderr, "IOAPIC ID: %d\n", ioapic_state.id);
+
+ if (ioapic_state.id != 0) {
+ fprintf(stderr, "unexpected value of IOAPIC ID: %d\n", ioapic_state.id);
+ return 1;
+ }
+
+ ioapic_state.id = 1;
+ ret = crosvm_set_ioapic_state(crosvm, &ioapic_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to update PIC1 state: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_get_ioapic_state(crosvm, &ioapic_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get updated PIC1 state: %d\n", ret);
+ return 1;
+ }
+
+ if (ioapic_state.id != 1) {
+ fprintf(stderr, "unexpected value of IOAPIC ID after update: %d\n",
+ ioapic_state.id);
+ return 1;
+ }
+
+ // Test retrieving and setting PIT state.
+ struct kvm_pit_state2 pit_state;
+ ret = crosvm_get_pit_state(crosvm, &pit_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get initial PIT state: %d\n", ret);
+ return 1;
+ }
+
+ if (pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY) {
+ fprintf(stderr, "unexpected value of KVM_PIT_FLAGS_HPET_LEGACY flag\n");
+ return 1;
+ }
+
+ pit_state.flags |= KVM_PIT_FLAGS_HPET_LEGACY;
+ ret = crosvm_set_pit_state(crosvm, &pit_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to update PIT state: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_get_pit_state(crosvm, &pit_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get updated PIT state: %d\n", ret);
+ return 1;
+ }
+
+ if (!(pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY)) {
+ fprintf(stderr,
+ "unexpected value of KVM_PIT_FLAGS_HPET_LEGACY after update\n");
+ return 1;
+ }
+
+ // Test retrieving and setting clock state.
+ struct kvm_clock_data clock_data = { .clock = 0, .flags = -1U, };
+ ret = crosvm_get_clock(crosvm, &clock_data);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get initial clock state: %d\n", ret);
+ return 1;
+ }
+
+ if (clock_data.clock == 0 || clock_data.flags != 0) {
+ fprintf(stderr, "invalid clock data returned (%llu, %u)\n",
+ clock_data.clock, clock_data.flags);
+ }
+
+ clock_data.clock += 10000000;
+
+ ret = crosvm_set_clock(crosvm, &clock_data);
+ if (ret < 0) {
+ fprintf(stderr, "failed to update clock: %d\n", ret);
+ return 1;
+ }
+
+ clock_data.flags = -1U;
+ ret = crosvm_set_clock(crosvm, &clock_data);
+ if (ret >= 0) {
+ fprintf(stderr, "unexpected success updating clock\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/plugins.rs b/tests/plugins.rs
new file mode 100644
index 0000000..ec094d1
--- /dev/null
+++ b/tests/plugins.rs
@@ -0,0 +1,708 @@
+// Copyright 2017 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(feature = "plugin")]
+
+use std::env::{current_exe, var_os};
+use std::ffi::OsString;
+use std::fs::{remove_file, 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;
+use std::time::Duration;
+
+use rand_ish::urandom_str;
+use sys_util::{ioctl, SharedMemory};
+
+struct RemovePath(PathBuf);
+impl Drop for RemovePath {
+ fn drop(&mut self) {
+ if let Err(e) = remove_file(&self.0) {
+ eprintln!("failed to remove path: {}", e);
+ }
+ }
+}
+
+fn get_target_path() -> PathBuf {
+ current_exe()
+ .ok()
+ .map(|mut path| {
+ path.pop();
+ path
+ })
+ .expect("failed to get crosvm binary directory")
+}
+
+fn get_crosvm_path() -> PathBuf {
+ current_exe()
+ .ok()
+ .map(|mut path| {
+ path.pop();
+ if path.ends_with("deps") {
+ path.pop();
+ }
+ path
+ })
+ .expect("failed to get crosvm binary directory")
+}
+
+fn build_plugin(src: &str) -> RemovePath {
+ let libcrosvm_plugin_dir = get_target_path();
+ let mut out_bin = libcrosvm_plugin_dir.clone();
+ let randbin = urandom_str(10).expect("failed to generate random bin name");
+ out_bin.push(randbin);
+ let mut child = Command::new(var_os("CC").unwrap_or(OsString::from("cc")))
+ .args(&["-Icrosvm_plugin", "-pthread", "-o"]) // crosvm.h location and set output path.
+ .arg(&out_bin)
+ .arg("-L") // Path of shared object to link to.
+ .arg(&libcrosvm_plugin_dir)
+ .arg("-lcrosvm_plugin")
+ .arg("-Wl,-rpath") // Search for shared object in the same path when exec'd.
+ .arg(&libcrosvm_plugin_dir)
+ .args(&["-Wl,-rpath", "."]) // Also check current directory in case of sandboxing.
+ .args(&["-xc", "-"]) // Read source code from piped stdin.
+ .stdin(Stdio::piped())
+ .spawn()
+ .expect("failed to spawn compiler");
+ let stdin = child.stdin.as_mut().expect("failed to open stdin");
+ stdin
+ .write_all(src.as_bytes())
+ .expect("failed to write source to stdin");
+
+ let status = child.wait().expect("failed to wait for compiler");
+ assert!(status.success(), "failed to build plugin");
+
+ RemovePath(PathBuf::from(out_bin))
+}
+
+fn run_plugin(bin_path: &Path, with_sandbox: bool) {
+ let mut crosvm_path = get_crosvm_path();
+ crosvm_path.push("crosvm");
+ let mut cmd = Command::new(crosvm_path);
+ cmd.args(&[
+ "run",
+ "-c",
+ "1",
+ "--host_ip",
+ "100.115.92.5",
+ "--netmask",
+ "255.255.255.252",
+ "--mac",
+ "de:21:e8:47:6b:6a",
+ "--seccomp-policy-dir",
+ "tests",
+ "--plugin",
+ ])
+ .arg(
+ bin_path
+ .canonicalize()
+ .expect("failed to canonicalize plugin path"),
+ );
+ if !with_sandbox {
+ cmd.arg("--disable-sandbox");
+ }
+
+ let mut child = cmd.spawn().expect("failed to spawn crosvm");
+ for _ in 0..12 {
+ match child.try_wait().expect("failed to wait for crosvm") {
+ Some(status) => {
+ assert!(status.success());
+ return;
+ }
+ None => sleep(Duration::from_millis(100)),
+ }
+ }
+ child.kill().expect("failed to kill crosvm");
+ panic!("crosvm process has timed out");
+}
+
+fn test_plugin(src: &str) {
+ let bin_path = build_plugin(src);
+ // Run with and without the sandbox enabled.
+ run_plugin(&bin_path.0, false);
+ run_plugin(&bin_path.0, true);
+}
+
+fn keep_fd_on_exec<F: AsRawFd>(f: &F) {
+ unsafe {
+ ioctl(f, 0x5450 /* FIONCLEX */);
+ }
+}
+
+/// Takes assembly source code and returns the resulting assembly code.
+fn build_assembly(src: &str) -> Vec<u8> {
+ // Creates a shared memory region with the assembly source code in it.
+ let in_shm = SharedMemory::new(None).unwrap();
+ let mut in_shm_file: File = in_shm.into();
+ keep_fd_on_exec(&in_shm_file);
+ in_shm_file.write_all(src.as_bytes()).unwrap();
+
+ // Creates a shared memory region that will hold the nasm output.
+ let mut out_shm_file: File = SharedMemory::new(None).unwrap().into();
+ keep_fd_on_exec(&out_shm_file);
+
+ // Runs nasm with the input and output files set to the FDs of the above shared memory regions,
+ // which we have preserved accross exec.
+ let status = Command::new("nasm")
+ .arg(format!("/proc/self/fd/{}", in_shm_file.as_raw_fd()))
+ .args(&["-f", "bin", "-o"])
+ .arg(format!("/proc/self/fd/{}", out_shm_file.as_raw_fd()))
+ .status()
+ .expect("failed to spawn assembler");
+ assert!(status.success());
+
+ let mut out_bytes = Vec::new();
+ out_shm_file.read_to_end(&mut out_bytes).unwrap();
+ out_bytes
+}
+
+// Converts the input bytes to an output string in the format "0x01,0x02,0x03...".
+fn format_as_hex(data: &[u8]) -> String {
+ let mut out = String::new();
+ for (i, d) in data.iter().enumerate() {
+ out.push_str(&format!("0x{:02x}", d));
+ if i < data.len() - 1 {
+ out.push(',')
+ }
+ }
+ out
+}
+
+// A testing framework for creating simple plugins.
+struct MiniPlugin {
+ // The size in bytes of the guest memory based at 0x0000.
+ mem_size: u64,
+ // The address in guest memory to load the assembly code.
+ load_address: u32,
+ // The nasm syntax 16-bit assembly code that will assembled and loaded into guest memory.
+ assembly_src: &'static str,
+ // The C source code that will be included in the mini_plugin_template.c file. This code must
+ // define the forward declarations above the {src} line so that the completed plugin source will
+ // compile.
+ src: &'static str,
+}
+
+impl Default for MiniPlugin {
+ fn default() -> Self {
+ MiniPlugin {
+ mem_size: 0x2000,
+ load_address: 0x1000,
+ assembly_src: "hlt",
+ src: "",
+ }
+ }
+}
+
+// Builds and tests the given MiniPlugin definiton.
+fn test_mini_plugin(plugin: &MiniPlugin) {
+ // Adds a preamble to ensure the output opcodes are 16-bit real mode and the lables start at the
+ // load address.
+ let assembly_src = format!(
+ "org 0x{:x}\nbits 16\n{}",
+ plugin.load_address, plugin.assembly_src
+ );
+
+ // Builds the assembly and convert it to a C literal array format.
+ let assembly = build_assembly(&assembly_src);
+ let assembly_hex = format_as_hex(&assembly);
+
+ // Glues the pieces of this plugin together and tests the completed plugin.
+ let generated_src = format!(
+ include_str!("mini_plugin_template.c"),
+ mem_size = plugin.mem_size,
+ load_address = plugin.load_address,
+ assembly_code = assembly_hex,
+ src = plugin.src
+ );
+ test_plugin(&generated_src);
+}
+
+#[test]
+fn test_adder() {
+ test_plugin(include_str!("plugin_adder.c"));
+}
+
+#[test]
+fn test_dirty_log() {
+ test_plugin(include_str!("plugin_dirty_log.c"));
+}
+
+#[test]
+fn test_ioevent() {
+ test_plugin(include_str!("plugin_ioevent.c"));
+}
+
+#[test]
+fn test_irqfd() {
+ test_plugin(include_str!("plugin_irqfd.c"));
+}
+
+#[test]
+fn test_extensions() {
+ test_plugin(include_str!("plugin_extensions.c"));
+}
+
+#[test]
+fn test_supported_cpuid() {
+ test_plugin(include_str!("plugin_supported_cpuid.c"));
+}
+
+#[test]
+fn test_msr_index_list() {
+ test_plugin(include_str!("plugin_msr_index_list.c"));
+}
+
+#[test]
+fn test_vm_state_manipulation() {
+ test_plugin(include_str!("plugin_vm_state.c"));
+}
+
+#[test]
+fn test_vcpu_pause() {
+ test_plugin(include_str!("plugin_vcpu_pause.c"));
+}
+
+#[test]
+fn test_net_config() {
+ test_plugin(include_str!("plugin_net_config.c"));
+}
+
+#[test]
+fn test_debugregs() {
+ let mini_plugin = MiniPlugin {
+ assembly_src: "org 0x1000
+ bits 16
+ mov dr0, ebx
+ mov eax, dr1
+ mov byte [0x3000], 1",
+ src: r#"
+ #define DR1_VALUE 0x12
+ #define RBX_VALUE 0xabcdef00
+ #define KILL_ADDRESS 0x3000
+
+ int g_kill_evt;
+ struct kvm_regs g_regs;
+ struct kvm_debugregs g_dregs;
+
+ int setup_vm(struct crosvm *crosvm, void *mem) {
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
+ return 0;
+ }
+
+ int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
+ struct kvm_sregs *sregs)
+ {
+ regs->rbx = RBX_VALUE;
+ struct kvm_debugregs dregs;
+ crosvm_vcpu_get_debugregs(vcpu, &dregs);
+ dregs.db[1] = DR1_VALUE;
+ crosvm_vcpu_set_debugregs(vcpu, &dregs);
+ return 0;
+ }
+
+ int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ uint64_t dummy = 1;
+ crosvm_vcpu_get_debugregs(vcpu, &g_dregs);
+ crosvm_vcpu_get_regs(vcpu, &g_regs);
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return 1;
+ }
+ return 0;
+ }
+
+ int check_result(struct crosvm *vcpu, void *mem) {
+ if (g_dregs.db[1] != DR1_VALUE) {
+ fprintf(stderr, "dr1 register has unexpected value: 0x%x\n", g_dregs.db[1]);
+ return 1;
+ }
+ if (g_dregs.db[0] != RBX_VALUE) {
+ fprintf(stderr, "dr0 register has unexpected value: 0x%x\n", g_dregs.db[0]);
+ return 1;
+ }
+ if (g_regs.rax != DR1_VALUE) {
+ fprintf(stderr, "eax register has unexpected value: 0x%x\n", g_regs.rax);
+ return 1;
+ }
+ return 0;
+ }"#,
+ ..Default::default()
+ };
+ test_mini_plugin(&mini_plugin);
+}
+
+#[test]
+fn test_xcrs() {
+ let mini_plugin = MiniPlugin {
+ assembly_src: "org 0x1000
+ bits 16
+ mov byte [0x3000], 1",
+ src: r#"
+ #define XCR0_VALUE 0x1
+ #define KILL_ADDRESS 0x3000
+
+ int g_kill_evt;
+ struct kvm_xcrs g_xcrs;
+
+ int setup_vm(struct crosvm *crosvm, void *mem) {
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
+ return 0;
+ }
+
+ int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
+ struct kvm_sregs *sregs)
+ {
+ struct kvm_xcrs xcrs = {};
+ xcrs.nr_xcrs = 1;
+ xcrs.xcrs[0].value = XCR0_VALUE;
+ crosvm_vcpu_set_xcrs(vcpu, &xcrs);
+ return 0;
+ }
+
+ int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ uint64_t dummy = 1;
+ crosvm_vcpu_get_xcrs(vcpu, &g_xcrs);
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return 1;
+ }
+ return 0;
+ }
+
+ int check_result(struct crosvm *vcpu, void *mem) {
+ if (g_xcrs.xcrs[0].value != XCR0_VALUE) {
+ fprintf(stderr, "xcr0 register has unexpected value: 0x%x\n",
+ g_xcrs.xcrs[0].value);
+ return 1;
+ }
+ return 0;
+ }"#,
+ ..Default::default()
+ };
+ test_mini_plugin(&mini_plugin);
+}
+
+#[test]
+fn test_msrs() {
+ let mini_plugin = MiniPlugin {
+ assembly_src: "org 0x1000
+ bits 16
+ rdmsr
+ mov [0x0], eax
+ mov [0x4], edx
+ mov ecx, ebx
+ mov eax, [0x8]
+ mov edx, [0xc]
+ wrmsr
+ mov byte [es:0], 1",
+ src: r#"
+ #define MSR1_INDEX 0x00000174
+ #define MSR1_DATA 1
+ #define MSR2_INDEX 0x00000175
+ #define MSR2_DATA 2
+ #define KILL_ADDRESS 0x3000
+
+ int g_kill_evt;
+ uint32_t g_msr2_count;
+ struct kvm_msr_entry g_msr2;
+
+ int setup_vm(struct crosvm *crosvm, void *mem) {
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
+ ((uint64_t*)mem)[1] = MSR2_DATA;
+ return 0;
+ }
+
+ int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
+ struct kvm_sregs *sregs)
+ {
+ regs->rcx = MSR1_INDEX;
+ regs->rbx = MSR2_INDEX;
+ sregs->es.base = KILL_ADDRESS;
+
+ struct kvm_msr_entry msr1 = {0};
+ msr1.index = MSR1_INDEX;
+ msr1.data = MSR1_DATA;
+ crosvm_vcpu_set_msrs(vcpu, 1, &msr1);
+
+ return 0;
+ }
+
+ int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ uint64_t dummy = 1;
+ g_msr2.index = MSR2_INDEX;
+ crosvm_vcpu_get_msrs(vcpu, 1, &g_msr2, &g_msr2_count);
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return 1;
+ }
+ return 0;
+ }
+
+ int check_result(struct crosvm *vcpu, void *mem) {
+ uint64_t msr1_data = ((uint64_t*)mem)[0];
+ if (msr1_data != MSR1_DATA) {
+ fprintf(stderr, "msr1 has unexpected value: 0x%x\n", msr1_data);
+ return 1;
+ }
+ if (g_msr2_count != 1) {
+ fprintf(stderr, "incorrect number of returned MSRSs: %d\n", g_msr2_count);
+ return 1;
+ }
+ if (g_msr2.data != MSR2_DATA) {
+ fprintf(stderr, "msr2 has unexpected value: 0x%x\n", g_msr2.data);
+ return 1;
+ }
+ return 0;
+ }"#,
+ ..Default::default()
+ };
+ test_mini_plugin(&mini_plugin);
+}
+
+#[test]
+fn test_cpuid() {
+ let mini_plugin = MiniPlugin {
+ assembly_src: "org 0x1000
+ bits 16
+ push eax
+ push ecx
+ cpuid
+ mov [0x0], eax
+ mov [0x4], ebx
+ mov [0x8], ecx
+ mov [0xc], edx
+ pop ecx
+ pop eax
+ add ecx, 1
+ cpuid
+ mov [0x10], eax
+ mov [0x14], ebx
+ mov [0x18], ecx
+ mov [0x1c], edx
+ mov byte [es:0], 1",
+ src: r#"
+ #define ENTRY1_INDEX 0
+ #define ENTRY1_EAX 0x40414243
+ #define ENTRY1_EBX 0x50515253
+ #define ENTRY1_ECX 0x60616263
+ #define ENTRY1_EDX 0x71727374
+ #define ENTRY2_INDEX 1
+ #define ENTRY2_EAX 0xAABBCCDD
+ #define ENTRY2_EBX 0xEEFF0011
+ #define ENTRY2_ECX 0x22334455
+ #define ENTRY2_EDX 0x66778899
+ #define KILL_ADDRESS 0x3000
+
+ int g_kill_evt;
+ struct kvm_msr_entry g_msr2;
+
+ int setup_vm(struct crosvm *crosvm, void *mem) {
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
+ return 0;
+ }
+
+ int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
+ struct kvm_sregs *sregs)
+ {
+ regs->rax = ENTRY1_INDEX;
+ regs->rcx = 0;
+ regs->rsp = 0x1000;
+ sregs->es.base = KILL_ADDRESS;
+
+ struct kvm_cpuid_entry2 entries[2];
+ entries[0].function = 0;
+ entries[0].index = ENTRY1_INDEX;
+ entries[0].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ entries[0].eax = ENTRY1_EAX;
+ entries[0].ebx = ENTRY1_EBX;
+ entries[0].ecx = ENTRY1_ECX;
+ entries[0].edx = ENTRY1_EDX;
+ entries[1].function = 0;
+ entries[1].index = ENTRY2_INDEX;
+ entries[1].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ entries[1].eax = ENTRY2_EAX;
+ entries[1].ebx = ENTRY2_EBX;
+ entries[1].ecx = ENTRY2_ECX;
+ entries[1].edx = ENTRY2_EDX;
+ return crosvm_vcpu_set_cpuid(vcpu, 2, entries);
+ }
+
+ int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ uint64_t dummy = 1;
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return 1;
+ }
+ return 0;
+ }
+
+ int check_result(struct crosvm *vcpu, void *memory) {
+ uint32_t *mem = (uint32_t*)memory;
+ if (mem[0] != ENTRY1_EAX) {
+ fprintf(stderr, "entry 1 eax has unexpected value: 0x%x\n", mem[0]);
+ return 1;
+ }
+ if (mem[1] != ENTRY1_EBX) {
+ fprintf(stderr, "entry 1 ebx has unexpected value: 0x%x\n", mem[1]);
+ return 1;
+ }
+ if (mem[2] != ENTRY1_ECX) {
+ fprintf(stderr, "entry 1 ecx has unexpected value: 0x%x\n", mem[2]);
+ return 1;
+ }
+ if (mem[3] != ENTRY1_EDX) {
+ fprintf(stderr, "entry 1 edx has unexpected value: 0x%x\n", mem[3]);
+ return 1;
+ }
+ if (mem[4] != ENTRY2_EAX) {
+ fprintf(stderr, "entry 2 eax has unexpected value: 0x%x\n", mem[4]);
+ return 1;
+ }
+ if (mem[5] != ENTRY2_EBX) {
+ fprintf(stderr, "entry 2 ebx has unexpected value: 0x%x\n", mem[5]);
+ return 1;
+ }
+ if (mem[6] != ENTRY2_ECX) {
+ fprintf(stderr, "entry 2 ecx has unexpected value: 0x%x\n", mem[6]);
+ return 1;
+ }
+ if (mem[7] != ENTRY2_EDX) {
+ fprintf(stderr, "entry 2 edx has unexpected value: 0x%x\n", mem[7]);
+ return 1;
+ }
+ return 0;
+ }"#,
+ ..Default::default()
+ };
+ test_mini_plugin(&mini_plugin);
+}
+
+#[test]
+fn test_vcpu_state_manipulation() {
+ let mini_plugin = MiniPlugin {
+ assembly_src: "org 0x1000
+ bits 16
+ mov byte [0x3000], 1",
+ src: r#"
+ #define KILL_ADDRESS 0x3000
+
+ int g_kill_evt;
+ bool success = false;
+
+ int setup_vm(struct crosvm *crosvm, void *mem) {
+ g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
+ crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
+ return 0;
+ }
+
+ int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
+ struct kvm_sregs *sregs)
+ {
+ int ret;
+
+ struct kvm_lapic_state lapic;
+ ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get initial LAPIC state: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_vcpu_set_lapic_state(vcpu, &lapic);
+ if (ret < 0) {
+ fprintf(stderr, "failed to update LAPIC state: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get updated LAPIC state: %d\n", ret);
+ return 1;
+ }
+
+ struct kvm_mp_state mp_state;
+ ret = crosvm_vcpu_get_mp_state(vcpu, &mp_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get initial MP state: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_vcpu_set_mp_state(vcpu, &mp_state);
+ if (ret < 0) {
+ fprintf(stderr, "failed to update MP state: %d\n", ret);
+ return 1;
+ }
+
+ struct kvm_vcpu_events events;
+ ret = crosvm_vcpu_get_vcpu_events(vcpu, &events);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get VCPU events: %d\n", ret);
+ return 1;
+ }
+
+ ret = crosvm_vcpu_set_vcpu_events(vcpu, &events);
+ if (ret < 0) {
+ fprintf(stderr, "failed to set VCPU events: %d\n", ret);
+ return 1;
+ }
+
+ success = true;
+ return 0;
+ }
+
+ int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
+ if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
+ evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
+ evt.io_access.address == KILL_ADDRESS &&
+ evt.io_access.is_write &&
+ evt.io_access.length == 1 &&
+ evt.io_access.data[0] == 1)
+ {
+ uint64_t dummy = 1;
+ write(g_kill_evt, &dummy, sizeof(dummy));
+ return 1;
+ }
+ return 0;
+ }
+
+ int check_result(struct crosvm *vcpu, void *mem) {
+ if (!success) {
+ fprintf(stderr, "test failed\n");
+ return 1;
+ }
+ return 0;
+ }"#,
+ ..Default::default()
+ };
+ test_mini_plugin(&mini_plugin);
+}
diff --git a/tpm2-sys/Cargo.toml b/tpm2-sys/Cargo.toml
new file mode 100644
index 0000000..12dc5ac
--- /dev/null
+++ b/tpm2-sys/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "tpm2-sys"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+links = "tpm2"
+
+[build-dependencies]
+num_cpus = "*"
+pkg-config = "*"
diff --git a/tpm2-sys/build.rs b/tpm2-sys/build.rs
new file mode 100644
index 0000000..604d936
--- /dev/null
+++ b/tpm2-sys/build.rs
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::env;
+use std::io;
+use std::path::Path;
+use std::process::{self, Command};
+
+fn main() -> io::Result<()> {
+ if pkg_config::Config::new()
+ .statik(true)
+ .probe("libtpm2")
+ .is_ok()
+ {
+ // Use tpm2 package from the standard system location if available.
+ return Ok(());
+ }
+
+ // Build with `RUSTFLAGS='--cfg hermetic'` to disallow building our own
+ // libtpm2 in a production build context. Building from the libtpm2
+ // submodule is a convenience only intended for developer environments.
+ if cfg!(hermetic) {
+ eprintln!("libtpm2 not found; unable to perform hermetic build");
+ process::exit(1);
+ }
+
+ if !Path::new("libtpm2/.git").exists() {
+ Command::new("git")
+ .args(&["submodule", "update", "--init"])
+ .status()?;
+ }
+
+ if !Path::new("libtpm2/build/libtpm2.a").exists() {
+ let ncpu = num_cpus::get();
+ let status = Command::new("make")
+ .arg(format!("-j{}", ncpu))
+ .current_dir("libtpm2")
+ .status()?;
+ if !status.success() {
+ process::exit(status.code().unwrap_or(1));
+ }
+ }
+
+ let dir = env::var("CARGO_MANIFEST_DIR").unwrap();
+ println!("cargo:rustc-link-search={}/libtpm2/build", dir);
+ println!("cargo:rustc-link-lib=static=tpm2");
+ println!("cargo:rustc-link-lib=ssl");
+ println!("cargo:rustc-link-lib=crypto");
+ Ok(())
+}
diff --git a/tpm2-sys/libtpm2 b/tpm2-sys/libtpm2
new file mode 160000
index 0000000..15260c8
--- /dev/null
+++ b/tpm2-sys/libtpm2
@@ -0,0 +1 @@
+Subproject commit 15260c8cd98eb10b4976d2161cd5cb9bc0c3adac
diff --git a/tpm2-sys/src/lib.rs b/tpm2-sys/src/lib.rs
new file mode 100644
index 0000000..456cc87
--- /dev/null
+++ b/tpm2-sys/src/lib.rs
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::raw::{c_int, c_uchar, c_uint};
+
+extern "C" {
+ pub fn TPM_Manufacture(firstTime: c_int) -> c_int;
+ pub fn _plat__SetNvAvail();
+ pub fn _plat__Signal_PowerOn() -> c_int;
+ pub fn _TPM_Init();
+ pub fn ExecuteCommand(
+ requestSize: c_uint,
+ request: *mut c_uchar,
+ responseSize: *mut c_uint,
+ response: *mut *mut c_uchar,
+ );
+}
diff --git a/tpm2/Cargo.toml b/tpm2/Cargo.toml
new file mode 100644
index 0000000..56ee5fb
--- /dev/null
+++ b/tpm2/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "tpm2"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+tpm2-sys = { path = "../tpm2-sys" }
diff --git a/tpm2/src/lib.rs b/tpm2/src/lib.rs
new file mode 100644
index 0000000..e8d6a1e
--- /dev/null
+++ b/tpm2/src/lib.rs
@@ -0,0 +1,223 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::raw::{c_int, c_uint};
+use std::ptr;
+use std::slice;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+static SIMULATOR_EXISTS: AtomicBool = AtomicBool::new(false);
+
+/// A libtpm2-based TPM simulator.
+///
+/// At most one simulator may exist per process because libtpm2 uses a static
+/// global response buffer.
+///
+/// # Examples
+///
+/// ```no_run
+/// let mut simulator = tpm2::Simulator::singleton_in_current_directory();
+///
+/// let command = &[ /* ... */ ];
+/// let response = simulator.execute_command(command);
+/// println!("{:?}", response);
+/// ```
+pub struct Simulator {
+ _priv: (),
+}
+
+impl Simulator {
+ /// Initializes a TPM simulator in the current working directory.
+ ///
+ /// # Panics
+ ///
+ /// Panics if a TPM simulator has already been initialized by this process.
+ pub fn singleton_in_current_directory() -> Self {
+ let already_existed = SIMULATOR_EXISTS.swap(true, Ordering::SeqCst);
+ if already_existed {
+ panic!("libtpm2 simulator singleton already exists");
+ }
+
+ // Based on trunks:
+ // https://chromium.googlesource.com/chromiumos/platform2/+/e4cf13c05773f3446bd76a13c4e37f0b80728711/trunks/tpm_simulator_handle.cc
+ tpm_manufacture(true);
+ plat_set_nv_avail();
+ plat_signal_power_on();
+ tpm_init();
+
+ let mut simulator = Simulator { _priv: () };
+
+ // Send TPM2_Startup(TPM_SU_CLEAR), ignore the result. This is normally
+ // done by firmware.
+ let startup_command = &[
+ 0x80, 0x01, // TPM_ST_NO_SESSIONS
+ 0x00, 0x00, 0x00, 0x0c, // commandSize = 12
+ 0x00, 0x00, 0x01, 0x44, // TPM_CC_Startup
+ 0x00, 0x00, // TPM_SU_CLEAR
+ ];
+ let _ = simulator.execute_command(startup_command);
+
+ simulator
+ }
+
+ /// Sends a TPM command to the TPM simulator, waits for the work to be
+ /// performed, and receives back the TPM response.
+ ///
+ /// Executing a command requires exclusive access to the TPM simulator
+ /// because it mutates libtpm2 static state.
+ ///
+ /// The returned response buffer remains valid until the next TPM command is
+ /// executed.
+ #[must_use]
+ pub fn execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8] {
+ let request_size = command.len() as c_uint;
+ let request = command.as_ptr() as *mut u8;
+ let mut response_size: c_uint = 0;
+ let mut response: *mut u8 = ptr::null_mut();
+
+ // We need to provide the following guarantees in order for this block
+ // of code to be safe:
+ //
+ // - The TPM must have been initialized.
+ //
+ // - There must not be a concurrently executing call to
+ // ExecuteCommand.
+ //
+ // - The `request` pointer must be a valid pointer to `request_size`
+ // bytes of data that remain valid and constant for the full
+ // duration of the call to ExecuteCommand. The implementation may
+ // read up to `request_size` bytes of data from this address.
+ //
+ // - The `response_size` pointer must be a valid pointer to a mutable
+ // unsigned int. The implementation will write the response buffer
+ // size to this address.
+ //
+ // - The `response` pointer must be a valid pointer to a mutable
+ // unsigned char pointer. The implementation will write a pointer to
+ // the start of the response buffer to this address.
+ //
+ // - No more than `response_size` bytes may be read from the response
+ // buffer after the call returns.
+ //
+ // - Data may be read from the response buffer only until the next
+ // call to ExecuteCommand.
+ //
+ // The first guarantee is enforced by the public API of the Simulator
+ // struct, and in particular the singleton_in_current_directory
+ // constructor, which only makes a value of type Simulator available
+ // outside of this module after TPM initialization has been performed.
+ // Thus any Simulator on which the caller may be calling execute_command
+ // from outside of this module is witness that initialization has taken
+ // place.
+ //
+ // The second guarantee is made jointly by the &mut self reference in
+ // execute_command and the singleton_in_current_directory constructor
+ // which uses the SIMULATOR_EXISTS atomic flag to ensure that at most
+ // one value of type Simulator is ever made available to code outside of
+ // this module. Since at most one Simulator exists, and the caller is
+ // holding an exclusive reference to a Simulator, we know that no other
+ // code can be calling execute_command at the same time because they too
+ // would need their own exclusive reference to the same Simulator. We
+ // assume here that all use of libtpm2 within crosvm happens through the
+ // safe bindings provided by this tpm2 crate, so that the codebase
+ // contains no other unsafe calls to ExecuteCommand.
+ //
+ // The remaining guarantees are upheld by the signature and
+ // implementation of execute_command. In particular, note the lifetime
+ // 'a which ties the lifetime of the response slice we return to the
+ // caller to the lifetime of their exclusively held reference to
+ // Simulator. This signature looks the same to Rust as if the response
+ // buffer were a field inside the Simulator struct, rather than a
+ // statically allocated buffer inside libtpm2. As soon as the caller
+ // "mutates" the Simulator by performing another call to
+ // execute_command, the response buffer returned by the previous call is
+ // assumed to be invalidated and is made inaccessible by the borrow
+ // checker.
+ //
+ // Altogether we have guaranteed that execute_command is a safe
+ // abstraction around unsafe code and is entirely safe to call from
+ // outside of this module.
+ //
+ // Note additionally that the call to ExecuteCommand is over FFI so we
+ // need to know that the signature declared by tpm2-sys is
+ // ABI-compatible with the symbol provided by libtpm2.
+ unsafe {
+ tpm2_sys::ExecuteCommand(request_size, request, &mut response_size, &mut response);
+ slice::from_raw_parts(response, response_size as usize)
+ }
+ }
+}
+
+fn tpm_manufacture(first_time: bool) {
+ // From libtpm2 documentation:
+ //
+ // This function initializes the TPM values in preparation for the TPM's
+ // first use. This function will fail if previously called. The TPM can
+ // be re-manufactured by calling TPM_Teardown() first and then calling
+ // this function again.
+ //
+ // Arguments
+ //
+ // firstTime: indicates if this is the first call from main()
+ //
+ // Return value
+ //
+ // 0 = success
+ // 1 = manufacturing process previously performed
+ //
+ // Unsafe only because this is over FFI and we need to know that the
+ // signature declared by tpm2-sys is ABI-compatible with the symbol provided
+ // by libtpm2. There are no other invariants to uphold.
+ let ret: c_int = unsafe { tpm2_sys::TPM_Manufacture(first_time as c_int) };
+
+ // We expect that the TPM must not already have been manufactured. The
+ // SIMULATOR_EXISTS atomic flag guards calls to this function such that only
+ // one call can ever be performed by a process.
+ assert!(ret == 0);
+}
+
+fn plat_set_nv_avail() {
+ // From libtpm2 documentation:
+ //
+ // Set the current NV state to available. This function is for testing
+ // purpose only. It is not part of the platform NV logic.
+ //
+ // The "for testing purpose only" is unsettling but trunks performs the same
+ // call during initialization so we trust that it is okay.
+ //
+ // Unsafe only because this is over FFI and we need to know that the
+ // signature declared by tpm2-sys is ABI-compatible with the symbol provided
+ // by libtpm2. There are no other invariants to uphold.
+ unsafe {
+ tpm2_sys::_plat__SetNvAvail();
+ }
+}
+
+fn plat_signal_power_on() {
+ // From libtpm2 documentation:
+ //
+ // Signal platform power on.
+ //
+ // The libtpm2 implementation always returns 0 but does not document what
+ // the return value means, so we aren't checking it.
+ //
+ // Unsafe only because this is over FFI and we need to know that the
+ // signature declared by tpm2-sys is ABI-compatible with the symbol provided
+ // by libtpm2. There are no other invariants to uphold.
+ unsafe {
+ let _: c_int = tpm2_sys::_plat__Signal_PowerOn();
+ }
+}
+
+fn tpm_init() {
+ // This function is not documented in libtpm2. Trunks performs the same call
+ // during initialization so we trust that it is okay.
+ //
+ // Unsafe only because this is over FFI and we need to know that the
+ // signature declared by tpm2-sys is ABI-compatible with the symbol provided
+ // by libtpm2. There are no other invariants to uphold.
+ unsafe {
+ tpm2_sys::_TPM_Init();
+ }
+}
diff --git a/usb_util/Cargo.toml b/usb_util/Cargo.toml
new file mode 100644
index 0000000..fab07d6
--- /dev/null
+++ b/usb_util/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "usb_util"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+build = "build.rs"
+
+[features]
+sandboxed-libusb = []
+
+[dependencies]
+assertions = { path = "../assertions" }
+data_model = { path = "../data_model" }
+sync = { path = "../sync" }
+
+[build-dependencies]
+pkg-config = "=0.3.11"
diff --git a/usb_util/build.rs b/usb_util/build.rs
new file mode 100644
index 0000000..34e14da
--- /dev/null
+++ b/usb_util/build.rs
@@ -0,0 +1,10 @@
+// Copyright 2018 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::env;
+
+fn main() {
+ env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");
+ pkg_config::probe_library("libusb-1.0").unwrap();
+}
diff --git a/usb_util/src/bindings.rs b/usb_util/src/bindings.rs
new file mode 100644
index 0000000..a3e7152
--- /dev/null
+++ b/usb_util/src/bindings.rs
@@ -0,0 +1,4648 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData)
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self::new()
+ }
+}
+impl<T> ::std::marker::Copy for __IncompleteArrayField<T> {}
+pub const _STDINT_H: u32 = 1;
+pub const _FEATURES_H: u32 = 1;
+pub const _DEFAULT_SOURCE: u32 = 1;
+pub const __USE_ISOC11: u32 = 1;
+pub const __USE_ISOC99: u32 = 1;
+pub const __USE_ISOC95: u32 = 1;
+pub const __USE_POSIX_IMPLICITLY: u32 = 1;
+pub const _POSIX_SOURCE: u32 = 1;
+pub const _POSIX_C_SOURCE: u32 = 200809;
+pub const __USE_POSIX: u32 = 1;
+pub const __USE_POSIX2: u32 = 1;
+pub const __USE_POSIX199309: u32 = 1;
+pub const __USE_POSIX199506: u32 = 1;
+pub const __USE_XOPEN2K: u32 = 1;
+pub const __USE_XOPEN2K8: u32 = 1;
+pub const _ATFILE_SOURCE: u32 = 1;
+pub const __USE_MISC: u32 = 1;
+pub const __USE_ATFILE: u32 = 1;
+pub const __USE_FORTIFY_LEVEL: u32 = 0;
+pub const _STDC_PREDEF_H: u32 = 1;
+pub const __STDC_IEC_559__: u32 = 1;
+pub const __STDC_IEC_559_COMPLEX__: u32 = 1;
+pub const __STDC_ISO_10646__: u32 = 201605;
+pub const __STDC_NO_THREADS__: u32 = 1;
+pub const __GNU_LIBRARY__: u32 = 6;
+pub const __GLIBC__: u32 = 2;
+pub const __GLIBC_MINOR__: u32 = 24;
+pub const _SYS_CDEFS_H: u32 = 1;
+pub const __WORDSIZE: u32 = 64;
+pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1;
+pub const __SYSCALL_WORDSIZE: u32 = 64;
+pub const _BITS_WCHAR_H: u32 = 1;
+pub const INT8_MIN: i32 = -128;
+pub const INT16_MIN: i32 = -32768;
+pub const INT32_MIN: i32 = -2147483648;
+pub const INT8_MAX: u32 = 127;
+pub const INT16_MAX: u32 = 32767;
+pub const INT32_MAX: u32 = 2147483647;
+pub const UINT8_MAX: u32 = 255;
+pub const UINT16_MAX: u32 = 65535;
+pub const UINT32_MAX: u32 = 4294967295;
+pub const INT_LEAST8_MIN: i32 = -128;
+pub const INT_LEAST16_MIN: i32 = -32768;
+pub const INT_LEAST32_MIN: i32 = -2147483648;
+pub const INT_LEAST8_MAX: u32 = 127;
+pub const INT_LEAST16_MAX: u32 = 32767;
+pub const INT_LEAST32_MAX: u32 = 2147483647;
+pub const UINT_LEAST8_MAX: u32 = 255;
+pub const UINT_LEAST16_MAX: u32 = 65535;
+pub const UINT_LEAST32_MAX: u32 = 4294967295;
+pub const INT_FAST8_MIN: i32 = -128;
+pub const INT_FAST16_MIN: i64 = -9223372036854775808;
+pub const INT_FAST32_MIN: i64 = -9223372036854775808;
+pub const INT_FAST8_MAX: u32 = 127;
+pub const INT_FAST16_MAX: u64 = 9223372036854775807;
+pub const INT_FAST32_MAX: u64 = 9223372036854775807;
+pub const UINT_FAST8_MAX: u32 = 255;
+pub const UINT_FAST16_MAX: i32 = -1;
+pub const UINT_FAST32_MAX: i32 = -1;
+pub const INTPTR_MIN: i64 = -9223372036854775808;
+pub const INTPTR_MAX: u64 = 9223372036854775807;
+pub const UINTPTR_MAX: i32 = -1;
+pub const PTRDIFF_MIN: i64 = -9223372036854775808;
+pub const PTRDIFF_MAX: u64 = 9223372036854775807;
+pub const SIG_ATOMIC_MIN: i32 = -2147483648;
+pub const SIG_ATOMIC_MAX: u32 = 2147483647;
+pub const SIZE_MAX: i32 = -1;
+pub const WINT_MIN: u32 = 0;
+pub const WINT_MAX: u32 = 4294967295;
+pub const _SYS_TYPES_H: u32 = 1;
+pub const _BITS_TYPES_H: u32 = 1;
+pub const _BITS_TYPESIZES_H: u32 = 1;
+pub const __OFF_T_MATCHES_OFF64_T: u32 = 1;
+pub const __INO_T_MATCHES_INO64_T: u32 = 1;
+pub const __FD_SETSIZE: u32 = 1024;
+pub const __clock_t_defined: u32 = 1;
+pub const __time_t_defined: u32 = 1;
+pub const __clockid_t_defined: u32 = 1;
+pub const __timer_t_defined: u32 = 1;
+pub const __BIT_TYPES_DEFINED__: u32 = 1;
+pub const _ENDIAN_H: u32 = 1;
+pub const __LITTLE_ENDIAN: u32 = 1234;
+pub const __BIG_ENDIAN: u32 = 4321;
+pub const __PDP_ENDIAN: u32 = 3412;
+pub const __BYTE_ORDER: u32 = 1234;
+pub const __FLOAT_WORD_ORDER: u32 = 1234;
+pub const LITTLE_ENDIAN: u32 = 1234;
+pub const BIG_ENDIAN: u32 = 4321;
+pub const PDP_ENDIAN: u32 = 3412;
+pub const BYTE_ORDER: u32 = 1234;
+pub const _BITS_BYTESWAP_H: u32 = 1;
+pub const _SYS_SELECT_H: u32 = 1;
+pub const __FD_ZERO_STOS: &'static [u8; 6usize] = b"stosq\0";
+pub const _SIGSET_H_types: u32 = 1;
+pub const __timespec_defined: u32 = 1;
+pub const _STRUCT_TIMEVAL: u32 = 1;
+pub const FD_SETSIZE: u32 = 1024;
+pub const _SYS_SYSMACROS_H: u32 = 1;
+pub const _BITS_PTHREADTYPES_H: u32 = 1;
+pub const __SIZEOF_PTHREAD_ATTR_T: u32 = 56;
+pub const __SIZEOF_PTHREAD_MUTEX_T: u32 = 40;
+pub const __SIZEOF_PTHREAD_MUTEXATTR_T: u32 = 4;
+pub const __SIZEOF_PTHREAD_COND_T: u32 = 48;
+pub const __SIZEOF_PTHREAD_CONDATTR_T: u32 = 4;
+pub const __SIZEOF_PTHREAD_RWLOCK_T: u32 = 56;
+pub const __SIZEOF_PTHREAD_RWLOCKATTR_T: u32 = 8;
+pub const __SIZEOF_PTHREAD_BARRIER_T: u32 = 32;
+pub const __SIZEOF_PTHREAD_BARRIERATTR_T: u32 = 4;
+pub const __have_pthread_attr_t: u32 = 1;
+pub const __PTHREAD_MUTEX_HAVE_PREV: u32 = 1;
+pub const __PTHREAD_RWLOCK_INT_FLAGS_SHARED: u32 = 1;
+pub const _SYS_TIME_H: u32 = 1;
+pub const _TIME_H: u32 = 1;
+pub const _BITS_TIME_H: u32 = 1;
+pub const CLOCK_REALTIME: u32 = 0;
+pub const CLOCK_MONOTONIC: u32 = 1;
+pub const CLOCK_PROCESS_CPUTIME_ID: u32 = 2;
+pub const CLOCK_THREAD_CPUTIME_ID: u32 = 3;
+pub const CLOCK_MONOTONIC_RAW: u32 = 4;
+pub const CLOCK_REALTIME_COARSE: u32 = 5;
+pub const CLOCK_MONOTONIC_COARSE: u32 = 6;
+pub const CLOCK_BOOTTIME: u32 = 7;
+pub const CLOCK_REALTIME_ALARM: u32 = 8;
+pub const CLOCK_BOOTTIME_ALARM: u32 = 9;
+pub const CLOCK_TAI: u32 = 11;
+pub const TIMER_ABSTIME: u32 = 1;
+pub const TIME_UTC: u32 = 1;
+pub const _XLOCALE_H: u32 = 1;
+pub const _LIBC_LIMITS_H_: u32 = 1;
+pub const MB_LEN_MAX: u32 = 16;
+pub const _BITS_POSIX1_LIM_H: u32 = 1;
+pub const _POSIX_AIO_LISTIO_MAX: u32 = 2;
+pub const _POSIX_AIO_MAX: u32 = 1;
+pub const _POSIX_ARG_MAX: u32 = 4096;
+pub const _POSIX_CHILD_MAX: u32 = 25;
+pub const _POSIX_DELAYTIMER_MAX: u32 = 32;
+pub const _POSIX_HOST_NAME_MAX: u32 = 255;
+pub const _POSIX_LINK_MAX: u32 = 8;
+pub const _POSIX_LOGIN_NAME_MAX: u32 = 9;
+pub const _POSIX_MAX_CANON: u32 = 255;
+pub const _POSIX_MAX_INPUT: u32 = 255;
+pub const _POSIX_MQ_OPEN_MAX: u32 = 8;
+pub const _POSIX_MQ_PRIO_MAX: u32 = 32;
+pub const _POSIX_NAME_MAX: u32 = 14;
+pub const _POSIX_NGROUPS_MAX: u32 = 8;
+pub const _POSIX_OPEN_MAX: u32 = 20;
+pub const _POSIX_PATH_MAX: u32 = 256;
+pub const _POSIX_PIPE_BUF: u32 = 512;
+pub const _POSIX_RE_DUP_MAX: u32 = 255;
+pub const _POSIX_RTSIG_MAX: u32 = 8;
+pub const _POSIX_SEM_NSEMS_MAX: u32 = 256;
+pub const _POSIX_SEM_VALUE_MAX: u32 = 32767;
+pub const _POSIX_SIGQUEUE_MAX: u32 = 32;
+pub const _POSIX_SSIZE_MAX: u32 = 32767;
+pub const _POSIX_STREAM_MAX: u32 = 8;
+pub const _POSIX_SYMLINK_MAX: u32 = 255;
+pub const _POSIX_SYMLOOP_MAX: u32 = 8;
+pub const _POSIX_TIMER_MAX: u32 = 32;
+pub const _POSIX_TTY_NAME_MAX: u32 = 9;
+pub const _POSIX_TZNAME_MAX: u32 = 6;
+pub const _POSIX_CLOCKRES_MIN: u32 = 20000000;
+pub const NR_OPEN: u32 = 1024;
+pub const NGROUPS_MAX: u32 = 65536;
+pub const ARG_MAX: u32 = 131072;
+pub const LINK_MAX: u32 = 127;
+pub const MAX_CANON: u32 = 255;
+pub const MAX_INPUT: u32 = 255;
+pub const NAME_MAX: u32 = 255;
+pub const PATH_MAX: u32 = 4096;
+pub const PIPE_BUF: u32 = 4096;
+pub const XATTR_NAME_MAX: u32 = 255;
+pub const XATTR_SIZE_MAX: u32 = 65536;
+pub const XATTR_LIST_MAX: u32 = 65536;
+pub const RTSIG_MAX: u32 = 32;
+pub const _POSIX_THREAD_KEYS_MAX: u32 = 128;
+pub const PTHREAD_KEYS_MAX: u32 = 1024;
+pub const _POSIX_THREAD_DESTRUCTOR_ITERATIONS: u32 = 4;
+pub const PTHREAD_DESTRUCTOR_ITERATIONS: u32 = 4;
+pub const _POSIX_THREAD_THREADS_MAX: u32 = 64;
+pub const AIO_PRIO_DELTA_MAX: u32 = 20;
+pub const PTHREAD_STACK_MIN: u32 = 16384;
+pub const DELAYTIMER_MAX: u32 = 2147483647;
+pub const TTY_NAME_MAX: u32 = 32;
+pub const LOGIN_NAME_MAX: u32 = 256;
+pub const HOST_NAME_MAX: u32 = 64;
+pub const MQ_PRIO_MAX: u32 = 32768;
+pub const SEM_VALUE_MAX: u32 = 2147483647;
+pub const _BITS_POSIX2_LIM_H: u32 = 1;
+pub const _POSIX2_BC_BASE_MAX: u32 = 99;
+pub const _POSIX2_BC_DIM_MAX: u32 = 2048;
+pub const _POSIX2_BC_SCALE_MAX: u32 = 99;
+pub const _POSIX2_BC_STRING_MAX: u32 = 1000;
+pub const _POSIX2_COLL_WEIGHTS_MAX: u32 = 2;
+pub const _POSIX2_EXPR_NEST_MAX: u32 = 32;
+pub const _POSIX2_LINE_MAX: u32 = 2048;
+pub const _POSIX2_RE_DUP_MAX: u32 = 255;
+pub const _POSIX2_CHARCLASS_NAME_MAX: u32 = 14;
+pub const BC_BASE_MAX: u32 = 99;
+pub const BC_DIM_MAX: u32 = 2048;
+pub const BC_SCALE_MAX: u32 = 99;
+pub const BC_STRING_MAX: u32 = 1000;
+pub const COLL_WEIGHTS_MAX: u32 = 255;
+pub const EXPR_NEST_MAX: u32 = 32;
+pub const LINE_MAX: u32 = 2048;
+pub const CHARCLASS_NAME_MAX: u32 = 2048;
+pub const RE_DUP_MAX: u32 = 32767;
+pub const LIBUSB_API_VERSION: u32 = 16777477;
+pub const LIBUSBX_API_VERSION: u32 = 16777477;
+pub const LIBUSB_DT_DEVICE_SIZE: u32 = 18;
+pub const LIBUSB_DT_CONFIG_SIZE: u32 = 9;
+pub const LIBUSB_DT_INTERFACE_SIZE: u32 = 9;
+pub const LIBUSB_DT_ENDPOINT_SIZE: u32 = 7;
+pub const LIBUSB_DT_ENDPOINT_AUDIO_SIZE: u32 = 9;
+pub const LIBUSB_DT_HUB_NONVAR_SIZE: u32 = 7;
+pub const LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE: u32 = 6;
+pub const LIBUSB_DT_BOS_SIZE: u32 = 5;
+pub const LIBUSB_DT_DEVICE_CAPABILITY_SIZE: u32 = 3;
+pub const LIBUSB_BT_USB_2_0_EXTENSION_SIZE: u32 = 7;
+pub const LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE: u32 = 10;
+pub const LIBUSB_BT_CONTAINER_ID_SIZE: u32 = 20;
+pub const LIBUSB_DT_BOS_MAX_SIZE: u32 = 42;
+pub const LIBUSB_ENDPOINT_ADDRESS_MASK: u32 = 15;
+pub const LIBUSB_ENDPOINT_DIR_MASK: u32 = 128;
+pub const LIBUSB_TRANSFER_TYPE_MASK: u32 = 3;
+pub const LIBUSB_ISO_SYNC_TYPE_MASK: u32 = 12;
+pub const LIBUSB_ISO_USAGE_TYPE_MASK: u32 = 48;
+pub const LIBUSB_ERROR_COUNT: u32 = 14;
+pub const LIBUSB_HOTPLUG_MATCH_ANY: i32 = -1;
+pub type int_least8_t = ::std::os::raw::c_schar;
+pub type int_least16_t = ::std::os::raw::c_short;
+pub type int_least32_t = ::std::os::raw::c_int;
+pub type int_least64_t = ::std::os::raw::c_long;
+pub type uint_least8_t = ::std::os::raw::c_uchar;
+pub type uint_least16_t = ::std::os::raw::c_ushort;
+pub type uint_least32_t = ::std::os::raw::c_uint;
+pub type uint_least64_t = ::std::os::raw::c_ulong;
+pub type int_fast8_t = ::std::os::raw::c_schar;
+pub type int_fast16_t = ::std::os::raw::c_long;
+pub type int_fast32_t = ::std::os::raw::c_long;
+pub type int_fast64_t = ::std::os::raw::c_long;
+pub type uint_fast8_t = ::std::os::raw::c_uchar;
+pub type uint_fast16_t = ::std::os::raw::c_ulong;
+pub type uint_fast32_t = ::std::os::raw::c_ulong;
+pub type uint_fast64_t = ::std::os::raw::c_ulong;
+pub type intmax_t = ::std::os::raw::c_long;
+pub type uintmax_t = ::std::os::raw::c_ulong;
+pub type __u_char = ::std::os::raw::c_uchar;
+pub type __u_short = ::std::os::raw::c_ushort;
+pub type __u_int = ::std::os::raw::c_uint;
+pub type __u_long = ::std::os::raw::c_ulong;
+pub type __int8_t = ::std::os::raw::c_schar;
+pub type __uint8_t = ::std::os::raw::c_uchar;
+pub type __int16_t = ::std::os::raw::c_short;
+pub type __uint16_t = ::std::os::raw::c_ushort;
+pub type __int32_t = ::std::os::raw::c_int;
+pub type __uint32_t = ::std::os::raw::c_uint;
+pub type __int64_t = ::std::os::raw::c_long;
+pub type __uint64_t = ::std::os::raw::c_ulong;
+pub type __quad_t = ::std::os::raw::c_long;
+pub type __u_quad_t = ::std::os::raw::c_ulong;
+pub type __dev_t = ::std::os::raw::c_ulong;
+pub type __uid_t = ::std::os::raw::c_uint;
+pub type __gid_t = ::std::os::raw::c_uint;
+pub type __ino_t = ::std::os::raw::c_ulong;
+pub type __ino64_t = ::std::os::raw::c_ulong;
+pub type __mode_t = ::std::os::raw::c_uint;
+pub type __nlink_t = ::std::os::raw::c_ulong;
+pub type __off_t = ::std::os::raw::c_long;
+pub type __off64_t = ::std::os::raw::c_long;
+pub type __pid_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __fsid_t {
+ pub __val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___fsid_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__fsid_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(__fsid_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__fsid_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(__fsid_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__fsid_t>())).__val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__fsid_t),
+ "::",
+ stringify!(__val)
+ )
+ );
+}
+pub type __clock_t = ::std::os::raw::c_long;
+pub type __rlim_t = ::std::os::raw::c_ulong;
+pub type __rlim64_t = ::std::os::raw::c_ulong;
+pub type __id_t = ::std::os::raw::c_uint;
+pub type __time_t = ::std::os::raw::c_long;
+pub type __useconds_t = ::std::os::raw::c_uint;
+pub type __suseconds_t = ::std::os::raw::c_long;
+pub type __daddr_t = ::std::os::raw::c_int;
+pub type __key_t = ::std::os::raw::c_int;
+pub type __clockid_t = ::std::os::raw::c_int;
+pub type __timer_t = *mut ::std::os::raw::c_void;
+pub type __blksize_t = ::std::os::raw::c_long;
+pub type __blkcnt_t = ::std::os::raw::c_long;
+pub type __blkcnt64_t = ::std::os::raw::c_long;
+pub type __fsblkcnt_t = ::std::os::raw::c_ulong;
+pub type __fsblkcnt64_t = ::std::os::raw::c_ulong;
+pub type __fsfilcnt_t = ::std::os::raw::c_ulong;
+pub type __fsfilcnt64_t = ::std::os::raw::c_ulong;
+pub type __fsword_t = ::std::os::raw::c_long;
+pub type __ssize_t = ::std::os::raw::c_long;
+pub type __syscall_slong_t = ::std::os::raw::c_long;
+pub type __syscall_ulong_t = ::std::os::raw::c_ulong;
+pub type __loff_t = __off64_t;
+pub type __qaddr_t = *mut __quad_t;
+pub type __caddr_t = *mut ::std::os::raw::c_char;
+pub type __intptr_t = ::std::os::raw::c_long;
+pub type __socklen_t = ::std::os::raw::c_uint;
+pub type u_char = __u_char;
+pub type u_short = __u_short;
+pub type u_int = __u_int;
+pub type u_long = __u_long;
+pub type quad_t = __quad_t;
+pub type u_quad_t = __u_quad_t;
+pub type fsid_t = __fsid_t;
+pub type loff_t = __loff_t;
+pub type ino_t = __ino_t;
+pub type dev_t = __dev_t;
+pub type gid_t = __gid_t;
+pub type mode_t = __mode_t;
+pub type nlink_t = __nlink_t;
+pub type uid_t = __uid_t;
+pub type off_t = __off_t;
+pub type pid_t = __pid_t;
+pub type id_t = __id_t;
+pub type daddr_t = __daddr_t;
+pub type caddr_t = __caddr_t;
+pub type key_t = __key_t;
+pub type clock_t = __clock_t;
+pub type time_t = __time_t;
+pub type clockid_t = __clockid_t;
+pub type timer_t = __timer_t;
+pub type ulong = ::std::os::raw::c_ulong;
+pub type ushort = ::std::os::raw::c_ushort;
+pub type uint = ::std::os::raw::c_uint;
+pub type u_int8_t = ::std::os::raw::c_uchar;
+pub type u_int16_t = ::std::os::raw::c_ushort;
+pub type u_int32_t = ::std::os::raw::c_uint;
+pub type u_int64_t = ::std::os::raw::c_ulong;
+pub type register_t = ::std::os::raw::c_long;
+pub type __sig_atomic_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __sigset_t {
+ pub __val: [::std::os::raw::c_ulong; 16usize],
+}
+#[test]
+fn bindgen_test_layout___sigset_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__sigset_t>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__sigset_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__sigset_t>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__sigset_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__sigset_t>())).__val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__sigset_t),
+ "::",
+ stringify!(__val)
+ )
+ );
+}
+pub type sigset_t = __sigset_t;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct timespec {
+ pub tv_sec: __time_t,
+ pub tv_nsec: __syscall_slong_t,
+}
+#[test]
+fn bindgen_test_layout_timespec() {
+ assert_eq!(
+ ::std::mem::size_of::<timespec>(),
+ 16usize,
+ concat!("Size of: ", stringify!(timespec))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<timespec>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(timespec))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<timespec>())).tv_sec as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timespec),
+ "::",
+ stringify!(tv_sec)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<timespec>())).tv_nsec as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timespec),
+ "::",
+ stringify!(tv_nsec)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct timeval {
+ pub tv_sec: __time_t,
+ pub tv_usec: __suseconds_t,
+}
+#[test]
+fn bindgen_test_layout_timeval() {
+ assert_eq!(
+ ::std::mem::size_of::<timeval>(),
+ 16usize,
+ concat!("Size of: ", stringify!(timeval))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<timeval>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(timeval))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<timeval>())).tv_sec as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeval),
+ "::",
+ stringify!(tv_sec)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<timeval>())).tv_usec as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timeval),
+ "::",
+ stringify!(tv_usec)
+ )
+ );
+}
+pub type suseconds_t = __suseconds_t;
+pub type __fd_mask = ::std::os::raw::c_long;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct fd_set {
+ pub __fds_bits: [__fd_mask; 16usize],
+}
+#[test]
+fn bindgen_test_layout_fd_set() {
+ assert_eq!(
+ ::std::mem::size_of::<fd_set>(),
+ 128usize,
+ concat!("Size of: ", stringify!(fd_set))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<fd_set>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(fd_set))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<fd_set>())).__fds_bits as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(fd_set),
+ "::",
+ stringify!(__fds_bits)
+ )
+ );
+}
+pub type fd_mask = __fd_mask;
+extern "C" {
+ pub fn select(
+ __nfds: ::std::os::raw::c_int,
+ __readfds: *mut fd_set,
+ __writefds: *mut fd_set,
+ __exceptfds: *mut fd_set,
+ __timeout: *mut timeval,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn pselect(
+ __nfds: ::std::os::raw::c_int,
+ __readfds: *mut fd_set,
+ __writefds: *mut fd_set,
+ __exceptfds: *mut fd_set,
+ __timeout: *const timespec,
+ __sigmask: *const __sigset_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn gnu_dev_major(__dev: ::std::os::raw::c_ulonglong) -> ::std::os::raw::c_uint;
+}
+extern "C" {
+ pub fn gnu_dev_minor(__dev: ::std::os::raw::c_ulonglong) -> ::std::os::raw::c_uint;
+}
+extern "C" {
+ pub fn gnu_dev_makedev(
+ __major: ::std::os::raw::c_uint,
+ __minor: ::std::os::raw::c_uint,
+ ) -> ::std::os::raw::c_ulonglong;
+}
+pub type blksize_t = __blksize_t;
+pub type blkcnt_t = __blkcnt_t;
+pub type fsblkcnt_t = __fsblkcnt_t;
+pub type fsfilcnt_t = __fsfilcnt_t;
+pub type pthread_t = ::std::os::raw::c_ulong;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_attr_t {
+ pub __size: [::std::os::raw::c_char; 56usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: [u64; 7usize],
+}
+#[test]
+fn bindgen_test_layout_pthread_attr_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_attr_t>(),
+ 56usize,
+ concat!("Size of: ", stringify!(pthread_attr_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_attr_t>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(pthread_attr_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_attr_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_attr_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_attr_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_attr_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __pthread_internal_list {
+ pub __prev: *mut __pthread_internal_list,
+ pub __next: *mut __pthread_internal_list,
+}
+#[test]
+fn bindgen_test_layout___pthread_internal_list() {
+ assert_eq!(
+ ::std::mem::size_of::<__pthread_internal_list>(),
+ 16usize,
+ concat!("Size of: ", stringify!(__pthread_internal_list))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__pthread_internal_list>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__pthread_internal_list))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__pthread_internal_list>())).__prev as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__pthread_internal_list),
+ "::",
+ stringify!(__prev)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__pthread_internal_list>())).__next as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__pthread_internal_list),
+ "::",
+ stringify!(__next)
+ )
+ );
+}
+pub type __pthread_list_t = __pthread_internal_list;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_mutex_t {
+ pub __data: pthread_mutex_t___pthread_mutex_s,
+ pub __size: [::std::os::raw::c_char; 40usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: [u64; 5usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pthread_mutex_t___pthread_mutex_s {
+ pub __lock: ::std::os::raw::c_int,
+ pub __count: ::std::os::raw::c_uint,
+ pub __owner: ::std::os::raw::c_int,
+ pub __nusers: ::std::os::raw::c_uint,
+ pub __kind: ::std::os::raw::c_int,
+ pub __spins: ::std::os::raw::c_short,
+ pub __elision: ::std::os::raw::c_short,
+ pub __list: __pthread_list_t,
+}
+#[test]
+fn bindgen_test_layout_pthread_mutex_t___pthread_mutex_s() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_mutex_t___pthread_mutex_s>(),
+ 40usize,
+ concat!("Size of: ", stringify!(pthread_mutex_t___pthread_mutex_s))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_mutex_t___pthread_mutex_s>(),
+ 8usize,
+ concat!(
+ "Alignment of ",
+ stringify!(pthread_mutex_t___pthread_mutex_s)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_mutex_t___pthread_mutex_s>())).__lock as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t___pthread_mutex_s),
+ "::",
+ stringify!(__lock)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_mutex_t___pthread_mutex_s>())).__count as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t___pthread_mutex_s),
+ "::",
+ stringify!(__count)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_mutex_t___pthread_mutex_s>())).__owner as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t___pthread_mutex_s),
+ "::",
+ stringify!(__owner)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_mutex_t___pthread_mutex_s>())).__nusers as *const _
+ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t___pthread_mutex_s),
+ "::",
+ stringify!(__nusers)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_mutex_t___pthread_mutex_s>())).__kind as *const _
+ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t___pthread_mutex_s),
+ "::",
+ stringify!(__kind)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_mutex_t___pthread_mutex_s>())).__spins as *const _
+ as usize
+ },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t___pthread_mutex_s),
+ "::",
+ stringify!(__spins)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_mutex_t___pthread_mutex_s>())).__elision as *const _
+ as usize
+ },
+ 22usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t___pthread_mutex_s),
+ "::",
+ stringify!(__elision)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_mutex_t___pthread_mutex_s>())).__list as *const _
+ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t___pthread_mutex_s),
+ "::",
+ stringify!(__list)
+ )
+ );
+}
+#[test]
+fn bindgen_test_layout_pthread_mutex_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_mutex_t>(),
+ 40usize,
+ concat!("Size of: ", stringify!(pthread_mutex_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_mutex_t>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(pthread_mutex_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_mutex_t>())).__data as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t),
+ "::",
+ stringify!(__data)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_mutex_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_mutex_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutex_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_mutexattr_t {
+ pub __size: [::std::os::raw::c_char; 4usize],
+ pub __align: ::std::os::raw::c_int,
+ _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_pthread_mutexattr_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_mutexattr_t>(),
+ 4usize,
+ concat!("Size of: ", stringify!(pthread_mutexattr_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_mutexattr_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(pthread_mutexattr_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_mutexattr_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutexattr_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_mutexattr_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_mutexattr_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_cond_t {
+ pub __data: pthread_cond_t__bindgen_ty_1,
+ pub __size: [::std::os::raw::c_char; 48usize],
+ pub __align: ::std::os::raw::c_longlong,
+ _bindgen_union_align: [u64; 6usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pthread_cond_t__bindgen_ty_1 {
+ pub __lock: ::std::os::raw::c_int,
+ pub __futex: ::std::os::raw::c_uint,
+ pub __total_seq: ::std::os::raw::c_ulonglong,
+ pub __wakeup_seq: ::std::os::raw::c_ulonglong,
+ pub __woken_seq: ::std::os::raw::c_ulonglong,
+ pub __mutex: *mut ::std::os::raw::c_void,
+ pub __nwaiters: ::std::os::raw::c_uint,
+ pub __broadcast_seq: ::std::os::raw::c_uint,
+}
+#[test]
+fn bindgen_test_layout_pthread_cond_t__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_cond_t__bindgen_ty_1>(),
+ 48usize,
+ concat!("Size of: ", stringify!(pthread_cond_t__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_cond_t__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(pthread_cond_t__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_cond_t__bindgen_ty_1>())).__lock as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t__bindgen_ty_1),
+ "::",
+ stringify!(__lock)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_cond_t__bindgen_ty_1>())).__futex as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t__bindgen_ty_1),
+ "::",
+ stringify!(__futex)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_cond_t__bindgen_ty_1>())).__total_seq as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t__bindgen_ty_1),
+ "::",
+ stringify!(__total_seq)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_cond_t__bindgen_ty_1>())).__wakeup_seq as *const _
+ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t__bindgen_ty_1),
+ "::",
+ stringify!(__wakeup_seq)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_cond_t__bindgen_ty_1>())).__woken_seq as *const _
+ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t__bindgen_ty_1),
+ "::",
+ stringify!(__woken_seq)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_cond_t__bindgen_ty_1>())).__mutex as *const _ as usize
+ },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t__bindgen_ty_1),
+ "::",
+ stringify!(__mutex)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_cond_t__bindgen_ty_1>())).__nwaiters as *const _ as usize
+ },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t__bindgen_ty_1),
+ "::",
+ stringify!(__nwaiters)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_cond_t__bindgen_ty_1>())).__broadcast_seq as *const _
+ as usize
+ },
+ 44usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t__bindgen_ty_1),
+ "::",
+ stringify!(__broadcast_seq)
+ )
+ );
+}
+#[test]
+fn bindgen_test_layout_pthread_cond_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_cond_t>(),
+ 48usize,
+ concat!("Size of: ", stringify!(pthread_cond_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_cond_t>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(pthread_cond_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_cond_t>())).__data as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t),
+ "::",
+ stringify!(__data)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_cond_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_cond_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_cond_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_condattr_t {
+ pub __size: [::std::os::raw::c_char; 4usize],
+ pub __align: ::std::os::raw::c_int,
+ _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_pthread_condattr_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_condattr_t>(),
+ 4usize,
+ concat!("Size of: ", stringify!(pthread_condattr_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_condattr_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(pthread_condattr_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_condattr_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_condattr_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_condattr_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_condattr_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+pub type pthread_key_t = ::std::os::raw::c_uint;
+pub type pthread_once_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_rwlock_t {
+ pub __data: pthread_rwlock_t__bindgen_ty_1,
+ pub __size: [::std::os::raw::c_char; 56usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: [u64; 7usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pthread_rwlock_t__bindgen_ty_1 {
+ pub __lock: ::std::os::raw::c_int,
+ pub __nr_readers: ::std::os::raw::c_uint,
+ pub __readers_wakeup: ::std::os::raw::c_uint,
+ pub __writer_wakeup: ::std::os::raw::c_uint,
+ pub __nr_readers_queued: ::std::os::raw::c_uint,
+ pub __nr_writers_queued: ::std::os::raw::c_uint,
+ pub __writer: ::std::os::raw::c_int,
+ pub __shared: ::std::os::raw::c_int,
+ pub __rwelision: ::std::os::raw::c_schar,
+ pub __pad1: [::std::os::raw::c_uchar; 7usize],
+ pub __pad2: ::std::os::raw::c_ulong,
+ pub __flags: ::std::os::raw::c_uint,
+}
+#[test]
+fn bindgen_test_layout_pthread_rwlock_t__bindgen_ty_1() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_rwlock_t__bindgen_ty_1>(),
+ 56usize,
+ concat!("Size of: ", stringify!(pthread_rwlock_t__bindgen_ty_1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_rwlock_t__bindgen_ty_1>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(pthread_rwlock_t__bindgen_ty_1))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__lock as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__lock)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__nr_readers as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__nr_readers)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__readers_wakeup as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__readers_wakeup)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__writer_wakeup as *const _
+ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__writer_wakeup)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__nr_readers_queued
+ as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__nr_readers_queued)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__nr_writers_queued
+ as *const _ as usize
+ },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__nr_writers_queued)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__writer as *const _ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__writer)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__shared as *const _ as usize
+ },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__shared)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__rwelision as *const _
+ as usize
+ },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__rwelision)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__pad1 as *const _ as usize
+ },
+ 33usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__pad1)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__pad2 as *const _ as usize
+ },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__pad2)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<pthread_rwlock_t__bindgen_ty_1>())).__flags as *const _ as usize
+ },
+ 48usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t__bindgen_ty_1),
+ "::",
+ stringify!(__flags)
+ )
+ );
+}
+#[test]
+fn bindgen_test_layout_pthread_rwlock_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_rwlock_t>(),
+ 56usize,
+ concat!("Size of: ", stringify!(pthread_rwlock_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_rwlock_t>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(pthread_rwlock_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_rwlock_t>())).__data as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t),
+ "::",
+ stringify!(__data)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_rwlock_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_rwlock_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlock_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_rwlockattr_t {
+ pub __size: [::std::os::raw::c_char; 8usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: u64,
+}
+#[test]
+fn bindgen_test_layout_pthread_rwlockattr_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_rwlockattr_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(pthread_rwlockattr_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_rwlockattr_t>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(pthread_rwlockattr_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_rwlockattr_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlockattr_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_rwlockattr_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_rwlockattr_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+pub type pthread_spinlock_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_barrier_t {
+ pub __size: [::std::os::raw::c_char; 32usize],
+ pub __align: ::std::os::raw::c_long,
+ _bindgen_union_align: [u64; 4usize],
+}
+#[test]
+fn bindgen_test_layout_pthread_barrier_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_barrier_t>(),
+ 32usize,
+ concat!("Size of: ", stringify!(pthread_barrier_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_barrier_t>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(pthread_barrier_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_barrier_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_barrier_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_barrier_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_barrier_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union pthread_barrierattr_t {
+ pub __size: [::std::os::raw::c_char; 4usize],
+ pub __align: ::std::os::raw::c_int,
+ _bindgen_union_align: u32,
+}
+#[test]
+fn bindgen_test_layout_pthread_barrierattr_t() {
+ assert_eq!(
+ ::std::mem::size_of::<pthread_barrierattr_t>(),
+ 4usize,
+ concat!("Size of: ", stringify!(pthread_barrierattr_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<pthread_barrierattr_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(pthread_barrierattr_t))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_barrierattr_t>())).__size as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_barrierattr_t),
+ "::",
+ stringify!(__size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<pthread_barrierattr_t>())).__align as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(pthread_barrierattr_t),
+ "::",
+ stringify!(__align)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct timezone {
+ pub tz_minuteswest: ::std::os::raw::c_int,
+ pub tz_dsttime: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_timezone() {
+ assert_eq!(
+ ::std::mem::size_of::<timezone>(),
+ 8usize,
+ concat!("Size of: ", stringify!(timezone))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<timezone>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(timezone))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<timezone>())).tz_minuteswest as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timezone),
+ "::",
+ stringify!(tz_minuteswest)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<timezone>())).tz_dsttime as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(timezone),
+ "::",
+ stringify!(tz_dsttime)
+ )
+ );
+}
+pub type __timezone_ptr_t = *mut timezone;
+extern "C" {
+ pub fn gettimeofday(__tv: *mut timeval, __tz: __timezone_ptr_t) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn settimeofday(__tv: *const timeval, __tz: *const timezone) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn adjtime(__delta: *const timeval, __olddelta: *mut timeval) -> ::std::os::raw::c_int;
+}
+pub const ITIMER_REAL: __itimer_which = 0;
+pub const ITIMER_VIRTUAL: __itimer_which = 1;
+pub const ITIMER_PROF: __itimer_which = 2;
+pub type __itimer_which = u32;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct itimerval {
+ pub it_interval: timeval,
+ pub it_value: timeval,
+}
+#[test]
+fn bindgen_test_layout_itimerval() {
+ assert_eq!(
+ ::std::mem::size_of::<itimerval>(),
+ 32usize,
+ concat!("Size of: ", stringify!(itimerval))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<itimerval>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(itimerval))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<itimerval>())).it_interval as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(itimerval),
+ "::",
+ stringify!(it_interval)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<itimerval>())).it_value as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(itimerval),
+ "::",
+ stringify!(it_value)
+ )
+ );
+}
+pub type __itimer_which_t = ::std::os::raw::c_int;
+extern "C" {
+ pub fn getitimer(__which: __itimer_which_t, __value: *mut itimerval) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn setitimer(
+ __which: __itimer_which_t,
+ __new: *const itimerval,
+ __old: *mut itimerval,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn utimes(
+ __file: *const ::std::os::raw::c_char,
+ __tvp: *const timeval,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn lutimes(
+ __file: *const ::std::os::raw::c_char,
+ __tvp: *const timeval,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn futimes(__fd: ::std::os::raw::c_int, __tvp: *const timeval) -> ::std::os::raw::c_int;
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct tm {
+ pub tm_sec: ::std::os::raw::c_int,
+ pub tm_min: ::std::os::raw::c_int,
+ pub tm_hour: ::std::os::raw::c_int,
+ pub tm_mday: ::std::os::raw::c_int,
+ pub tm_mon: ::std::os::raw::c_int,
+ pub tm_year: ::std::os::raw::c_int,
+ pub tm_wday: ::std::os::raw::c_int,
+ pub tm_yday: ::std::os::raw::c_int,
+ pub tm_isdst: ::std::os::raw::c_int,
+ pub tm_gmtoff: ::std::os::raw::c_long,
+ pub tm_zone: *const ::std::os::raw::c_char,
+}
+#[test]
+fn bindgen_test_layout_tm() {
+ assert_eq!(
+ ::std::mem::size_of::<tm>(),
+ 56usize,
+ concat!("Size of: ", stringify!(tm))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<tm>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(tm))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_sec as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_sec)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_min as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_min)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_hour as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_hour)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_mday as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_mday)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_mon as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_mon)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_year as *const _ as usize },
+ 20usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_year)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_wday as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_wday)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_yday as *const _ as usize },
+ 28usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_yday)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_isdst as *const _ as usize },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_isdst)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_gmtoff as *const _ as usize },
+ 40usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_gmtoff)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<tm>())).tm_zone as *const _ as usize },
+ 48usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(tm),
+ "::",
+ stringify!(tm_zone)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct itimerspec {
+ pub it_interval: timespec,
+ pub it_value: timespec,
+}
+#[test]
+fn bindgen_test_layout_itimerspec() {
+ assert_eq!(
+ ::std::mem::size_of::<itimerspec>(),
+ 32usize,
+ concat!("Size of: ", stringify!(itimerspec))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<itimerspec>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(itimerspec))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<itimerspec>())).it_interval as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(itimerspec),
+ "::",
+ stringify!(it_interval)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<itimerspec>())).it_value as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(itimerspec),
+ "::",
+ stringify!(it_value)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sigevent {
+ _unused: [u8; 0],
+}
+extern "C" {
+ pub fn clock() -> clock_t;
+}
+extern "C" {
+ pub fn time(__timer: *mut time_t) -> time_t;
+}
+extern "C" {
+ pub fn difftime(__time1: time_t, __time0: time_t) -> f64;
+}
+extern "C" {
+ pub fn mktime(__tp: *mut tm) -> time_t;
+}
+extern "C" {
+ pub fn strftime(
+ __s: *mut ::std::os::raw::c_char,
+ __maxsize: usize,
+ __format: *const ::std::os::raw::c_char,
+ __tp: *const tm,
+ ) -> usize;
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __locale_struct {
+ pub __locales: [*mut __locale_data; 13usize],
+ pub __ctype_b: *const ::std::os::raw::c_ushort,
+ pub __ctype_tolower: *const ::std::os::raw::c_int,
+ pub __ctype_toupper: *const ::std::os::raw::c_int,
+ pub __names: [*const ::std::os::raw::c_char; 13usize],
+}
+#[test]
+fn bindgen_test_layout___locale_struct() {
+ assert_eq!(
+ ::std::mem::size_of::<__locale_struct>(),
+ 232usize,
+ concat!("Size of: ", stringify!(__locale_struct))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__locale_struct>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__locale_struct))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__locale_struct>())).__locales as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__locale_struct),
+ "::",
+ stringify!(__locales)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__locale_struct>())).__ctype_b as *const _ as usize },
+ 104usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__locale_struct),
+ "::",
+ stringify!(__ctype_b)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__locale_struct>())).__ctype_tolower as *const _ as usize },
+ 112usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__locale_struct),
+ "::",
+ stringify!(__ctype_tolower)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__locale_struct>())).__ctype_toupper as *const _ as usize },
+ 120usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__locale_struct),
+ "::",
+ stringify!(__ctype_toupper)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<__locale_struct>())).__names as *const _ as usize },
+ 128usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(__locale_struct),
+ "::",
+ stringify!(__names)
+ )
+ );
+}
+pub type __locale_t = *mut __locale_struct;
+pub type locale_t = __locale_t;
+extern "C" {
+ pub fn strftime_l(
+ __s: *mut ::std::os::raw::c_char,
+ __maxsize: usize,
+ __format: *const ::std::os::raw::c_char,
+ __tp: *const tm,
+ __loc: __locale_t,
+ ) -> usize;
+}
+extern "C" {
+ pub fn gmtime(__timer: *const time_t) -> *mut tm;
+}
+extern "C" {
+ pub fn localtime(__timer: *const time_t) -> *mut tm;
+}
+extern "C" {
+ pub fn gmtime_r(__timer: *const time_t, __tp: *mut tm) -> *mut tm;
+}
+extern "C" {
+ pub fn localtime_r(__timer: *const time_t, __tp: *mut tm) -> *mut tm;
+}
+extern "C" {
+ pub fn asctime(__tp: *const tm) -> *mut ::std::os::raw::c_char;
+}
+extern "C" {
+ pub fn ctime(__timer: *const time_t) -> *mut ::std::os::raw::c_char;
+}
+extern "C" {
+ pub fn asctime_r(
+ __tp: *const tm,
+ __buf: *mut ::std::os::raw::c_char,
+ ) -> *mut ::std::os::raw::c_char;
+}
+extern "C" {
+ pub fn ctime_r(
+ __timer: *const time_t,
+ __buf: *mut ::std::os::raw::c_char,
+ ) -> *mut ::std::os::raw::c_char;
+}
+extern "C" {
+ #[link_name = "\u{1}__tzname"]
+ pub static mut __tzname: [*mut ::std::os::raw::c_char; 2usize];
+}
+extern "C" {
+ #[link_name = "\u{1}__daylight"]
+ pub static mut __daylight: ::std::os::raw::c_int;
+}
+extern "C" {
+ #[link_name = "\u{1}__timezone"]
+ pub static mut __timezone: ::std::os::raw::c_long;
+}
+extern "C" {
+ #[link_name = "\u{1}tzname"]
+ pub static mut tzname: [*mut ::std::os::raw::c_char; 2usize];
+}
+extern "C" {
+ pub fn tzset();
+}
+extern "C" {
+ #[link_name = "\u{1}daylight"]
+ pub static mut daylight: ::std::os::raw::c_int;
+}
+extern "C" {
+ #[link_name = "\u{1}timezone"]
+ pub static mut timezone: ::std::os::raw::c_long;
+}
+extern "C" {
+ pub fn stime(__when: *const time_t) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn timegm(__tp: *mut tm) -> time_t;
+}
+extern "C" {
+ pub fn timelocal(__tp: *mut tm) -> time_t;
+}
+extern "C" {
+ pub fn dysize(__year: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn nanosleep(
+ __requested_time: *const timespec,
+ __remaining: *mut timespec,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn clock_getres(__clock_id: clockid_t, __res: *mut timespec) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn clock_gettime(__clock_id: clockid_t, __tp: *mut timespec) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn clock_settime(__clock_id: clockid_t, __tp: *const timespec) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn clock_nanosleep(
+ __clock_id: clockid_t,
+ __flags: ::std::os::raw::c_int,
+ __req: *const timespec,
+ __rem: *mut timespec,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn clock_getcpuclockid(__pid: pid_t, __clock_id: *mut clockid_t) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn timer_create(
+ __clock_id: clockid_t,
+ __evp: *mut sigevent,
+ __timerid: *mut timer_t,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn timer_delete(__timerid: timer_t) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn timer_settime(
+ __timerid: timer_t,
+ __flags: ::std::os::raw::c_int,
+ __value: *const itimerspec,
+ __ovalue: *mut itimerspec,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn timer_gettime(__timerid: timer_t, __value: *mut itimerspec) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn timer_getoverrun(__timerid: timer_t) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn timespec_get(
+ __ts: *mut timespec,
+ __base: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+/// In the context of a \ref libusb_device_descriptor "device descriptor",
+/// this bDeviceClass value indicates that each interface specifies its
+/// own class information and all interfaces operate independently.
+pub const LIBUSB_CLASS_PER_INTERFACE: libusb_class_code = 0;
+/// Audio class
+pub const LIBUSB_CLASS_AUDIO: libusb_class_code = 1;
+/// Communications class
+pub const LIBUSB_CLASS_COMM: libusb_class_code = 2;
+/// Human Interface Device class
+pub const LIBUSB_CLASS_HID: libusb_class_code = 3;
+/// Physical
+pub const LIBUSB_CLASS_PHYSICAL: libusb_class_code = 5;
+/// Printer class
+pub const LIBUSB_CLASS_PRINTER: libusb_class_code = 7;
+/// Image class
+pub const LIBUSB_CLASS_PTP: libusb_class_code = 6;
+/// Image class
+pub const LIBUSB_CLASS_IMAGE: libusb_class_code = 6;
+/// Mass storage class
+pub const LIBUSB_CLASS_MASS_STORAGE: libusb_class_code = 8;
+/// Hub class
+pub const LIBUSB_CLASS_HUB: libusb_class_code = 9;
+/// Data class
+pub const LIBUSB_CLASS_DATA: libusb_class_code = 10;
+/// Smart Card
+pub const LIBUSB_CLASS_SMART_CARD: libusb_class_code = 11;
+/// Content Security
+pub const LIBUSB_CLASS_CONTENT_SECURITY: libusb_class_code = 13;
+/// Video
+pub const LIBUSB_CLASS_VIDEO: libusb_class_code = 14;
+/// Personal Healthcare
+pub const LIBUSB_CLASS_PERSONAL_HEALTHCARE: libusb_class_code = 15;
+/// Diagnostic Device
+pub const LIBUSB_CLASS_DIAGNOSTIC_DEVICE: libusb_class_code = 220;
+/// Wireless class
+pub const LIBUSB_CLASS_WIRELESS: libusb_class_code = 224;
+/// Application class
+pub const LIBUSB_CLASS_APPLICATION: libusb_class_code = 254;
+/// Class is vendor-specific
+pub const LIBUSB_CLASS_VENDOR_SPEC: libusb_class_code = 255;
+/// \ingroup libusb_desc
+/// Device and/or Interface Class codes
+pub type libusb_class_code = u32;
+/// Device descriptor. See libusb_device_descriptor.
+pub const LIBUSB_DT_DEVICE: libusb_descriptor_type = 1;
+/// Configuration descriptor. See libusb_config_descriptor.
+pub const LIBUSB_DT_CONFIG: libusb_descriptor_type = 2;
+/// String descriptor
+pub const LIBUSB_DT_STRING: libusb_descriptor_type = 3;
+/// Interface descriptor. See libusb_interface_descriptor.
+pub const LIBUSB_DT_INTERFACE: libusb_descriptor_type = 4;
+/// Endpoint descriptor. See libusb_endpoint_descriptor.
+pub const LIBUSB_DT_ENDPOINT: libusb_descriptor_type = 5;
+/// BOS descriptor
+pub const LIBUSB_DT_BOS: libusb_descriptor_type = 15;
+/// Device Capability descriptor
+pub const LIBUSB_DT_DEVICE_CAPABILITY: libusb_descriptor_type = 16;
+/// HID descriptor
+pub const LIBUSB_DT_HID: libusb_descriptor_type = 33;
+/// HID report descriptor
+pub const LIBUSB_DT_REPORT: libusb_descriptor_type = 34;
+/// Physical descriptor
+pub const LIBUSB_DT_PHYSICAL: libusb_descriptor_type = 35;
+/// Hub descriptor
+pub const LIBUSB_DT_HUB: libusb_descriptor_type = 41;
+/// SuperSpeed Hub descriptor
+pub const LIBUSB_DT_SUPERSPEED_HUB: libusb_descriptor_type = 42;
+/// SuperSpeed Endpoint Companion descriptor
+pub const LIBUSB_DT_SS_ENDPOINT_COMPANION: libusb_descriptor_type = 48;
+/// \ingroup libusb_desc
+/// Descriptor types as defined by the USB specification.
+pub type libusb_descriptor_type = u32;
+/// In: device-to-host
+pub const LIBUSB_ENDPOINT_IN: libusb_endpoint_direction = 128;
+/// Out: host-to-device
+pub const LIBUSB_ENDPOINT_OUT: libusb_endpoint_direction = 0;
+/// \ingroup libusb_desc
+/// Endpoint direction. Values for bit 7 of the
+/// \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme.
+pub type libusb_endpoint_direction = u32;
+/// Control endpoint
+pub const LIBUSB_TRANSFER_TYPE_CONTROL: libusb_transfer_type = 0;
+/// Isochronous endpoint
+pub const LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: libusb_transfer_type = 1;
+/// Bulk endpoint
+pub const LIBUSB_TRANSFER_TYPE_BULK: libusb_transfer_type = 2;
+/// Interrupt endpoint
+pub const LIBUSB_TRANSFER_TYPE_INTERRUPT: libusb_transfer_type = 3;
+/// Stream endpoint
+pub const LIBUSB_TRANSFER_TYPE_BULK_STREAM: libusb_transfer_type = 4;
+/// \ingroup libusb_desc
+/// Endpoint transfer type. Values for bits 0:1 of the
+/// \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field.
+pub type libusb_transfer_type = u32;
+/// Request status of the specific recipient
+pub const LIBUSB_REQUEST_GET_STATUS: libusb_standard_request = 0;
+/// Clear or disable a specific feature
+pub const LIBUSB_REQUEST_CLEAR_FEATURE: libusb_standard_request = 1;
+/// Set or enable a specific feature
+pub const LIBUSB_REQUEST_SET_FEATURE: libusb_standard_request = 3;
+/// Set device address for all future accesses
+pub const LIBUSB_REQUEST_SET_ADDRESS: libusb_standard_request = 5;
+/// Get the specified descriptor
+pub const LIBUSB_REQUEST_GET_DESCRIPTOR: libusb_standard_request = 6;
+/// Used to update existing descriptors or add new descriptors
+pub const LIBUSB_REQUEST_SET_DESCRIPTOR: libusb_standard_request = 7;
+/// Get the current device configuration value
+pub const LIBUSB_REQUEST_GET_CONFIGURATION: libusb_standard_request = 8;
+/// Set device configuration
+pub const LIBUSB_REQUEST_SET_CONFIGURATION: libusb_standard_request = 9;
+/// Return the selected alternate setting for the specified interface
+pub const LIBUSB_REQUEST_GET_INTERFACE: libusb_standard_request = 10;
+/// Select an alternate interface for the specified interface
+pub const LIBUSB_REQUEST_SET_INTERFACE: libusb_standard_request = 11;
+/// Set then report an endpoint's synchronization frame
+pub const LIBUSB_REQUEST_SYNCH_FRAME: libusb_standard_request = 12;
+/// Sets both the U1 and U2 Exit Latency
+pub const LIBUSB_REQUEST_SET_SEL: libusb_standard_request = 48;
+/// Delay from the time a host transmits a packet to the time it is
+/// received by the device.
+pub const LIBUSB_SET_ISOCH_DELAY: libusb_standard_request = 49;
+/// \ingroup libusb_misc
+/// Standard requests, as defined in table 9-5 of the USB 3.0 specifications
+pub type libusb_standard_request = u32;
+/// Standard
+pub const LIBUSB_REQUEST_TYPE_STANDARD: libusb_request_type = 0;
+/// Class
+pub const LIBUSB_REQUEST_TYPE_CLASS: libusb_request_type = 32;
+/// Vendor
+pub const LIBUSB_REQUEST_TYPE_VENDOR: libusb_request_type = 64;
+/// Reserved
+pub const LIBUSB_REQUEST_TYPE_RESERVED: libusb_request_type = 96;
+/// \ingroup libusb_misc
+/// Request type bits of the
+/// \ref libusb_control_setup::bmRequestType "bmRequestType" field in control
+/// transfers.
+pub type libusb_request_type = u32;
+/// Device
+pub const LIBUSB_RECIPIENT_DEVICE: libusb_request_recipient = 0;
+/// Interface
+pub const LIBUSB_RECIPIENT_INTERFACE: libusb_request_recipient = 1;
+/// Endpoint
+pub const LIBUSB_RECIPIENT_ENDPOINT: libusb_request_recipient = 2;
+/// Other
+pub const LIBUSB_RECIPIENT_OTHER: libusb_request_recipient = 3;
+/// \ingroup libusb_misc
+/// Recipient bits of the
+/// \ref libusb_control_setup::bmRequestType "bmRequestType" field in control
+/// transfers. Values 4 through 31 are reserved.
+pub type libusb_request_recipient = u32;
+/// No synchronization
+pub const LIBUSB_ISO_SYNC_TYPE_NONE: libusb_iso_sync_type = 0;
+/// Asynchronous
+pub const LIBUSB_ISO_SYNC_TYPE_ASYNC: libusb_iso_sync_type = 1;
+/// Adaptive
+pub const LIBUSB_ISO_SYNC_TYPE_ADAPTIVE: libusb_iso_sync_type = 2;
+/// Synchronous
+pub const LIBUSB_ISO_SYNC_TYPE_SYNC: libusb_iso_sync_type = 3;
+/// \ingroup libusb_desc
+/// Synchronization type for isochronous endpoints. Values for bits 2:3 of the
+/// \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in
+/// libusb_endpoint_descriptor.
+pub type libusb_iso_sync_type = u32;
+/// Data endpoint
+pub const LIBUSB_ISO_USAGE_TYPE_DATA: libusb_iso_usage_type = 0;
+/// Feedback endpoint
+pub const LIBUSB_ISO_USAGE_TYPE_FEEDBACK: libusb_iso_usage_type = 1;
+/// Implicit feedback Data endpoint
+pub const LIBUSB_ISO_USAGE_TYPE_IMPLICIT: libusb_iso_usage_type = 2;
+/// \ingroup libusb_desc
+/// Usage type for isochronous endpoints. Values for bits 4:5 of the
+/// \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in
+/// libusb_endpoint_descriptor.
+pub type libusb_iso_usage_type = u32;
+/// \ingroup libusb_desc
+/// A structure representing the standard USB device descriptor. This
+/// descriptor is documented in section 9.6.1 of the USB 3.0 specification.
+/// All multiple-byte fields are represented in host-endian format.
+#[repr(C)]
+#[derive(Default, Debug, Copy, Clone)]
+pub struct libusb_device_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this
+ /// context.
+ pub bDescriptorType: u8,
+ /// USB specification release number in binary-coded decimal. A value of
+ /// 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc.
+ pub bcdUSB: u16,
+ /// USB-IF class code for the device. See \ref libusb_class_code.
+ pub bDeviceClass: u8,
+ /// USB-IF subclass code for the device, qualified by the bDeviceClass
+ /// value
+ pub bDeviceSubClass: u8,
+ /// USB-IF protocol code for the device, qualified by the bDeviceClass and
+ /// bDeviceSubClass values
+ pub bDeviceProtocol: u8,
+ /// Maximum packet size for endpoint 0
+ pub bMaxPacketSize0: u8,
+ /// USB-IF vendor ID
+ pub idVendor: u16,
+ /// USB-IF product ID
+ pub idProduct: u16,
+ /// Device release number in binary-coded decimal
+ pub bcdDevice: u16,
+ /// Index of string descriptor describing manufacturer
+ pub iManufacturer: u8,
+ /// Index of string descriptor describing product
+ pub iProduct: u8,
+ /// Index of string descriptor containing device serial number
+ pub iSerialNumber: u8,
+ /// Number of possible configurations
+ pub bNumConfigurations: u8,
+}
+#[test]
+fn bindgen_test_layout_libusb_device_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_device_descriptor>(),
+ 18usize,
+ concat!("Size of: ", stringify!(libusb_device_descriptor))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_device_descriptor>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(libusb_device_descriptor))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).bLength as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).bDescriptorType as *const _
+ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bDescriptorType)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_device_descriptor>())).bcdUSB as *const _ as usize },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bcdUSB)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).bDeviceClass as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bDeviceClass)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).bDeviceSubClass as *const _
+ as usize
+ },
+ 5usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bDeviceSubClass)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).bDeviceProtocol as *const _
+ as usize
+ },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bDeviceProtocol)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).bMaxPacketSize0 as *const _
+ as usize
+ },
+ 7usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bMaxPacketSize0)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).idVendor as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(idVendor)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).idProduct as *const _ as usize
+ },
+ 10usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(idProduct)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).bcdDevice as *const _ as usize
+ },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bcdDevice)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).iManufacturer as *const _ as usize
+ },
+ 14usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(iManufacturer)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).iProduct as *const _ as usize
+ },
+ 15usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(iProduct)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).iSerialNumber as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(iSerialNumber)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_device_descriptor>())).bNumConfigurations as *const _
+ as usize
+ },
+ 17usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_device_descriptor),
+ "::",
+ stringify!(bNumConfigurations)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A structure representing the standard USB endpoint descriptor. This
+/// descriptor is documented in section 9.6.6 of the USB 3.0 specification.
+/// All multiple-byte fields are represented in host-endian format.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_endpoint_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in
+ /// this context.
+ pub bDescriptorType: u8,
+ /// The address of the endpoint described by this descriptor. Bits 0:3 are
+ /// the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction,
+ /// see \ref libusb_endpoint_direction.
+ pub bEndpointAddress: u8,
+ /// Attributes which apply to the endpoint when it is configured using
+ /// the bConfigurationValue. Bits 0:1 determine the transfer type and
+ /// correspond to \ref libusb_transfer_type. Bits 2:3 are only used for
+ /// isochronous endpoints and correspond to \ref libusb_iso_sync_type.
+ /// Bits 4:5 are also only used for isochronous endpoints and correspond to
+ /// \ref libusb_iso_usage_type. Bits 6:7 are reserved.
+ pub bmAttributes: u8,
+ /// Maximum packet size this endpoint is capable of sending/receiving.
+ pub wMaxPacketSize: u16,
+ /// Interval for polling endpoint for data transfers.
+ pub bInterval: u8,
+ /// For audio devices only: the rate at which synchronization feedback
+ /// is provided.
+ pub bRefresh: u8,
+ /// For audio devices only: the address if the synch endpoint
+ pub bSynchAddress: u8,
+ /// Extra descriptors. If libusb encounters unknown endpoint descriptors,
+ /// it will store them here, should you wish to parse them.
+ pub extra: *const ::std::os::raw::c_uchar,
+ /// Length of the extra descriptors, in bytes.
+ pub extra_length: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_libusb_endpoint_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_endpoint_descriptor>(),
+ 32usize,
+ concat!("Size of: ", stringify!(libusb_endpoint_descriptor))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_endpoint_descriptor>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(libusb_endpoint_descriptor))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).bLength as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(bLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).bDescriptorType as *const _
+ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(bDescriptorType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).bEndpointAddress as *const _
+ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(bEndpointAddress)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).bmAttributes as *const _ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(bmAttributes)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).wMaxPacketSize as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(wMaxPacketSize)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).bInterval as *const _ as usize
+ },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(bInterval)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).bRefresh as *const _ as usize
+ },
+ 7usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(bRefresh)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).bSynchAddress as *const _
+ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(bSynchAddress)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).extra as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(extra)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_endpoint_descriptor>())).extra_length as *const _ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_endpoint_descriptor),
+ "::",
+ stringify!(extra_length)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A structure representing the standard USB interface descriptor. This
+/// descriptor is documented in section 9.6.5 of the USB 3.0 specification.
+/// All multiple-byte fields are represented in host-endian format.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_interface_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE
+ /// in this context.
+ pub bDescriptorType: u8,
+ /// Number of this interface
+ pub bInterfaceNumber: u8,
+ /// Value used to select this alternate setting for this interface
+ pub bAlternateSetting: u8,
+ /// Number of endpoints used by this interface (excluding the control
+ /// endpoint).
+ pub bNumEndpoints: u8,
+ /// USB-IF class code for this interface. See \ref libusb_class_code.
+ pub bInterfaceClass: u8,
+ /// USB-IF subclass code for this interface, qualified by the
+ /// bInterfaceClass value
+ pub bInterfaceSubClass: u8,
+ /// USB-IF protocol code for this interface, qualified by the
+ /// bInterfaceClass and bInterfaceSubClass values
+ pub bInterfaceProtocol: u8,
+ /// Index of string descriptor describing this interface
+ pub iInterface: u8,
+ /// Array of endpoint descriptors. This length of this array is determined
+ /// by the bNumEndpoints field.
+ pub endpoint: *const libusb_endpoint_descriptor,
+ /// Extra descriptors. If libusb encounters unknown interface descriptors,
+ /// it will store them here, should you wish to parse them.
+ pub extra: *const ::std::os::raw::c_uchar,
+ /// Length of the extra descriptors, in bytes.
+ pub extra_length: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_libusb_interface_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_interface_descriptor>(),
+ 40usize,
+ concat!("Size of: ", stringify!(libusb_interface_descriptor))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_interface_descriptor>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(libusb_interface_descriptor))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).bLength as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(bLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).bDescriptorType as *const _
+ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(bDescriptorType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).bInterfaceNumber as *const _
+ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(bInterfaceNumber)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).bAlternateSetting as *const _
+ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(bAlternateSetting)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).bNumEndpoints as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(bNumEndpoints)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).bInterfaceClass as *const _
+ as usize
+ },
+ 5usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(bInterfaceClass)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).bInterfaceSubClass as *const _
+ as usize
+ },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(bInterfaceSubClass)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).bInterfaceProtocol as *const _
+ as usize
+ },
+ 7usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(bInterfaceProtocol)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).iInterface as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(iInterface)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).endpoint as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(endpoint)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).extra as *const _ as usize
+ },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(extra)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_interface_descriptor>())).extra_length as *const _
+ as usize
+ },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface_descriptor),
+ "::",
+ stringify!(extra_length)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A collection of alternate settings for a particular USB interface.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_interface {
+ /// Array of interface descriptors. The length of this array is determined
+ /// by the num_altsetting field.
+ pub altsetting: *const libusb_interface_descriptor,
+ /// The number of alternate settings that belong to this interface
+ pub num_altsetting: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_libusb_interface() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_interface>(),
+ 16usize,
+ concat!("Size of: ", stringify!(libusb_interface))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_interface>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(libusb_interface))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_interface>())).altsetting as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface),
+ "::",
+ stringify!(altsetting)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_interface>())).num_altsetting as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_interface),
+ "::",
+ stringify!(num_altsetting)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A structure representing the standard USB configuration descriptor. This
+/// descriptor is documented in section 9.6.3 of the USB 3.0 specification.
+/// All multiple-byte fields are represented in host-endian format.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_config_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG
+ /// in this context.
+ pub bDescriptorType: u8,
+ /// Total length of data returned for this configuration
+ pub wTotalLength: u16,
+ /// Number of interfaces supported by this configuration
+ pub bNumInterfaces: u8,
+ /// Identifier value for this configuration
+ pub bConfigurationValue: u8,
+ /// Index of string descriptor describing this configuration
+ pub iConfiguration: u8,
+ /// Configuration characteristics
+ pub bmAttributes: u8,
+ /// Maximum power consumption of the USB device from this bus in this
+ /// configuration when the device is fully operation. Expressed in units
+ /// of 2 mA when the device is operating in high-speed mode and in units
+ /// of 8 mA when the device is operating in super-speed mode.
+ pub MaxPower: u8,
+ /// Array of interfaces supported by this configuration. The length of
+ /// this array is determined by the bNumInterfaces field.
+ pub interface: *const libusb_interface,
+ /// Extra descriptors. If libusb encounters unknown configuration
+ /// descriptors, it will store them here, should you wish to parse them.
+ pub extra: *const ::std::os::raw::c_uchar,
+ /// Length of the extra descriptors, in bytes.
+ pub extra_length: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_libusb_config_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_config_descriptor>(),
+ 40usize,
+ concat!("Size of: ", stringify!(libusb_config_descriptor))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_config_descriptor>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(libusb_config_descriptor))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).bLength as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(bLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).bDescriptorType as *const _
+ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(bDescriptorType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).wTotalLength as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(wTotalLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).bNumInterfaces as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(bNumInterfaces)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).bConfigurationValue as *const _
+ as usize
+ },
+ 5usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(bConfigurationValue)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).iConfiguration as *const _ as usize
+ },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(iConfiguration)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).bmAttributes as *const _ as usize
+ },
+ 7usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(bmAttributes)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).MaxPower as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(MaxPower)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).interface as *const _ as usize
+ },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(interface)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_config_descriptor>())).extra as *const _ as usize },
+ 24usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(extra)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_config_descriptor>())).extra_length as *const _ as usize
+ },
+ 32usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_config_descriptor),
+ "::",
+ stringify!(extra_length)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A structure representing the superspeed endpoint companion
+/// descriptor. This descriptor is documented in section 9.6.7 of
+/// the USB 3.0 specification. All multiple-byte fields are represented in
+/// host-endian format.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_ss_endpoint_companion_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in
+ /// this context.
+ pub bDescriptorType: u8,
+ /// The maximum number of packets the endpoint can send or
+ /// receive as part of a burst.
+ pub bMaxBurst: u8,
+ /// In bulk EP: bits 4:0 represents the maximum number of
+ /// streams the EP supports. In isochronous EP: bits 1:0
+ /// represents the Mult - a zero based value that determines
+ /// the maximum number of packets within a service interval
+ pub bmAttributes: u8,
+ /// The total number of bytes this EP will transfer every
+ /// service interval. valid only for periodic EPs.
+ pub wBytesPerInterval: u16,
+}
+#[test]
+fn bindgen_test_layout_libusb_ss_endpoint_companion_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_ss_endpoint_companion_descriptor>(),
+ 6usize,
+ concat!(
+ "Size of: ",
+ stringify!(libusb_ss_endpoint_companion_descriptor)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_ss_endpoint_companion_descriptor>(),
+ 2usize,
+ concat!(
+ "Alignment of ",
+ stringify!(libusb_ss_endpoint_companion_descriptor)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_endpoint_companion_descriptor>())).bLength as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_endpoint_companion_descriptor),
+ "::",
+ stringify!(bLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_endpoint_companion_descriptor>())).bDescriptorType
+ as *const _ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_endpoint_companion_descriptor),
+ "::",
+ stringify!(bDescriptorType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_endpoint_companion_descriptor>())).bMaxBurst
+ as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_endpoint_companion_descriptor),
+ "::",
+ stringify!(bMaxBurst)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_endpoint_companion_descriptor>())).bmAttributes
+ as *const _ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_endpoint_companion_descriptor),
+ "::",
+ stringify!(bmAttributes)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_endpoint_companion_descriptor>())).wBytesPerInterval
+ as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_endpoint_companion_descriptor),
+ "::",
+ stringify!(wBytesPerInterval)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A generic representation of a BOS Device Capability descriptor. It is
+/// advised to check bDevCapabilityType and call the matching
+/// libusb_get_*_descriptor function to get a structure fully matching the type.
+#[repr(C)]
+#[derive(Debug)]
+pub struct libusb_bos_dev_capability_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ /// LIBUSB_DT_DEVICE_CAPABILITY in this context.
+ pub bDescriptorType: u8,
+ /// Device Capability type
+ pub bDevCapabilityType: u8,
+ /// Device Capability data (bLength - 3 bytes)
+ pub dev_capability_data: __IncompleteArrayField<u8>,
+}
+#[test]
+fn bindgen_test_layout_libusb_bos_dev_capability_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_bos_dev_capability_descriptor>(),
+ 3usize,
+ concat!(
+ "Size of: ",
+ stringify!(libusb_bos_dev_capability_descriptor)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_bos_dev_capability_descriptor>(),
+ 1usize,
+ concat!(
+ "Alignment of ",
+ stringify!(libusb_bos_dev_capability_descriptor)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A structure representing the Binary Device Object Store (BOS) descriptor.
+/// This descriptor is documented in section 9.6.2 of the USB 3.0 specification.
+/// All multiple-byte fields are represented in host-endian format.
+#[repr(C)]
+#[derive(Debug)]
+pub struct libusb_bos_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS
+ /// in this context.
+ pub bDescriptorType: u8,
+ /// Length of this descriptor and all of its sub descriptors
+ pub wTotalLength: u16,
+ /// The number of separate device capability descriptors in
+ /// the BOS
+ pub bNumDeviceCaps: u8,
+ /// bNumDeviceCap Device Capability Descriptors
+ pub dev_capability: __IncompleteArrayField<*mut libusb_bos_dev_capability_descriptor>,
+ pub __bindgen_align: [u64; 0usize],
+}
+#[test]
+fn bindgen_test_layout_libusb_bos_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_bos_descriptor>(),
+ 8usize,
+ concat!("Size of: ", stringify!(libusb_bos_descriptor))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_bos_descriptor>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(libusb_bos_descriptor))
+ );
+}
+/// \ingroup libusb_desc
+/// A structure representing the USB 2.0 Extension descriptor
+/// This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification.
+/// All multiple-byte fields are represented in host-endian format.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_usb_2_0_extension_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ /// LIBUSB_DT_DEVICE_CAPABILITY in this context.
+ pub bDescriptorType: u8,
+ /// Capability type. Will have value
+ /// \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION
+ /// LIBUSB_BT_USB_2_0_EXTENSION in this context.
+ pub bDevCapabilityType: u8,
+ /// Bitmap encoding of supported device level features.
+ /// A value of one in a bit location indicates a feature is
+ /// supported; a value of zero indicates it is not supported.
+ /// See \ref libusb_usb_2_0_extension_attributes.
+ pub bmAttributes: u32,
+}
+#[test]
+fn bindgen_test_layout_libusb_usb_2_0_extension_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_usb_2_0_extension_descriptor>(),
+ 8usize,
+ concat!("Size of: ", stringify!(libusb_usb_2_0_extension_descriptor))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_usb_2_0_extension_descriptor>(),
+ 4usize,
+ concat!(
+ "Alignment of ",
+ stringify!(libusb_usb_2_0_extension_descriptor)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_usb_2_0_extension_descriptor>())).bLength as *const _
+ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_usb_2_0_extension_descriptor),
+ "::",
+ stringify!(bLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_usb_2_0_extension_descriptor>())).bDescriptorType
+ as *const _ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_usb_2_0_extension_descriptor),
+ "::",
+ stringify!(bDescriptorType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_usb_2_0_extension_descriptor>())).bDevCapabilityType
+ as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_usb_2_0_extension_descriptor),
+ "::",
+ stringify!(bDevCapabilityType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_usb_2_0_extension_descriptor>())).bmAttributes as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_usb_2_0_extension_descriptor),
+ "::",
+ stringify!(bmAttributes)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A structure representing the SuperSpeed USB Device Capability descriptor
+/// This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification.
+/// All multiple-byte fields are represented in host-endian format.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_ss_usb_device_capability_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ /// LIBUSB_DT_DEVICE_CAPABILITY in this context.
+ pub bDescriptorType: u8,
+ /// Capability type. Will have value
+ /// \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY
+ /// LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context.
+ pub bDevCapabilityType: u8,
+ /// Bitmap encoding of supported device level features.
+ /// A value of one in a bit location indicates a feature is
+ /// supported; a value of zero indicates it is not supported.
+ /// See \ref libusb_ss_usb_device_capability_attributes.
+ pub bmAttributes: u8,
+ /// Bitmap encoding of the speed supported by this device when
+ /// operating in SuperSpeed mode. See \ref libusb_supported_speed.
+ pub wSpeedSupported: u16,
+ /// The lowest speed at which all the functionality supported
+ /// by the device is available to the user. For example if the
+ /// device supports all its functionality when connected at
+ /// full speed and above then it sets this value to 1.
+ pub bFunctionalitySupport: u8,
+ /// U1 Device Exit Latency.
+ pub bU1DevExitLat: u8,
+ /// U2 Device Exit Latency.
+ pub bU2DevExitLat: u16,
+}
+#[test]
+fn bindgen_test_layout_libusb_ss_usb_device_capability_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_ss_usb_device_capability_descriptor>(),
+ 10usize,
+ concat!(
+ "Size of: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor)
+ )
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_ss_usb_device_capability_descriptor>(),
+ 2usize,
+ concat!(
+ "Alignment of ",
+ stringify!(libusb_ss_usb_device_capability_descriptor)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_usb_device_capability_descriptor>())).bLength
+ as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor),
+ "::",
+ stringify!(bLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_usb_device_capability_descriptor>())).bDescriptorType
+ as *const _ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor),
+ "::",
+ stringify!(bDescriptorType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_usb_device_capability_descriptor>()))
+ .bDevCapabilityType as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor),
+ "::",
+ stringify!(bDevCapabilityType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_usb_device_capability_descriptor>())).bmAttributes
+ as *const _ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor),
+ "::",
+ stringify!(bmAttributes)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_usb_device_capability_descriptor>())).wSpeedSupported
+ as *const _ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor),
+ "::",
+ stringify!(wSpeedSupported)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_usb_device_capability_descriptor>()))
+ .bFunctionalitySupport as *const _ as usize
+ },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor),
+ "::",
+ stringify!(bFunctionalitySupport)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_usb_device_capability_descriptor>())).bU1DevExitLat
+ as *const _ as usize
+ },
+ 7usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor),
+ "::",
+ stringify!(bU1DevExitLat)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_ss_usb_device_capability_descriptor>())).bU2DevExitLat
+ as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_ss_usb_device_capability_descriptor),
+ "::",
+ stringify!(bU2DevExitLat)
+ )
+ );
+}
+/// \ingroup libusb_desc
+/// A structure representing the Container ID descriptor.
+/// This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification.
+/// All multiple-byte fields, except UUIDs, are represented in host-endian format.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_container_id_descriptor {
+ /// Size of this descriptor (in bytes)
+ pub bLength: u8,
+ /// Descriptor type. Will have value
+ /// \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ /// LIBUSB_DT_DEVICE_CAPABILITY in this context.
+ pub bDescriptorType: u8,
+ /// Capability type. Will have value
+ /// \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID
+ /// LIBUSB_BT_CONTAINER_ID in this context.
+ pub bDevCapabilityType: u8,
+ /// Reserved field
+ pub bReserved: u8,
+ /// 128 bit UUID
+ pub ContainerID: [u8; 16usize],
+}
+#[test]
+fn bindgen_test_layout_libusb_container_id_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_container_id_descriptor>(),
+ 20usize,
+ concat!("Size of: ", stringify!(libusb_container_id_descriptor))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_container_id_descriptor>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(libusb_container_id_descriptor))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_container_id_descriptor>())).bLength as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_container_id_descriptor),
+ "::",
+ stringify!(bLength)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_container_id_descriptor>())).bDescriptorType as *const _
+ as usize
+ },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_container_id_descriptor),
+ "::",
+ stringify!(bDescriptorType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_container_id_descriptor>())).bDevCapabilityType
+ as *const _ as usize
+ },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_container_id_descriptor),
+ "::",
+ stringify!(bDevCapabilityType)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_container_id_descriptor>())).bReserved as *const _
+ as usize
+ },
+ 3usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_container_id_descriptor),
+ "::",
+ stringify!(bReserved)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_container_id_descriptor>())).ContainerID as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_container_id_descriptor),
+ "::",
+ stringify!(ContainerID)
+ )
+ );
+}
+/// \ingroup libusb_asyncio
+/// Setup packet for control transfers.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_control_setup {
+ /// Request type. Bits 0:4 determine recipient, see
+ /// \ref libusb_request_recipient. Bits 5:6 determine type, see
+ /// \ref libusb_request_type. Bit 7 determines data transfer direction, see
+ /// \ref libusb_endpoint_direction.
+ pub bmRequestType: u8,
+ /// Request. If the type bits of bmRequestType are equal to
+ /// \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD
+ /// "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to
+ /// \ref libusb_standard_request. For other cases, use of this field is
+ /// application-specific.
+ pub bRequest: u8,
+ /// Value. Varies according to request
+ pub wValue: u16,
+ /// Index. Varies according to request, typically used to pass an index
+ /// or offset
+ pub wIndex: u16,
+ /// Number of bytes to transfer
+ pub wLength: u16,
+}
+#[test]
+fn bindgen_test_layout_libusb_control_setup() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_control_setup>(),
+ 8usize,
+ concat!("Size of: ", stringify!(libusb_control_setup))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_control_setup>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(libusb_control_setup))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_control_setup>())).bmRequestType as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_control_setup),
+ "::",
+ stringify!(bmRequestType)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_control_setup>())).bRequest as *const _ as usize },
+ 1usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_control_setup),
+ "::",
+ stringify!(bRequest)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_control_setup>())).wValue as *const _ as usize },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_control_setup),
+ "::",
+ stringify!(wValue)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_control_setup>())).wIndex as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_control_setup),
+ "::",
+ stringify!(wIndex)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_control_setup>())).wLength as *const _ as usize },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_control_setup),
+ "::",
+ stringify!(wLength)
+ )
+ );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_context {
+ _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_device {
+ _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_device_handle {
+ _unused: [u8; 0],
+}
+/// \ingroup libusb_lib
+/// Structure providing the version of the libusb runtime
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_version {
+ /// Library major version.
+ pub major: u16,
+ /// Library minor version.
+ pub minor: u16,
+ /// Library micro version.
+ pub micro: u16,
+ /// Library nano version.
+ pub nano: u16,
+ /// Library release candidate suffix string, e.g. "-rc4".
+ pub rc: *const ::std::os::raw::c_char,
+ /// For ABI compatibility only.
+ pub describe: *const ::std::os::raw::c_char,
+}
+#[test]
+fn bindgen_test_layout_libusb_version() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_version>(),
+ 24usize,
+ concat!("Size of: ", stringify!(libusb_version))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_version>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(libusb_version))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_version>())).major as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_version),
+ "::",
+ stringify!(major)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_version>())).minor as *const _ as usize },
+ 2usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_version),
+ "::",
+ stringify!(minor)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_version>())).micro as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_version),
+ "::",
+ stringify!(micro)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_version>())).nano as *const _ as usize },
+ 6usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_version),
+ "::",
+ stringify!(nano)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_version>())).rc as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_version),
+ "::",
+ stringify!(rc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_version>())).describe as *const _ as usize },
+ 16usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_version),
+ "::",
+ stringify!(describe)
+ )
+ );
+}
+/// The OS doesn't report or know the device speed.
+pub const LIBUSB_SPEED_UNKNOWN: libusb_speed = 0;
+/// The device is operating at low speed (1.5MBit/s).
+pub const LIBUSB_SPEED_LOW: libusb_speed = 1;
+/// The device is operating at full speed (12MBit/s).
+pub const LIBUSB_SPEED_FULL: libusb_speed = 2;
+/// The device is operating at high speed (480MBit/s).
+pub const LIBUSB_SPEED_HIGH: libusb_speed = 3;
+/// The device is operating at super speed (5000MBit/s).
+pub const LIBUSB_SPEED_SUPER: libusb_speed = 4;
+/// \ingroup libusb_dev
+/// Speed codes. Indicates the speed at which the device is operating.
+pub type libusb_speed = u32;
+/// Low speed operation supported (1.5MBit/s).
+pub const LIBUSB_LOW_SPEED_OPERATION: libusb_supported_speed = 1;
+/// Full speed operation supported (12MBit/s).
+pub const LIBUSB_FULL_SPEED_OPERATION: libusb_supported_speed = 2;
+/// High speed operation supported (480MBit/s).
+pub const LIBUSB_HIGH_SPEED_OPERATION: libusb_supported_speed = 4;
+/// Superspeed operation supported (5000MBit/s).
+pub const LIBUSB_SUPER_SPEED_OPERATION: libusb_supported_speed = 8;
+/// \ingroup libusb_dev
+/// Supported speeds (wSpeedSupported) bitfield. Indicates what
+/// speeds the device supports.
+pub type libusb_supported_speed = u32;
+/// Supports Link Power Management (LPM)
+pub const LIBUSB_BM_LPM_SUPPORT: libusb_usb_2_0_extension_attributes = 2;
+/// \ingroup libusb_dev
+/// Masks for the bits of the
+/// \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field
+/// of the USB 2.0 Extension descriptor.
+pub type libusb_usb_2_0_extension_attributes = u32;
+/// Supports Latency Tolerance Messages (LTM)
+pub const LIBUSB_BM_LTM_SUPPORT: libusb_ss_usb_device_capability_attributes = 2;
+/// \ingroup libusb_dev
+/// Masks for the bits of the
+/// \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field
+/// field of the SuperSpeed USB Device Capability descriptor.
+pub type libusb_ss_usb_device_capability_attributes = u32;
+/// Wireless USB device capability
+pub const LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY: libusb_bos_type = 1;
+/// USB 2.0 extensions
+pub const LIBUSB_BT_USB_2_0_EXTENSION: libusb_bos_type = 2;
+/// SuperSpeed USB device capability
+pub const LIBUSB_BT_SS_USB_DEVICE_CAPABILITY: libusb_bos_type = 3;
+/// Container ID type
+pub const LIBUSB_BT_CONTAINER_ID: libusb_bos_type = 4;
+/// \ingroup libusb_dev
+/// USB capability types
+pub type libusb_bos_type = u32;
+/// Success (no error)
+pub const LIBUSB_SUCCESS: libusb_error = 0;
+/// Input/output error
+pub const LIBUSB_ERROR_IO: libusb_error = -1;
+/// Invalid parameter
+pub const LIBUSB_ERROR_INVALID_PARAM: libusb_error = -2;
+/// Access denied (insufficient permissions)
+pub const LIBUSB_ERROR_ACCESS: libusb_error = -3;
+/// No such device (it may have been disconnected)
+pub const LIBUSB_ERROR_NO_DEVICE: libusb_error = -4;
+/// Entity not found
+pub const LIBUSB_ERROR_NOT_FOUND: libusb_error = -5;
+/// Resource busy
+pub const LIBUSB_ERROR_BUSY: libusb_error = -6;
+/// Operation timed out
+pub const LIBUSB_ERROR_TIMEOUT: libusb_error = -7;
+/// Overflow
+pub const LIBUSB_ERROR_OVERFLOW: libusb_error = -8;
+/// Pipe error
+pub const LIBUSB_ERROR_PIPE: libusb_error = -9;
+/// System call interrupted (perhaps due to signal)
+pub const LIBUSB_ERROR_INTERRUPTED: libusb_error = -10;
+/// Insufficient memory
+pub const LIBUSB_ERROR_NO_MEM: libusb_error = -11;
+/// Operation not supported or unimplemented on this platform
+pub const LIBUSB_ERROR_NOT_SUPPORTED: libusb_error = -12;
+/// Other error
+pub const LIBUSB_ERROR_OTHER: libusb_error = -99;
+/// \ingroup libusb_misc
+/// Error codes. Most libusb functions return 0 on success or one of these
+/// codes on failure.
+/// You can call libusb_error_name() to retrieve a string representation of an
+/// error code or libusb_strerror() to get an end-user suitable description of
+/// an error code.
+pub type libusb_error = i32;
+/// Transfer completed without error. Note that this does not indicate
+/// that the entire amount of requested data was transferred.
+pub const LIBUSB_TRANSFER_COMPLETED: libusb_transfer_status = 0;
+/// Transfer failed
+pub const LIBUSB_TRANSFER_ERROR: libusb_transfer_status = 1;
+/// Transfer timed out
+pub const LIBUSB_TRANSFER_TIMED_OUT: libusb_transfer_status = 2;
+/// Transfer was cancelled
+pub const LIBUSB_TRANSFER_CANCELLED: libusb_transfer_status = 3;
+/// For bulk/interrupt endpoints: halt condition detected (endpoint
+/// stalled). For control endpoints: control request not supported.
+pub const LIBUSB_TRANSFER_STALL: libusb_transfer_status = 4;
+/// Device was disconnected
+pub const LIBUSB_TRANSFER_NO_DEVICE: libusb_transfer_status = 5;
+/// Device sent more data than requested
+pub const LIBUSB_TRANSFER_OVERFLOW: libusb_transfer_status = 6;
+/// \ingroup libusb_asyncio
+/// Transfer status codes
+pub type libusb_transfer_status = u32;
+/// Report short frames as errors
+pub const LIBUSB_TRANSFER_SHORT_NOT_OK: libusb_transfer_flags = 1;
+/// Automatically free() transfer buffer during libusb_free_transfer().
+/// Note that buffers allocated with libusb_dev_mem_alloc() should not
+/// be attempted freed in this way, since free() is not an appropriate
+/// way to release such memory.
+pub const LIBUSB_TRANSFER_FREE_BUFFER: libusb_transfer_flags = 2;
+/// Automatically call libusb_free_transfer() after callback returns.
+/// If this flag is set, it is illegal to call libusb_free_transfer()
+/// from your transfer callback, as this will result in a double-free
+/// when this flag is acted upon.
+pub const LIBUSB_TRANSFER_FREE_TRANSFER: libusb_transfer_flags = 4;
+/// Terminate transfers that are a multiple of the endpoint's
+/// wMaxPacketSize with an extra zero length packet. This is useful
+/// when a device protocol mandates that each logical request is
+/// terminated by an incomplete packet (i.e. the logical requests are
+/// not separated by other means).
+///
+/// This flag only affects host-to-device transfers to bulk and interrupt
+/// endpoints. In other situations, it is ignored.
+///
+/// This flag only affects transfers with a length that is a multiple of
+/// the endpoint's wMaxPacketSize. On transfers of other lengths, this
+/// flag has no effect. Therefore, if you are working with a device that
+/// needs a ZLP whenever the end of the logical request falls on a packet
+/// boundary, then it is sensible to set this flag on <em>every</em>
+/// transfer (you do not have to worry about only setting it on transfers
+/// that end on the boundary).
+///
+/// This flag is currently only supported on Linux.
+/// On other systems, libusb_submit_transfer() will return
+/// LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set.
+///
+/// Available since libusb-1.0.9.
+pub const LIBUSB_TRANSFER_ADD_ZERO_PACKET: libusb_transfer_flags = 8;
+/// \ingroup libusb_asyncio
+/// libusb_transfer.flags values
+pub type libusb_transfer_flags = u32;
+/// \ingroup libusb_asyncio
+/// Isochronous packet descriptor.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_iso_packet_descriptor {
+ /// Length of data to request in this packet
+ pub length: ::std::os::raw::c_uint,
+ /// Amount of data that was actually transferred
+ pub actual_length: ::std::os::raw::c_uint,
+ /// Status code for this packet
+ pub status: libusb_transfer_status,
+}
+#[test]
+fn bindgen_test_layout_libusb_iso_packet_descriptor() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_iso_packet_descriptor>(),
+ 12usize,
+ concat!("Size of: ", stringify!(libusb_iso_packet_descriptor))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_iso_packet_descriptor>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(libusb_iso_packet_descriptor))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_iso_packet_descriptor>())).length as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_iso_packet_descriptor),
+ "::",
+ stringify!(length)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_iso_packet_descriptor>())).actual_length as *const _
+ as usize
+ },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_iso_packet_descriptor),
+ "::",
+ stringify!(actual_length)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<libusb_iso_packet_descriptor>())).status as *const _ as usize
+ },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_iso_packet_descriptor),
+ "::",
+ stringify!(status)
+ )
+ );
+}
+/// \ingroup libusb_asyncio
+/// Asynchronous transfer callback function type. When submitting asynchronous
+/// transfers, you pass a pointer to a callback function of this type via the
+/// \ref libusb_transfer::callback "callback" member of the libusb_transfer
+/// structure. libusb will call this function later, when the transfer has
+/// completed or failed. See \ref libusb_asyncio for more information.
+/// \param transfer The libusb_transfer struct the callback function is being
+/// notified about.
+pub type libusb_transfer_cb_fn =
+ ::std::option::Option<unsafe extern "C" fn(transfer: *mut libusb_transfer)>;
+/// \ingroup libusb_asyncio
+/// The generic USB transfer structure. The user populates this structure and
+/// then submits it in order to request a transfer. After the transfer has
+/// completed, the library populates the transfer with the results and passes
+/// it back to the user.
+#[repr(C)]
+#[derive(Debug)]
+pub struct libusb_transfer {
+ /// Handle of the device that this transfer will be submitted to
+ pub dev_handle: *mut libusb_device_handle,
+ /// A bitwise OR combination of \ref libusb_transfer_flags.
+ pub flags: u8,
+ /// Address of the endpoint where this transfer will be sent.
+ pub endpoint: ::std::os::raw::c_uchar,
+ /// Type of the endpoint from \ref libusb_transfer_type
+ pub type_: ::std::os::raw::c_uchar,
+ /// Timeout for this transfer in millseconds. A value of 0 indicates no
+ /// timeout.
+ pub timeout: ::std::os::raw::c_uint,
+ /// The status of the transfer. Read-only, and only for use within
+ /// transfer callback function.
+ ///
+ /// If this is an isochronous transfer, this field may read COMPLETED even
+ /// if there were errors in the frames. Use the
+ /// \ref libusb_iso_packet_descriptor::status "status" field in each packet
+ /// to determine if errors occurred.
+ pub status: libusb_transfer_status,
+ /// Length of the data buffer
+ pub length: ::std::os::raw::c_int,
+ /// Actual length of data that was transferred. Read-only, and only for
+ /// use within transfer callback function. Not valid for isochronous
+ /// endpoint transfers.
+ pub actual_length: ::std::os::raw::c_int,
+ /// Callback function. This will be invoked when the transfer completes,
+ /// fails, or is cancelled.
+ pub callback: libusb_transfer_cb_fn,
+ /// User context data to pass to the callback function.
+ pub user_data: *mut ::std::os::raw::c_void,
+ /// Data buffer
+ pub buffer: *mut ::std::os::raw::c_uchar,
+ /// Number of isochronous packets. Only used for I/O with isochronous
+ /// endpoints.
+ pub num_iso_packets: ::std::os::raw::c_int,
+ /// Isochronous packet descriptors, for isochronous transfers only.
+ pub iso_packet_desc: __IncompleteArrayField<libusb_iso_packet_descriptor>,
+}
+#[test]
+fn bindgen_test_layout_libusb_transfer() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_transfer>(),
+ 64usize,
+ concat!("Size of: ", stringify!(libusb_transfer))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_transfer>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(libusb_transfer))
+ );
+}
+/// The libusb_has_capability() API is available.
+pub const LIBUSB_CAP_HAS_CAPABILITY: libusb_capability = 0;
+/// Hotplug support is available on this platform.
+pub const LIBUSB_CAP_HAS_HOTPLUG: libusb_capability = 1;
+/// The library can access HID devices without requiring user intervention.
+/// Note that before being able to actually access an HID device, you may
+/// still have to call additional libusb functions such as
+/// \ref libusb_detach_kernel_driver().
+pub const LIBUSB_CAP_HAS_HID_ACCESS: libusb_capability = 256;
+/// The library supports detaching of the default USB driver, using
+/// \ref libusb_detach_kernel_driver(), if one is set by the OS kernel
+pub const LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER: libusb_capability = 257;
+/// \ingroup libusb_misc
+/// Capabilities supported by an instance of libusb on the current running
+/// platform. Test if the loaded library supports a given capability by calling
+/// \ref libusb_has_capability().
+pub type libusb_capability = u32;
+pub const LIBUSB_LOG_LEVEL_NONE: libusb_log_level = 0;
+pub const LIBUSB_LOG_LEVEL_ERROR: libusb_log_level = 1;
+pub const LIBUSB_LOG_LEVEL_WARNING: libusb_log_level = 2;
+pub const LIBUSB_LOG_LEVEL_INFO: libusb_log_level = 3;
+pub const LIBUSB_LOG_LEVEL_DEBUG: libusb_log_level = 4;
+/// \ingroup libusb_lib
+/// Log message levels.
+/// - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default)
+/// - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr
+/// - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr
+/// - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stdout, warning
+/// and error messages are printed to stderr
+/// - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stdout,
+/// warnings and errors to stderr
+pub type libusb_log_level = u32;
+extern "C" {
+ pub fn libusb_init(ctx: *mut *mut libusb_context) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_init_jailed(ctx: *mut *mut libusb_context) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_exit(ctx: *mut libusb_context);
+}
+extern "C" {
+ pub fn libusb_get_device_from_fd(
+ ctx: *mut libusb_context,
+ fd: ::std::os::raw::c_int,
+ device: *mut *mut libusb_device,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_set_debug(ctx: *mut libusb_context, level: ::std::os::raw::c_int);
+}
+extern "C" {
+ pub fn libusb_get_version() -> *const libusb_version;
+}
+extern "C" {
+ pub fn libusb_has_capability(capability: u32) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_error_name(errcode: ::std::os::raw::c_int) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+ pub fn libusb_setlocale(locale: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_strerror(errcode: libusb_error) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+ pub fn libusb_get_device_list(
+ ctx: *mut libusb_context,
+ list: *mut *mut *mut libusb_device,
+ ) -> isize;
+}
+extern "C" {
+ pub fn libusb_free_device_list(
+ list: *mut *mut libusb_device,
+ unref_devices: ::std::os::raw::c_int,
+ );
+}
+extern "C" {
+ pub fn libusb_ref_device(dev: *mut libusb_device) -> *mut libusb_device;
+}
+extern "C" {
+ pub fn libusb_unref_device(dev: *mut libusb_device);
+}
+extern "C" {
+ pub fn libusb_get_configuration(
+ dev: *mut libusb_device_handle,
+ config: *mut ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_device_descriptor(
+ dev: *mut libusb_device,
+ desc: *mut libusb_device_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_active_config_descriptor(
+ dev: *mut libusb_device,
+ config: *mut *mut libusb_config_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_config_descriptor(
+ dev: *mut libusb_device,
+ config_index: u8,
+ config: *mut *mut libusb_config_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_config_descriptor_by_value(
+ dev: *mut libusb_device,
+ bConfigurationValue: u8,
+ config: *mut *mut libusb_config_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_free_config_descriptor(config: *mut libusb_config_descriptor);
+}
+extern "C" {
+ pub fn libusb_get_ss_endpoint_companion_descriptor(
+ ctx: *mut libusb_context,
+ endpoint: *const libusb_endpoint_descriptor,
+ ep_comp: *mut *mut libusb_ss_endpoint_companion_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_free_ss_endpoint_companion_descriptor(
+ ep_comp: *mut libusb_ss_endpoint_companion_descriptor,
+ );
+}
+extern "C" {
+ pub fn libusb_get_bos_descriptor(
+ dev_handle: *mut libusb_device_handle,
+ bos: *mut *mut libusb_bos_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_free_bos_descriptor(bos: *mut libusb_bos_descriptor);
+}
+extern "C" {
+ pub fn libusb_get_usb_2_0_extension_descriptor(
+ ctx: *mut libusb_context,
+ dev_cap: *mut libusb_bos_dev_capability_descriptor,
+ usb_2_0_extension: *mut *mut libusb_usb_2_0_extension_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_free_usb_2_0_extension_descriptor(
+ usb_2_0_extension: *mut libusb_usb_2_0_extension_descriptor,
+ );
+}
+extern "C" {
+ pub fn libusb_get_ss_usb_device_capability_descriptor(
+ ctx: *mut libusb_context,
+ dev_cap: *mut libusb_bos_dev_capability_descriptor,
+ ss_usb_device_cap: *mut *mut libusb_ss_usb_device_capability_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_free_ss_usb_device_capability_descriptor(
+ ss_usb_device_cap: *mut libusb_ss_usb_device_capability_descriptor,
+ );
+}
+extern "C" {
+ pub fn libusb_get_container_id_descriptor(
+ ctx: *mut libusb_context,
+ dev_cap: *mut libusb_bos_dev_capability_descriptor,
+ container_id: *mut *mut libusb_container_id_descriptor,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_free_container_id_descriptor(container_id: *mut libusb_container_id_descriptor);
+}
+extern "C" {
+ pub fn libusb_get_bus_number(dev: *mut libusb_device) -> u8;
+}
+extern "C" {
+ pub fn libusb_get_port_number(dev: *mut libusb_device) -> u8;
+}
+extern "C" {
+ pub fn libusb_get_port_numbers(
+ dev: *mut libusb_device,
+ port_numbers: *mut u8,
+ port_numbers_len: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_port_path(
+ ctx: *mut libusb_context,
+ dev: *mut libusb_device,
+ path: *mut u8,
+ path_length: u8,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_parent(dev: *mut libusb_device) -> *mut libusb_device;
+}
+extern "C" {
+ pub fn libusb_get_device_address(dev: *mut libusb_device) -> u8;
+}
+extern "C" {
+ pub fn libusb_get_device_speed(dev: *mut libusb_device) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_max_packet_size(
+ dev: *mut libusb_device,
+ endpoint: ::std::os::raw::c_uchar,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_max_iso_packet_size(
+ dev: *mut libusb_device,
+ endpoint: ::std::os::raw::c_uchar,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_open(
+ dev: *mut libusb_device,
+ dev_handle: *mut *mut libusb_device_handle,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_open_fd(
+ dev: *mut libusb_device,
+ fd: ::std::os::raw::c_int,
+ handle: *mut *mut libusb_device_handle,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_close(dev_handle: *mut libusb_device_handle);
+}
+extern "C" {
+ pub fn libusb_get_device(dev_handle: *mut libusb_device_handle) -> *mut libusb_device;
+}
+extern "C" {
+ pub fn libusb_set_configuration(
+ dev_handle: *mut libusb_device_handle,
+ configuration: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_claim_interface(
+ dev_handle: *mut libusb_device_handle,
+ interface_number: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_release_interface(
+ dev_handle: *mut libusb_device_handle,
+ interface_number: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_open_device_with_vid_pid(
+ ctx: *mut libusb_context,
+ vendor_id: u16,
+ product_id: u16,
+ ) -> *mut libusb_device_handle;
+}
+extern "C" {
+ pub fn libusb_set_interface_alt_setting(
+ dev_handle: *mut libusb_device_handle,
+ interface_number: ::std::os::raw::c_int,
+ alternate_setting: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_clear_halt(
+ dev_handle: *mut libusb_device_handle,
+ endpoint: ::std::os::raw::c_uchar,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_reset_device(dev_handle: *mut libusb_device_handle) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_alloc_streams(
+ dev_handle: *mut libusb_device_handle,
+ num_streams: u32,
+ endpoints: *mut ::std::os::raw::c_uchar,
+ num_endpoints: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_free_streams(
+ dev_handle: *mut libusb_device_handle,
+ endpoints: *mut ::std::os::raw::c_uchar,
+ num_endpoints: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_dev_mem_alloc(
+ dev_handle: *mut libusb_device_handle,
+ length: usize,
+ ) -> *mut ::std::os::raw::c_uchar;
+}
+extern "C" {
+ pub fn libusb_dev_mem_free(
+ dev_handle: *mut libusb_device_handle,
+ buffer: *mut ::std::os::raw::c_uchar,
+ length: usize,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_kernel_driver_active(
+ dev_handle: *mut libusb_device_handle,
+ interface_number: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_detach_kernel_driver(
+ dev_handle: *mut libusb_device_handle,
+ interface_number: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_attach_kernel_driver(
+ dev_handle: *mut libusb_device_handle,
+ interface_number: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_set_auto_detach_kernel_driver(
+ dev_handle: *mut libusb_device_handle,
+ enable: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_alloc_transfer(iso_packets: ::std::os::raw::c_int) -> *mut libusb_transfer;
+}
+extern "C" {
+ pub fn libusb_submit_transfer(transfer: *mut libusb_transfer) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_cancel_transfer(transfer: *mut libusb_transfer) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_free_transfer(transfer: *mut libusb_transfer);
+}
+extern "C" {
+ pub fn libusb_transfer_set_stream_id(transfer: *mut libusb_transfer, stream_id: u32);
+}
+extern "C" {
+ pub fn libusb_transfer_get_stream_id(transfer: *mut libusb_transfer) -> u32;
+}
+extern "C" {
+ pub fn libusb_control_transfer(
+ dev_handle: *mut libusb_device_handle,
+ request_type: u8,
+ bRequest: u8,
+ wValue: u16,
+ wIndex: u16,
+ data: *mut ::std::os::raw::c_uchar,
+ wLength: u16,
+ timeout: ::std::os::raw::c_uint,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_bulk_transfer(
+ dev_handle: *mut libusb_device_handle,
+ endpoint: ::std::os::raw::c_uchar,
+ data: *mut ::std::os::raw::c_uchar,
+ length: ::std::os::raw::c_int,
+ actual_length: *mut ::std::os::raw::c_int,
+ timeout: ::std::os::raw::c_uint,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_interrupt_transfer(
+ dev_handle: *mut libusb_device_handle,
+ endpoint: ::std::os::raw::c_uchar,
+ data: *mut ::std::os::raw::c_uchar,
+ length: ::std::os::raw::c_int,
+ actual_length: *mut ::std::os::raw::c_int,
+ timeout: ::std::os::raw::c_uint,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_string_descriptor_ascii(
+ dev_handle: *mut libusb_device_handle,
+ desc_index: u8,
+ data: *mut ::std::os::raw::c_uchar,
+ length: ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_try_lock_events(ctx: *mut libusb_context) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_lock_events(ctx: *mut libusb_context);
+}
+extern "C" {
+ pub fn libusb_unlock_events(ctx: *mut libusb_context);
+}
+extern "C" {
+ pub fn libusb_event_handling_ok(ctx: *mut libusb_context) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_event_handler_active(ctx: *mut libusb_context) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_interrupt_event_handler(ctx: *mut libusb_context);
+}
+extern "C" {
+ pub fn libusb_lock_event_waiters(ctx: *mut libusb_context);
+}
+extern "C" {
+ pub fn libusb_unlock_event_waiters(ctx: *mut libusb_context);
+}
+extern "C" {
+ pub fn libusb_wait_for_event(
+ ctx: *mut libusb_context,
+ tv: *mut timeval,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_handle_events_timeout(
+ ctx: *mut libusb_context,
+ tv: *mut timeval,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_handle_events_timeout_completed(
+ ctx: *mut libusb_context,
+ tv: *mut timeval,
+ completed: *mut ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_handle_events(ctx: *mut libusb_context) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_handle_events_completed(
+ ctx: *mut libusb_context,
+ completed: *mut ::std::os::raw::c_int,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_handle_events_locked(
+ ctx: *mut libusb_context,
+ tv: *mut timeval,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_pollfds_handle_timeouts(ctx: *mut libusb_context) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ pub fn libusb_get_next_timeout(
+ ctx: *mut libusb_context,
+ tv: *mut timeval,
+ ) -> ::std::os::raw::c_int;
+}
+/// \ingroup libusb_poll
+/// File descriptor for polling
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct libusb_pollfd {
+ /// Numeric file descriptor
+ pub fd: ::std::os::raw::c_int,
+ /// Event flags to poll for from <poll.h>. POLLIN indicates that you
+ /// should monitor this file descriptor for becoming ready to read from,
+ /// and POLLOUT indicates that you should monitor this file descriptor for
+ /// nonblocking write readiness.
+ pub events: ::std::os::raw::c_short,
+}
+#[test]
+fn bindgen_test_layout_libusb_pollfd() {
+ assert_eq!(
+ ::std::mem::size_of::<libusb_pollfd>(),
+ 8usize,
+ concat!("Size of: ", stringify!(libusb_pollfd))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<libusb_pollfd>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(libusb_pollfd))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_pollfd>())).fd as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_pollfd),
+ "::",
+ stringify!(fd)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<libusb_pollfd>())).events as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(libusb_pollfd),
+ "::",
+ stringify!(events)
+ )
+ );
+}
+/// \ingroup libusb_poll
+/// Callback function, invoked when a new file descriptor should be added
+/// to the set of file descriptors monitored for events.
+/// \param fd the new file descriptor
+/// \param events events to monitor for, see \ref libusb_pollfd for a
+/// description
+/// \param user_data User data pointer specified in
+/// libusb_set_pollfd_notifiers() call
+/// \see libusb_set_pollfd_notifiers()
+pub type libusb_pollfd_added_cb = ::std::option::Option<
+ unsafe extern "C" fn(
+ fd: ::std::os::raw::c_int,
+ events: ::std::os::raw::c_short,
+ user_data: *mut ::std::os::raw::c_void,
+ ),
+>;
+/// \ingroup libusb_poll
+/// Callback function, invoked when a file descriptor should be removed from
+/// the set of file descriptors being monitored for events. After returning
+/// from this callback, do not use that file descriptor again.
+/// \param fd the file descriptor to stop monitoring
+/// \param user_data User data pointer specified in
+/// libusb_set_pollfd_notifiers() call
+/// \see libusb_set_pollfd_notifiers()
+pub type libusb_pollfd_removed_cb = ::std::option::Option<
+ unsafe extern "C" fn(fd: ::std::os::raw::c_int, user_data: *mut ::std::os::raw::c_void),
+>;
+extern "C" {
+ pub fn libusb_get_pollfds(ctx: *mut libusb_context) -> *mut *const libusb_pollfd;
+}
+extern "C" {
+ pub fn libusb_free_pollfds(pollfds: *mut *const libusb_pollfd);
+}
+extern "C" {
+ pub fn libusb_set_pollfd_notifiers(
+ ctx: *mut libusb_context,
+ added_cb: libusb_pollfd_added_cb,
+ removed_cb: libusb_pollfd_removed_cb,
+ user_data: *mut ::std::os::raw::c_void,
+ );
+}
+/// \ingroup libusb_hotplug
+/// Callback handle.
+///
+/// Callbacks handles are generated by libusb_hotplug_register_callback()
+/// and can be used to deregister callbacks. Callback handles are unique
+/// per libusb_context and it is safe to call libusb_hotplug_deregister_callback()
+/// on an already deregisted callback.
+///
+/// Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+///
+/// For more information, see \ref libusb_hotplug.
+pub type libusb_hotplug_callback_handle = ::std::os::raw::c_int;
+/// Default value when not using any flags.
+pub const LIBUSB_HOTPLUG_NO_FLAGS: libusb_hotplug_flag = 0;
+/// Arm the callback and fire it for all matching currently attached devices.
+pub const LIBUSB_HOTPLUG_ENUMERATE: libusb_hotplug_flag = 1;
+/// \ingroup libusb_hotplug
+///
+/// Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+///
+/// Flags for hotplug events
+pub type libusb_hotplug_flag = u32;
+/// A device has been plugged in and is ready to use
+pub const LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: libusb_hotplug_event = 1;
+/// A device has left and is no longer available.
+/// It is the user's responsibility to call libusb_close on any handle associated with a disconnected device.
+/// It is safe to call libusb_get_device_descriptor on a device that has left
+pub const LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: libusb_hotplug_event = 2;
+/// \ingroup libusb_hotplug
+///
+/// Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+///
+/// Hotplug events
+pub type libusb_hotplug_event = u32;
+/// \ingroup libusb_hotplug
+/// Hotplug callback function type. When requesting hotplug event notifications,
+/// you pass a pointer to a callback function of this type.
+///
+/// This callback may be called by an internal event thread and as such it is
+/// recommended the callback do minimal processing before returning.
+///
+/// libusb will call this function later, when a matching event had happened on
+/// a matching device. See \ref libusb_hotplug for more information.
+///
+/// It is safe to call either libusb_hotplug_register_callback() or
+/// libusb_hotplug_deregister_callback() from within a callback function.
+///
+/// Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+///
+/// \param ctx context of this notification
+/// \param device libusb_device this event occurred on
+/// \param event event that occurred
+/// \param user_data user data provided when this callback was registered
+/// \returns bool whether this callback is finished processing events.
+/// returning 1 will cause this callback to be deregistered
+pub type libusb_hotplug_callback_fn = ::std::option::Option<
+ unsafe extern "C" fn(
+ ctx: *mut libusb_context,
+ device: *mut libusb_device,
+ event: libusb_hotplug_event,
+ user_data: *mut ::std::os::raw::c_void,
+ ) -> ::std::os::raw::c_int,
+>;
+extern "C" {
+ /// \ingroup libusb_hotplug
+ /// Register a hotplug callback function
+ ///
+ /// Register a callback with the libusb_context. The callback will fire
+ /// when a matching event occurs on a matching device. The callback is
+ /// armed until either it is deregistered with libusb_hotplug_deregister_callback()
+ /// or the supplied callback returns 1 to indicate it is finished processing events.
+ ///
+ /// If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be
+ /// called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices
+ /// already plugged into the machine. Note that libusb modifies its internal
+ /// device list from a separate thread, while calling hotplug callbacks from
+ /// libusb_handle_events(), so it is possible for a device to already be present
+ /// on, or removed from, its internal device list, while the hotplug callbacks
+ /// still need to be dispatched. This means that when using \ref
+ /// LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival
+ /// of the same device, once from libusb_hotplug_register_callback() and once
+ /// from libusb_handle_events(); and/or your callback may be called for the
+ /// removal of a device for which an arrived call was never made.
+ ///
+ /// Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ ///
+ /// \param[in] ctx context to register this callback with
+ /// \param[in] events bitwise or of events that will trigger this callback. See \ref
+ /// libusb_hotplug_event
+ /// \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag
+ /// \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ /// \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ /// \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ /// \param[in] cb_fn the function to be invoked on a matching event/device
+ /// \param[in] user_data user data to pass to the callback function
+ /// \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL)
+ /// \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
+ pub fn libusb_hotplug_register_callback(
+ ctx: *mut libusb_context,
+ events: libusb_hotplug_event,
+ flags: libusb_hotplug_flag,
+ vendor_id: ::std::os::raw::c_int,
+ product_id: ::std::os::raw::c_int,
+ dev_class: ::std::os::raw::c_int,
+ cb_fn: libusb_hotplug_callback_fn,
+ user_data: *mut ::std::os::raw::c_void,
+ callback_handle: *mut libusb_hotplug_callback_handle,
+ ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+ /// \ingroup libusb_hotplug
+ /// Deregisters a hotplug callback.
+ ///
+ /// Deregister a callback from a libusb_context. This function is safe to call from within
+ /// a hotplug callback.
+ ///
+ /// Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ ///
+ /// \param[in] ctx context this callback is registered with
+ /// \param[in] callback_handle the handle of the callback to deregister
+ pub fn libusb_hotplug_deregister_callback(
+ ctx: *mut libusb_context,
+ callback_handle: libusb_hotplug_callback_handle,
+ );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __locale_data {
+ pub _address: u8,
+}
diff --git a/usb_util/src/config_descriptor.rs b/usb_util/src/config_descriptor.rs
new file mode 100644
index 0000000..8aebbac
--- /dev/null
+++ b/usb_util/src/config_descriptor.rs
@@ -0,0 +1,66 @@
+// Copyright 2018 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::ops::Deref;
+
+use crate::bindings::{self, libusb_config_descriptor};
+use crate::interface_descriptor::InterfaceDescriptor;
+
+/// ConfigDescriptor wraps libusb_config_descriptor.
+pub struct ConfigDescriptor {
+ descriptor: *mut libusb_config_descriptor,
+}
+
+impl Drop for ConfigDescriptor {
+ // Free configuration descriptor.
+ // Safe because 'self' is intialized with a valid libusb_config_descriptor from libusb
+ // functions.
+ fn drop(&mut self) {
+ unsafe {
+ bindings::libusb_free_config_descriptor(self.descriptor);
+ }
+ }
+}
+
+impl ConfigDescriptor {
+ /// Build ConfigDescriptor. 'descriptor' should be a valid pointer from
+ /// libusb_config_descriptor. This function will panic if it's null.
+ pub unsafe fn new(descriptor: *mut libusb_config_descriptor) -> ConfigDescriptor {
+ assert!(!descriptor.is_null());
+ ConfigDescriptor { descriptor }
+ }
+
+ /// Get interface by number and alt setting.
+ pub fn get_interface_descriptor(
+ &self,
+ interface_num: u8,
+ alt_setting: i32,
+ ) -> Option<InterfaceDescriptor> {
+ let config_descriptor = self.deref();
+ if interface_num >= config_descriptor.bNumInterfaces {
+ return None;
+ }
+ // Safe because interface num is checked.
+ let interface = unsafe { &*(config_descriptor.interface.offset(interface_num as isize)) };
+
+ if alt_setting >= interface.num_altsetting {
+ return None;
+ }
+ // Safe because setting num is checked.
+ unsafe {
+ Some(InterfaceDescriptor::new(
+ &*(interface.altsetting.offset(alt_setting as isize)),
+ ))
+ }
+ }
+}
+
+impl Deref for ConfigDescriptor {
+ type Target = libusb_config_descriptor;
+
+ fn deref(&self) -> &libusb_config_descriptor {
+ // Safe because 'self.descriptor' is valid.
+ unsafe { &*(self.descriptor) }
+ }
+}
diff --git a/usb_util/src/device_handle.rs b/usb_util/src/device_handle.rs
new file mode 100644
index 0000000..32a963f
--- /dev/null
+++ b/usb_util/src/device_handle.rs
@@ -0,0 +1,148 @@
+// Copyright 2018 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::raw::c_int;
+use std::sync::Arc;
+
+use crate::bindings;
+use crate::error::{Error, Result};
+use crate::libusb_context::LibUsbContextInner;
+use crate::usb_transfer::{UsbTransfer, UsbTransferBuffer};
+
+/// DeviceHandle wraps libusb_device_handle.
+pub struct DeviceHandle {
+ _context: Arc<LibUsbContextInner>,
+ handle: *mut bindings::libusb_device_handle,
+}
+
+unsafe impl Send for DeviceHandle {}
+
+impl Drop for DeviceHandle {
+ fn drop(&mut self) {
+ // Safe because self.handle is a valid pointer to libusb_device_handle.
+ unsafe {
+ bindings::libusb_close(self.handle);
+ }
+ }
+}
+
+impl DeviceHandle {
+ /// Create a new DeviceHande. 'handle' should be a valid pointer to libusb_device_handle.
+ pub unsafe fn new(
+ ctx: Arc<LibUsbContextInner>,
+ handle: *mut bindings::libusb_device_handle,
+ ) -> DeviceHandle {
+ DeviceHandle {
+ _context: ctx,
+ handle,
+ }
+ }
+
+ /// Reset this usb device.
+ pub fn reset(&self) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe { bindings::libusb_reset_device(self.handle) });
+ Ok(())
+ }
+ /// Get bConfigurationValue of the currently active configuration.
+ pub fn get_active_configuration(&self) -> Result<i32> {
+ let mut config: c_int = 0;
+ // Safe because 'self.handle' is a valid pointer to device handle and '&mut config' is a
+ // valid output location.
+ try_libusb!(unsafe { bindings::libusb_get_configuration(self.handle, &mut config) });
+ Ok(config as i32)
+ }
+
+ /// Set active configuration for a device.
+ pub fn set_active_configuration(&mut self, config: i32) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe { bindings::libusb_set_configuration(self.handle, config as c_int) });
+ Ok(())
+ }
+
+ /// Claim an interface on this deivce handle.
+ pub fn claim_interface(&self, interface_number: i32) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe { bindings::libusb_claim_interface(self.handle, interface_number) });
+ Ok(())
+ }
+
+ /// Release an interface previously claimed with libusb_claim_interface.
+ pub fn release_interface(&self, interface_number: i32) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe { bindings::libusb_release_interface(self.handle, interface_number) });
+ Ok(())
+ }
+
+ /// Perform a USB port reset to reinitialize a device.
+ pub fn reset_device(&self) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe { bindings::libusb_reset_device(self.handle) });
+ Ok(())
+ }
+
+ /// Determine if a kernel driver is active on an interface.
+ pub fn kernel_driver_active(&self, interface_number: i32) -> Result<bool> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ let v = try_libusb!(unsafe {
+ bindings::libusb_kernel_driver_active(self.handle, interface_number)
+ });
+ Ok(v != 0)
+ }
+
+ /// Detach a kernel driver from an interface.
+ pub fn detach_kernel_driver(&self, interface_number: i32) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe {
+ bindings::libusb_detach_kernel_driver(self.handle, interface_number)
+ });
+ Ok(())
+ }
+
+ /// Re-attach an interfae's kernel driver, which was previously detached using
+ /// detach_kernel_driver.
+ pub fn attach_kernel_driver(&self, interface_number: i32) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe {
+ bindings::libusb_attach_kernel_driver(self.handle, interface_number)
+ });
+ Ok(())
+ }
+
+ /// Active an alternate setting for an interface.
+ pub fn set_interface_alt_setting(
+ &self,
+ interface_number: i32,
+ alternative_setting: i32,
+ ) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe {
+ bindings::libusb_set_interface_alt_setting(
+ self.handle,
+ interface_number,
+ alternative_setting,
+ )
+ });
+ Ok(())
+ }
+
+ /// Clear the halt/stall condition for an endpoint.
+ pub fn clear_halt(&self, endpoint: u8) -> Result<()> {
+ // Safe because 'self.handle' is a valid pointer to device handle.
+ try_libusb!(unsafe { bindings::libusb_clear_halt(self.handle, endpoint) });
+ Ok(())
+ }
+
+ /// Libusb asynchronous I/O interface has a 5 step process. It gives lots of
+ /// flexibility but makes it hard to manage object life cycle and easy to
+ /// write unsafe code. We wrap this interface to a simple "transfer" and "cancel"
+ /// interface. Resubmission is not supported and deallocation is handled safely
+ /// here.
+ pub fn submit_async_transfer<T: UsbTransferBuffer>(
+ &self,
+ transfer: UsbTransfer<T>,
+ ) -> Result<()> {
+ unsafe { transfer.submit(self.handle) }
+ }
+}
diff --git a/usb_util/src/endpoint_descriptor.rs b/usb_util/src/endpoint_descriptor.rs
new file mode 100644
index 0000000..29eb4ea
--- /dev/null
+++ b/usb_util/src/endpoint_descriptor.rs
@@ -0,0 +1,57 @@
+// Copyright 2018 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::ops::Deref;
+
+use crate::bindings::libusb_endpoint_descriptor;
+use crate::types::{EndpointDirection, EndpointType};
+
+/// EndpointDescriptor wraps libusb_endpoint_descriptor.
+pub struct EndpointDescriptor<'a>(&'a libusb_endpoint_descriptor);
+
+const ENDPOINT_DESCRIPTOR_DIRECTION_MASK: u8 = 1 << 7;
+const ENDPOINT_DESCRIPTOR_NUMBER_MASK: u8 = 0xf;
+const ENDPOINT_DESCRIPTOR_ATTRIBUTES_TYPE_MASK: u8 = 0x3;
+
+impl<'a> EndpointDescriptor<'a> {
+ // Create new endpoint descriptor.
+ pub fn new(descriptor: &libusb_endpoint_descriptor) -> EndpointDescriptor {
+ EndpointDescriptor(descriptor)
+ }
+
+ // Get direction of this endpoint.
+ pub fn get_direction(&self) -> EndpointDirection {
+ let direction = self.0.bEndpointAddress & ENDPOINT_DESCRIPTOR_DIRECTION_MASK;
+ if direction > 0 {
+ EndpointDirection::DeviceToHost
+ } else {
+ EndpointDirection::HostToDevice
+ }
+ }
+
+ // Get endpoint number.
+ pub fn get_endpoint_number(&self) -> u8 {
+ self.0.bEndpointAddress & ENDPOINT_DESCRIPTOR_NUMBER_MASK
+ }
+
+ // Get endpoint type.
+ pub fn get_endpoint_type(&self) -> Option<EndpointType> {
+ let ep_type = self.0.bmAttributes & ENDPOINT_DESCRIPTOR_ATTRIBUTES_TYPE_MASK;
+ match ep_type {
+ 0 => Some(EndpointType::Control),
+ 1 => Some(EndpointType::Isochronous),
+ 2 => Some(EndpointType::Bulk),
+ 3 => Some(EndpointType::Interrupt),
+ _ => None,
+ }
+ }
+}
+
+impl<'a> Deref for EndpointDescriptor<'a> {
+ type Target = libusb_endpoint_descriptor;
+
+ fn deref(&self) -> &libusb_endpoint_descriptor {
+ self.0
+ }
+}
diff --git a/usb_util/src/error.rs b/usb_util/src/error.rs
new file mode 100644
index 0000000..b6dd91e
--- /dev/null
+++ b/usb_util/src/error.rs
@@ -0,0 +1,85 @@
+// Copyright 2018 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;
+use std::fmt;
+
+use crate::bindings;
+
+/// Error type for libusb.
+pub enum Error {
+ Success(i32),
+ IO,
+ InvalidParam,
+ Access,
+ NoDevice,
+ NotFound,
+ Busy,
+ Timeout,
+ Overflow,
+ Pipe,
+ Interrupted,
+ NoMem,
+ NotSupported,
+ Other,
+}
+
+impl fmt::Debug for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Error::Success(_v) => write!(f, "Success (no error)"),
+ Error::IO => write!(f, "Input/output error"),
+ Error::InvalidParam => write!(f, "Invalid parameter"),
+ Error::Access => write!(f, "Access denied (insufficient permissions)"),
+ Error::NoDevice => write!(f, "No such device (it may have been disconnected)"),
+ Error::NotFound => write!(f, "Entity not found"),
+ Error::Busy => write!(f, "Resource busy"),
+ Error::Timeout => write!(f, "Operation timed out"),
+ Error::Overflow => write!(f, "Overflow"),
+ Error::Pipe => write!(f, "Pipe error"),
+ Error::Interrupted => write!(f, "System call interrupted (perhaps due to signal)"),
+ Error::NoMem => write!(f, "Insufficient memory"),
+ Error::NotSupported => write!(
+ f,
+ "Operation not supported or unimplemented on this platform"
+ ),
+ Error::Other => write!(f, "Other error"),
+ }
+ }
+}
+
+impl From<bindings::libusb_error> for Error {
+ fn from(e: bindings::libusb_error) -> Self {
+ match e {
+ bindings::LIBUSB_ERROR_IO => Error::IO,
+ bindings::LIBUSB_ERROR_INVALID_PARAM => Error::InvalidParam,
+ bindings::LIBUSB_ERROR_ACCESS => Error::Access,
+ bindings::LIBUSB_ERROR_NO_DEVICE => Error::NoDevice,
+ bindings::LIBUSB_ERROR_NOT_FOUND => Error::NotFound,
+ bindings::LIBUSB_ERROR_BUSY => Error::Busy,
+ bindings::LIBUSB_ERROR_TIMEOUT => Error::Timeout,
+ bindings::LIBUSB_ERROR_OVERFLOW => Error::Overflow,
+ bindings::LIBUSB_ERROR_PIPE => Error::Pipe,
+ bindings::LIBUSB_ERROR_INTERRUPTED => Error::Interrupted,
+ bindings::LIBUSB_ERROR_NO_MEM => Error::NoMem,
+ bindings::LIBUSB_ERROR_NOT_SUPPORTED => Error::NotSupported,
+ bindings::LIBUSB_ERROR_OTHER => Error::Other,
+ // All possible errors are defined above, other values mean success,
+ // see libusb_get_device_list for example.
+ _ => Error::Success(e),
+ }
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[macro_export]
+macro_rules! try_libusb {
+ ($x:expr) => {
+ match Error::from($x as i32) {
+ Error::Success(e) => e,
+ err => return Err(err),
+ }
+ };
+}
diff --git a/usb_util/src/hotplug.rs b/usb_util/src/hotplug.rs
new file mode 100644
index 0000000..e131ab9
--- /dev/null
+++ b/usb_util/src/hotplug.rs
@@ -0,0 +1,77 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::raw::{c_int, c_void};
+use std::sync::Arc;
+
+use crate::bindings;
+use crate::libusb_context::LibUsbContextInner;
+use crate::libusb_device::LibUsbDevice;
+
+#[derive(PartialEq)]
+pub enum HotplugEvent {
+ DeviceArrived,
+ DeviceLeft,
+}
+
+impl HotplugEvent {
+ /// Create a new HotplugEvent from raw libusb_hotplug_event.
+ pub fn new(event: bindings::libusb_hotplug_event) -> Self {
+ match event {
+ bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => HotplugEvent::DeviceArrived,
+ bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => HotplugEvent::DeviceLeft,
+ _ => {
+ // TODO(jkwang) handle this with option.
+ // libusb_hotplug_event is a C enum.
+ panic!("Invaild libusb_hotplug_event");
+ }
+ }
+ }
+}
+
+pub trait UsbHotplugHandler: Send + Sync + 'static {
+ fn hotplug_event(&self, device: LibUsbDevice, event: HotplugEvent);
+}
+
+/// UsbHotplugHandlerHolder owns UsbHotplugHandler and LibUsbContext. It will be passed as
+/// user_data to libusb_hotplug_register_callback.
+pub struct UsbHotplugHandlerHolder {
+ context: Arc<LibUsbContextInner>,
+ handler: Box<dyn UsbHotplugHandler>,
+}
+
+impl UsbHotplugHandlerHolder {
+ /// Create UsbHotplugHandlerHodler from context and handler.
+ pub fn new<H: UsbHotplugHandler>(
+ context: Arc<LibUsbContextInner>,
+ handler: H,
+ ) -> Box<UsbHotplugHandlerHolder> {
+ let holder = UsbHotplugHandlerHolder {
+ context,
+ handler: Box::new(handler),
+ };
+ Box::new(holder)
+ }
+}
+
+/// This function is safe when:
+/// libusb_device is allocated by libusb
+/// user_data points to valid UsbHotPlugHandlerHolder released from Box.
+///
+/// Do not invoke this function. It should only be used as a callback for
+/// libusb_hotplug_register_callback.
+pub unsafe extern "C" fn hotplug_cb(
+ _: *mut bindings::libusb_context,
+ device: *mut bindings::libusb_device,
+ event: bindings::libusb_hotplug_event,
+ user_data: *mut c_void,
+) -> c_int {
+ // Safe because user_data was casted from holder.
+ let holder = &*(user_data as *mut UsbHotplugHandlerHolder);
+ let device = LibUsbDevice::new(holder.context.clone(), device);
+ let event = HotplugEvent::new(event);
+ holder.handler.hotplug_event(device, event);
+ // The handler should always succeed.
+ bindings::LIBUSB_SUCCESS
+}
diff --git a/usb_util/src/interface_descriptor.rs b/usb_util/src/interface_descriptor.rs
new file mode 100644
index 0000000..a2bf1b2
--- /dev/null
+++ b/usb_util/src/interface_descriptor.rs
@@ -0,0 +1,40 @@
+// Copyright 2018 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::ops::Deref;
+
+use crate::bindings::libusb_interface_descriptor;
+use crate::endpoint_descriptor::EndpointDescriptor;
+
+/// InterfaceDescriptor wraps libusb_interface_descriptor.
+pub struct InterfaceDescriptor<'a>(&'a libusb_interface_descriptor);
+
+impl<'a> InterfaceDescriptor<'a> {
+ /// Create a new Interface descriptor.
+ pub fn new(descriptor: &'a libusb_interface_descriptor) -> InterfaceDescriptor<'a> {
+ InterfaceDescriptor(descriptor)
+ }
+
+ /// Get endpoint descriptor at index.
+ pub fn endpoint_descriptor(&self, ep_idx: u8) -> Option<EndpointDescriptor> {
+ if ep_idx >= self.0.bNumEndpoints {
+ return None;
+ }
+
+ // Safe because idx is checked.
+ unsafe {
+ Some(EndpointDescriptor::new(
+ &*(self.0.endpoint.offset(ep_idx as isize)),
+ ))
+ }
+ }
+}
+
+impl<'a> Deref for InterfaceDescriptor<'a> {
+ type Target = libusb_interface_descriptor;
+
+ fn deref(&self) -> &libusb_interface_descriptor {
+ self.0
+ }
+}
diff --git a/usb_util/src/lib.rs b/usb_util/src/lib.rs
new file mode 100644
index 0000000..07af5ad
--- /dev/null
+++ b/usb_util/src/lib.rs
@@ -0,0 +1,23 @@
+// Copyright 2018 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.
+
+// Generated with bindgen libusb.h -no-prepend-enum-name -o bindings.rs.
+#![allow(dead_code)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#[allow(clippy::all)]
+mod bindings;
+
+#[macro_use]
+pub mod error;
+pub mod config_descriptor;
+pub mod device_handle;
+pub mod endpoint_descriptor;
+pub mod hotplug;
+pub mod interface_descriptor;
+pub mod libusb_context;
+pub mod libusb_device;
+pub mod types;
+pub mod usb_transfer;
diff --git a/usb_util/src/libusb_context.rs b/usb_util/src/libusb_context.rs
new file mode 100644
index 0000000..a1bab32
--- /dev/null
+++ b/usb_util/src/libusb_context.rs
@@ -0,0 +1,280 @@
+// Copyright 2018 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;
+use std::os::raw::{c_short, c_void};
+use std::os::unix::io::RawFd;
+use std::sync::Arc;
+
+use crate::bindings;
+use crate::error::{Error, Result};
+use crate::hotplug::{hotplug_cb, UsbHotplugHandler, UsbHotplugHandlerHolder};
+use crate::libusb_device::LibUsbDevice;
+
+use sync::Mutex;
+
+pub struct LibUsbContextInner {
+ context: *mut bindings::libusb_context,
+ pollfd_change_handler: Mutex<Option<Box<PollfdChangeHandlerHolder>>>,
+}
+
+// Safe because libusb_context could be accessed from multiple threads safely.
+unsafe impl Send for LibUsbContextInner {}
+unsafe impl Sync for LibUsbContextInner {}
+
+impl LibUsbContextInner {
+ /// Remove the previous registered notifiers.
+ pub fn remove_pollfd_notifiers(&self) {
+ // Safe because 'self.context' is valid.
+ unsafe {
+ bindings::libusb_set_pollfd_notifiers(self.context, None, None, std::ptr::null_mut());
+ }
+ }
+}
+
+impl Drop for LibUsbContextInner {
+ fn drop(&mut self) {
+ // Avoid pollfd change handler call when libusb_exit is called.
+ self.remove_pollfd_notifiers();
+ // Safe beacuse 'self.context' points to a valid context allocated by libusb_init.
+ unsafe {
+ bindings::libusb_exit(self.context);
+ }
+ }
+}
+
+/// Wrapper for libusb_context. The libusb libary initialization/deinitialization
+/// is managed by this context.
+/// See: http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html
+#[derive(Clone)]
+pub struct LibUsbContext {
+ inner: Arc<LibUsbContextInner>,
+}
+
+impl LibUsbContext {
+ /// Create a new LibUsbContext.
+ pub fn new() -> Result<LibUsbContext> {
+ let mut ctx: *mut bindings::libusb_context = std::ptr::null_mut();
+ // Safe because '&mut ctx' points to a valid memory (on stack).
+ try_libusb!(unsafe { bindings::libusb_init(&mut ctx) });
+ Ok(LibUsbContext {
+ inner: Arc::new(LibUsbContextInner {
+ context: ctx,
+ pollfd_change_handler: Mutex::new(None),
+ }),
+ })
+ }
+
+ /// Create a new jailed LibUsbContext.
+ #[cfg(feature = "sandboxed-libusb")]
+ pub fn new_jailed() -> Result<LibUsbContext> {
+ let mut ctx: *mut bindings::libusb_context = std::ptr::null_mut();
+ // Safe because '&mut ctx' points to a valid memory (on stack).
+ try_libusb!(unsafe { bindings::libusb_init_jailed(&mut ctx) });
+ Ok(LibUsbContext {
+ inner: Arc::new(LibUsbContextInner {
+ context: ctx,
+ pollfd_change_handler: Mutex::new(None),
+ }),
+ })
+ }
+
+ /// Build device from File.
+ #[cfg(feature = "sandboxed-libusb")]
+ pub fn get_device_from_fd(&self, fd: std::fs::File) -> Result<LibUsbDevice> {
+ use std::os::unix::io::IntoRawFd;
+
+ let fd = fd.into_raw_fd();
+ let mut device: *mut bindings::libusb_device = std::ptr::null_mut();
+ // Safe because fd is valid and owned, and '&mut device' points to valid memory.
+ try_libusb!(unsafe {
+ bindings::libusb_get_device_from_fd(self.inner.context, fd, &mut device)
+ });
+ unsafe { Ok(LibUsbDevice::new(self.inner.clone(), device)) }
+ }
+
+ /// Returns a list of USB devices currently attached to the system.
+ pub fn get_device_iter(&self) -> Result<DeviceIter> {
+ let mut list: *mut *mut bindings::libusb_device = std::ptr::null_mut();
+ // Safe because 'inner.context' points to a valid context and '&mut list' points to a valid
+ // memory.
+ try_libusb!(unsafe { bindings::libusb_get_device_list(self.inner.context, &mut list) });
+
+ Ok(DeviceIter {
+ context: self.inner.clone(),
+ list,
+ index: 0,
+ })
+ }
+
+ /// Check at runtime if the loaded library has a given capability.
+ pub fn has_capability(&self, cap: u32) -> bool {
+ // Safe because libusb_init is called before this call happens.
+ unsafe { bindings::libusb_has_capability(cap) != 0 }
+ }
+
+ /// Return an iter of poll fds. Those fds that should be polled to handle libusb events.
+ pub fn get_pollfd_iter(&self) -> PollFdIter {
+ // Safe because 'inner.context' is inited.
+ let list: *mut *const bindings::libusb_pollfd =
+ unsafe { bindings::libusb_get_pollfds(self.inner.context) };
+ PollFdIter { list, index: 0 }
+ }
+
+ /// Handle libusb events in a non block way.
+ pub fn handle_events_nonblock(&self) {
+ static mut zero_time: bindings::timeval = bindings::timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ };
+ // Safe because 'inner.context' points to valid context.
+ unsafe {
+ bindings::libusb_handle_events_timeout_completed(
+ self.inner.context,
+ &mut zero_time as *mut bindings::timeval,
+ std::ptr::null_mut(),
+ );
+ }
+ }
+
+ /// Set a handler that could handle pollfd change events.
+ pub fn set_pollfd_notifiers(&self, handler: Box<dyn LibUsbPollfdChangeHandler>) {
+ // LibUsbContext is alive when any libusb related function is called. It owns the handler,
+ // thus the handler memory is always valid when callback is invoked.
+ let holder = Box::new(PollfdChangeHandlerHolder { handler });
+ let raw_holder = Box::into_raw(holder);
+ unsafe {
+ bindings::libusb_set_pollfd_notifiers(
+ self.inner.context,
+ Some(pollfd_added_cb),
+ Some(pollfd_removed_cb),
+ raw_holder as *mut c_void,
+ );
+ }
+ // Safe because raw_holder is from Boxed pointer.
+ let holder = unsafe { Box::from_raw(raw_holder) };
+ *self.inner.pollfd_change_handler.lock() = Some(holder);
+ }
+
+ /// Remove the previous registered notifiers.
+ pub fn remove_pollfd_notifiers(&self) {
+ self.inner.remove_pollfd_notifiers();
+ }
+
+ /// Set a callback that could handle hotplug events. Currently, this function listen to hotplug
+ /// event of all devices.
+ pub fn set_hotplug_cb<H: UsbHotplugHandler + Sized>(&self, handler: H) -> Result<()> {
+ let event = bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
+ | bindings::LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
+ let holder = UsbHotplugHandlerHolder::new(self.inner.clone(), handler);
+ let raw_holder = Box::into_raw(holder);
+ // Safe becuase hotpulg cb is a vaild c function and raw_holder points to memory for that
+ // function argument.
+ try_libusb!(unsafe {
+ bindings::libusb_hotplug_register_callback(
+ self.inner.context,
+ event,
+ bindings::LIBUSB_HOTPLUG_NO_FLAGS,
+ bindings::LIBUSB_HOTPLUG_MATCH_ANY,
+ bindings::LIBUSB_HOTPLUG_MATCH_ANY,
+ bindings::LIBUSB_HOTPLUG_MATCH_ANY,
+ Some(hotplug_cb),
+ raw_holder as *mut c_void,
+ std::ptr::null_mut(),
+ )
+ });
+ Ok(())
+ }
+}
+
+/// Iterator for device list.
+pub struct DeviceIter {
+ context: Arc<LibUsbContextInner>,
+ list: *mut *mut bindings::libusb_device,
+ index: isize,
+}
+
+impl Drop for DeviceIter {
+ fn drop(&mut self) {
+ // Safe because 'self.list' is inited by a valid pointer from libusb_get_device_list.
+ unsafe {
+ bindings::libusb_free_device_list(self.list, 1);
+ }
+ }
+}
+
+impl Iterator for DeviceIter {
+ type Item = LibUsbDevice;
+
+ fn next(&mut self) -> Option<LibUsbDevice> {
+ // Safe becuase 'self.list' is valid, the list is null terminated.
+ unsafe {
+ let current_ptr = self.list.offset(self.index);
+ if (*current_ptr).is_null() {
+ return None;
+ }
+ self.index += 1;
+ Some(LibUsbDevice::new(self.context.clone(), *current_ptr))
+ }
+ }
+}
+
+/// Iterator for pollfds.
+pub struct PollFdIter {
+ list: *mut *const bindings::libusb_pollfd,
+ index: isize,
+}
+
+impl Drop for PollFdIter {
+ fn drop(&mut self) {
+ // Safe because 'self.list' points to valid memory of pollfd list.
+ unsafe {
+ bindings::libusb_free_pollfds(self.list);
+ }
+ }
+}
+
+impl Iterator for PollFdIter {
+ type Item = bindings::libusb_pollfd;
+
+ fn next(&mut self) -> Option<bindings::libusb_pollfd> {
+ // Safe because 'self.index' never grow out of the null pointer index.
+ unsafe {
+ let current_ptr = self.list.offset(self.index);
+ if (*current_ptr).is_null() {
+ return None;
+ }
+
+ self.index += 1;
+ // Safe because '*current_ptr' is not null.
+ Some(**current_ptr)
+ }
+ }
+}
+
+/// Trait for handler that handles Pollfd Change events.
+pub trait LibUsbPollfdChangeHandler: Send + Sync + 'static {
+ fn add_poll_fd(&self, fd: RawFd, events: c_short);
+ fn remove_poll_fd(&self, fd: RawFd);
+}
+
+// This struct owns LibUsbPollfdChangeHandler. We need it because it's not possible to cast void
+// pointer to trait pointer.
+struct PollfdChangeHandlerHolder {
+ handler: Box<dyn LibUsbPollfdChangeHandler>,
+}
+
+// This function is safe when user_data points to valid PollfdChangeHandlerHolder.
+unsafe extern "C" fn pollfd_added_cb(fd: RawFd, events: c_short, user_data: *mut c_void) {
+ // Safe because user_data was casted from holder.
+ let keeper = &*(user_data as *mut PollfdChangeHandlerHolder);
+ keeper.handler.add_poll_fd(fd, events);
+}
+
+// This function is safe when user_data points to valid PollfdChangeHandlerHolder.
+unsafe extern "C" fn pollfd_removed_cb(fd: RawFd, user_data: *mut c_void) {
+ // Safe because user_data was casted from holder.
+ let keeper = &*(user_data as *mut PollfdChangeHandlerHolder);
+ keeper.handler.remove_poll_fd(fd);
+}
diff --git a/usb_util/src/libusb_device.rs b/usb_util/src/libusb_device.rs
new file mode 100644
index 0000000..e8f5120
--- /dev/null
+++ b/usb_util/src/libusb_device.rs
@@ -0,0 +1,120 @@
+// Copyright 2018 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;
+#[cfg(feature = "sandboxed-libusb")]
+use std::os::unix::io::RawFd;
+use std::sync::Arc;
+
+use crate::bindings;
+use crate::config_descriptor::ConfigDescriptor;
+use crate::device_handle::DeviceHandle;
+use crate::error::{Error, Result};
+use crate::libusb_context::LibUsbContextInner;
+use crate::types::Speed;
+
+pub type DeviceDescriptor = bindings::libusb_device_descriptor;
+
+/// LibUsbDevice wraps libusb_device.
+pub struct LibUsbDevice {
+ _context: Arc<LibUsbContextInner>,
+ device: *mut bindings::libusb_device,
+}
+
+unsafe impl Send for LibUsbDevice {}
+
+impl Drop for LibUsbDevice {
+ fn drop(&mut self) {
+ // Safe because 'self.device' is a valid pointer and libusb_ref_device is invoked when
+ // 'self' is built.
+ unsafe {
+ bindings::libusb_unref_device(self.device);
+ }
+ }
+}
+
+impl LibUsbDevice {
+ /// Create a new LibUsbDevice. 'device' should be a valid pointer to libusb_device.
+ pub unsafe fn new(
+ ctx: Arc<LibUsbContextInner>,
+ device: *mut bindings::libusb_device,
+ ) -> LibUsbDevice {
+ bindings::libusb_ref_device(device);
+ LibUsbDevice {
+ _context: ctx,
+ device,
+ }
+ }
+
+ /// Get device descriptor of this device.
+ pub fn get_device_descriptor(&self) -> Result<DeviceDescriptor> {
+ // Safe because memory is initialized later.
+ let mut descriptor: bindings::libusb_device_descriptor = Default::default();
+ // Safe because 'self.device' is valid and '&mut descriptor' is valid.
+ try_libusb!(unsafe {
+ bindings::libusb_get_device_descriptor(self.device, &mut descriptor)
+ });
+ Ok(descriptor)
+ }
+
+ /// Get config descriptor at index of idx.
+ pub fn get_config_descriptor(&self, idx: u8) -> Result<ConfigDescriptor> {
+ let mut descriptor: *mut bindings::libusb_config_descriptor = std::ptr::null_mut();
+ // Safe because 'self.device' is valid and '&mut descriptor' is valid.
+ try_libusb!(unsafe {
+ bindings::libusb_get_config_descriptor(self.device, idx, &mut descriptor)
+ });
+ // Safe because descriptor is inited with valid pointer.
+ Ok(unsafe { ConfigDescriptor::new(descriptor) })
+ }
+
+ /// Get active config descriptor of this device.
+ pub fn get_active_config_descriptor(&self) -> Result<ConfigDescriptor> {
+ let mut descriptor: *mut bindings::libusb_config_descriptor = std::ptr::null_mut();
+ // Safe because 'self.device' is valid and '&mut descriptor' is valid.
+ try_libusb!(unsafe {
+ bindings::libusb_get_active_config_descriptor(self.device, &mut descriptor)
+ });
+ // Safe becuase descriptor points to valid memory.
+ Ok(unsafe { ConfigDescriptor::new(descriptor) })
+ }
+
+ /// Get bus number of this device.
+ pub fn get_bus_number(&self) -> u8 {
+ // Safe because 'self.device' is valid.
+ unsafe { bindings::libusb_get_bus_number(self.device) }
+ }
+
+ /// Get address of this device.
+ pub fn get_address(&self) -> u8 {
+ // Safe because 'self.device' is valid.
+ unsafe { bindings::libusb_get_device_address(self.device) }
+ }
+
+ /// Get speed of this device.
+ pub fn get_speed(&self) -> Speed {
+ // Safe because 'self.device' is valid.
+ let speed = unsafe { bindings::libusb_get_device_speed(self.device) };
+ Speed::from(speed as u32)
+ }
+
+ /// Get device handle of this device.
+ pub fn open(&self) -> Result<DeviceHandle> {
+ let mut handle: *mut bindings::libusb_device_handle = std::ptr::null_mut();
+ // Safe because 'self.device' is constructed from libusb device list and handle is on stack.
+ try_libusb!(unsafe { bindings::libusb_open(self.device, &mut handle) });
+ // Safe because handle points to valid memory.
+ Ok(unsafe { DeviceHandle::new(self._context.clone(), handle) })
+ }
+
+ /// Get device handle of this device. Take an external fd. This function is only safe when fd
+ /// is an fd of this usb device.
+ #[cfg(feature = "sandboxed-libusb")]
+ pub unsafe fn open_fd(&self, fd: RawFd) -> Result<DeviceHandle> {
+ let mut handle: *mut bindings::libusb_device_handle = std::ptr::null_mut();
+ // Safe when 'self.device' is constructed from libusb device list and handle is on stack.
+ try_libusb!(bindings::libusb_open_fd(self.device, fd, &mut handle));
+ Ok(DeviceHandle::new(self._context.clone(), handle))
+ }
+}
diff --git a/usb_util/src/types.rs b/usb_util/src/types.rs
new file mode 100644
index 0000000..f40dd6c
--- /dev/null
+++ b/usb_util/src/types.rs
@@ -0,0 +1,196 @@
+// Copyright 2018 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 assertions::const_assert;
+use data_model::DataInit;
+
+use std::mem::size_of;
+
+use crate::bindings;
+
+/// Speed of usb device. See usb spec for more details.
+#[derive(Debug)]
+pub enum Speed {
+ /// The OS doesn't report or know the device speed.
+ Unknown,
+ /// The device is operating at low speed (1.5MBit/s).
+ Low,
+ /// The device is operating at full speed (12MBit/s).
+ Full,
+ /// The device is operating at high speed (480MBit/s).
+ High,
+ /// The device is operating at super speed (5000MBit/s).
+ Super,
+}
+
+impl From<bindings::libusb_speed> for Speed {
+ fn from(speed: bindings::libusb_speed) -> Speed {
+ match speed {
+ bindings::LIBUSB_SPEED_LOW => Speed::Low,
+ bindings::LIBUSB_SPEED_FULL => Speed::Full,
+ bindings::LIBUSB_SPEED_HIGH => Speed::High,
+ bindings::LIBUSB_SPEED_SUPER => Speed::Super,
+ _ => Speed::Unknown,
+ }
+ }
+}
+
+/// Endpoint types.
+#[derive(PartialEq)]
+pub enum EndpointType {
+ Control,
+ Isochronous,
+ Bulk,
+ Interrupt,
+}
+
+/// Endpoint Directions.
+#[derive(PartialEq, Clone, Copy)]
+pub enum EndpointDirection {
+ HostToDevice = 0,
+ DeviceToHost = 1,
+}
+/// Endpoint direction offset.
+pub const ENDPOINT_DIRECTION_OFFSET: u8 = 7;
+
+/// Offset of data phase transfer direction.
+pub const DATA_PHASE_DIRECTION_OFFSET: u8 = 7;
+/// Bit mask of data phase transfer direction.
+pub const DATA_PHASE_DIRECTION: u8 = 1u8 << DATA_PHASE_DIRECTION_OFFSET;
+// Types of data phase transfer directions.
+#[derive(Copy, Clone, PartialEq)]
+pub enum ControlRequestDataPhaseTransferDirection {
+ HostToDevice = 0,
+ DeviceToHost = 1,
+}
+
+/// Offset of control request type.
+pub const CONTROL_REQUEST_TYPE_OFFSET: u8 = 5;
+/// Bit mask of control request type.
+pub const CONTROL_REQUEST_TYPE: u8 = 0b11 << CONTROL_REQUEST_TYPE_OFFSET;
+/// Request types.
+#[derive(PartialEq)]
+pub enum ControlRequestType {
+ Standard = 0,
+ Class = 1,
+ Vendor = 2,
+ Reserved = 3,
+}
+
+/// Recipient type bits.
+pub const REQUEST_RECIPIENT_TYPE: u8 = 0b1111;
+/// Recipient type of control request.
+#[derive(PartialEq)]
+pub enum ControlRequestRecipient {
+ Device = 0,
+ Interface = 1,
+ Endpoint = 2,
+ Other = 3,
+ Reserved,
+}
+
+/// Standard request defined in usb spec.
+#[derive(PartialEq)]
+pub enum StandardControlRequest {
+ GetStatus = 0x00,
+ ClearFeature = 0x01,
+ SetFeature = 0x03,
+ SetAddress = 0x05,
+ GetDescriptor = 0x06,
+ SetDescriptor = 0x07,
+ GetConfiguration = 0x08,
+ SetConfiguration = 0x09,
+ GetInterface = 0x0a,
+ SetInterface = 0x11,
+ SynchFrame = 0x12,
+}
+
+/// RequestSetup is first part of control transfer buffer.
+#[repr(C, packed)]
+#[derive(Copy, Clone, Debug)]
+pub struct UsbRequestSetup {
+ // USB Device Request. USB spec. rev. 2.0 9.3
+ pub request_type: u8, // bmRequestType
+ pub request: u8, // bRequest
+ pub value: u16, // wValue
+ pub index: u16, // wIndex
+ pub length: u16, // wLength
+}
+
+fn _assert() {
+ const_assert!(size_of::<UsbRequestSetup>() == 8);
+}
+
+unsafe impl DataInit for UsbRequestSetup {}
+
+impl UsbRequestSetup {
+ pub fn new(
+ request_type: u8,
+ request: u8,
+ value: u16,
+ index: u16,
+ length: u16,
+ ) -> UsbRequestSetup {
+ UsbRequestSetup {
+ request_type,
+ request,
+ value,
+ index,
+ length,
+ }
+ }
+
+ /// Get type of request.
+ pub fn get_type(&self) -> ControlRequestType {
+ let ty = (self.request_type & CONTROL_REQUEST_TYPE) >> CONTROL_REQUEST_TYPE_OFFSET;
+ match ty {
+ 0 => ControlRequestType::Standard,
+ 1 => ControlRequestType::Class,
+ 2 => ControlRequestType::Vendor,
+ _ => ControlRequestType::Reserved,
+ }
+ }
+
+ /// Get request direction.
+ pub fn get_direction(&self) -> ControlRequestDataPhaseTransferDirection {
+ let dir = (self.request_type & DATA_PHASE_DIRECTION) >> DATA_PHASE_DIRECTION_OFFSET;
+ match dir {
+ 0 => ControlRequestDataPhaseTransferDirection::HostToDevice,
+ _ => ControlRequestDataPhaseTransferDirection::DeviceToHost,
+ }
+ }
+
+ /// Get recipient of this control transfer.
+ pub fn get_recipient(&self) -> ControlRequestRecipient {
+ let recipient = self.request_type & REQUEST_RECIPIENT_TYPE;
+ match recipient {
+ 0 => ControlRequestRecipient::Device,
+ 1 => ControlRequestRecipient::Interface,
+ 2 => ControlRequestRecipient::Endpoint,
+ 3 => ControlRequestRecipient::Other,
+ _ => ControlRequestRecipient::Reserved,
+ }
+ }
+
+ /// Return the type of standard control request.
+ pub fn get_standard_request(&self) -> Option<StandardControlRequest> {
+ if self.get_type() != ControlRequestType::Standard {
+ return None;
+ }
+ match self.request {
+ 0x00 => Some(StandardControlRequest::GetStatus),
+ 0x01 => Some(StandardControlRequest::ClearFeature),
+ 0x03 => Some(StandardControlRequest::SetFeature),
+ 0x05 => Some(StandardControlRequest::SetAddress),
+ 0x06 => Some(StandardControlRequest::GetDescriptor),
+ 0x07 => Some(StandardControlRequest::SetDescriptor),
+ 0x08 => Some(StandardControlRequest::GetConfiguration),
+ 0x09 => Some(StandardControlRequest::SetConfiguration),
+ 0x0a => Some(StandardControlRequest::GetInterface),
+ 0x11 => Some(StandardControlRequest::SetInterface),
+ 0x12 => Some(StandardControlRequest::SynchFrame),
+ _ => None,
+ }
+ }
+}
diff --git a/usb_util/src/usb_transfer.rs b/usb_util/src/usb_transfer.rs
new file mode 100644
index 0000000..e08bc92
--- /dev/null
+++ b/usb_util/src/usb_transfer.rs
@@ -0,0 +1,387 @@
+// Copyright 2018 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::mem::size_of;
+use std::os::raw::c_void;
+use std::sync::{Arc, Weak};
+
+use crate::bindings::{
+ libusb_alloc_transfer, libusb_cancel_transfer, libusb_device_handle, libusb_free_transfer,
+ libusb_submit_transfer, libusb_transfer, libusb_transfer_status, LIBUSB_TRANSFER_CANCELLED,
+ LIBUSB_TRANSFER_COMPLETED, LIBUSB_TRANSFER_ERROR, LIBUSB_TRANSFER_NO_DEVICE,
+ LIBUSB_TRANSFER_OVERFLOW, LIBUSB_TRANSFER_STALL, LIBUSB_TRANSFER_TIMED_OUT,
+ LIBUSB_TRANSFER_TYPE_BULK, LIBUSB_TRANSFER_TYPE_CONTROL, LIBUSB_TRANSFER_TYPE_INTERRUPT,
+};
+use crate::error::{Error, Result};
+use crate::types::UsbRequestSetup;
+
+/// Status of transfer.
+#[derive(PartialEq)]
+pub enum TransferStatus {
+ Completed,
+ Error,
+ TimedOut,
+ Cancelled,
+ Stall,
+ NoDevice,
+ Overflow,
+}
+
+impl From<libusb_transfer_status> for TransferStatus {
+ fn from(s: libusb_transfer_status) -> Self {
+ match s {
+ LIBUSB_TRANSFER_COMPLETED => TransferStatus::Completed,
+ LIBUSB_TRANSFER_ERROR => TransferStatus::Error,
+ LIBUSB_TRANSFER_TIMED_OUT => TransferStatus::TimedOut,
+ LIBUSB_TRANSFER_CANCELLED => TransferStatus::Cancelled,
+ LIBUSB_TRANSFER_STALL => TransferStatus::Stall,
+ LIBUSB_TRANSFER_NO_DEVICE => TransferStatus::NoDevice,
+ LIBUSB_TRANSFER_OVERFLOW => TransferStatus::Overflow,
+ _ => TransferStatus::Error,
+ }
+ }
+}
+
+/// Trait for usb transfer buffer.
+pub trait UsbTransferBuffer: Send {
+ fn as_ptr(&mut self) -> *mut u8;
+ fn len(&self) -> i32;
+}
+
+/// Default buffer size for control data transfer.
+const CONTROL_DATA_BUFFER_SIZE: usize = 1024;
+
+/// Buffer type for control transfer. The first 8-bytes is a UsbRequestSetup struct.
+#[repr(C, packed)]
+pub struct ControlTransferBuffer {
+ pub setup_buffer: UsbRequestSetup,
+ pub data_buffer: [u8; CONTROL_DATA_BUFFER_SIZE],
+}
+
+impl ControlTransferBuffer {
+ fn new() -> ControlTransferBuffer {
+ ControlTransferBuffer {
+ setup_buffer: UsbRequestSetup {
+ request_type: 0,
+ request: 0,
+ value: 0,
+ index: 0,
+ length: 0,
+ },
+ data_buffer: [0; CONTROL_DATA_BUFFER_SIZE],
+ }
+ }
+
+ /// Set request setup for this control buffer.
+ pub fn set_request_setup(&mut self, request_setup: &UsbRequestSetup) {
+ self.setup_buffer = *request_setup;
+ }
+}
+
+impl UsbTransferBuffer for ControlTransferBuffer {
+ fn as_ptr(&mut self) -> *mut u8 {
+ self as *mut ControlTransferBuffer as *mut u8
+ }
+
+ fn len(&self) -> i32 {
+ if self.setup_buffer.length as usize > CONTROL_DATA_BUFFER_SIZE {
+ panic!("Setup packet has an oversize length");
+ }
+ self.setup_buffer.length as i32 + size_of::<UsbRequestSetup>() as i32
+ }
+}
+
+/// Buffer type for Bulk transfer.
+pub struct BulkTransferBuffer {
+ buffer: Vec<u8>,
+}
+
+impl BulkTransferBuffer {
+ fn with_size(buffer_size: usize) -> Self {
+ BulkTransferBuffer {
+ buffer: vec![0; buffer_size],
+ }
+ }
+
+ /// Get mutable interal slice of this buffer.
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ &mut self.buffer
+ }
+
+ /// Get interal slice of this buffer.
+ pub fn as_slice(&self) -> &[u8] {
+ &self.buffer
+ }
+}
+
+impl UsbTransferBuffer for BulkTransferBuffer {
+ fn as_ptr(&mut self) -> *mut u8 {
+ if self.buffer.len() == 0 {
+ // Vec::as_mut_ptr() won't give 0x0 even if len() is 0.
+ std::ptr::null_mut()
+ } else {
+ self.buffer.as_mut_ptr()
+ }
+ }
+
+ fn len(&self) -> i32 {
+ self.buffer.len() as i32
+ }
+}
+
+type UsbTransferCompletionCallback<T> = dyn Fn(UsbTransfer<T>) + Send + 'static;
+
+// This wraps libusb_transfer pointer.
+struct LibUsbTransfer {
+ ptr: *mut libusb_transfer,
+}
+
+impl Drop for LibUsbTransfer {
+ fn drop(&mut self) {
+ // Safe because 'self.ptr' is allocated by libusb_alloc_transfer.
+ unsafe {
+ libusb_free_transfer(self.ptr);
+ }
+ }
+}
+
+// It is safe to invoke libusb functions from multiple threads.
+// We cannot modify libusb_transfer safely from multiple threads. All the modifications happens
+// in construct (UsbTransfer::new) or consume (UsbTransfer::into_raw), we can consider this thread
+// safe.
+unsafe impl Send for LibUsbTransfer {}
+unsafe impl Sync for LibUsbTransfer {}
+
+/// TransferCanceller can cancel the transfer.
+pub struct TransferCanceller {
+ transfer: Weak<LibUsbTransfer>,
+}
+
+impl TransferCanceller {
+ /// Return false if fail to cancel.
+ pub fn try_cancel(&self) -> bool {
+ match self.transfer.upgrade() {
+ Some(t) => {
+ // Safe because self.transfer has ownership of the raw pointer.
+ let r = unsafe { libusb_cancel_transfer(t.ptr) };
+ if r == 0 {
+ true
+ } else {
+ false
+ }
+ }
+ None => false,
+ }
+ }
+}
+
+struct UsbTransferInner<T: UsbTransferBuffer> {
+ transfer: Arc<LibUsbTransfer>,
+ callback: Option<Box<UsbTransferCompletionCallback<T>>>,
+ buffer: T,
+}
+
+/// UsbTransfer owns a LibUsbTransfer, it's buffer and callback.
+pub struct UsbTransfer<T: UsbTransferBuffer> {
+ inner: Box<UsbTransferInner<T>>,
+}
+
+/// Build a control transfer.
+pub fn control_transfer(timeout: u32) -> UsbTransfer<ControlTransferBuffer> {
+ UsbTransfer::<ControlTransferBuffer>::new(
+ 0,
+ LIBUSB_TRANSFER_TYPE_CONTROL as u8,
+ timeout,
+ ControlTransferBuffer::new(),
+ )
+}
+
+/// Build a data transfer.
+pub fn bulk_transfer(endpoint: u8, timeout: u32, size: usize) -> UsbTransfer<BulkTransferBuffer> {
+ UsbTransfer::<BulkTransferBuffer>::new(
+ endpoint,
+ LIBUSB_TRANSFER_TYPE_BULK as u8,
+ timeout,
+ BulkTransferBuffer::with_size(size),
+ )
+}
+
+/// Build a data transfer.
+pub fn interrupt_transfer(
+ endpoint: u8,
+ timeout: u32,
+ size: usize,
+) -> UsbTransfer<BulkTransferBuffer> {
+ UsbTransfer::<BulkTransferBuffer>::new(
+ endpoint,
+ LIBUSB_TRANSFER_TYPE_INTERRUPT as u8,
+ timeout,
+ BulkTransferBuffer::with_size(size),
+ )
+}
+
+impl<T: UsbTransferBuffer> UsbTransfer<T> {
+ fn new(endpoint: u8, type_: u8, timeout: u32, buffer: T) -> Self {
+ // Safe because alloc is safe.
+ let transfer: *mut libusb_transfer = unsafe { libusb_alloc_transfer(0) };
+ // Just panic on OOM.
+ assert!(!transfer.is_null());
+ let inner = Box::new(UsbTransferInner {
+ transfer: Arc::new(LibUsbTransfer { ptr: transfer }),
+ callback: None,
+ buffer,
+ });
+ // Safe because we inited transfer.
+ let raw_transfer: &mut libusb_transfer = unsafe { &mut *(inner.transfer.ptr) };
+ raw_transfer.endpoint = endpoint;
+ raw_transfer.type_ = type_;
+ raw_transfer.timeout = timeout;
+ raw_transfer.callback = Some(UsbTransfer::<T>::on_transfer_completed);
+ UsbTransfer { inner }
+ }
+
+ /// Get canceller of this transfer.
+ pub fn get_canceller(&self) -> TransferCanceller {
+ let weak_transfer = Arc::downgrade(&self.inner.transfer);
+ TransferCanceller {
+ transfer: weak_transfer,
+ }
+ }
+
+ /// Set callback function for transfer completion.
+ pub fn set_callback<C: 'static + Fn(UsbTransfer<T>) + Send>(&mut self, cb: C) {
+ self.inner.callback = Some(Box::new(cb));
+ }
+
+ /// Get a reference to the buffer.
+ pub fn buffer(&self) -> &T {
+ &self.inner.buffer
+ }
+
+ /// Get a mutable reference to the buffer.
+ pub fn buffer_mut(&mut self) -> &mut T {
+ &mut self.inner.buffer
+ }
+
+ /// Get actual length of data that was transferred.
+ pub fn actual_length(&self) -> i32 {
+ let transfer = self.inner.transfer.ptr;
+ // Safe because inner.ptr is always allocated by libusb_alloc_transfer.
+ unsafe { (*transfer).actual_length }
+ }
+
+ /// Get the transfer status of this transfer.
+ pub fn status(&self) -> TransferStatus {
+ let transfer = self.inner.transfer.ptr;
+ // Safe because inner.ptr is always allocated by libusb_alloc_transfer.
+ unsafe { TransferStatus::from((*transfer).status) }
+ }
+
+ /// Submit this transfer to device handle. 'self' is consumed. On success, the memory will be
+ /// 'leaked' (and stored in user_data) and sent to libusb, when the async operation is done,
+ /// on_transfer_completed will recreate 'self' and deliver it to callback/free 'self'. On
+ /// faliure, 'self' is returned with an error.
+ ///
+ /// # Safety
+ ///
+ /// Assumes libusb_device_handle is an handled opened by libusb, self.inner.transfer.ptr is
+ /// initialized with correct buffer and length.
+ pub unsafe fn submit(self, handle: *mut libusb_device_handle) -> Result<()> {
+ let transfer = self.into_raw();
+ (*transfer).dev_handle = handle;
+ match Error::from(libusb_submit_transfer(transfer)) {
+ Error::Success(_e) => Ok(()),
+ err => {
+ UsbTransfer::<T>::from_raw(transfer);
+ Err(err)
+ }
+ }
+ }
+
+ /// Invoke callback when transfer is completed.
+ ///
+ /// # Safety
+ ///
+ /// Assumes libusb_tranfser is finished. This function is called by libusb, don't call it
+ /// manually.
+ unsafe extern "C" fn on_transfer_completed(transfer: *mut libusb_transfer) {
+ let mut transfer = UsbTransfer::<T>::from_raw(transfer);
+ // Callback is reset to None.
+ if let Some(cb) = transfer.inner.callback.take() {
+ cb(transfer);
+ }
+ }
+
+ fn into_raw(mut self) -> *mut libusb_transfer {
+ let transfer: *mut libusb_transfer = self.inner.transfer.ptr;
+ // Safe because transfer is allocated by libusb_alloc_transfer.
+ unsafe {
+ (*transfer).buffer = self.buffer_mut().as_ptr();
+ (*transfer).length = self.buffer_mut().len();
+ (*transfer).user_data = Box::into_raw(self.inner) as *mut c_void;
+ }
+ transfer
+ }
+
+ unsafe fn from_raw(transfer: *mut libusb_transfer) -> Self {
+ UsbTransfer {
+ inner: Box::<UsbTransferInner<T>>::from_raw(
+ (*transfer).user_data as *mut UsbTransferInner<T>,
+ ),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::sync::Mutex;
+
+ pub fn fake_submit_transfer<T: UsbTransferBuffer>(transfer: UsbTransfer<T>) {
+ let transfer = transfer.into_raw();
+ unsafe {
+ match (*transfer).callback {
+ Some(cb) => cb(transfer),
+ // Although no callback is invoked, we still need on_transfer_completed to
+ // free memory.
+ None => panic!("Memory leak!"),
+ };
+ }
+ }
+
+ #[test]
+ fn check_control_buffer_size() {
+ assert_eq!(
+ size_of::<ControlTransferBuffer>(),
+ size_of::<UsbRequestSetup>() + CONTROL_DATA_BUFFER_SIZE
+ );
+ }
+
+ #[test]
+ fn submit_transfer_no_callback_test() {
+ let t = control_transfer(0);
+ fake_submit_transfer(t);
+ let t = bulk_transfer(0, 0, 1);
+ fake_submit_transfer(t);
+ }
+
+ struct FakeTransferController {
+ data: Mutex<u8>,
+ }
+
+ #[test]
+ fn submit_transfer_with_callback() {
+ let c = Arc::new(FakeTransferController {
+ data: Mutex::new(0),
+ });
+ let c1 = Arc::downgrade(&c);
+ let mut t = control_transfer(0);
+ t.set_callback(move |_t| {
+ let c = c1.upgrade().unwrap();
+ *c.data.lock().unwrap() = 3;
+ });
+ fake_submit_transfer(t);
+ assert_eq!(*c.data.lock().unwrap(), 3);
+ }
+}
diff --git a/vhost/Cargo.toml b/vhost/Cargo.toml
new file mode 100644
index 0000000..567e880
--- /dev/null
+++ b/vhost/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "vhost"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+assertions = { path = "../assertions" }
+libc = "*"
+net_util = { path = "../net_util" }
+sys_util = { path = "../sys_util" }
+virtio_sys = { path = "../virtio_sys" }
diff --git a/vhost/src/lib.rs b/vhost/src/lib.rs
new file mode 100644
index 0000000..04ba655
--- /dev/null
+++ b/vhost/src/lib.rs
@@ -0,0 +1,443 @@
+// Copyright 2017 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.
+
+pub mod net;
+mod vsock;
+
+pub use crate::net::Net;
+pub use crate::net::NetT;
+pub use crate::vsock::Vsock;
+
+use std::alloc::Layout;
+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 sys_util::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};
+use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError, LayoutAllocation};
+
+#[derive(Debug)]
+pub enum Error {
+ /// Error opening vhost device.
+ VhostOpen(IoError),
+ /// Error while running ioctl.
+ IoctlError(IoError),
+ /// Invalid queue.
+ InvalidQueue,
+ /// Invalid descriptor table address.
+ DescriptorTableAddress(GuestMemoryError),
+ /// Invalid used address.
+ UsedAddress(GuestMemoryError),
+ /// Invalid available address.
+ AvailAddress(GuestMemoryError),
+ /// Invalid log address.
+ LogAddress(GuestMemoryError),
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ VhostOpen(e) => write!(f, "failed to open vhost device: {}", e),
+ IoctlError(e) => write!(f, "failed to run ioctl: {}", e),
+ InvalidQueue => write!(f, "invalid queue"),
+ DescriptorTableAddress(e) => write!(f, "invalid descriptor table address: {}", e),
+ UsedAddress(e) => write!(f, "invalid used address: {}", e),
+ AvailAddress(e) => write!(f, "invalid available address: {}", e),
+ LogAddress(e) => write!(f, "invalid log address: {}", e),
+ }
+ }
+}
+
+fn ioctl_result<T>() -> Result<T> {
+ Err(Error::IoctlError(IoError::last_os_error()))
+}
+
+/// An interface for setting up vhost-based virtio devices. Vhost-based devices are different
+/// 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 {
+ /// Get the guest memory mapping.
+ fn mem(&self) -> &GuestMemory;
+
+ /// Set the current process as the owner of this file descriptor.
+ /// This must be run before any other vhost ioctls.
+ fn set_owner(&self) -> Result<()> {
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret = unsafe { ioctl(self, virtio_sys::VHOST_SET_OWNER()) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+
+ /// Get a bitmask of supported virtio/vhost features.
+ fn get_features(&self) -> Result<u64> {
+ let mut avail_features: u64 = 0;
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret = unsafe {
+ ioctl_with_mut_ref(self, virtio_sys::VHOST_GET_FEATURES(), &mut avail_features)
+ };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(avail_features)
+ }
+
+ /// Inform the vhost subsystem which features to enable. This should be a subset of
+ /// supported features from VHOST_GET_FEATURES.
+ ///
+ /// # Arguments
+ /// * `features` - Bitmask of features to set.
+ fn set_features(&self, features: u64) -> Result<()> {
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_FEATURES(), &features) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+
+ /// Set the guest memory mappings for vhost to use.
+ fn set_mem_table(&self) -> Result<()> {
+ const SIZE_OF_MEMORY: usize = mem::size_of::<virtio_sys::vhost_memory>();
+ const SIZE_OF_REGION: usize = mem::size_of::<virtio_sys::vhost_memory_region>();
+ const ALIGN_OF_MEMORY: usize = mem::align_of::<virtio_sys::vhost_memory>();
+ const ALIGN_OF_REGION: usize = mem::align_of::<virtio_sys::vhost_memory_region>();
+ const_assert!(ALIGN_OF_MEMORY >= ALIGN_OF_REGION);
+
+ let num_regions = self.mem().num_regions() as usize;
+ let size = SIZE_OF_MEMORY + num_regions * SIZE_OF_REGION;
+ let layout = Layout::from_size_align(size, ALIGN_OF_MEMORY).expect("impossible layout");
+ let mut allocation = LayoutAllocation::zeroed(layout);
+
+ // Safe to obtain an exclusive reference because there are no other
+ // references to the allocation yet and all-zero is a valid bit pattern.
+ let vhost_memory = unsafe { allocation.as_mut::<virtio_sys::vhost_memory>() };
+
+ vhost_memory.nregions = num_regions as u32;
+ // regions is a zero-length array, so taking a mut slice requires that
+ // we correctly specify the size to match the amount of backing memory.
+ let vhost_regions = unsafe { vhost_memory.regions.as_mut_slice(num_regions as usize) };
+
+ let _ = self
+ .mem()
+ .with_regions::<_, ()>(|index, guest_addr, size, host_addr, _| {
+ vhost_regions[index] = virtio_sys::vhost_memory_region {
+ guest_phys_addr: guest_addr.offset() as u64,
+ memory_size: size as u64,
+ userspace_addr: host_addr as u64,
+ flags_padding: 0u64,
+ };
+ Ok(())
+ });
+
+ // This ioctl is called with a pointer that is valid for the lifetime
+ // of this function. The kernel will make its own copy of the memory
+ // tables. As always, check the return value.
+ let ret = unsafe { ioctl_with_ptr(self, virtio_sys::VHOST_SET_MEM_TABLE(), vhost_memory) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+
+ Ok(())
+
+ // vhost_memory allocation is deallocated.
+ }
+
+ /// Set the number of descriptors in the vring.
+ ///
+ /// # Arguments
+ /// * `queue_index` - Index of the queue to set descriptor count for.
+ /// * `num` - Number of descriptors in the queue.
+ fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
+ let vring_state = virtio_sys::vhost_vring_state {
+ index: queue_index as u32,
+ num: num as u32,
+ };
+
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_NUM(), &vring_state) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+
+ // TODO(smbarber): This is copypasta. Eliminate the copypasta.
+ #[allow(clippy::if_same_then_else)]
+ fn is_valid(
+ &self,
+ queue_max_size: u16,
+ queue_size: u16,
+ desc_addr: GuestAddress,
+ avail_addr: GuestAddress,
+ used_addr: GuestAddress,
+ ) -> bool {
+ let desc_table_size = 16 * queue_size as usize;
+ let avail_ring_size = 6 + 2 * queue_size as usize;
+ let used_ring_size = 6 + 8 * queue_size as usize;
+ if queue_size > queue_max_size || queue_size == 0 || (queue_size & (queue_size - 1)) != 0 {
+ false
+ } else if desc_addr
+ .checked_add(desc_table_size as u64)
+ .map_or(true, |v| !self.mem().address_in_range(v))
+ {
+ false
+ } else if avail_addr
+ .checked_add(avail_ring_size as u64)
+ .map_or(true, |v| !self.mem().address_in_range(v))
+ {
+ false
+ } else if used_addr
+ .checked_add(used_ring_size as u64)
+ .map_or(true, |v| !self.mem().address_in_range(v))
+ {
+ false
+ } else {
+ true
+ }
+ }
+
+ /// Set the addresses for a given vring.
+ ///
+ /// # Arguments
+ /// * `queue_max_size` - Maximum queue size supported by the device.
+ /// * `queue_size` - Actual queue size negotiated by the driver.
+ /// * `queue_index` - Index of the queue to set addresses for.
+ /// * `flags` - Bitmask of vring flags.
+ /// * `desc_addr` - Descriptor table address.
+ /// * `used_addr` - Used ring buffer address.
+ /// * `avail_addr` - Available ring buffer address.
+ /// * `log_addr` - Optional address for logging.
+ fn set_vring_addr(
+ &self,
+ queue_max_size: u16,
+ queue_size: u16,
+ queue_index: usize,
+ flags: u32,
+ desc_addr: GuestAddress,
+ used_addr: GuestAddress,
+ avail_addr: GuestAddress,
+ log_addr: Option<GuestAddress>,
+ ) -> Result<()> {
+ // TODO(smbarber): Refactor out virtio from crosvm so we can
+ // validate a Queue struct directly.
+ if !self.is_valid(queue_max_size, queue_size, desc_addr, used_addr, avail_addr) {
+ return Err(Error::InvalidQueue);
+ }
+
+ let desc_addr = self
+ .mem()
+ .get_host_address(desc_addr)
+ .map_err(Error::DescriptorTableAddress)?;
+ let used_addr = self
+ .mem()
+ .get_host_address(used_addr)
+ .map_err(Error::UsedAddress)?;
+ let avail_addr = self
+ .mem()
+ .get_host_address(avail_addr)
+ .map_err(Error::AvailAddress)?;
+ let log_addr = match log_addr {
+ None => null(),
+ Some(a) => self.mem().get_host_address(a).map_err(Error::LogAddress)?,
+ };
+
+ let vring_addr = virtio_sys::vhost_vring_addr {
+ index: queue_index as u32,
+ flags,
+ desc_user_addr: desc_addr as u64,
+ used_user_addr: used_addr as u64,
+ avail_user_addr: avail_addr as u64,
+ log_guest_addr: log_addr as u64,
+ };
+
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_ADDR(), &vring_addr) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+
+ /// Set the first index to look for available descriptors.
+ ///
+ /// # Arguments
+ /// * `queue_index` - Index of the queue to modify.
+ /// * `num` - Index where available descriptors start.
+ fn set_vring_base(&self, queue_index: usize, num: u16) -> Result<()> {
+ let vring_state = virtio_sys::vhost_vring_state {
+ index: queue_index as u32,
+ num: num as u32,
+ };
+
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_BASE(), &vring_state) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+
+ /// Set the eventfd to trigger when buffers have been used by the host.
+ ///
+ /// # Arguments
+ /// * `queue_index` - Index of the queue to modify.
+ /// * `fd` - EventFd to trigger.
+ fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
+ let vring_file = virtio_sys::vhost_vring_file {
+ index: queue_index as u32,
+ fd: fd.as_raw_fd(),
+ };
+
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_CALL(), &vring_file) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+
+ /// Set the eventfd that will be signaled by the guest when buffers are
+ /// available for the host to process.
+ ///
+ /// # Arguments
+ /// * `queue_index` - Index of the queue to modify.
+ /// * `fd` - EventFd that will be signaled from guest.
+ fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
+ let vring_file = virtio_sys::vhost_vring_file {
+ index: queue_index as u32,
+ fd: fd.as_raw_fd(),
+ };
+
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_KICK(), &vring_file) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::net::fakes::FakeNet;
+ use net_util::fakes::FakeTap;
+ use std::result;
+ use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
+
+ fn create_guest_memory() -> result::Result<GuestMemory, GuestMemoryError> {
+ let start_addr1 = GuestAddress(0x0);
+ let start_addr2 = GuestAddress(0x1000);
+ GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x4000)])
+ }
+
+ fn assert_ok_or_known_failure<T>(res: Result<T>) {
+ match &res {
+ // FakeNet won't respond to ioctl's
+ Ok(_t) => {}
+ Err(Error::IoctlError(ioe)) if ioe.raw_os_error().unwrap() == 25 => {}
+ Err(e) => panic!("Unexpected Error:\n{}", e),
+ }
+ }
+
+ fn create_fake_vhost_net() -> FakeNet<FakeTap> {
+ let gm = create_guest_memory().unwrap();
+ FakeNet::<FakeTap>::new(&gm).unwrap()
+ }
+
+ #[test]
+ fn test_create_fake_vhost_net() {
+ create_fake_vhost_net();
+ }
+
+ #[test]
+ fn set_owner() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.set_owner();
+ assert_ok_or_known_failure(res);
+ }
+
+ #[test]
+ fn get_features() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.get_features();
+ assert_ok_or_known_failure(res);
+ }
+
+ #[test]
+ fn set_features() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.set_features(0);
+ assert_ok_or_known_failure(res);
+ }
+
+ #[test]
+ fn set_mem_table() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.set_mem_table();
+ assert_ok_or_known_failure(res);
+ }
+
+ #[test]
+ fn set_vring_num() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.set_vring_num(0, 1);
+ assert_ok_or_known_failure(res);
+ }
+
+ #[test]
+ fn set_vring_addr() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.set_vring_addr(
+ 1,
+ 1,
+ 0,
+ 0x0,
+ GuestAddress(0x0),
+ GuestAddress(0x0),
+ GuestAddress(0x0),
+ None,
+ );
+ assert_ok_or_known_failure(res);
+ }
+
+ #[test]
+ fn set_vring_base() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.set_vring_base(0, 1);
+ assert_ok_or_known_failure(res);
+ }
+
+ #[test]
+ fn set_vring_call() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.set_vring_call(0, &EventFd::new().unwrap());
+ assert_ok_or_known_failure(res);
+ }
+
+ #[test]
+ fn set_vring_kick() {
+ let vhost_net = create_fake_vhost_net();
+ let res = vhost_net.set_vring_kick(0, &EventFd::new().unwrap());
+ assert_ok_or_known_failure(res);
+ }
+}
diff --git a/vhost/src/net.rs b/vhost/src/net.rs
new file mode 100644
index 0000000..0f1cd86
--- /dev/null
+++ b/vhost/src/net.rs
@@ -0,0 +1,146 @@
+// Copyright 2017 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 libc;
+use net_util::TapT;
+use std::fs::{File, OpenOptions};
+use std::marker::PhantomData;
+use std::os::unix::fs::OpenOptionsExt;
+use std::os::unix::io::{AsRawFd, RawFd};
+use virtio_sys;
+
+use sys_util::{ioctl_with_ref, GuestMemory};
+
+use super::{ioctl_result, Error, Result, Vhost};
+
+static DEVICE: &'static str = "/dev/vhost-net";
+
+/// Handle to run VHOST_NET ioctls.
+///
+/// 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
+ // vhost-net worker before GuestMemory can potentially be unmapped.
+ fd: File,
+ mem: GuestMemory,
+ phantom: PhantomData<T>,
+}
+
+pub trait NetT<T: TapT>: Vhost + AsRawFd + Send + Sized {
+ /// Create a new NetT instance
+ fn new(mem: &GuestMemory) -> Result<Self>;
+
+ /// Set the tap file descriptor that will serve as the VHOST_NET backend.
+ /// This will start the vhost worker for the given queue.
+ ///
+ /// # 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: &T) -> Result<()>;
+}
+
+impl<T> NetT<T> for Net<T>
+where
+ T: TapT,
+{
+ /// Opens /dev/vhost-net and holds a file descriptor open for it.
+ ///
+ /// # Arguments
+ /// * `mem` - Guest memory mapping.
+ fn new(mem: &GuestMemory) -> Result<Net<T>> {
+ Ok(Net::<T> {
+ fd: OpenOptions::new()
+ .read(true)
+ .write(true)
+ .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
+ .open(DEVICE)
+ .map_err(Error::VhostOpen)?,
+ mem: mem.clone(),
+ phantom: PhantomData,
+ })
+ }
+
+ fn set_backend(&self, queue_index: usize, fd: &T) -> Result<()> {
+ let vring_file = virtio_sys::vhost_vring_file {
+ index: queue_index as u32,
+ fd: fd.as_raw_fd(),
+ };
+
+ // This ioctl is called on a valid vhost_net fd and has its
+ // return value checked.
+ let ret =
+ unsafe { ioctl_with_ref(&self.fd, virtio_sys::VHOST_NET_SET_BACKEND(), &vring_file) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+}
+
+impl<T> Vhost for Net<T> {
+ fn mem(&self) -> &GuestMemory {
+ &self.mem
+ }
+}
+
+impl<T> AsRawFd for Net<T> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+pub mod fakes {
+ use super::*;
+ use std::fs::remove_file;
+ use std::fs::OpenOptions;
+
+ const TMP_FILE: &str = "/tmp/crosvm_vhost_test_file";
+
+ pub struct FakeNet<T> {
+ fd: File,
+ mem: GuestMemory,
+ phantom: PhantomData<T>,
+ }
+
+ impl<T> Drop for FakeNet<T> {
+ fn drop(&mut self) {
+ let _ = remove_file(TMP_FILE);
+ }
+ }
+
+ impl<T> NetT<T> for FakeNet<T>
+ where
+ T: TapT,
+ {
+ fn new(mem: &GuestMemory) -> Result<FakeNet<T>> {
+ Ok(FakeNet::<T> {
+ fd: OpenOptions::new()
+ .read(true)
+ .append(true)
+ .create(true)
+ .open(TMP_FILE)
+ .unwrap(),
+ mem: mem.clone(),
+ phantom: PhantomData,
+ })
+ }
+
+ fn set_backend(&self, _queue_index: usize, _fd: &T) -> Result<()> {
+ Ok(())
+ }
+ }
+
+ impl<T> Vhost for FakeNet<T> {
+ fn mem(&self) -> &GuestMemory {
+ &self.mem
+ }
+ }
+
+ impl<T> AsRawFd for FakeNet<T> {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+ }
+}
diff --git a/vhost/src/vsock.rs b/vhost/src/vsock.rs
new file mode 100644
index 0000000..8c96b9f
--- /dev/null
+++ b/vhost/src/vsock.rs
@@ -0,0 +1,82 @@
+// Copyright 2017 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 libc;
+use std::fs::{File, OpenOptions};
+use std::os::unix::fs::OpenOptionsExt;
+use std::os::unix::io::{AsRawFd, RawFd};
+
+use sys_util::{ioctl_with_ref, GuestMemory};
+use virtio_sys::{VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING};
+
+use super::{ioctl_result, Error, Result, Vhost};
+
+static DEVICE: &'static str = "/dev/vhost-vsock";
+
+/// Handle for running VHOST_VSOCK ioctls.
+pub struct Vsock {
+ fd: File,
+ mem: GuestMemory,
+}
+
+impl Vsock {
+ /// Open a handle to a new VHOST_VSOCK instance.
+ pub fn new(mem: &GuestMemory) -> Result<Vsock> {
+ Ok(Vsock {
+ fd: OpenOptions::new()
+ .read(true)
+ .write(true)
+ .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
+ .open(DEVICE)
+ .map_err(Error::VhostOpen)?,
+ mem: mem.clone(),
+ })
+ }
+
+ /// Set the CID for the guest. This number is used for routing all data destined for
+ /// programs
+ /// running in the guest.
+ ///
+ /// # 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) };
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+
+ /// Tell the VHOST driver to start performing data transfer.
+ pub fn start(&self) -> Result<()> {
+ self.set_running(true)
+ }
+
+ /// Tell the VHOST driver to stop performing data transfer.
+ pub fn stop(&self) -> Result<()> {
+ self.set_running(false)
+ }
+
+ 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) };
+
+ if ret < 0 {
+ return ioctl_result();
+ }
+ Ok(())
+ }
+}
+
+impl Vhost for Vsock {
+ fn mem(&self) -> &GuestMemory {
+ &self.mem
+ }
+}
+
+impl AsRawFd for Vsock {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
diff --git a/virtio_sys/Cargo.toml b/virtio_sys/Cargo.toml
new file mode 100644
index 0000000..e6d2e03
--- /dev/null
+++ b/virtio_sys/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "virtio_sys"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[dependencies]
+sys_util = { path = "../sys_util" }
diff --git a/virtio_sys/src/lib.rs b/virtio_sys/src/lib.rs
new file mode 100644
index 0000000..680d133
--- /dev/null
+++ b/virtio_sys/src/lib.rs
@@ -0,0 +1,64 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+
+use sys_util::{ioctl_io_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr};
+
+// generated with bindgen /usr/include/linux/vhost.h --no-unstable-rust --constified-enum '*' --with-derive-default
+pub mod vhost;
+// generated with bindgen /usr/include/linux/virtio_net.h --no-unstable-rust --constified-enum '*' --with-derive-default
+pub mod virtio_net;
+// generated with bindgen /usr/include/linux/virtio_ring.h --no-unstable-rust --constified-enum '*' --with-derive-default
+pub mod virtio_ring;
+pub use crate::vhost::*;
+pub use crate::virtio_net::*;
+pub use crate::virtio_ring::*;
+
+pub const VHOST: ::std::os::raw::c_uint = 0xaf;
+
+ioctl_ior_nr!(VHOST_GET_FEATURES, VHOST, 0x00, ::std::os::raw::c_ulonglong);
+ioctl_iow_nr!(VHOST_SET_FEATURES, VHOST, 0x00, ::std::os::raw::c_ulonglong);
+ioctl_io_nr!(VHOST_SET_OWNER, VHOST, 0x01);
+ioctl_io_nr!(VHOST_RESET_OWNER, VHOST, 0x02);
+ioctl_iow_nr!(VHOST_SET_MEM_TABLE, VHOST, 0x03, vhost_memory);
+ioctl_iow_nr!(VHOST_SET_LOG_BASE, VHOST, 0x04, ::std::os::raw::c_ulonglong);
+ioctl_iow_nr!(VHOST_SET_LOG_FD, VHOST, 0x07, ::std::os::raw::c_int);
+ioctl_iow_nr!(VHOST_SET_VRING_NUM, VHOST, 0x10, vhost_vring_state);
+ioctl_iow_nr!(VHOST_SET_VRING_ADDR, VHOST, 0x11, vhost_vring_addr);
+ioctl_iow_nr!(VHOST_SET_VRING_BASE, VHOST, 0x12, vhost_vring_state);
+ioctl_iowr_nr!(VHOST_GET_VRING_BASE, VHOST, 0x12, vhost_vring_state);
+ioctl_iow_nr!(VHOST_SET_VRING_KICK, VHOST, 0x20, vhost_vring_file);
+ioctl_iow_nr!(VHOST_SET_VRING_CALL, VHOST, 0x21, vhost_vring_file);
+ioctl_iow_nr!(VHOST_SET_VRING_ERR, VHOST, 0x22, vhost_vring_file);
+ioctl_iow_nr!(VHOST_NET_SET_BACKEND, VHOST, 0x30, vhost_vring_file);
+ioctl_iow_nr!(VHOST_SCSI_SET_ENDPOINT, VHOST, 0x40, vhost_scsi_target);
+ioctl_iow_nr!(VHOST_SCSI_CLEAR_ENDPOINT, VHOST, 0x41, vhost_scsi_target);
+ioctl_iow_nr!(
+ VHOST_SCSI_GET_ABI_VERSION,
+ VHOST,
+ 0x42,
+ ::std::os::raw::c_int
+);
+ioctl_iow_nr!(
+ VHOST_SCSI_SET_EVENTS_MISSED,
+ VHOST,
+ 0x43,
+ ::std::os::raw::c_uint
+);
+ioctl_iow_nr!(
+ VHOST_SCSI_GET_EVENTS_MISSED,
+ VHOST,
+ 0x44,
+ ::std::os::raw::c_uint
+);
+ioctl_iow_nr!(
+ VHOST_VSOCK_SET_GUEST_CID,
+ VHOST,
+ 0x60,
+ ::std::os::raw::c_ulonglong
+);
+ioctl_iow_nr!(VHOST_VSOCK_SET_RUNNING, VHOST, 0x61, ::std::os::raw::c_int);
diff --git a/virtio_sys/src/vhost.rs b/virtio_sys/src/vhost.rs
new file mode 100644
index 0000000..70d2db6
--- /dev/null
+++ b/virtio_sys/src/vhost.rs
@@ -0,0 +1,891 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData)
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ 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 VIRTIO_CONFIG_S_ACKNOWLEDGE: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_CONFIG_S_DRIVER: ::std::os::raw::c_uint = 2;
+pub const VIRTIO_CONFIG_S_DRIVER_OK: ::std::os::raw::c_uint = 4;
+pub const VIRTIO_CONFIG_S_FEATURES_OK: ::std::os::raw::c_uint = 8;
+pub const VIRTIO_CONFIG_S_FAILED: ::std::os::raw::c_uint = 128;
+pub const VIRTIO_TRANSPORT_F_START: ::std::os::raw::c_uint = 28;
+pub const VIRTIO_TRANSPORT_F_END: ::std::os::raw::c_uint = 33;
+pub const VIRTIO_F_NOTIFY_ON_EMPTY: ::std::os::raw::c_uint = 24;
+pub const VIRTIO_F_ANY_LAYOUT: ::std::os::raw::c_uint = 27;
+pub const VIRTIO_F_VERSION_1: ::std::os::raw::c_uint = 32;
+pub const _STDINT_H: ::std::os::raw::c_uint = 1;
+pub const _FEATURES_H: ::std::os::raw::c_uint = 1;
+pub const _DEFAULT_SOURCE: ::std::os::raw::c_uint = 1;
+pub const __USE_ISOC11: ::std::os::raw::c_uint = 1;
+pub const __USE_ISOC99: ::std::os::raw::c_uint = 1;
+pub const __USE_ISOC95: ::std::os::raw::c_uint = 1;
+pub const __USE_POSIX_IMPLICITLY: ::std::os::raw::c_uint = 1;
+pub const _POSIX_SOURCE: ::std::os::raw::c_uint = 1;
+pub const _POSIX_C_SOURCE: ::std::os::raw::c_uint = 200809;
+pub const __USE_POSIX: ::std::os::raw::c_uint = 1;
+pub const __USE_POSIX2: ::std::os::raw::c_uint = 1;
+pub const __USE_POSIX199309: ::std::os::raw::c_uint = 1;
+pub const __USE_POSIX199506: ::std::os::raw::c_uint = 1;
+pub const __USE_XOPEN2K: ::std::os::raw::c_uint = 1;
+pub const __USE_XOPEN2K8: ::std::os::raw::c_uint = 1;
+pub const _ATFILE_SOURCE: ::std::os::raw::c_uint = 1;
+pub const __USE_MISC: ::std::os::raw::c_uint = 1;
+pub const __USE_ATFILE: ::std::os::raw::c_uint = 1;
+pub const __USE_FORTIFY_LEVEL: ::std::os::raw::c_uint = 0;
+pub const _STDC_PREDEF_H: ::std::os::raw::c_uint = 1;
+pub const __STDC_IEC_559__: ::std::os::raw::c_uint = 1;
+pub const __STDC_IEC_559_COMPLEX__: ::std::os::raw::c_uint = 1;
+pub const __STDC_ISO_10646__: ::std::os::raw::c_uint = 201505;
+pub const __STDC_NO_THREADS__: ::std::os::raw::c_uint = 1;
+pub const __GNU_LIBRARY__: ::std::os::raw::c_uint = 6;
+pub const __GLIBC__: ::std::os::raw::c_uint = 2;
+pub const __GLIBC_MINOR__: ::std::os::raw::c_uint = 23;
+pub const _SYS_CDEFS_H: ::std::os::raw::c_uint = 1;
+pub const __WORDSIZE: ::std::os::raw::c_uint = 64;
+pub const __WORDSIZE_TIME64_COMPAT32: ::std::os::raw::c_uint = 1;
+pub const __SYSCALL_WORDSIZE: ::std::os::raw::c_uint = 64;
+pub const _BITS_WCHAR_H: ::std::os::raw::c_uint = 1;
+pub const INT8_MIN: ::std::os::raw::c_int = -128;
+pub const INT16_MIN: ::std::os::raw::c_int = -32768;
+pub const INT32_MIN: ::std::os::raw::c_int = -2147483648;
+pub const INT8_MAX: ::std::os::raw::c_uint = 127;
+pub const INT16_MAX: ::std::os::raw::c_uint = 32767;
+pub const INT32_MAX: ::std::os::raw::c_uint = 2147483647;
+pub const UINT8_MAX: ::std::os::raw::c_uint = 255;
+pub const UINT16_MAX: ::std::os::raw::c_uint = 65535;
+pub const UINT32_MAX: ::std::os::raw::c_uint = 4294967295;
+pub const INT_LEAST8_MIN: ::std::os::raw::c_int = -128;
+pub const INT_LEAST16_MIN: ::std::os::raw::c_int = -32768;
+pub const INT_LEAST32_MIN: ::std::os::raw::c_int = -2147483648;
+pub const INT_LEAST8_MAX: ::std::os::raw::c_uint = 127;
+pub const INT_LEAST16_MAX: ::std::os::raw::c_uint = 32767;
+pub const INT_LEAST32_MAX: ::std::os::raw::c_uint = 2147483647;
+pub const UINT_LEAST8_MAX: ::std::os::raw::c_uint = 255;
+pub const UINT_LEAST16_MAX: ::std::os::raw::c_uint = 65535;
+pub const UINT_LEAST32_MAX: ::std::os::raw::c_uint = 4294967295;
+pub const INT_FAST8_MIN: ::std::os::raw::c_int = -128;
+pub const INT_FAST16_MIN: ::std::os::raw::c_longlong = -9223372036854775808;
+pub const INT_FAST32_MIN: ::std::os::raw::c_longlong = -9223372036854775808;
+pub const INT_FAST8_MAX: ::std::os::raw::c_uint = 127;
+pub const INT_FAST16_MAX: ::std::os::raw::c_ulonglong = 9223372036854775807;
+pub const INT_FAST32_MAX: ::std::os::raw::c_ulonglong = 9223372036854775807;
+pub const UINT_FAST8_MAX: ::std::os::raw::c_uint = 255;
+pub const UINT_FAST16_MAX: ::std::os::raw::c_int = -1;
+pub const UINT_FAST32_MAX: ::std::os::raw::c_int = -1;
+pub const INTPTR_MIN: ::std::os::raw::c_longlong = -9223372036854775808;
+pub const INTPTR_MAX: ::std::os::raw::c_ulonglong = 9223372036854775807;
+pub const UINTPTR_MAX: ::std::os::raw::c_int = -1;
+pub const PTRDIFF_MIN: ::std::os::raw::c_longlong = -9223372036854775808;
+pub const PTRDIFF_MAX: ::std::os::raw::c_ulonglong = 9223372036854775807;
+pub const SIG_ATOMIC_MIN: ::std::os::raw::c_int = -2147483648;
+pub const SIG_ATOMIC_MAX: ::std::os::raw::c_uint = 2147483647;
+pub const SIZE_MAX: ::std::os::raw::c_int = -1;
+pub const WINT_MIN: ::std::os::raw::c_uint = 0;
+pub const WINT_MAX: ::std::os::raw::c_uint = 4294967295;
+pub const VRING_DESC_F_NEXT: ::std::os::raw::c_uint = 1;
+pub const VRING_DESC_F_WRITE: ::std::os::raw::c_uint = 2;
+pub const VRING_DESC_F_INDIRECT: ::std::os::raw::c_uint = 4;
+pub const VRING_USED_F_NO_NOTIFY: ::std::os::raw::c_uint = 1;
+pub const VRING_AVAIL_F_NO_INTERRUPT: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_RING_F_INDIRECT_DESC: ::std::os::raw::c_uint = 28;
+pub const VIRTIO_RING_F_EVENT_IDX: ::std::os::raw::c_uint = 29;
+pub const VRING_AVAIL_ALIGN_SIZE: ::std::os::raw::c_uint = 2;
+pub const VRING_USED_ALIGN_SIZE: ::std::os::raw::c_uint = 4;
+pub const VRING_DESC_ALIGN_SIZE: ::std::os::raw::c_uint = 16;
+pub const VHOST_VRING_F_LOG: ::std::os::raw::c_uint = 0;
+pub const VHOST_PAGE_SIZE: ::std::os::raw::c_uint = 4096;
+pub const VHOST_VIRTIO: ::std::os::raw::c_uint = 175;
+pub const VHOST_VRING_LITTLE_ENDIAN: ::std::os::raw::c_uint = 0;
+pub const VHOST_VRING_BIG_ENDIAN: ::std::os::raw::c_uint = 1;
+pub const VHOST_F_LOG_ALL: ::std::os::raw::c_uint = 26;
+pub const VHOST_NET_F_VIRTIO_NET_HDR: ::std::os::raw::c_uint = 27;
+pub const VHOST_SCSI_ABI_VERSION: ::std::os::raw::c_uint = 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;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __s32 = ::std::os::raw::c_int;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __s64 = ::std::os::raw::c_longlong;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct __kernel_fd_set {
+ pub fds_bits: [::std::os::raw::c_ulong; 16usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fd_set() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fd_set>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fd_set>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fd_set)).fds_bits as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fd_set),
+ "::",
+ stringify!(fds_bits)
+ )
+ );
+}
+impl Clone for __kernel_fd_set {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub type __kernel_sighandler_t =
+ ::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
+pub type __kernel_key_t = ::std::os::raw::c_int;
+pub type __kernel_mqd_t = ::std::os::raw::c_int;
+pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
+pub type __kernel_long_t = ::std::os::raw::c_long;
+pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
+pub type __kernel_ino_t = __kernel_ulong_t;
+pub type __kernel_mode_t = ::std::os::raw::c_uint;
+pub type __kernel_pid_t = ::std::os::raw::c_int;
+pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
+pub type __kernel_uid_t = ::std::os::raw::c_uint;
+pub type __kernel_gid_t = ::std::os::raw::c_uint;
+pub type __kernel_suseconds_t = __kernel_long_t;
+pub type __kernel_daddr_t = ::std::os::raw::c_int;
+pub type __kernel_uid32_t = ::std::os::raw::c_uint;
+pub type __kernel_gid32_t = ::std::os::raw::c_uint;
+pub type __kernel_size_t = __kernel_ulong_t;
+pub type __kernel_ssize_t = __kernel_long_t;
+pub type __kernel_ptrdiff_t = __kernel_long_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct __kernel_fsid_t {
+ pub val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fsid_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fsid_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fsid_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fsid_t)).val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fsid_t),
+ "::",
+ stringify!(val)
+ )
+ );
+}
+impl Clone for __kernel_fsid_t {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+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_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;
+pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
+pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
+pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
+pub type __le16 = __u16;
+pub type __be16 = __u16;
+pub type __le32 = __u32;
+pub type __be32 = __u32;
+pub type __le64 = __u64;
+pub type __be64 = __u64;
+pub type __sum16 = __u16;
+pub type __wsum = __u32;
+pub type int_least8_t = ::std::os::raw::c_schar;
+pub type int_least16_t = ::std::os::raw::c_short;
+pub type int_least32_t = ::std::os::raw::c_int;
+pub type int_least64_t = ::std::os::raw::c_long;
+pub type uint_least8_t = ::std::os::raw::c_uchar;
+pub type uint_least16_t = ::std::os::raw::c_ushort;
+pub type uint_least32_t = ::std::os::raw::c_uint;
+pub type uint_least64_t = ::std::os::raw::c_ulong;
+pub type int_fast8_t = ::std::os::raw::c_schar;
+pub type int_fast16_t = ::std::os::raw::c_long;
+pub type int_fast32_t = ::std::os::raw::c_long;
+pub type int_fast64_t = ::std::os::raw::c_long;
+pub type uint_fast8_t = ::std::os::raw::c_uchar;
+pub type uint_fast16_t = ::std::os::raw::c_ulong;
+pub type uint_fast32_t = ::std::os::raw::c_ulong;
+pub type uint_fast64_t = ::std::os::raw::c_ulong;
+pub type intmax_t = ::std::os::raw::c_long;
+pub type uintmax_t = ::std::os::raw::c_ulong;
+pub type __virtio16 = __u16;
+pub type __virtio32 = __u32;
+pub type __virtio64 = __u64;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vring_desc {
+ pub addr: __virtio64,
+ pub len: __virtio32,
+ pub flags: __virtio16,
+ pub next: __virtio16,
+}
+#[test]
+fn bindgen_test_layout_vring_desc() {
+ assert_eq!(
+ ::std::mem::size_of::<vring_desc>(),
+ 16usize,
+ concat!("Size of: ", stringify!(vring_desc))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring_desc>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(vring_desc))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_desc)).addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_desc),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_desc)).len as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_desc),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_desc)).flags as *const _ as usize },
+ 12usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_desc),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_desc)).next as *const _ as usize },
+ 14usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_desc),
+ "::",
+ stringify!(next)
+ )
+ );
+}
+impl Clone for vring_desc {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vring_avail {
+ pub flags: __virtio16,
+ pub idx: __virtio16,
+ pub ring: __IncompleteArrayField<__virtio16>,
+}
+#[test]
+fn bindgen_test_layout_vring_avail() {
+ assert_eq!(
+ ::std::mem::size_of::<vring_avail>(),
+ 4usize,
+ concat!("Size of: ", stringify!(vring_avail))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring_avail>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(vring_avail))
+ );
+}
+impl Clone for vring_avail {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vring_used_elem {
+ pub id: __virtio32,
+ pub len: __virtio32,
+}
+#[test]
+fn bindgen_test_layout_vring_used_elem() {
+ assert_eq!(
+ ::std::mem::size_of::<vring_used_elem>(),
+ 8usize,
+ concat!("Size of: ", stringify!(vring_used_elem))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring_used_elem>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(vring_used_elem))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_used_elem)).id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_used_elem),
+ "::",
+ stringify!(id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_used_elem)).len as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_used_elem),
+ "::",
+ stringify!(len)
+ )
+ );
+}
+impl Clone for vring_used_elem {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vring_used {
+ pub flags: __virtio16,
+ pub idx: __virtio16,
+ pub ring: __IncompleteArrayField<vring_used_elem>,
+ pub __bindgen_align: [u32; 0usize],
+}
+#[test]
+fn bindgen_test_layout_vring_used() {
+ assert_eq!(
+ ::std::mem::size_of::<vring_used>(),
+ 4usize,
+ concat!("Size of: ", stringify!(vring_used))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring_used>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(vring_used))
+ );
+}
+impl Clone for vring_used {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct vring {
+ pub num: ::std::os::raw::c_uint,
+ pub desc: *mut vring_desc,
+ pub avail: *mut vring_avail,
+ pub used: *mut vring_used,
+}
+#[test]
+fn bindgen_test_layout_vring() {
+ assert_eq!(
+ ::std::mem::size_of::<vring>(),
+ 32usize,
+ concat!("Size of: ", stringify!(vring))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(vring))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring)).num as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring),
+ "::",
+ stringify!(num)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring)).desc as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring),
+ "::",
+ stringify!(desc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring)).avail as *const _ as usize },
+ 16usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring),
+ "::",
+ stringify!(avail)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring)).used as *const _ as usize },
+ 24usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring),
+ "::",
+ stringify!(used)
+ )
+ );
+}
+impl Clone for vring {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl Default for vring {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vhost_vring_state {
+ pub index: ::std::os::raw::c_uint,
+ pub num: ::std::os::raw::c_uint,
+}
+#[test]
+fn bindgen_test_layout_vhost_vring_state() {
+ assert_eq!(
+ ::std::mem::size_of::<vhost_vring_state>(),
+ 8usize,
+ concat!("Size of: ", stringify!(vhost_vring_state))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vhost_vring_state>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(vhost_vring_state))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_state)).index as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_state),
+ "::",
+ stringify!(index)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_state)).num as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_state),
+ "::",
+ stringify!(num)
+ )
+ );
+}
+impl Clone for vhost_vring_state {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vhost_vring_file {
+ pub index: ::std::os::raw::c_uint,
+ pub fd: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_vhost_vring_file() {
+ assert_eq!(
+ ::std::mem::size_of::<vhost_vring_file>(),
+ 8usize,
+ concat!("Size of: ", stringify!(vhost_vring_file))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vhost_vring_file>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(vhost_vring_file))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_file)).index as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_file),
+ "::",
+ stringify!(index)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_file)).fd as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_file),
+ "::",
+ stringify!(fd)
+ )
+ );
+}
+impl Clone for vhost_vring_file {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vhost_vring_addr {
+ pub index: ::std::os::raw::c_uint,
+ pub flags: ::std::os::raw::c_uint,
+ pub desc_user_addr: __u64,
+ pub used_user_addr: __u64,
+ pub avail_user_addr: __u64,
+ pub log_guest_addr: __u64,
+}
+#[test]
+fn bindgen_test_layout_vhost_vring_addr() {
+ assert_eq!(
+ ::std::mem::size_of::<vhost_vring_addr>(),
+ 40usize,
+ concat!("Size of: ", stringify!(vhost_vring_addr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vhost_vring_addr>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(vhost_vring_addr))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_addr)).index as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_addr),
+ "::",
+ stringify!(index)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_addr)).flags as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_addr),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_addr)).desc_user_addr as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_addr),
+ "::",
+ stringify!(desc_user_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_addr)).used_user_addr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_addr),
+ "::",
+ stringify!(used_user_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_addr)).avail_user_addr as *const _ as usize },
+ 24usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_addr),
+ "::",
+ stringify!(avail_user_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_vring_addr)).log_guest_addr as *const _ as usize },
+ 32usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_vring_addr),
+ "::",
+ stringify!(log_guest_addr)
+ )
+ );
+}
+impl Clone for vhost_vring_addr {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vhost_memory_region {
+ pub guest_phys_addr: __u64,
+ pub memory_size: __u64,
+ pub userspace_addr: __u64,
+ pub flags_padding: __u64,
+}
+#[test]
+fn bindgen_test_layout_vhost_memory_region() {
+ assert_eq!(
+ ::std::mem::size_of::<vhost_memory_region>(),
+ 32usize,
+ concat!("Size of: ", stringify!(vhost_memory_region))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vhost_memory_region>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(vhost_memory_region))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_memory_region)).guest_phys_addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_memory_region),
+ "::",
+ stringify!(guest_phys_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_memory_region)).memory_size as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_memory_region),
+ "::",
+ stringify!(memory_size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_memory_region)).userspace_addr as *const _ as usize },
+ 16usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_memory_region),
+ "::",
+ stringify!(userspace_addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_memory_region)).flags_padding as *const _ as usize },
+ 24usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_memory_region),
+ "::",
+ stringify!(flags_padding)
+ )
+ );
+}
+impl Clone for vhost_memory_region {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct vhost_memory {
+ pub nregions: __u32,
+ pub padding: __u32,
+ pub regions: __IncompleteArrayField<vhost_memory_region>,
+ pub __force_alignment: [u64; 0],
+}
+#[test]
+fn bindgen_test_layout_vhost_memory() {
+ assert_eq!(
+ ::std::mem::size_of::<vhost_memory>(),
+ 8usize,
+ concat!("Size of: ", stringify!(vhost_memory))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vhost_memory>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(vhost_memory))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_memory)).nregions as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_memory),
+ "::",
+ stringify!(nregions)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_memory)).padding as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_memory),
+ "::",
+ stringify!(padding)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_memory)).regions as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_memory),
+ "::",
+ stringify!(regions)
+ )
+ );
+}
+impl Clone for vhost_memory {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+pub struct vhost_scsi_target {
+ pub abi_version: ::std::os::raw::c_int,
+ pub vhost_wwpn: [::std::os::raw::c_char; 224usize],
+ pub vhost_tpgt: ::std::os::raw::c_ushort,
+ pub reserved: ::std::os::raw::c_ushort,
+}
+#[test]
+fn bindgen_test_layout_vhost_scsi_target() {
+ assert_eq!(
+ ::std::mem::size_of::<vhost_scsi_target>(),
+ 232usize,
+ concat!("Size of: ", stringify!(vhost_scsi_target))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vhost_scsi_target>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(vhost_scsi_target))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_scsi_target)).abi_version as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_scsi_target),
+ "::",
+ stringify!(abi_version)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_scsi_target)).vhost_wwpn as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_scsi_target),
+ "::",
+ stringify!(vhost_wwpn)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_scsi_target)).vhost_tpgt as *const _ as usize },
+ 228usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_scsi_target),
+ "::",
+ stringify!(vhost_tpgt)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vhost_scsi_target)).reserved as *const _ as usize },
+ 230usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vhost_scsi_target),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Default for vhost_scsi_target {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
diff --git a/virtio_sys/src/virtio_net.rs b/virtio_sys/src/virtio_net.rs
new file mode 100644
index 0000000..5cb370e
--- /dev/null
+++ b/virtio_sys/src/virtio_net.rs
@@ -0,0 +1,779 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData)
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ 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 VIRTIO_ID_NET: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_ID_BLOCK: ::std::os::raw::c_uint = 2;
+pub const VIRTIO_ID_CONSOLE: ::std::os::raw::c_uint = 3;
+pub const VIRTIO_ID_RNG: ::std::os::raw::c_uint = 4;
+pub const VIRTIO_ID_BALLOON: ::std::os::raw::c_uint = 5;
+pub const VIRTIO_ID_RPMSG: ::std::os::raw::c_uint = 7;
+pub const VIRTIO_ID_SCSI: ::std::os::raw::c_uint = 8;
+pub const VIRTIO_ID_9P: ::std::os::raw::c_uint = 9;
+pub const VIRTIO_ID_RPROC_SERIAL: ::std::os::raw::c_uint = 11;
+pub const VIRTIO_ID_CAIF: ::std::os::raw::c_uint = 12;
+pub const VIRTIO_ID_GPU: ::std::os::raw::c_uint = 16;
+pub const VIRTIO_ID_INPUT: ::std::os::raw::c_uint = 18;
+pub const VIRTIO_CONFIG_S_ACKNOWLEDGE: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_CONFIG_S_DRIVER: ::std::os::raw::c_uint = 2;
+pub const VIRTIO_CONFIG_S_DRIVER_OK: ::std::os::raw::c_uint = 4;
+pub const VIRTIO_CONFIG_S_FEATURES_OK: ::std::os::raw::c_uint = 8;
+pub const VIRTIO_CONFIG_S_FAILED: ::std::os::raw::c_uint = 128;
+pub const VIRTIO_TRANSPORT_F_START: ::std::os::raw::c_uint = 28;
+pub const VIRTIO_TRANSPORT_F_END: ::std::os::raw::c_uint = 33;
+pub const VIRTIO_F_NOTIFY_ON_EMPTY: ::std::os::raw::c_uint = 24;
+pub const VIRTIO_F_ANY_LAYOUT: ::std::os::raw::c_uint = 27;
+pub const VIRTIO_F_VERSION_1: ::std::os::raw::c_uint = 32;
+pub const ETH_ALEN: ::std::os::raw::c_uint = 6;
+pub const ETH_HLEN: ::std::os::raw::c_uint = 14;
+pub const ETH_ZLEN: ::std::os::raw::c_uint = 60;
+pub const ETH_DATA_LEN: ::std::os::raw::c_uint = 1500;
+pub const ETH_FRAME_LEN: ::std::os::raw::c_uint = 1514;
+pub const ETH_FCS_LEN: ::std::os::raw::c_uint = 4;
+pub const ETH_P_LOOP: ::std::os::raw::c_uint = 96;
+pub const ETH_P_PUP: ::std::os::raw::c_uint = 512;
+pub const ETH_P_PUPAT: ::std::os::raw::c_uint = 513;
+pub const ETH_P_TSN: ::std::os::raw::c_uint = 8944;
+pub const ETH_P_IP: ::std::os::raw::c_uint = 2048;
+pub const ETH_P_X25: ::std::os::raw::c_uint = 2053;
+pub const ETH_P_ARP: ::std::os::raw::c_uint = 2054;
+pub const ETH_P_BPQ: ::std::os::raw::c_uint = 2303;
+pub const ETH_P_IEEEPUP: ::std::os::raw::c_uint = 2560;
+pub const ETH_P_IEEEPUPAT: ::std::os::raw::c_uint = 2561;
+pub const ETH_P_BATMAN: ::std::os::raw::c_uint = 17157;
+pub const ETH_P_DEC: ::std::os::raw::c_uint = 24576;
+pub const ETH_P_DNA_DL: ::std::os::raw::c_uint = 24577;
+pub const ETH_P_DNA_RC: ::std::os::raw::c_uint = 24578;
+pub const ETH_P_DNA_RT: ::std::os::raw::c_uint = 24579;
+pub const ETH_P_LAT: ::std::os::raw::c_uint = 24580;
+pub const ETH_P_DIAG: ::std::os::raw::c_uint = 24581;
+pub const ETH_P_CUST: ::std::os::raw::c_uint = 24582;
+pub const ETH_P_SCA: ::std::os::raw::c_uint = 24583;
+pub const ETH_P_TEB: ::std::os::raw::c_uint = 25944;
+pub const ETH_P_RARP: ::std::os::raw::c_uint = 32821;
+pub const ETH_P_ATALK: ::std::os::raw::c_uint = 32923;
+pub const ETH_P_AARP: ::std::os::raw::c_uint = 33011;
+pub const ETH_P_8021Q: ::std::os::raw::c_uint = 33024;
+pub const ETH_P_IPX: ::std::os::raw::c_uint = 33079;
+pub const ETH_P_IPV6: ::std::os::raw::c_uint = 34525;
+pub const ETH_P_PAUSE: ::std::os::raw::c_uint = 34824;
+pub const ETH_P_SLOW: ::std::os::raw::c_uint = 34825;
+pub const ETH_P_WCCP: ::std::os::raw::c_uint = 34878;
+pub const ETH_P_MPLS_UC: ::std::os::raw::c_uint = 34887;
+pub const ETH_P_MPLS_MC: ::std::os::raw::c_uint = 34888;
+pub const ETH_P_ATMMPOA: ::std::os::raw::c_uint = 34892;
+pub const ETH_P_PPP_DISC: ::std::os::raw::c_uint = 34915;
+pub const ETH_P_PPP_SES: ::std::os::raw::c_uint = 34916;
+pub const ETH_P_LINK_CTL: ::std::os::raw::c_uint = 34924;
+pub const ETH_P_ATMFATE: ::std::os::raw::c_uint = 34948;
+pub const ETH_P_PAE: ::std::os::raw::c_uint = 34958;
+pub const ETH_P_AOE: ::std::os::raw::c_uint = 34978;
+pub const ETH_P_8021AD: ::std::os::raw::c_uint = 34984;
+pub const ETH_P_802_EX1: ::std::os::raw::c_uint = 34997;
+pub const ETH_P_TIPC: ::std::os::raw::c_uint = 35018;
+pub const ETH_P_8021AH: ::std::os::raw::c_uint = 35047;
+pub const ETH_P_MVRP: ::std::os::raw::c_uint = 35061;
+pub const ETH_P_1588: ::std::os::raw::c_uint = 35063;
+pub const ETH_P_PRP: ::std::os::raw::c_uint = 35067;
+pub const ETH_P_FCOE: ::std::os::raw::c_uint = 35078;
+pub const ETH_P_TDLS: ::std::os::raw::c_uint = 35085;
+pub const ETH_P_FIP: ::std::os::raw::c_uint = 35092;
+pub const ETH_P_80221: ::std::os::raw::c_uint = 35095;
+pub const ETH_P_LOOPBACK: ::std::os::raw::c_uint = 36864;
+pub const ETH_P_QINQ1: ::std::os::raw::c_uint = 37120;
+pub const ETH_P_QINQ2: ::std::os::raw::c_uint = 37376;
+pub const ETH_P_QINQ3: ::std::os::raw::c_uint = 37632;
+pub const ETH_P_EDSA: ::std::os::raw::c_uint = 56026;
+pub const ETH_P_AF_IUCV: ::std::os::raw::c_uint = 64507;
+pub const ETH_P_802_3_MIN: ::std::os::raw::c_uint = 1536;
+pub const ETH_P_802_3: ::std::os::raw::c_uint = 1;
+pub const ETH_P_AX25: ::std::os::raw::c_uint = 2;
+pub const ETH_P_ALL: ::std::os::raw::c_uint = 3;
+pub const ETH_P_802_2: ::std::os::raw::c_uint = 4;
+pub const ETH_P_SNAP: ::std::os::raw::c_uint = 5;
+pub const ETH_P_DDCMP: ::std::os::raw::c_uint = 6;
+pub const ETH_P_WAN_PPP: ::std::os::raw::c_uint = 7;
+pub const ETH_P_PPP_MP: ::std::os::raw::c_uint = 8;
+pub const ETH_P_LOCALTALK: ::std::os::raw::c_uint = 9;
+pub const ETH_P_CAN: ::std::os::raw::c_uint = 12;
+pub const ETH_P_CANFD: ::std::os::raw::c_uint = 13;
+pub const ETH_P_PPPTALK: ::std::os::raw::c_uint = 16;
+pub const ETH_P_TR_802_2: ::std::os::raw::c_uint = 17;
+pub const ETH_P_MOBITEX: ::std::os::raw::c_uint = 21;
+pub const ETH_P_CONTROL: ::std::os::raw::c_uint = 22;
+pub const ETH_P_IRDA: ::std::os::raw::c_uint = 23;
+pub const ETH_P_ECONET: ::std::os::raw::c_uint = 24;
+pub const ETH_P_HDLC: ::std::os::raw::c_uint = 25;
+pub const ETH_P_ARCNET: ::std::os::raw::c_uint = 26;
+pub const ETH_P_DSA: ::std::os::raw::c_uint = 27;
+pub const ETH_P_TRAILER: ::std::os::raw::c_uint = 28;
+pub const ETH_P_PHONET: ::std::os::raw::c_uint = 245;
+pub const ETH_P_IEEE802154: ::std::os::raw::c_uint = 246;
+pub const ETH_P_CAIF: ::std::os::raw::c_uint = 247;
+pub const ETH_P_XDSA: ::std::os::raw::c_uint = 248;
+pub const VIRTIO_NET_F_CSUM: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_F_GUEST_CSUM: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_F_CTRL_GUEST_OFFLOADS: ::std::os::raw::c_uint = 2;
+pub const VIRTIO_NET_F_MTU: ::std::os::raw::c_uint = 3;
+pub const VIRTIO_NET_F_MAC: ::std::os::raw::c_uint = 5;
+pub const VIRTIO_NET_F_GUEST_TSO4: ::std::os::raw::c_uint = 7;
+pub const VIRTIO_NET_F_GUEST_TSO6: ::std::os::raw::c_uint = 8;
+pub const VIRTIO_NET_F_GUEST_ECN: ::std::os::raw::c_uint = 9;
+pub const VIRTIO_NET_F_GUEST_UFO: ::std::os::raw::c_uint = 10;
+pub const VIRTIO_NET_F_HOST_TSO4: ::std::os::raw::c_uint = 11;
+pub const VIRTIO_NET_F_HOST_TSO6: ::std::os::raw::c_uint = 12;
+pub const VIRTIO_NET_F_HOST_ECN: ::std::os::raw::c_uint = 13;
+pub const VIRTIO_NET_F_HOST_UFO: ::std::os::raw::c_uint = 14;
+pub const VIRTIO_NET_F_MRG_RXBUF: ::std::os::raw::c_uint = 15;
+pub const VIRTIO_NET_F_STATUS: ::std::os::raw::c_uint = 16;
+pub const VIRTIO_NET_F_CTRL_VQ: ::std::os::raw::c_uint = 17;
+pub const VIRTIO_NET_F_CTRL_RX: ::std::os::raw::c_uint = 18;
+pub const VIRTIO_NET_F_CTRL_VLAN: ::std::os::raw::c_uint = 19;
+pub const VIRTIO_NET_F_CTRL_RX_EXTRA: ::std::os::raw::c_uint = 20;
+pub const VIRTIO_NET_F_GUEST_ANNOUNCE: ::std::os::raw::c_uint = 21;
+pub const VIRTIO_NET_F_MQ: ::std::os::raw::c_uint = 22;
+pub const VIRTIO_NET_F_CTRL_MAC_ADDR: ::std::os::raw::c_uint = 23;
+pub const VIRTIO_NET_F_GSO: ::std::os::raw::c_uint = 6;
+pub const VIRTIO_NET_S_LINK_UP: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_S_ANNOUNCE: ::std::os::raw::c_uint = 2;
+pub const VIRTIO_NET_HDR_F_NEEDS_CSUM: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_HDR_F_DATA_VALID: ::std::os::raw::c_uint = 2;
+pub const VIRTIO_NET_HDR_GSO_NONE: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_HDR_GSO_TCPV4: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_HDR_GSO_UDP: ::std::os::raw::c_uint = 3;
+pub const VIRTIO_NET_HDR_GSO_TCPV6: ::std::os::raw::c_uint = 4;
+pub const VIRTIO_NET_HDR_GSO_ECN: ::std::os::raw::c_uint = 128;
+pub const VIRTIO_NET_OK: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_ERR: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_CTRL_RX: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_CTRL_RX_PROMISC: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_CTRL_RX_ALLMULTI: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_CTRL_RX_ALLUNI: ::std::os::raw::c_uint = 2;
+pub const VIRTIO_NET_CTRL_RX_NOMULTI: ::std::os::raw::c_uint = 3;
+pub const VIRTIO_NET_CTRL_RX_NOUNI: ::std::os::raw::c_uint = 4;
+pub const VIRTIO_NET_CTRL_RX_NOBCAST: ::std::os::raw::c_uint = 5;
+pub const VIRTIO_NET_CTRL_MAC: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_CTRL_MAC_TABLE_SET: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_CTRL_MAC_ADDR_SET: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_CTRL_VLAN: ::std::os::raw::c_uint = 2;
+pub const VIRTIO_NET_CTRL_VLAN_ADD: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_CTRL_VLAN_DEL: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_CTRL_ANNOUNCE: ::std::os::raw::c_uint = 3;
+pub const VIRTIO_NET_CTRL_ANNOUNCE_ACK: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_CTRL_MQ: ::std::os::raw::c_uint = 4;
+pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: ::std::os::raw::c_uint = 0;
+pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX: ::std::os::raw::c_uint = 32768;
+pub const VIRTIO_NET_CTRL_GUEST_OFFLOADS: ::std::os::raw::c_uint = 5;
+pub const VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET: ::std::os::raw::c_uint = 0;
+pub type __s8 = ::std::os::raw::c_schar;
+pub type __u8 = ::std::os::raw::c_uchar;
+pub type __s16 = ::std::os::raw::c_short;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __s32 = ::std::os::raw::c_int;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __s64 = ::std::os::raw::c_longlong;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct __kernel_fd_set {
+ pub fds_bits: [::std::os::raw::c_ulong; 16usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fd_set() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fd_set>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fd_set>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fd_set)).fds_bits as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fd_set),
+ "::",
+ stringify!(fds_bits)
+ )
+ );
+}
+impl Clone for __kernel_fd_set {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub type __kernel_sighandler_t =
+ ::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
+pub type __kernel_key_t = ::std::os::raw::c_int;
+pub type __kernel_mqd_t = ::std::os::raw::c_int;
+pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
+pub type __kernel_long_t = ::std::os::raw::c_long;
+pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
+pub type __kernel_ino_t = __kernel_ulong_t;
+pub type __kernel_mode_t = ::std::os::raw::c_uint;
+pub type __kernel_pid_t = ::std::os::raw::c_int;
+pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
+pub type __kernel_uid_t = ::std::os::raw::c_uint;
+pub type __kernel_gid_t = ::std::os::raw::c_uint;
+pub type __kernel_suseconds_t = __kernel_long_t;
+pub type __kernel_daddr_t = ::std::os::raw::c_int;
+pub type __kernel_uid32_t = ::std::os::raw::c_uint;
+pub type __kernel_gid32_t = ::std::os::raw::c_uint;
+pub type __kernel_size_t = __kernel_ulong_t;
+pub type __kernel_ssize_t = __kernel_long_t;
+pub type __kernel_ptrdiff_t = __kernel_long_t;
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct __kernel_fsid_t {
+ pub val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fsid_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fsid_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fsid_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fsid_t)).val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fsid_t),
+ "::",
+ stringify!(val)
+ )
+ );
+}
+impl Clone for __kernel_fsid_t {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+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_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;
+pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
+pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
+pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
+pub type __le16 = __u16;
+pub type __be16 = __u16;
+pub type __le32 = __u32;
+pub type __be32 = __u32;
+pub type __le64 = __u64;
+pub type __be64 = __u64;
+pub type __sum16 = __u16;
+pub type __wsum = __u32;
+pub type __virtio16 = __u16;
+pub type __virtio32 = __u32;
+pub type __virtio64 = __u64;
+#[repr(C, packed)]
+#[derive(Debug, Copy)]
+pub struct ethhdr {
+ pub h_dest: [::std::os::raw::c_uchar; 6usize],
+ pub h_source: [::std::os::raw::c_uchar; 6usize],
+ pub h_proto: __be16,
+}
+#[test]
+fn bindgen_test_layout_ethhdr() {
+ assert_eq!(
+ ::std::mem::size_of::<ethhdr>(),
+ 14usize,
+ concat!("Size of: ", stringify!(ethhdr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<ethhdr>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(ethhdr))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ethhdr)).h_dest as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ethhdr),
+ "::",
+ stringify!(h_dest)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ethhdr)).h_source as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ethhdr),
+ "::",
+ stringify!(h_source)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const ethhdr)).h_proto as *const _ as usize },
+ 12usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(ethhdr),
+ "::",
+ stringify!(h_proto)
+ )
+ );
+}
+impl Clone for ethhdr {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C, packed)]
+#[derive(Debug, Copy)]
+pub struct virtio_net_config {
+ pub mac: [__u8; 6usize],
+ pub status: __u16,
+ pub max_virtqueue_pairs: __u16,
+ pub mtu: __u16,
+}
+#[test]
+fn bindgen_test_layout_virtio_net_config() {
+ assert_eq!(
+ ::std::mem::size_of::<virtio_net_config>(),
+ 12usize,
+ concat!("Size of: ", stringify!(virtio_net_config))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<virtio_net_config>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(virtio_net_config))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_config)).mac as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_config),
+ "::",
+ stringify!(mac)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_config)).status as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_config),
+ "::",
+ stringify!(status)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_config)).max_virtqueue_pairs as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_config),
+ "::",
+ stringify!(max_virtqueue_pairs)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_config)).mtu as *const _ as usize },
+ 10usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_config),
+ "::",
+ stringify!(mtu)
+ )
+ );
+}
+impl Clone for virtio_net_config {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct virtio_net_hdr_v1 {
+ pub flags: __u8,
+ pub gso_type: __u8,
+ pub hdr_len: __virtio16,
+ pub gso_size: __virtio16,
+ pub csum_start: __virtio16,
+ pub csum_offset: __virtio16,
+ pub num_buffers: __virtio16,
+}
+#[test]
+fn bindgen_test_layout_virtio_net_hdr_v1() {
+ assert_eq!(
+ ::std::mem::size_of::<virtio_net_hdr_v1>(),
+ 12usize,
+ concat!("Size of: ", stringify!(virtio_net_hdr_v1))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<virtio_net_hdr_v1>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(virtio_net_hdr_v1))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_v1)).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_v1),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_v1)).gso_type as *const _ as usize },
+ 1usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_v1),
+ "::",
+ stringify!(gso_type)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_v1)).hdr_len as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_v1),
+ "::",
+ stringify!(hdr_len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_v1)).gso_size as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_v1),
+ "::",
+ stringify!(gso_size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_v1)).csum_start as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_v1),
+ "::",
+ stringify!(csum_start)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_v1)).csum_offset as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_v1),
+ "::",
+ stringify!(csum_offset)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_v1)).num_buffers as *const _ as usize },
+ 10usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_v1),
+ "::",
+ stringify!(num_buffers)
+ )
+ );
+}
+impl Clone for virtio_net_hdr_v1 {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct virtio_net_hdr {
+ pub flags: __u8,
+ pub gso_type: __u8,
+ pub hdr_len: __virtio16,
+ pub gso_size: __virtio16,
+ pub csum_start: __virtio16,
+ pub csum_offset: __virtio16,
+}
+#[test]
+fn bindgen_test_layout_virtio_net_hdr() {
+ assert_eq!(
+ ::std::mem::size_of::<virtio_net_hdr>(),
+ 10usize,
+ concat!("Size of: ", stringify!(virtio_net_hdr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<virtio_net_hdr>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(virtio_net_hdr))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr)).flags as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr)).gso_type as *const _ as usize },
+ 1usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr),
+ "::",
+ stringify!(gso_type)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr)).hdr_len as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr),
+ "::",
+ stringify!(hdr_len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr)).gso_size as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr),
+ "::",
+ stringify!(gso_size)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr)).csum_start as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr),
+ "::",
+ stringify!(csum_start)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr)).csum_offset as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr),
+ "::",
+ stringify!(csum_offset)
+ )
+ );
+}
+impl Clone for virtio_net_hdr {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct virtio_net_hdr_mrg_rxbuf {
+ pub hdr: virtio_net_hdr,
+ pub num_buffers: __virtio16,
+}
+#[test]
+fn bindgen_test_layout_virtio_net_hdr_mrg_rxbuf() {
+ assert_eq!(
+ ::std::mem::size_of::<virtio_net_hdr_mrg_rxbuf>(),
+ 12usize,
+ concat!("Size of: ", stringify!(virtio_net_hdr_mrg_rxbuf))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<virtio_net_hdr_mrg_rxbuf>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(virtio_net_hdr_mrg_rxbuf))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_mrg_rxbuf)).hdr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_mrg_rxbuf),
+ "::",
+ stringify!(hdr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_hdr_mrg_rxbuf)).num_buffers as *const _ as usize },
+ 10usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_hdr_mrg_rxbuf),
+ "::",
+ stringify!(num_buffers)
+ )
+ );
+}
+impl Clone for virtio_net_hdr_mrg_rxbuf {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C, packed)]
+#[derive(Debug, Copy)]
+pub struct virtio_net_ctrl_hdr {
+ pub class: __u8,
+ pub cmd: __u8,
+}
+#[test]
+fn bindgen_test_layout_virtio_net_ctrl_hdr() {
+ assert_eq!(
+ ::std::mem::size_of::<virtio_net_ctrl_hdr>(),
+ 2usize,
+ concat!("Size of: ", stringify!(virtio_net_ctrl_hdr))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<virtio_net_ctrl_hdr>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(virtio_net_ctrl_hdr))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_ctrl_hdr)).class as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_ctrl_hdr),
+ "::",
+ stringify!(class)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_ctrl_hdr)).cmd as *const _ as usize },
+ 1usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_ctrl_hdr),
+ "::",
+ stringify!(cmd)
+ )
+ );
+}
+impl Clone for virtio_net_ctrl_hdr {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub type virtio_net_ctrl_ack = __u8;
+#[repr(C, packed)]
+#[derive(Debug, Copy)]
+pub struct virtio_net_ctrl_mac {
+ pub entries: __virtio32,
+ pub macs: __IncompleteArrayField<[__u8; 6usize]>,
+}
+#[test]
+fn bindgen_test_layout_virtio_net_ctrl_mac() {
+ assert_eq!(
+ ::std::mem::size_of::<virtio_net_ctrl_mac>(),
+ 4usize,
+ concat!("Size of: ", stringify!(virtio_net_ctrl_mac))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<virtio_net_ctrl_mac>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(virtio_net_ctrl_mac))
+ );
+}
+impl Clone for virtio_net_ctrl_mac {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct virtio_net_ctrl_mq {
+ pub virtqueue_pairs: __virtio16,
+}
+#[test]
+fn bindgen_test_layout_virtio_net_ctrl_mq() {
+ assert_eq!(
+ ::std::mem::size_of::<virtio_net_ctrl_mq>(),
+ 2usize,
+ concat!("Size of: ", stringify!(virtio_net_ctrl_mq))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<virtio_net_ctrl_mq>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(virtio_net_ctrl_mq))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const virtio_net_ctrl_mq)).virtqueue_pairs as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(virtio_net_ctrl_mq),
+ "::",
+ stringify!(virtqueue_pairs)
+ )
+ );
+}
+impl Clone for virtio_net_ctrl_mq {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
diff --git a/virtio_sys/src/virtio_ring.rs b/virtio_sys/src/virtio_ring.rs
new file mode 100644
index 0000000..c862851
--- /dev/null
+++ b/virtio_sys/src/virtio_ring.rs
@@ -0,0 +1,485 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData)
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self::new()
+ }
+}
+impl<T> ::std::marker::Copy for __IncompleteArrayField<T> {}
+pub const _STDINT_H: ::std::os::raw::c_uint = 1;
+pub const _FEATURES_H: ::std::os::raw::c_uint = 1;
+pub const _DEFAULT_SOURCE: ::std::os::raw::c_uint = 1;
+pub const __USE_ISOC11: ::std::os::raw::c_uint = 1;
+pub const __USE_ISOC99: ::std::os::raw::c_uint = 1;
+pub const __USE_ISOC95: ::std::os::raw::c_uint = 1;
+pub const __USE_POSIX_IMPLICITLY: ::std::os::raw::c_uint = 1;
+pub const _POSIX_SOURCE: ::std::os::raw::c_uint = 1;
+pub const _POSIX_C_SOURCE: ::std::os::raw::c_uint = 200809;
+pub const __USE_POSIX: ::std::os::raw::c_uint = 1;
+pub const __USE_POSIX2: ::std::os::raw::c_uint = 1;
+pub const __USE_POSIX199309: ::std::os::raw::c_uint = 1;
+pub const __USE_POSIX199506: ::std::os::raw::c_uint = 1;
+pub const __USE_XOPEN2K: ::std::os::raw::c_uint = 1;
+pub const __USE_XOPEN2K8: ::std::os::raw::c_uint = 1;
+pub const _ATFILE_SOURCE: ::std::os::raw::c_uint = 1;
+pub const __USE_MISC: ::std::os::raw::c_uint = 1;
+pub const __USE_ATFILE: ::std::os::raw::c_uint = 1;
+pub const __USE_FORTIFY_LEVEL: ::std::os::raw::c_uint = 0;
+pub const _STDC_PREDEF_H: ::std::os::raw::c_uint = 1;
+pub const __STDC_IEC_559__: ::std::os::raw::c_uint = 1;
+pub const __STDC_IEC_559_COMPLEX__: ::std::os::raw::c_uint = 1;
+pub const __STDC_ISO_10646__: ::std::os::raw::c_uint = 201505;
+pub const __STDC_NO_THREADS__: ::std::os::raw::c_uint = 1;
+pub const __GNU_LIBRARY__: ::std::os::raw::c_uint = 6;
+pub const __GLIBC__: ::std::os::raw::c_uint = 2;
+pub const __GLIBC_MINOR__: ::std::os::raw::c_uint = 23;
+pub const _SYS_CDEFS_H: ::std::os::raw::c_uint = 1;
+pub const __WORDSIZE: ::std::os::raw::c_uint = 64;
+pub const __WORDSIZE_TIME64_COMPAT32: ::std::os::raw::c_uint = 1;
+pub const __SYSCALL_WORDSIZE: ::std::os::raw::c_uint = 64;
+pub const _BITS_WCHAR_H: ::std::os::raw::c_uint = 1;
+pub const INT8_MIN: ::std::os::raw::c_int = -128;
+pub const INT16_MIN: ::std::os::raw::c_int = -32768;
+pub const INT32_MIN: ::std::os::raw::c_int = -2147483648;
+pub const INT8_MAX: ::std::os::raw::c_uint = 127;
+pub const INT16_MAX: ::std::os::raw::c_uint = 32767;
+pub const INT32_MAX: ::std::os::raw::c_uint = 2147483647;
+pub const UINT8_MAX: ::std::os::raw::c_uint = 255;
+pub const UINT16_MAX: ::std::os::raw::c_uint = 65535;
+pub const UINT32_MAX: ::std::os::raw::c_uint = 4294967295;
+pub const INT_LEAST8_MIN: ::std::os::raw::c_int = -128;
+pub const INT_LEAST16_MIN: ::std::os::raw::c_int = -32768;
+pub const INT_LEAST32_MIN: ::std::os::raw::c_int = -2147483648;
+pub const INT_LEAST8_MAX: ::std::os::raw::c_uint = 127;
+pub const INT_LEAST16_MAX: ::std::os::raw::c_uint = 32767;
+pub const INT_LEAST32_MAX: ::std::os::raw::c_uint = 2147483647;
+pub const UINT_LEAST8_MAX: ::std::os::raw::c_uint = 255;
+pub const UINT_LEAST16_MAX: ::std::os::raw::c_uint = 65535;
+pub const UINT_LEAST32_MAX: ::std::os::raw::c_uint = 4294967295;
+pub const INT_FAST8_MIN: ::std::os::raw::c_int = -128;
+pub const INT_FAST16_MIN: ::std::os::raw::c_longlong = -9223372036854775808;
+pub const INT_FAST32_MIN: ::std::os::raw::c_longlong = -9223372036854775808;
+pub const INT_FAST8_MAX: ::std::os::raw::c_uint = 127;
+pub const INT_FAST16_MAX: ::std::os::raw::c_ulonglong = 9223372036854775807;
+pub const INT_FAST32_MAX: ::std::os::raw::c_ulonglong = 9223372036854775807;
+pub const UINT_FAST8_MAX: ::std::os::raw::c_uint = 255;
+pub const UINT_FAST16_MAX: ::std::os::raw::c_int = -1;
+pub const UINT_FAST32_MAX: ::std::os::raw::c_int = -1;
+pub const INTPTR_MIN: ::std::os::raw::c_longlong = -9223372036854775808;
+pub const INTPTR_MAX: ::std::os::raw::c_ulonglong = 9223372036854775807;
+pub const UINTPTR_MAX: ::std::os::raw::c_int = -1;
+pub const PTRDIFF_MIN: ::std::os::raw::c_longlong = -9223372036854775808;
+pub const PTRDIFF_MAX: ::std::os::raw::c_ulonglong = 9223372036854775807;
+pub const SIG_ATOMIC_MIN: ::std::os::raw::c_int = -2147483648;
+pub const SIG_ATOMIC_MAX: ::std::os::raw::c_uint = 2147483647;
+pub const SIZE_MAX: ::std::os::raw::c_int = -1;
+pub const WINT_MIN: ::std::os::raw::c_uint = 0;
+pub const WINT_MAX: ::std::os::raw::c_uint = 4294967295;
+pub const __BITS_PER_LONG: ::std::os::raw::c_uint = 64;
+pub const __FD_SETSIZE: ::std::os::raw::c_uint = 1024;
+pub const VRING_DESC_F_NEXT: ::std::os::raw::c_uint = 1;
+pub const VRING_DESC_F_WRITE: ::std::os::raw::c_uint = 2;
+pub const VRING_DESC_F_INDIRECT: ::std::os::raw::c_uint = 4;
+pub const VRING_USED_F_NO_NOTIFY: ::std::os::raw::c_uint = 1;
+pub const VRING_AVAIL_F_NO_INTERRUPT: ::std::os::raw::c_uint = 1;
+pub const VIRTIO_RING_F_INDIRECT_DESC: ::std::os::raw::c_uint = 28;
+pub const VIRTIO_RING_F_EVENT_IDX: ::std::os::raw::c_uint = 29;
+pub const VRING_AVAIL_ALIGN_SIZE: ::std::os::raw::c_uint = 2;
+pub const VRING_USED_ALIGN_SIZE: ::std::os::raw::c_uint = 4;
+pub const VRING_DESC_ALIGN_SIZE: ::std::os::raw::c_uint = 16;
+pub type int_least8_t = ::std::os::raw::c_schar;
+pub type int_least16_t = ::std::os::raw::c_short;
+pub type int_least32_t = ::std::os::raw::c_int;
+pub type int_least64_t = ::std::os::raw::c_long;
+pub type uint_least8_t = ::std::os::raw::c_uchar;
+pub type uint_least16_t = ::std::os::raw::c_ushort;
+pub type uint_least32_t = ::std::os::raw::c_uint;
+pub type uint_least64_t = ::std::os::raw::c_ulong;
+pub type int_fast8_t = ::std::os::raw::c_schar;
+pub type int_fast16_t = ::std::os::raw::c_long;
+pub type int_fast32_t = ::std::os::raw::c_long;
+pub type int_fast64_t = ::std::os::raw::c_long;
+pub type uint_fast8_t = ::std::os::raw::c_uchar;
+pub type uint_fast16_t = ::std::os::raw::c_ulong;
+pub type uint_fast32_t = ::std::os::raw::c_ulong;
+pub type uint_fast64_t = ::std::os::raw::c_ulong;
+pub type intmax_t = ::std::os::raw::c_long;
+pub type uintmax_t = ::std::os::raw::c_ulong;
+pub type __s8 = ::std::os::raw::c_schar;
+pub type __u8 = ::std::os::raw::c_uchar;
+pub type __s16 = ::std::os::raw::c_short;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __s32 = ::std::os::raw::c_int;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __s64 = ::std::os::raw::c_longlong;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct __kernel_fd_set {
+ pub fds_bits: [::std::os::raw::c_ulong; 16usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fd_set() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fd_set>(),
+ 128usize,
+ concat!("Size of: ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fd_set>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(__kernel_fd_set))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fd_set)).fds_bits as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fd_set),
+ "::",
+ stringify!(fds_bits)
+ )
+ );
+}
+impl Clone for __kernel_fd_set {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub type __kernel_sighandler_t =
+ ::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
+pub type __kernel_key_t = ::std::os::raw::c_int;
+pub type __kernel_mqd_t = ::std::os::raw::c_int;
+pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
+pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
+pub type __kernel_long_t = ::std::os::raw::c_long;
+pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
+pub type __kernel_ino_t = __kernel_ulong_t;
+pub type __kernel_mode_t = ::std::os::raw::c_uint;
+pub type __kernel_pid_t = ::std::os::raw::c_int;
+pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
+pub type __kernel_uid_t = ::std::os::raw::c_uint;
+pub type __kernel_gid_t = ::std::os::raw::c_uint;
+pub type __kernel_suseconds_t = __kernel_long_t;
+pub type __kernel_daddr_t = ::std::os::raw::c_int;
+pub type __kernel_uid32_t = ::std::os::raw::c_uint;
+pub type __kernel_gid32_t = ::std::os::raw::c_uint;
+pub type __kernel_size_t = __kernel_ulong_t;
+pub type __kernel_ssize_t = __kernel_long_t;
+pub type __kernel_ptrdiff_t = __kernel_long_t;
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct __kernel_fsid_t {
+ pub val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___kernel_fsid_t() {
+ assert_eq!(
+ ::std::mem::size_of::<__kernel_fsid_t>(),
+ 8usize,
+ concat!("Size of: ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<__kernel_fsid_t>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(__kernel_fsid_t))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const __kernel_fsid_t)).val as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(__kernel_fsid_t),
+ "::",
+ stringify!(val)
+ )
+ );
+}
+impl Clone for __kernel_fsid_t {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+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_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;
+pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
+pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
+pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
+pub type __le16 = __u16;
+pub type __be16 = __u16;
+pub type __le32 = __u32;
+pub type __be32 = __u32;
+pub type __le64 = __u64;
+pub type __be64 = __u64;
+pub type __sum16 = __u16;
+pub type __wsum = __u32;
+pub type __virtio16 = __u16;
+pub type __virtio32 = __u32;
+pub type __virtio64 = __u64;
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct vring_desc {
+ pub addr: __virtio64,
+ pub len: __virtio32,
+ pub flags: __virtio16,
+ pub next: __virtio16,
+}
+#[test]
+fn bindgen_test_layout_vring_desc() {
+ assert_eq!(
+ ::std::mem::size_of::<vring_desc>(),
+ 16usize,
+ concat!("Size of: ", stringify!(vring_desc))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring_desc>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(vring_desc))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_desc)).addr as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_desc),
+ "::",
+ stringify!(addr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_desc)).len as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_desc),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_desc)).flags as *const _ as usize },
+ 12usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_desc),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_desc)).next as *const _ as usize },
+ 14usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_desc),
+ "::",
+ stringify!(next)
+ )
+ );
+}
+impl Clone for vring_desc {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct vring_avail {
+ pub flags: __virtio16,
+ pub idx: __virtio16,
+ pub ring: __IncompleteArrayField<__virtio16>,
+}
+#[test]
+fn bindgen_test_layout_vring_avail() {
+ assert_eq!(
+ ::std::mem::size_of::<vring_avail>(),
+ 4usize,
+ concat!("Size of: ", stringify!(vring_avail))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring_avail>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(vring_avail))
+ );
+}
+impl Clone for vring_avail {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct vring_used_elem {
+ pub id: __virtio32,
+ pub len: __virtio32,
+}
+#[test]
+fn bindgen_test_layout_vring_used_elem() {
+ assert_eq!(
+ ::std::mem::size_of::<vring_used_elem>(),
+ 8usize,
+ concat!("Size of: ", stringify!(vring_used_elem))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring_used_elem>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(vring_used_elem))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_used_elem)).id as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_used_elem),
+ "::",
+ stringify!(id)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring_used_elem)).len as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring_used_elem),
+ "::",
+ stringify!(len)
+ )
+ );
+}
+impl Clone for vring_used_elem {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct vring_used {
+ pub flags: __virtio16,
+ pub idx: __virtio16,
+ pub ring: __IncompleteArrayField<vring_used_elem>,
+ pub __bindgen_align: [u32; 0usize],
+}
+#[test]
+fn bindgen_test_layout_vring_used() {
+ assert_eq!(
+ ::std::mem::size_of::<vring_used>(),
+ 4usize,
+ concat!("Size of: ", stringify!(vring_used))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring_used>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(vring_used))
+ );
+}
+impl Clone for vring_used {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct vring {
+ pub num: ::std::os::raw::c_uint,
+ pub desc: *mut vring_desc,
+ pub avail: *mut vring_avail,
+ pub used: *mut vring_used,
+}
+#[test]
+fn bindgen_test_layout_vring() {
+ assert_eq!(
+ ::std::mem::size_of::<vring>(),
+ 32usize,
+ concat!("Size of: ", stringify!(vring))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<vring>(),
+ 8usize,
+ concat!("Alignment of ", stringify!(vring))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring)).num as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring),
+ "::",
+ stringify!(num)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring)).desc as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring),
+ "::",
+ stringify!(desc)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring)).avail as *const _ as usize },
+ 16usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring),
+ "::",
+ stringify!(avail)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const vring)).used as *const _ as usize },
+ 24usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(vring),
+ "::",
+ stringify!(used)
+ )
+ );
+}
+impl Clone for vring {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
diff --git a/vm_control/Cargo.toml b/vm_control/Cargo.toml
new file mode 100644
index 0000000..ba70fe2
--- /dev/null
+++ b/vm_control/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "vm_control"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+
+[features]
+sandboxed-libusb = []
+
+[dependencies]
+byteorder = "*"
+data_model = { path = "../data_model" }
+kvm = { path = "../kvm" }
+libc = "*"
+msg_socket = { path = "../msg_socket" }
+resources = { path = "../resources" }
+sys_util = { path = "../sys_util" }
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
new file mode 100644
index 0000000..8023abe
--- /dev/null
+++ b/vm_control/src/lib.rs
@@ -0,0 +1,456 @@
+// Copyright 2017 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.
+
+//! Handles IPC for controlling the main VM process.
+//!
+//! The VM Control IPC protocol is synchronous, meaning that each `VmRequest` sent over a connection
+//! will receive a `VmResponse` for that request next time data is received over that connection.
+//!
+//! The wire message format is a little-endian C-struct of fixed size, along with a file descriptor
+//! if the request type expects one.
+
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::{Seek, SeekFrom};
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+
+use libc::{EINVAL, EIO, ENODEV};
+
+use kvm::Vm;
+use msg_socket::{MsgOnSocket, MsgReceiver, MsgResult, MsgSender, MsgSocket};
+use resources::{GpuMemoryDesc, SystemAllocator};
+use sys_util::{error, Error as SysError, GuestAddress, MemoryMapping, MmapError, Result};
+
+/// A file descriptor either borrowed or owned by this.
+#[derive(Debug)]
+pub enum MaybeOwnedFd {
+ /// Owned by this enum variant, and will be destructed automatically if not moved out.
+ Owned(File),
+ /// A file descriptor borrwed by this enum.
+ Borrowed(RawFd),
+}
+
+impl AsRawFd for MaybeOwnedFd {
+ fn as_raw_fd(&self) -> RawFd {
+ match self {
+ MaybeOwnedFd::Owned(f) => f.as_raw_fd(),
+ MaybeOwnedFd::Borrowed(fd) => *fd,
+ }
+ }
+}
+
+// When sent, it could be owned or borrowed. On the receiver end, it always owned.
+impl MsgOnSocket for MaybeOwnedFd {
+ fn msg_size() -> usize {
+ 0usize
+ }
+ fn max_fd_count() -> usize {
+ 1usize
+ }
+ unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
+ let (fd, size) = RawFd::read_from_buffer(buffer, fds)?;
+ let file = File::from_raw_fd(fd);
+ Ok((MaybeOwnedFd::Owned(file), size))
+ }
+ fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
+ let fd = self.as_raw_fd();
+ fd.write_to_buffer(buffer, fds)
+ }
+}
+
+/// Mode of execution for the VM.
+#[derive(Debug)]
+pub enum VmRunMode {
+ /// The default run mode indicating the VCPUs are running.
+ Running,
+ /// Indicates that the VCPUs are suspending execution until the `Running` mode is set.
+ Suspending,
+ /// Indicates that the VM is exiting all processes.
+ Exiting,
+}
+
+impl Display for VmRunMode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::VmRunMode::*;
+
+ match self {
+ Running => write!(f, "running"),
+ Suspending => write!(f, "suspending"),
+ Exiting => write!(f, "exiting"),
+ }
+ }
+}
+
+impl Default for VmRunMode {
+ fn default() -> Self {
+ VmRunMode::Running
+ }
+}
+
+/// The maximum number of devices that can be listed in one `UsbControlCommand`.
+///
+/// This value was set to be equal to `xhci_regs::MAX_PORTS` for convenience, but it is not
+/// necessary for correctness. Importing that value directly would be overkill because it would
+/// require adding a big dependency for a single const.
+pub const USB_CONTROL_MAX_PORTS: usize = 16;
+
+#[derive(MsgOnSocket, Debug)]
+pub enum BalloonControlCommand {
+ /// Set the size of the VM's balloon.
+ Adjust { num_bytes: u64 },
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum DiskControlCommand {
+ /// Resize a disk to `new_size` in bytes.
+ Resize { new_size: u64 },
+}
+
+impl Display for DiskControlCommand {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::DiskControlCommand::*;
+
+ match self {
+ Resize { new_size } => write!(f, "disk_resize {}", new_size),
+ }
+ }
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum DiskControlResult {
+ Ok,
+ Err(SysError),
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum UsbControlCommand {
+ AttachDevice {
+ bus: u8,
+ addr: u8,
+ vid: u16,
+ pid: u16,
+ fd: Option<MaybeOwnedFd>,
+ },
+ DetachDevice {
+ port: u8,
+ },
+ ListDevice {
+ ports: [u8; USB_CONTROL_MAX_PORTS],
+ },
+}
+
+#[derive(MsgOnSocket, Copy, Clone, Debug, Default)]
+pub struct UsbControlAttachedDevice {
+ pub port: u8,
+ pub vendor_id: u16,
+ pub product_id: u16,
+}
+
+impl UsbControlAttachedDevice {
+ fn valid(self) -> bool {
+ self.port != 0
+ }
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum UsbControlResult {
+ Ok { port: u8 },
+ NoAvailablePort,
+ NoSuchDevice,
+ NoSuchPort,
+ FailedToOpenDevice,
+ Devices([UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS]),
+}
+
+impl Display for UsbControlResult {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::UsbControlResult::*;
+
+ match self {
+ Ok { port } => write!(f, "ok {}", port),
+ NoAvailablePort => write!(f, "no_available_port"),
+ NoSuchDevice => write!(f, "no_such_device"),
+ NoSuchPort => write!(f, "no_such_port"),
+ FailedToOpenDevice => write!(f, "failed_to_open_device"),
+ Devices(devices) => {
+ write!(f, "devices")?;
+ for d in devices.iter().filter(|d| d.valid()) {
+ write!(f, " {} {:04x} {:04x}", d.port, d.vendor_id, d.product_id)?;
+ }
+ std::result::Result::Ok(())
+ }
+ }
+ }
+}
+
+#[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),
+ /// Unregister the given memory slot that was previously registereed with `RegisterMemory`.
+ UnregisterMemory(u32),
+ /// Allocate GPU buffer of a given size/format and register the memory into guest address space.
+ /// The response variant is `VmResponse::AllocateAndRegisterGpuMemory`
+ AllocateAndRegisterGpuMemory {
+ width: u32,
+ height: u32,
+ format: u32,
+ },
+}
+
+impl VmMemoryRequest {
+ /// Executes this request on the given Vm.
+ ///
+ /// # Arguments
+ /// * `vm` - The `Vm` to perform the request on.
+ /// * `allocator` - Used to allocate addresses.
+ ///
+ /// This does not return a result, instead encapsulating the success or failure in a
+ /// `VmMemoryResponse` with the intended purpose of sending the response back over the socket
+ /// that received this `VmMemoryResponse`.
+ pub fn execute(&self, vm: &mut Vm, sys_allocator: &mut SystemAllocator) -> VmMemoryResponse {
+ use self::VmMemoryRequest::*;
+ match *self {
+ RegisterMemory(ref fd, size) => match register_memory(vm, sys_allocator, fd, size) {
+ Ok((pfn, slot)) => VmMemoryResponse::RegisterMemory { pfn, slot },
+ Err(e) => VmMemoryResponse::Err(e),
+ },
+ UnregisterMemory(slot) => match vm.remove_device_memory(slot) {
+ Ok(_) => VmMemoryResponse::Ok,
+ Err(e) => VmMemoryResponse::Err(e),
+ },
+ AllocateAndRegisterGpuMemory {
+ width,
+ height,
+ format,
+ } => {
+ let (mut fd, desc) = match sys_allocator.gpu_memory_allocator() {
+ Some(gpu_allocator) => match gpu_allocator.allocate(width, height, format) {
+ Ok(v) => v,
+ Err(e) => return VmMemoryResponse::Err(e),
+ },
+ None => return VmMemoryResponse::Err(SysError::new(ENODEV)),
+ };
+ // Determine size of buffer using 0 byte seek from end. This is preferred over
+ // `stride * height` as it's not limited to packed pixel formats.
+ let size = match fd.seek(SeekFrom::End(0)) {
+ Ok(v) => v,
+ Err(e) => return VmMemoryResponse::Err(SysError::from(e)),
+ };
+ match register_memory(vm, sys_allocator, &fd, size as usize) {
+ Ok((pfn, slot)) => VmMemoryResponse::AllocateAndRegisterGpuMemory {
+ fd: MaybeOwnedFd::Owned(fd),
+ pfn,
+ slot,
+ desc,
+ },
+ Err(e) => VmMemoryResponse::Err(e),
+ }
+ }
+ }
+ }
+}
+
+#[derive(MsgOnSocket, Debug)]
+pub enum VmMemoryResponse {
+ /// The request to register memory into guest address space was successfully done at page frame
+ /// number `pfn` and memory slot number `slot`.
+ RegisterMemory {
+ pfn: u64,
+ slot: u32,
+ },
+ /// 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,
+ pfn: u64,
+ slot: u32,
+ desc: GpuMemoryDesc,
+ },
+ Ok,
+ Err(SysError),
+}
+
+pub type BalloonControlRequestSocket = MsgSocket<BalloonControlCommand, ()>;
+pub type BalloonControlResponseSocket = MsgSocket<(), BalloonControlCommand>;
+
+pub type DiskControlRequestSocket = MsgSocket<DiskControlCommand, DiskControlResult>;
+pub type DiskControlResponseSocket = MsgSocket<DiskControlResult, DiskControlCommand>;
+
+pub type UsbControlSocket = MsgSocket<UsbControlCommand, UsbControlResult>;
+
+pub type VmMemoryControlRequestSocket = MsgSocket<VmMemoryRequest, VmMemoryResponse>;
+pub type VmMemoryControlResponseSocket = MsgSocket<VmMemoryResponse, VmMemoryRequest>;
+
+pub type VmControlRequestSocket = MsgSocket<VmRequest, VmResponse>;
+pub type VmControlResponseSocket = MsgSocket<VmResponse, VmRequest>;
+
+/// A request to the main process to perform some operation on the VM.
+///
+/// Unless otherwise noted, each request should expect a `VmResponse::Ok` to be received on success.
+#[derive(MsgOnSocket, Debug)]
+pub enum VmRequest {
+ /// Break the VM's run loop and exit.
+ Exit,
+ /// Suspend the VM's VCPUs until resume.
+ Suspend,
+ /// Resume the VM's VCPUs that were previously suspended.
+ Resume,
+ /// Command for balloon driver.
+ BalloonCommand(BalloonControlCommand),
+ /// Send a command to a disk chosen by `disk_index`.
+ /// `disk_index` is a 0-based count of `--disk`, `--rwdisk`, and `-r` command-line options.
+ DiskCommand {
+ disk_index: usize,
+ command: DiskControlCommand,
+ },
+ /// Command to use controller.
+ UsbCommand(UsbControlCommand),
+}
+
+fn register_memory(
+ vm: &mut Vm,
+ allocator: &mut SystemAllocator,
+ fd: &dyn AsRawFd,
+ size: usize,
+) -> Result<(u64, u32)> {
+ let mmap = match MemoryMapping::from_fd(fd, size) {
+ Ok(v) => v,
+ Err(MmapError::SystemCallFailed(e)) => return Err(e),
+ _ => return Err(SysError::new(EINVAL)),
+ };
+ let alloc = allocator.get_anon_alloc();
+ let addr = match allocator.device_allocator().allocate(
+ size as u64,
+ alloc,
+ "vmcontrol_register_memory".to_string(),
+ ) {
+ Ok(a) => a,
+ Err(_) => return Err(SysError::new(EINVAL)),
+ };
+ let slot = match vm.add_device_memory(GuestAddress(addr), mmap, false, false) {
+ Ok(v) => v,
+ Err(e) => return Err(e),
+ };
+
+ Ok((addr >> 12, slot))
+}
+
+impl VmRequest {
+ /// Executes this request on the given Vm and other mutable state.
+ ///
+ /// This does not return a result, instead encapsulating the success or failure in a
+ /// `VmResponse` with the intended purpose of sending the response back over the socket that
+ /// received this `VmRequest`.
+ pub fn execute(
+ &self,
+ run_mode: &mut Option<VmRunMode>,
+ balloon_host_socket: &BalloonControlRequestSocket,
+ disk_host_sockets: &[DiskControlRequestSocket],
+ usb_control_socket: &UsbControlSocket,
+ ) -> VmResponse {
+ match *self {
+ VmRequest::Exit => {
+ *run_mode = Some(VmRunMode::Exiting);
+ VmResponse::Ok
+ }
+ VmRequest::Suspend => {
+ *run_mode = Some(VmRunMode::Suspending);
+ VmResponse::Ok
+ }
+ VmRequest::Resume => {
+ *run_mode = Some(VmRunMode::Running);
+ VmResponse::Ok
+ }
+ VmRequest::BalloonCommand(ref command) => match balloon_host_socket.send(command) {
+ Ok(_) => VmResponse::Ok,
+ Err(_) => VmResponse::Err(SysError::last()),
+ },
+ VmRequest::DiskCommand {
+ disk_index,
+ ref command,
+ } => {
+ // Forward the request to the block device process via its control socket.
+ if let Some(sock) = disk_host_sockets.get(disk_index) {
+ if let Err(e) = sock.send(command) {
+ error!("disk socket send failed: {}", e);
+ VmResponse::Err(SysError::new(EINVAL))
+ } else {
+ match sock.recv() {
+ Ok(DiskControlResult::Ok) => VmResponse::Ok,
+ Ok(DiskControlResult::Err(e)) => VmResponse::Err(e),
+ Err(e) => {
+ error!("disk socket recv failed: {}", e);
+ VmResponse::Err(SysError::new(EINVAL))
+ }
+ }
+ }
+ } else {
+ VmResponse::Err(SysError::new(ENODEV))
+ }
+ }
+ VmRequest::UsbCommand(ref cmd) => {
+ let res = usb_control_socket.send(cmd);
+ if let Err(e) = res {
+ error!("fail to send command to usb control socket: {}", e);
+ return VmResponse::Err(SysError::new(EIO));
+ }
+ match usb_control_socket.recv() {
+ Ok(response) => VmResponse::UsbResponse(response),
+ Err(e) => {
+ error!("fail to recv command from usb control socket: {}", e);
+ VmResponse::Err(SysError::new(EIO))
+ }
+ }
+ }
+ }
+ }
+}
+
+/// Indication of success or failure of a `VmRequest`.
+///
+/// Success is usually indicated `VmResponse::Ok` unless there is data associated with the response.
+#[derive(MsgOnSocket, Debug)]
+pub enum VmResponse {
+ /// Indicates the request was executed successfully.
+ Ok,
+ /// Indicates the request encountered some error during execution.
+ Err(SysError),
+ /// The request to register memory into guest address space was successfully done at page frame
+ /// number `pfn` and memory slot number `slot`.
+ RegisterMemory { pfn: u64, slot: u32 },
+ /// 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,
+ pfn: u64,
+ slot: u32,
+ desc: GpuMemoryDesc,
+ },
+ /// Results of usb control commands.
+ UsbResponse(UsbControlResult),
+}
+
+impl Display for VmResponse {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::VmResponse::*;
+
+ match self {
+ Ok => write!(f, "ok"),
+ Err(e) => write!(f, "error: {}", e),
+ RegisterMemory { pfn, slot } => write!(
+ f,
+ "memory registered to page frame number {:#x} and memory slot {}",
+ pfn, slot
+ ),
+ AllocateAndRegisterGpuMemory { pfn, slot, .. } => write!(
+ f,
+ "gpu memory allocated and registered to page frame number {:#x} and memory slot {}",
+ pfn, slot
+ ),
+ UsbResponse(result) => write!(f, "usb control request get result {:?}", result),
+ }
+ }
+}
diff --git a/x86_64/Cargo.toml b/x86_64/Cargo.toml
new file mode 100644
index 0000000..58d688b
--- /dev/null
+++ b/x86_64/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "x86_64"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+build = "build.rs"
+
+[dependencies]
+arch = { path = "../arch" }
+assertions = { path = "../assertions" }
+byteorder = "*"
+data_model = { path = "../data_model" }
+devices = { path = "../devices" }
+io_jail = { path = "../io_jail" }
+kernel_cmdline = { path = "../kernel_cmdline" }
+kernel_loader = { path = "../kernel_loader" }
+kvm = { path = "../kvm" }
+kvm_sys = { path = "../kvm_sys" }
+libc = "*"
+remain = "*"
+resources = { path = "../resources" }
+sync = { path = "../sync" }
+sys_util = { path = "../sys_util" }
+
+[build-dependencies]
+cc = "=1.0.25"
diff --git a/x86_64/build.rs b/x86_64/build.rs
new file mode 100644
index 0000000..5f2c1eb
--- /dev/null
+++ b/x86_64/build.rs
@@ -0,0 +1,7 @@
+// Copyright 2018 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.
+
+fn main() {
+ cc::Build::new().file("host_cpuid.c").compile("host_cpuid");
+}
diff --git a/x86_64/host_cpuid.c b/x86_64/host_cpuid.c
new file mode 100644
index 0000000..3230c90
--- /dev/null
+++ b/x86_64/host_cpuid.c
@@ -0,0 +1,11 @@
+// Copyright 2018 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 <stdint.h>
+
+void host_cpuid(uint32_t func, uint32_t func2, uint32_t *pEax,
+ uint32_t *pEbx, uint32_t *pEcx, uint32_t *pEdx) {
+ asm volatile("cpuid" : "=a"(*pEax), "=b"(*pEbx), "=c"(*pEcx), "=d"(*pEdx) :
+ "0"(func), "2"(func2) : "cc");
+}
diff --git a/x86_64/src/bootparam.rs b/x86_64/src/bootparam.rs
new file mode 100644
index 0000000..33bd90a
--- /dev/null
+++ b/x86_64/src/bootparam.rs
@@ -0,0 +1,437 @@
+// Copyright 2017 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.
+
+/*
+ * automatically generated by bindgen
+ * From chromeos-linux v4.19
+ * $ bindgen \
+ * --no-layout-tests --with-derive-default --no-doc-comments \
+ * --whitelist-type boot_params --whitelist-type setup_data \
+ * arch/x86/include/uapi/asm/bootparam.h
+ */
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData, [])
+ }
+ #[inline]
+ pub unsafe fn as_ptr(&self) -> *const T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
+ ::std::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self::new()
+ }
+}
+pub type __u8 = ::std::os::raw::c_uchar;
+pub type __u16 = ::std::os::raw::c_ushort;
+pub type __u32 = ::std::os::raw::c_uint;
+pub type __u64 = ::std::os::raw::c_ulonglong;
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct screen_info {
+ pub orig_x: __u8,
+ pub orig_y: __u8,
+ pub ext_mem_k: __u16,
+ pub orig_video_page: __u16,
+ pub orig_video_mode: __u8,
+ pub orig_video_cols: __u8,
+ pub flags: __u8,
+ pub unused2: __u8,
+ pub orig_video_ega_bx: __u16,
+ pub unused3: __u16,
+ pub orig_video_lines: __u8,
+ pub orig_video_isVGA: __u8,
+ pub orig_video_points: __u16,
+ pub lfb_width: __u16,
+ pub lfb_height: __u16,
+ pub lfb_depth: __u16,
+ pub lfb_base: __u32,
+ pub lfb_size: __u32,
+ pub cl_magic: __u16,
+ pub cl_offset: __u16,
+ pub lfb_linelength: __u16,
+ pub red_size: __u8,
+ pub red_pos: __u8,
+ pub green_size: __u8,
+ pub green_pos: __u8,
+ pub blue_size: __u8,
+ pub blue_pos: __u8,
+ pub rsvd_size: __u8,
+ pub rsvd_pos: __u8,
+ pub vesapm_seg: __u16,
+ pub vesapm_off: __u16,
+ pub pages: __u16,
+ pub vesa_attributes: __u16,
+ pub capabilities: __u32,
+ pub ext_lfb_base: __u32,
+ pub _reserved: [__u8; 2usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct apm_bios_info {
+ pub version: __u16,
+ pub cseg: __u16,
+ pub offset: __u32,
+ pub cseg_16: __u16,
+ pub dseg: __u16,
+ pub flags: __u16,
+ pub cseg_len: __u16,
+ pub cseg_16_len: __u16,
+ pub dseg_len: __u16,
+}
+#[repr(C, packed)]
+#[derive(Copy, Clone)]
+pub struct edd_device_params {
+ pub length: __u16,
+ pub info_flags: __u16,
+ pub num_default_cylinders: __u32,
+ pub num_default_heads: __u32,
+ pub sectors_per_track: __u32,
+ pub number_of_sectors: __u64,
+ pub bytes_per_sector: __u16,
+ pub dpte_ptr: __u32,
+ pub key: __u16,
+ pub device_path_info_length: __u8,
+ pub reserved2: __u8,
+ pub reserved3: __u16,
+ pub host_bus_type: [__u8; 4usize],
+ pub interface_type: [__u8; 8usize],
+ pub interface_path: edd_device_params__bindgen_ty_1,
+ pub device_path: edd_device_params__bindgen_ty_2,
+ pub reserved4: __u8,
+ pub checksum: __u8,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union edd_device_params__bindgen_ty_1 {
+ pub isa: edd_device_params__bindgen_ty_1__bindgen_ty_1,
+ pub pci: edd_device_params__bindgen_ty_1__bindgen_ty_2,
+ pub ibnd: edd_device_params__bindgen_ty_1__bindgen_ty_3,
+ pub xprs: edd_device_params__bindgen_ty_1__bindgen_ty_4,
+ pub htpt: edd_device_params__bindgen_ty_1__bindgen_ty_5,
+ pub unknown: edd_device_params__bindgen_ty_1__bindgen_ty_6,
+ _bindgen_union_align: [u8; 8usize],
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_1__bindgen_ty_1 {
+ pub base_address: __u16,
+ pub reserved1: __u16,
+ pub reserved2: __u32,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_1__bindgen_ty_2 {
+ pub bus: __u8,
+ pub slot: __u8,
+ pub function: __u8,
+ pub channel: __u8,
+ pub reserved: __u32,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_1__bindgen_ty_3 {
+ pub reserved: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_1__bindgen_ty_4 {
+ pub reserved: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_1__bindgen_ty_5 {
+ pub reserved: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_1__bindgen_ty_6 {
+ pub reserved: __u64,
+}
+impl Default for edd_device_params__bindgen_ty_1 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union edd_device_params__bindgen_ty_2 {
+ pub ata: edd_device_params__bindgen_ty_2__bindgen_ty_1,
+ pub atapi: edd_device_params__bindgen_ty_2__bindgen_ty_2,
+ pub scsi: edd_device_params__bindgen_ty_2__bindgen_ty_3,
+ pub usb: edd_device_params__bindgen_ty_2__bindgen_ty_4,
+ pub i1394: edd_device_params__bindgen_ty_2__bindgen_ty_5,
+ pub fibre: edd_device_params__bindgen_ty_2__bindgen_ty_6,
+ pub i2o: edd_device_params__bindgen_ty_2__bindgen_ty_7,
+ pub raid: edd_device_params__bindgen_ty_2__bindgen_ty_8,
+ pub sata: edd_device_params__bindgen_ty_2__bindgen_ty_9,
+ pub unknown: edd_device_params__bindgen_ty_2__bindgen_ty_10,
+ _bindgen_union_align: [u8; 16usize],
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_1 {
+ pub device: __u8,
+ pub reserved1: __u8,
+ pub reserved2: __u16,
+ pub reserved3: __u32,
+ pub reserved4: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_2 {
+ pub device: __u8,
+ pub lun: __u8,
+ pub reserved1: __u8,
+ pub reserved2: __u8,
+ pub reserved3: __u32,
+ pub reserved4: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_3 {
+ pub id: __u16,
+ pub lun: __u64,
+ pub reserved1: __u16,
+ pub reserved2: __u32,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_4 {
+ pub serial_number: __u64,
+ pub reserved: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_5 {
+ pub eui: __u64,
+ pub reserved: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_6 {
+ pub wwid: __u64,
+ pub lun: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_7 {
+ pub identity_tag: __u64,
+ pub reserved: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_8 {
+ pub array_number: __u32,
+ pub reserved1: __u32,
+ pub reserved2: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_9 {
+ pub device: __u8,
+ pub reserved1: __u8,
+ pub reserved2: __u16,
+ pub reserved3: __u32,
+ pub reserved4: __u64,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct edd_device_params__bindgen_ty_2__bindgen_ty_10 {
+ pub reserved1: __u64,
+ pub reserved2: __u64,
+}
+impl Default for edd_device_params__bindgen_ty_2 {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+impl Default for edd_device_params {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C, packed)]
+#[derive(Copy, Clone)]
+pub struct edd_info {
+ pub device: __u8,
+ pub version: __u8,
+ pub interface_support: __u16,
+ pub legacy_max_cylinder: __u16,
+ pub legacy_max_head: __u8,
+ pub legacy_sectors_per_track: __u8,
+ pub params: edd_device_params,
+}
+impl Default for edd_info {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct ist_info {
+ pub signature: __u32,
+ pub command: __u32,
+ pub event: __u32,
+ pub perf_level: __u32,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct edid_info {
+ pub dummy: [::std::os::raw::c_uchar; 128usize],
+}
+impl Default for edid_info {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct setup_data {
+ pub next: __u64,
+ pub type_: __u32,
+ pub len: __u32,
+ pub data: __IncompleteArrayField<__u8>,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct setup_header {
+ pub setup_sects: __u8,
+ pub root_flags: __u16,
+ pub syssize: __u32,
+ pub ram_size: __u16,
+ pub vid_mode: __u16,
+ pub root_dev: __u16,
+ pub boot_flag: __u16,
+ pub jump: __u16,
+ pub header: __u32,
+ pub version: __u16,
+ pub realmode_swtch: __u32,
+ pub start_sys_seg: __u16,
+ pub kernel_version: __u16,
+ pub type_of_loader: __u8,
+ pub loadflags: __u8,
+ pub setup_move_size: __u16,
+ pub code32_start: __u32,
+ pub ramdisk_image: __u32,
+ pub ramdisk_size: __u32,
+ pub bootsect_kludge: __u32,
+ pub heap_end_ptr: __u16,
+ pub ext_loader_ver: __u8,
+ pub ext_loader_type: __u8,
+ pub cmd_line_ptr: __u32,
+ pub initrd_addr_max: __u32,
+ pub kernel_alignment: __u32,
+ pub relocatable_kernel: __u8,
+ pub min_alignment: __u8,
+ pub xloadflags: __u16,
+ pub cmdline_size: __u32,
+ pub hardware_subarch: __u32,
+ pub hardware_subarch_data: __u64,
+ pub payload_offset: __u32,
+ pub payload_length: __u32,
+ pub setup_data: __u64,
+ pub pref_address: __u64,
+ pub init_size: __u32,
+ pub handover_offset: __u32,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct sys_desc_table {
+ pub length: __u16,
+ pub table: [__u8; 14usize],
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct olpc_ofw_header {
+ pub ofw_magic: __u32,
+ pub ofw_version: __u32,
+ pub cif_handler: __u32,
+ pub irq_desc_table: __u32,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct efi_info {
+ pub efi_loader_signature: __u32,
+ pub efi_systab: __u32,
+ pub efi_memdesc_size: __u32,
+ pub efi_memdesc_version: __u32,
+ pub efi_memmap: __u32,
+ pub efi_memmap_size: __u32,
+ pub efi_systab_hi: __u32,
+ pub efi_memmap_hi: __u32,
+}
+#[repr(C, packed)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct boot_e820_entry {
+ pub addr: __u64,
+ pub size: __u64,
+ pub type_: __u32,
+}
+#[repr(C, packed)]
+#[derive(Copy, Clone)]
+pub struct boot_params {
+ pub screen_info: screen_info,
+ pub apm_bios_info: apm_bios_info,
+ pub _pad2: [__u8; 4usize],
+ pub tboot_addr: __u64,
+ pub ist_info: ist_info,
+ pub _pad3: [__u8; 16usize],
+ pub hd0_info: [__u8; 16usize],
+ pub hd1_info: [__u8; 16usize],
+ pub sys_desc_table: sys_desc_table,
+ pub olpc_ofw_header: olpc_ofw_header,
+ pub ext_ramdisk_image: __u32,
+ pub ext_ramdisk_size: __u32,
+ pub ext_cmd_line_ptr: __u32,
+ pub _pad4: [__u8; 116usize],
+ pub edid_info: edid_info,
+ pub efi_info: efi_info,
+ pub alt_mem_k: __u32,
+ pub scratch: __u32,
+ pub e820_entries: __u8,
+ pub eddbuf_entries: __u8,
+ pub edd_mbr_sig_buf_entries: __u8,
+ pub kbd_status: __u8,
+ pub secure_boot: __u8,
+ pub _pad5: [__u8; 2usize],
+ pub sentinel: __u8,
+ pub _pad6: [__u8; 1usize],
+ pub hdr: setup_header,
+ pub _pad7: [__u8; 40usize],
+ pub edd_mbr_sig_buffer: [__u32; 16usize],
+ pub e820_table: [boot_e820_entry; 128usize],
+ pub _pad8: [__u8; 48usize],
+ pub eddbuf: [edd_info; 6usize],
+ pub _pad9: [__u8; 276usize],
+}
+impl Default for boot_params {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
diff --git a/x86_64/src/bzimage.rs b/x86_64/src/bzimage.rs
new file mode 100644
index 0000000..d43d405
--- /dev/null
+++ b/x86_64/src/bzimage.rs
@@ -0,0 +1,102 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Loader for bzImage-format Linux kernels as described in
+// https://www.kernel.org/doc/Documentation/x86/boot.txt
+
+use std::fmt::{self, Display};
+use std::io::{Read, Seek, SeekFrom};
+use std::os::unix::io::AsRawFd;
+
+use sys_util::{GuestAddress, GuestMemory};
+
+use crate::bootparam::boot_params;
+
+#[derive(Debug, PartialEq)]
+pub enum Error {
+ BadSignature,
+ InvalidSetupSects,
+ InvalidSysSize,
+ ReadBootParams,
+ ReadKernelImage,
+ SeekBootParams,
+ SeekKernelStart,
+}
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ let description = match self {
+ BadSignature => "bad kernel header signature",
+ InvalidSetupSects => "invalid setup_sects value",
+ InvalidSysSize => "invalid syssize value",
+ ReadBootParams => "unable to read boot_params",
+ ReadKernelImage => "unable to read kernel image",
+ SeekBootParams => "unable to seek to boot_params",
+ SeekKernelStart => "unable to seek to kernel start",
+ };
+
+ write!(f, "bzImage loader: {}", description)
+ }
+}
+
+/// Loads a kernel from a bzImage to a slice
+///
+/// # Arguments
+///
+/// * `guest_mem` - The guest memory region the kernel is written to.
+/// * `kernel_start` - The offset into `guest_mem` at which to load the kernel.
+/// * `kernel_image` - Input bzImage.
+pub fn load_bzimage<F>(
+ guest_mem: &GuestMemory,
+ kernel_start: GuestAddress,
+ kernel_image: &mut F,
+) -> Result<(boot_params, u64)>
+where
+ F: Read + Seek + AsRawFd,
+{
+ let mut params: boot_params = Default::default();
+ kernel_image
+ .seek(SeekFrom::Start(0))
+ .map_err(|_| Error::SeekBootParams)?;
+ unsafe {
+ // read_struct is safe when reading a POD struct. It can be used and dropped without issue.
+ sys_util::read_struct(kernel_image, &mut params).map_err(|_| Error::ReadBootParams)?;
+ }
+
+ // bzImage header signature "HdrS"
+ if params.hdr.header != 0x53726448 {
+ return Err(Error::BadSignature);
+ }
+
+ let setup_sects = if params.hdr.setup_sects == 0 {
+ 4u64
+ } else {
+ params.hdr.setup_sects as u64
+ };
+
+ let kernel_offset = setup_sects
+ .checked_add(1)
+ .ok_or(Error::InvalidSetupSects)?
+ .checked_mul(512)
+ .ok_or(Error::InvalidSetupSects)?;
+ let kernel_size = (params.hdr.syssize as usize)
+ .checked_mul(16)
+ .ok_or(Error::InvalidSysSize)?;
+
+ kernel_image
+ .seek(SeekFrom::Start(kernel_offset))
+ .map_err(|_| Error::SeekKernelStart)?;
+
+ // Load the whole kernel image to kernel_start
+ guest_mem
+ .read_to_memory(kernel_start, kernel_image, kernel_size)
+ .map_err(|_| Error::ReadKernelImage)?;
+
+ Ok((params, kernel_start.offset() + kernel_size as u64))
+}
diff --git a/x86_64/src/cpuid.rs b/x86_64/src/cpuid.rs
new file mode 100644
index 0000000..9f76550
--- /dev/null
+++ b/x86_64/src/cpuid.rs
@@ -0,0 +1,150 @@
+// Copyright 2017 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::fmt::{self, Display};
+use std::result;
+
+use kvm;
+use sys_util;
+
+#[derive(Debug, PartialEq)]
+pub enum Error {
+ GetSupportedCpusFailed(sys_util::Error),
+ SetSupportedCpusFailed(sys_util::Error),
+}
+pub type Result<T> = result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ GetSupportedCpusFailed(e) => write!(f, "GetSupportedCpus ioctl failed: {}", e),
+ SetSupportedCpusFailed(e) => write!(f, "SetSupportedCpus ioctl failed: {}", e),
+ }
+ }
+}
+
+// This function is implemented in C because stable rustc does not
+// support inline assembly.
+extern "C" {
+ fn host_cpuid(
+ func: u32,
+ func2: u32,
+ rEax: *mut u32,
+ rEbx: *mut u32,
+ rEcx: *mut u32,
+ rEdx: *mut u32,
+ ) -> ();
+}
+
+// CPUID bits in ebx, ecx, and edx.
+const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
+const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
+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_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.
+
+fn filter_cpuid(cpu_id: u64, cpu_count: u64, kvm_cpuid: &mut kvm::CpuId) -> Result<()> {
+ let entries = kvm_cpuid.mut_entries_slice();
+
+ for entry in entries {
+ match entry.function {
+ 1 => {
+ // X86 hypervisor feature
+ if entry.index == 0 {
+ entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
+ }
+ entry.ebx = (cpu_id << EBX_CPUID_SHIFT) as u32
+ | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
+ if cpu_count > 1 {
+ entry.ebx |= (cpu_count as u32) << EBX_CPU_COUNT_SHIFT;
+ entry.edx |= 1 << EDX_HTT_SHIFT;
+ }
+ }
+ 2 | 0x80000005 | 0x80000006 => unsafe {
+ host_cpuid(
+ entry.function,
+ 0,
+ &mut entry.eax as *mut u32,
+ &mut entry.ebx as *mut u32,
+ &mut entry.ecx as *mut u32,
+ &mut entry.edx as *mut u32,
+ );
+ },
+ 4 => {
+ unsafe {
+ host_cpuid(
+ entry.function,
+ entry.index,
+ &mut entry.eax as *mut u32,
+ &mut entry.ebx as *mut u32,
+ &mut entry.ecx as *mut u32,
+ &mut entry.edx as *mut u32,
+ );
+ }
+ entry.eax &= !0xFC000000;
+ }
+ 6 => {
+ // Clear X86 EPB feature. No frequency selection in the hypervisor.
+ entry.ecx &= !(1 << ECX_EPB_SHIFT);
+ }
+ _ => (),
+ }
+ }
+
+ Ok(())
+}
+
+/// Sets up the cpuid entries for the given vcpu. Can fail if there are too many CPUs specified or
+/// if an ioctl returns an error.
+///
+/// # Arguments
+///
+/// * `kvm` - `Kvm` structure created with KVM_CREATE_VM ioctl.
+/// * `vcpu` - `Vcpu` for setting CPU ID.
+/// * `cpu_id` - The index of the CPU `vcpu` is for.
+/// * `nrcpus` - The number of vcpus being used by this VM.
+pub fn setup_cpuid(kvm: &kvm::Kvm, vcpu: &kvm::Vcpu, cpu_id: u64, nrcpus: u64) -> Result<()> {
+ let mut kvm_cpuid = kvm
+ .get_supported_cpuid()
+ .map_err(Error::GetSupportedCpusFailed)?;
+
+ filter_cpuid(cpu_id, nrcpus, &mut kvm_cpuid)?;
+
+ vcpu.set_cpuid2(&kvm_cpuid)
+ .map_err(Error::SetSupportedCpusFailed)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn feature_and_vendor_name() {
+ let mut cpuid = kvm::CpuId::new(2);
+
+ let entries = cpuid.mut_entries_slice();
+ entries[0].function = 0;
+ entries[1].function = 1;
+ entries[1].ecx = 0x10;
+ entries[1].edx = 0;
+ assert_eq!(Ok(()), filter_cpuid(1, 2, &mut cpuid));
+
+ let entries = cpuid.mut_entries_slice();
+ assert_eq!(entries[0].function, 0);
+ assert_eq!(1, (entries[1].ebx >> EBX_CPUID_SHIFT) & 0x000000ff);
+ assert_eq!(2, (entries[1].ebx >> EBX_CPU_COUNT_SHIFT) & 0x000000ff);
+ assert_eq!(
+ EBX_CLFLUSH_CACHELINE,
+ (entries[1].ebx >> EBX_CLFLUSH_SIZE_SHIFT) & 0x000000ff
+ );
+ assert_ne!(0, entries[1].ecx & (1 << ECX_HYPERVISOR_SHIFT));
+ assert_ne!(0, entries[1].edx & (1 << EDX_HTT_SHIFT));
+ }
+}
diff --git a/x86_64/src/fdt.rs b/x86_64/src/fdt.rs
new file mode 100644
index 0000000..655a812
--- /dev/null
+++ b/x86_64/src/fdt.rs
@@ -0,0 +1,84 @@
+// Copyright 2018 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 arch::android::create_android_fdt;
+use arch::fdt::{begin_node, end_node, finish_fdt, start_fdt, Error};
+use data_model::DataInit;
+use std::fs::File;
+use std::mem;
+use sys_util::{GuestAddress, GuestMemory};
+
+use crate::bootparam::setup_data;
+use crate::{SETUP_DTB, X86_64_FDT_MAX_SIZE};
+
+// Like `setup_data` without the incomplete array field at the end, which allows us to safely
+// implement Copy, Clone, and DataInit.
+#[repr(C)]
+#[derive(Copy, Clone, Default)]
+struct setup_data_hdr {
+ pub next: u64,
+ pub type_: u32,
+ pub len: u32,
+}
+
+unsafe impl DataInit for setup_data_hdr {}
+
+/// Creates a flattened device tree containing all of the parameters for the
+/// kernel and loads it into the guest memory at the specified offset.
+///
+/// # Arguments
+///
+/// * `fdt_max_size` - The amount of space reserved for the device tree
+/// * `guest_mem` - The guest memory object
+/// * `fdt_load_offset` - The offset into physical memory for the device tree
+/// * `android_fstab` - the File object for the android fstab
+pub fn create_fdt(
+ fdt_max_size: usize,
+ guest_mem: &GuestMemory,
+ fdt_load_offset: u64,
+ android_fstab: File,
+) -> Result<usize, Error> {
+ // Reserve space for the setup_data
+ let fdt_data_size = fdt_max_size - mem::size_of::<setup_data>();
+
+ let mut fdt = vec![0; fdt_data_size];
+ start_fdt(&mut fdt, fdt_data_size)?;
+
+ // The whole thing is put into one giant node with some top level properties
+ begin_node(&mut fdt, "")?;
+ create_android_fdt(&mut fdt, android_fstab)?;
+ end_node(&mut fdt)?;
+
+ // Allocate another buffer so we can format and then write fdt to guest
+ let mut fdt_final = vec![0; fdt_data_size];
+ finish_fdt(&mut fdt, &mut fdt_final, fdt_data_size)?;
+
+ assert_eq!(
+ mem::size_of::<setup_data>(),
+ mem::size_of::<setup_data_hdr>()
+ );
+ let mut hdr: setup_data_hdr = Default::default();
+ hdr.next = 0;
+ hdr.type_ = SETUP_DTB;
+ hdr.len = fdt_data_size as u32;
+
+ assert!(fdt_data_size as u64 <= X86_64_FDT_MAX_SIZE);
+
+ let fdt_address = GuestAddress(fdt_load_offset);
+ guest_mem
+ .checked_offset(fdt_address, fdt_data_size as u64)
+ .ok_or(Error::FdtGuestMemoryWriteError)?;
+ guest_mem
+ .write_obj_at_addr(hdr, fdt_address)
+ .map_err(|_| Error::FdtGuestMemoryWriteError)?;
+
+ let fdt_data_address = GuestAddress(fdt_load_offset + mem::size_of::<setup_data>() as u64);
+ let written = guest_mem
+ .write_at_addr(fdt_final.as_slice(), fdt_data_address)
+ .map_err(|_| Error::FdtGuestMemoryWriteError)?;
+ if written < fdt_data_size {
+ return Err(Error::FdtGuestMemoryWriteError);
+ }
+ Ok(fdt_data_size)
+}
diff --git a/x86_64/src/gdt.rs b/x86_64/src/gdt.rs
new file mode 100644
index 0000000..7eb1ff7
--- /dev/null
+++ b/x86_64/src/gdt.rs
@@ -0,0 +1,112 @@
+// Copyright 2017 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.
+//
+// For GDT details see arch/x86/include/asm/segment.h
+
+use kvm_sys::kvm_segment;
+
+/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
+pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
+ ((((base as u64) & 0xff000000u64) << (56 - 24))
+ | (((flags as u64) & 0x0000f0ffu64) << 40)
+ | (((limit as u64) & 0x000f0000u64) << (48 - 16))
+ | (((base as u64) & 0x00ffffffu64) << 16)
+ | ((limit as u64) & 0x0000ffffu64))
+}
+
+fn get_base(entry: u64) -> u64 {
+ ((((entry) & 0xFF00000000000000) >> 32)
+ | (((entry) & 0x000000FF00000000) >> 16)
+ | (((entry) & 0x00000000FFFF0000) >> 16))
+}
+
+fn get_limit(entry: u64) -> u32 {
+ ((((entry) & 0x000F000000000000) >> 32) | ((entry) & 0x000000000000FFFF)) as u32
+}
+
+fn get_g(entry: u64) -> u8 {
+ ((entry & 0x0080000000000000) >> 55) as u8
+}
+
+fn get_db(entry: u64) -> u8 {
+ ((entry & 0x0040000000000000) >> 54) as u8
+}
+
+fn get_l(entry: u64) -> u8 {
+ ((entry & 0x0020000000000000) >> 53) as u8
+}
+
+fn get_avl(entry: u64) -> u8 {
+ ((entry & 0x0010000000000000) >> 52) as u8
+}
+
+fn get_p(entry: u64) -> u8 {
+ ((entry & 0x0000800000000000) >> 47) as u8
+}
+
+fn get_dpl(entry: u64) -> u8 {
+ ((entry & 0x0000600000000000) >> 45) as u8
+}
+
+fn get_s(entry: u64) -> u8 {
+ ((entry & 0x0000100000000000) >> 44) as u8
+}
+
+fn get_type(entry: u64) -> u8 {
+ ((entry & 0x00000F0000000000) >> 40) as u8
+}
+
+/// Automatically build the kvm struct for SET_SREGS from the kernel bit fields.
+///
+/// # Arguments
+///
+/// * `entry` - The gdt entry.
+/// * `table_index` - Index of the entry in the gdt table.
+pub fn kvm_segment_from_gdt(entry: u64, table_index: u8) -> kvm_segment {
+ kvm_segment {
+ base: get_base(entry),
+ limit: get_limit(entry),
+ selector: (table_index * 8) as u16,
+ type_: get_type(entry),
+ present: get_p(entry),
+ dpl: get_dpl(entry),
+ db: get_db(entry),
+ s: get_s(entry),
+ l: get_l(entry),
+ g: get_g(entry),
+ avl: get_avl(entry),
+ padding: 0,
+ unusable: match get_p(entry) {
+ 0 => 1,
+ _ => 0,
+ },
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn field_parse() {
+ let gdt = gdt_entry(0xA09B, 0x100000, 0xfffff);
+ let seg = kvm_segment_from_gdt(gdt, 0);
+ // 0xA09B
+ // 'A'
+ assert_eq!(0x1, seg.g);
+ assert_eq!(0x0, seg.db);
+ assert_eq!(0x1, seg.l);
+ assert_eq!(0x0, seg.avl);
+ // '9'
+ assert_eq!(0x1, seg.present);
+ assert_eq!(0x0, seg.dpl);
+ assert_eq!(0x1, seg.s);
+ // 'B'
+ assert_eq!(0xB, seg.type_);
+ // base and limit
+ assert_eq!(0x100000, seg.base);
+ assert_eq!(0xfffff, seg.limit);
+ assert_eq!(0x0, seg.unusable);
+ }
+}
diff --git a/x86_64/src/interrupts.rs b/x86_64/src/interrupts.rs
new file mode 100644
index 0000000..6c2ebfd
--- /dev/null
+++ b/x86_64/src/interrupts.rs
@@ -0,0 +1,89 @@
+// Copyright 2017 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::fmt::{self, Display};
+use std::io::Cursor;
+use std::mem;
+use std::result;
+
+use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
+
+use kvm;
+use kvm_sys::kvm_lapic_state;
+use sys_util;
+
+#[derive(Debug)]
+pub enum Error {
+ GetLapic(sys_util::Error),
+ SetLapic(sys_util::Error),
+}
+pub type Result<T> = result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ GetLapic(e) => write!(f, "GetLapic ioctl failed: {}", e),
+ SetLapic(e) => write!(f, "SetLapic ioctl failed: {}", e),
+ }
+ }
+}
+
+// Defines poached from apicdef.h kernel header.
+const APIC_LVT0: usize = 0x350;
+const APIC_LVT1: usize = 0x360;
+const APIC_MODE_NMI: u32 = 0x4;
+const APIC_MODE_EXTINT: u32 = 0x7;
+
+fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 {
+ let sliceu8 = unsafe {
+ // This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
+ // Cursors are only readable on arrays of u8, not i8(c_char).
+ mem::transmute::<&[i8], &[u8]>(&klapic.regs[reg_offset..])
+ };
+ let mut reader = Cursor::new(sliceu8);
+ // read_u32 can't fail if the offsets defined above are correct.
+ reader.read_u32::<LittleEndian>().unwrap()
+}
+
+fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
+ let sliceu8 = unsafe {
+ // This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
+ // Cursors are only readable on arrays of u8, not i8(c_char).
+ mem::transmute::<&mut [i8], &mut [u8]>(&mut klapic.regs[reg_offset..])
+ };
+ let mut writer = Cursor::new(sliceu8);
+ // read_u32 can't fail if the offsets defined above are correct.
+ writer.write_u32::<LittleEndian>(value).unwrap()
+}
+
+fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
+ (((reg) & !0x700) | ((mode) << 8))
+}
+
+/// Configures LAPICs. LAPIC0 is set for external interrupts, LAPIC1 is set for NMI.
+///
+/// # Arguments
+/// * `vcpu` - The VCPU object to configure.
+pub fn set_lint(vcpu: &kvm::Vcpu) -> Result<()> {
+ let mut klapic = vcpu.get_lapic().map_err(Error::GetLapic)?;
+
+ let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0);
+ set_klapic_reg(
+ &mut klapic,
+ APIC_LVT0,
+ set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT),
+ );
+ let lvt_lint1 = get_klapic_reg(&klapic, APIC_LVT1);
+ set_klapic_reg(
+ &mut klapic,
+ APIC_LVT1,
+ set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI),
+ );
+
+ vcpu.set_lapic(&klapic).map_err(Error::SetLapic)
+}
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
new file mode 100644
index 0000000..a047b8c
--- /dev/null
+++ b/x86_64/src/lib.rs
@@ -0,0 +1,810 @@
+// Copyright 2017 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.
+
+mod fdt;
+
+const E820_RAM: u32 = 1;
+const SETUP_DTB: u32 = 2;
+const X86_64_FDT_MAX_SIZE: u64 = 0x200000;
+
+#[allow(dead_code)]
+#[allow(non_upper_case_globals)]
+#[allow(non_camel_case_types)]
+#[allow(non_snake_case)]
+mod bootparam;
+
+// boot_params is just a series of ints, it is safe to initialize it.
+unsafe impl data_model::DataInit for bootparam::boot_params {}
+
+#[allow(dead_code)]
+#[allow(non_upper_case_globals)]
+mod msr_index;
+
+#[allow(dead_code)]
+#[allow(non_upper_case_globals)]
+#[allow(non_camel_case_types)]
+#[allow(clippy::all)]
+mod mpspec;
+// These mpspec types are only data, reading them from data is a safe initialization.
+unsafe impl data_model::DataInit for mpspec::mpc_bus {}
+unsafe impl data_model::DataInit for mpspec::mpc_cpu {}
+unsafe impl data_model::DataInit for mpspec::mpc_intsrc {}
+unsafe impl data_model::DataInit for mpspec::mpc_ioapic {}
+unsafe impl data_model::DataInit for mpspec::mpc_table {}
+unsafe impl data_model::DataInit for mpspec::mpc_lintsrc {}
+unsafe impl data_model::DataInit for mpspec::mpf_intel {}
+
+mod bzimage;
+mod cpuid;
+mod gdt;
+mod interrupts;
+mod mptable;
+mod regs;
+mod smbios;
+
+use std::collections::BTreeMap;
+use std::error::Error as StdError;
+use std::ffi::{CStr, CString};
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::{self, Seek};
+use std::mem;
+use std::sync::Arc;
+
+use crate::bootparam::boot_params;
+use arch::{RunnableLinuxVm, VmComponents, VmImage};
+use devices::{get_serial_tty_string, PciConfigIo, PciDevice, PciInterruptPin, SerialParameters};
+use io_jail::Minijail;
+use kvm::*;
+use remain::sorted;
+use resources::SystemAllocator;
+use sync::Mutex;
+use sys_util::{Clock, EventFd, GuestAddress, GuestMemory, GuestMemoryError};
+
+#[sorted]
+#[derive(Debug)]
+pub enum Error {
+ CloneEventFd(sys_util::Error),
+ Cmdline(kernel_cmdline::Error),
+ ConfigureSystem,
+ CreateDevices(Box<dyn StdError>),
+ CreateEventFd(sys_util::Error),
+ CreateFdt(arch::fdt::Error),
+ CreateIrqChip(sys_util::Error),
+ CreateKvm(sys_util::Error),
+ CreatePciRoot(arch::DeviceRegistrationError),
+ CreatePit(sys_util::Error),
+ CreatePitDevice(devices::PitError),
+ CreateSerialDevices(arch::DeviceRegistrationError),
+ CreateSocket(io::Error),
+ CreateVcpu(sys_util::Error),
+ CreateVm(sys_util::Error),
+ E820Configuration,
+ KernelOffsetPastEnd,
+ LoadBios(io::Error),
+ LoadBzImage(bzimage::Error),
+ LoadCmdline(kernel_loader::Error),
+ LoadInitrd(arch::LoadImageError),
+ LoadKernel(kernel_loader::Error),
+ RegisterIrqfd(sys_util::Error),
+ RegisterVsock(arch::DeviceRegistrationError),
+ SetLint(interrupts::Error),
+ SetTssAddr(sys_util::Error),
+ SetupCpuid(cpuid::Error),
+ SetupFpu(regs::Error),
+ SetupGuestMemory(GuestMemoryError),
+ SetupMptable(mptable::Error),
+ SetupMsrs(regs::Error),
+ SetupRegs(regs::Error),
+ SetupSmbios(smbios::Error),
+ SetupSregs(regs::Error),
+ ZeroPagePastRamEnd,
+ ZeroPageSetup,
+}
+
+impl Display for Error {
+ #[remain::check]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ #[sorted]
+ match self {
+ CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
+ Cmdline(e) => write!(f, "the given kernel command line was invalid: {}", e),
+ ConfigureSystem => write!(f, "error configuring the system"),
+ CreateDevices(e) => write!(f, "error creating devices: {}", e),
+ CreateEventFd(e) => write!(f, "unable to make an EventFd: {}", e),
+ CreateFdt(e) => write!(f, "failed to create fdt: {}", e),
+ CreateIrqChip(e) => write!(f, "failed to create irq chip: {}", e),
+ CreateKvm(e) => write!(f, "failed to open /dev/kvm: {}", e),
+ CreatePciRoot(e) => write!(f, "failed to create a PCI root hub: {}", e),
+ CreatePit(e) => write!(f, "unable to create PIT: {}", e),
+ CreatePitDevice(e) => write!(f, "unable to make PIT device: {}", e),
+ CreateSerialDevices(e) => write!(f, "unable to create serial devices: {}", e),
+ CreateSocket(e) => write!(f, "failed to create socket: {}", e),
+ CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
+ CreateVm(e) => write!(f, "failed to create VM: {}", e),
+ E820Configuration => write!(f, "invalid e820 setup params"),
+ KernelOffsetPastEnd => write!(f, "the kernel extends past the end of RAM"),
+ LoadBios(e) => write!(f, "error loading bios: {}", e),
+ LoadBzImage(e) => write!(f, "error loading kernel bzImage: {}", e),
+ 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),
+ RegisterIrqfd(e) => write!(f, "error registering an IrqFd: {}", e),
+ RegisterVsock(e) => write!(f, "error registering virtual socket device: {}", 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),
+ SetupFpu(e) => write!(f, "failed to set up FPU: {}", e),
+ SetupGuestMemory(e) => write!(f, "failed to set up guest memory: {}", e),
+ SetupMptable(e) => write!(f, "failed to set up mptable: {}", e),
+ SetupMsrs(e) => write!(f, "failed to set up MSRs: {}", e),
+ 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),
+ ZeroPagePastRamEnd => write!(f, "the zero page extends past the end of guest_mem"),
+ ZeroPageSetup => write!(f, "error writing the zero page of guest memory"),
+ }
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+pub struct X8664arch;
+
+const BOOT_STACK_POINTER: u64 = 0x8000;
+const MEM_32BIT_GAP_SIZE: u64 = (768 << 20);
+const FIRST_ADDR_PAST_32BITS: u64 = (1 << 32);
+const KERNEL_64BIT_ENTRY_OFFSET: u64 = 0x200;
+const ZERO_PAGE_OFFSET: u64 = 0x7000;
+/// The x86 reset vector for i386+ and x86_64 puts the processor into an "unreal mode" where it
+/// can access the last 1 MB of the 32-bit address space in 16-bit mode, and starts the instruction
+/// pointer at the effective physical address 0xFFFFFFF0.
+const BIOS_LEN: usize = 1 << 20;
+const BIOS_START: u64 = FIRST_ADDR_PAST_32BITS - (BIOS_LEN as u64);
+
+const KERNEL_START_OFFSET: u64 = 0x200000;
+const CMDLINE_OFFSET: u64 = 0x20000;
+const CMDLINE_MAX_SIZE: u64 = KERNEL_START_OFFSET - CMDLINE_OFFSET;
+const X86_64_SERIAL_1_3_IRQ: u32 = 4;
+const X86_64_SERIAL_2_4_IRQ: u32 = 3;
+const X86_64_IRQ_BASE: u32 = 5;
+
+fn configure_system(
+ guest_mem: &GuestMemory,
+ _mem_size: u64,
+ kernel_addr: GuestAddress,
+ cmdline_addr: GuestAddress,
+ cmdline_size: usize,
+ num_cpus: u8,
+ pci_irqs: Vec<(u32, PciInterruptPin)>,
+ setup_data: Option<GuestAddress>,
+ initrd: Option<(GuestAddress, usize)>,
+ mut params: boot_params,
+) -> Result<()> {
+ const EBDA_START: u64 = 0x0009fc00;
+ const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
+ const KERNEL_HDR_MAGIC: u32 = 0x53726448;
+ const KERNEL_LOADER_OTHER: u8 = 0xff;
+ const KERNEL_MIN_ALIGNMENT_BYTES: u32 = 0x1000000; // Must be non-zero.
+ let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS);
+ let end_32bit_gap_start = GuestAddress(FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE);
+
+ // Note that this puts the mptable at 0x0 in guest physical memory.
+ mptable::setup_mptable(guest_mem, num_cpus, pci_irqs).map_err(Error::SetupMptable)?;
+
+ smbios::setup_smbios(guest_mem).map_err(Error::SetupSmbios)?;
+
+ params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
+ params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
+ params.hdr.header = KERNEL_HDR_MAGIC;
+ params.hdr.cmd_line_ptr = cmdline_addr.offset() as u32;
+ params.hdr.cmdline_size = cmdline_size as u32;
+ params.hdr.kernel_alignment = KERNEL_MIN_ALIGNMENT_BYTES;
+ if let Some(setup_data) = setup_data {
+ params.hdr.setup_data = setup_data.offset();
+ }
+ if let Some((initrd_addr, initrd_size)) = initrd {
+ params.hdr.ramdisk_image = initrd_addr.offset() as u32;
+ params.hdr.ramdisk_size = initrd_size as u32;
+ }
+
+ add_e820_entry(&mut params, 0, EBDA_START, E820_RAM)?;
+
+ let mem_end = guest_mem.end_addr();
+ if mem_end < end_32bit_gap_start {
+ add_e820_entry(
+ &mut params,
+ kernel_addr.offset() as u64,
+ mem_end.offset_from(kernel_addr) as u64,
+ E820_RAM,
+ )?;
+ } else {
+ add_e820_entry(
+ &mut params,
+ kernel_addr.offset() as u64,
+ end_32bit_gap_start.offset_from(kernel_addr) as u64,
+ E820_RAM,
+ )?;
+ if mem_end > first_addr_past_32bits {
+ add_e820_entry(
+ &mut params,
+ first_addr_past_32bits.offset() as u64,
+ mem_end.offset_from(first_addr_past_32bits) as u64,
+ E820_RAM,
+ )?;
+ }
+ }
+
+ let zero_page_addr = GuestAddress(ZERO_PAGE_OFFSET);
+ guest_mem
+ .checked_offset(zero_page_addr, mem::size_of::<boot_params>() as u64)
+ .ok_or(Error::ZeroPagePastRamEnd)?;
+ guest_mem
+ .write_obj_at_addr(params, zero_page_addr)
+ .map_err(|_| Error::ZeroPageSetup)?;
+ Ok(())
+}
+
+/// Add an e820 region to the e820 map.
+/// Returns Ok(()) if successful, or an error if there is no space left in the map.
+fn add_e820_entry(params: &mut boot_params, addr: u64, size: u64, mem_type: u32) -> Result<()> {
+ if params.e820_entries >= params.e820_table.len() as u8 {
+ return Err(Error::E820Configuration);
+ }
+
+ params.e820_table[params.e820_entries as usize].addr = addr;
+ params.e820_table[params.e820_entries as usize].size = size;
+ params.e820_table[params.e820_entries as usize].type_ = mem_type;
+ params.e820_entries += 1;
+
+ Ok(())
+}
+
+/// Returns a Vec of the valid memory addresses.
+/// These should be used to configure the GuestMemory structure for the platform.
+/// For x86_64 all addresses are valid from the start of the kernel except a
+/// carve out at the end of 32bit address space.
+fn arch_memory_regions(size: u64, has_bios: bool) -> Vec<(GuestAddress, u64)> {
+ let mem_end = GuestAddress(size);
+ let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS);
+ let end_32bit_gap_start = GuestAddress(FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE);
+
+ let mut regions = Vec::new();
+ if mem_end < end_32bit_gap_start {
+ regions.push((GuestAddress(0), size));
+ if has_bios {
+ regions.push((GuestAddress(BIOS_START), BIOS_LEN as u64));
+ }
+ } else {
+ regions.push((GuestAddress(0), end_32bit_gap_start.offset()));
+ if mem_end > first_addr_past_32bits {
+ let region_start = if has_bios {
+ GuestAddress(BIOS_START)
+ } else {
+ first_addr_past_32bits
+ };
+ regions.push((region_start, mem_end.offset_from(first_addr_past_32bits)));
+ } else if has_bios {
+ regions.push((GuestAddress(BIOS_START), BIOS_LEN as u64));
+ }
+ }
+
+ regions
+}
+
+impl arch::LinuxArch for X8664arch {
+ type Error = Error;
+
+ fn build_vm<F, E>(
+ mut components: VmComponents,
+ split_irqchip: bool,
+ serial_parameters: &BTreeMap<u8, SerialParameters>,
+ create_devices: F,
+ ) -> Result<RunnableLinuxVm>
+ where
+ F: FnOnce(
+ &GuestMemory,
+ &mut Vm,
+ &mut SystemAllocator,
+ &EventFd,
+ ) -> std::result::Result<Vec<(Box<dyn PciDevice>, Option<Minijail>)>, E>,
+ E: StdError + 'static,
+ {
+ let mut resources =
+ Self::get_resource_allocator(components.memory_size, components.wayland_dmabuf);
+ let has_bios = match components.vm_image {
+ VmImage::Bios(_) => true,
+ _ => false,
+ };
+ let mem = Self::setup_memory(components.memory_size, has_bios)?;
+ let kvm = Kvm::new().map_err(Error::CreateKvm)?;
+ let mut vm = Self::create_vm(&kvm, split_irqchip, mem.clone())?;
+
+ let vcpu_count = components.vcpu_count;
+ let mut vcpus = Vec::with_capacity(vcpu_count as usize);
+ for cpu_id in 0..vcpu_count {
+ let vcpu = Vcpu::new(cpu_id as libc::c_ulong, &kvm, &vm).map_err(Error::CreateVcpu)?;
+ if let VmImage::Kernel(_) = components.vm_image {
+ Self::configure_vcpu(
+ vm.get_memory(),
+ &kvm,
+ &vm,
+ &vcpu,
+ cpu_id as u64,
+ vcpu_count as u64,
+ )?;
+ }
+ vcpus.push(vcpu);
+ }
+
+ let vcpu_affinity = components.vcpu_affinity;
+
+ let irq_chip = Self::create_irq_chip(&vm)?;
+
+ let mut mmio_bus = devices::Bus::new();
+
+ let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
+
+ let pci_devices = create_devices(&mem, &mut vm, &mut resources, &exit_evt)
+ .map_err(|e| Error::CreateDevices(Box::new(e)))?;
+ let (pci, pci_irqs, pid_debug_label_map) =
+ arch::generate_pci_root(pci_devices, &mut mmio_bus, &mut resources, &mut vm)
+ .map_err(Error::CreatePciRoot)?;
+ let pci_bus = Arc::new(Mutex::new(PciConfigIo::new(pci)));
+
+ let mut io_bus = Self::setup_io_bus(
+ &mut vm,
+ split_irqchip,
+ exit_evt.try_clone().map_err(Error::CloneEventFd)?,
+ Some(pci_bus.clone()),
+ components.memory_size,
+ )?;
+
+ let (stdio_serial_num, stdio_serial) =
+ Self::setup_serial_devices(&mut vm, &mut io_bus, &serial_parameters)?;
+
+ match components.vm_image {
+ VmImage::Bios(ref mut bios) => Self::load_bios(&mem, bios)?,
+ VmImage::Kernel(ref mut kernel_image) => {
+ let mut cmdline = Self::get_base_linux_cmdline(stdio_serial_num);
+ for param in components.extra_kernel_params {
+ cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
+ }
+
+ // separate out load_kernel from other setup to get a specific error for
+ // kernel loading
+ let (params, kernel_end) = Self::load_kernel(&mem, kernel_image)?;
+
+ Self::setup_system_memory(
+ &mem,
+ components.memory_size,
+ vcpu_count,
+ &CString::new(cmdline).unwrap(),
+ components.initrd_image,
+ pci_irqs,
+ components.android_fstab,
+ kernel_end,
+ params,
+ )?;
+ }
+ }
+ Ok(RunnableLinuxVm {
+ vm,
+ kvm,
+ resources,
+ stdio_serial,
+ exit_evt,
+ vcpus,
+ vcpu_affinity,
+ irq_chip,
+ io_bus,
+ mmio_bus,
+ pid_debug_label_map,
+ })
+ }
+}
+
+impl X8664arch {
+ /// Loads the bios from an open file.
+ ///
+ /// # Arguments
+ ///
+ /// * `mem` - The memory to be used by the guest.
+ /// * `bios_image` - the File object for the specified bios
+ fn load_bios(mem: &GuestMemory, bios_image: &mut File) -> Result<()> {
+ let bios_image_length = bios_image
+ .seek(io::SeekFrom::End(0))
+ .map_err(Error::LoadBios)?;
+ if bios_image_length != BIOS_LEN as u64 {
+ return Err(Error::LoadBios(io::Error::new(
+ io::ErrorKind::InvalidData,
+ format!(
+ "bios was {} bytes, expected {}",
+ bios_image_length, BIOS_LEN
+ ),
+ )));
+ }
+ bios_image
+ .seek(io::SeekFrom::Start(0))
+ .map_err(Error::LoadBios)?;
+ mem.read_to_memory(GuestAddress(BIOS_START), bios_image, BIOS_LEN)
+ .map_err(Error::SetupGuestMemory)?;
+ Ok(())
+ }
+
+ /// Loads the kernel from an open file.
+ ///
+ /// # Arguments
+ ///
+ /// * `mem` - The memory to be used by the guest.
+ /// * `kernel_image` - the File object for the specified kernel.
+ fn load_kernel(mem: &GuestMemory, kernel_image: &mut File) -> Result<(boot_params, u64)> {
+ let elf_result =
+ kernel_loader::load_kernel(mem, GuestAddress(KERNEL_START_OFFSET), kernel_image);
+ if elf_result == Err(kernel_loader::Error::InvalidElfMagicNumber) {
+ bzimage::load_bzimage(mem, GuestAddress(KERNEL_START_OFFSET), kernel_image)
+ .map_err(Error::LoadBzImage)
+ } else {
+ let kernel_end = elf_result.map_err(Error::LoadKernel)?;
+ Ok((Default::default(), kernel_end))
+ }
+ }
+
+ /// Configures the system memory space should be called once per vm before
+ /// starting vcpu threads.
+ ///
+ /// # Arguments
+ ///
+ /// * `mem` - The memory to be used by the guest.
+ /// * `vcpu_count` - Number of virtual CPUs the guest will have.
+ /// * `cmdline` - the kernel commandline
+ /// * `initrd_file` - an initial ramdisk image
+ fn setup_system_memory(
+ mem: &GuestMemory,
+ mem_size: u64,
+ vcpu_count: u32,
+ cmdline: &CStr,
+ initrd_file: Option<File>,
+ pci_irqs: Vec<(u32, PciInterruptPin)>,
+ android_fstab: Option<File>,
+ kernel_end: u64,
+ params: boot_params,
+ ) -> Result<()> {
+ kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)
+ .map_err(Error::LoadCmdline)?;
+
+ // Track the first free address after the kernel - this is where extra
+ // data like the device tree blob and initrd will be loaded.
+ let mut free_addr = kernel_end;
+
+ let setup_data = if let Some(android_fstab) = android_fstab {
+ let free_addr_aligned = (((free_addr + 64 - 1) / 64) * 64) + 64;
+ let dtb_start = GuestAddress(free_addr_aligned);
+ let dtb_size = fdt::create_fdt(
+ X86_64_FDT_MAX_SIZE as usize,
+ mem,
+ dtb_start.offset(),
+ android_fstab,
+ )
+ .map_err(Error::CreateFdt)?;
+ free_addr = dtb_start.offset() + dtb_size as u64;
+ Some(dtb_start)
+ } else {
+ None
+ };
+
+ let initrd = match initrd_file {
+ Some(mut initrd_file) => {
+ let mut initrd_addr_max = u64::from(params.hdr.initrd_addr_max);
+ // Default initrd_addr_max for old kernels (see Documentation/x86/boot.txt).
+ if initrd_addr_max == 0 {
+ initrd_addr_max = 0x37FFFFFF;
+ }
+
+ let mem_max = mem.end_addr().offset() - 1;
+ if initrd_addr_max > mem_max {
+ initrd_addr_max = mem_max;
+ }
+
+ let (initrd_start, initrd_size) = arch::load_image_high(
+ mem,
+ &mut initrd_file,
+ GuestAddress(free_addr),
+ GuestAddress(initrd_addr_max),
+ sys_util::pagesize() as u64,
+ )
+ .map_err(Error::LoadInitrd)?;
+ Some((initrd_start, initrd_size))
+ }
+ None => None,
+ };
+
+ configure_system(
+ mem,
+ mem_size,
+ GuestAddress(KERNEL_START_OFFSET),
+ GuestAddress(CMDLINE_OFFSET),
+ cmdline.to_bytes().len() + 1,
+ vcpu_count as u8,
+ pci_irqs,
+ setup_data,
+ initrd,
+ params,
+ )?;
+ Ok(())
+ }
+
+ /// Creates a new VM object and initializes architecture specific devices
+ ///
+ /// # Arguments
+ ///
+ /// * `kvm` - The opened /dev/kvm object.
+ /// * `split_irqchip` - Whether to use a split IRQ chip.
+ /// * `mem` - The memory to be used by the guest.
+ fn create_vm(kvm: &Kvm, split_irqchip: bool, mem: GuestMemory) -> Result<Vm> {
+ let vm = Vm::new(&kvm, mem).map_err(Error::CreateVm)?;
+ let tss_addr = GuestAddress(0xfffbd000);
+ vm.set_tss_addr(tss_addr).map_err(Error::SetTssAddr)?;
+ if !split_irqchip {
+ vm.create_pit().map_err(Error::CreatePit)?;
+ vm.create_irq_chip().map_err(Error::CreateIrqChip)?;
+ }
+ Ok(vm)
+ }
+
+ /// This creates a GuestMemory object for this VM
+ ///
+ /// * `mem_size` - Desired physical memory size in bytes for this VM
+ fn setup_memory(mem_size: u64, has_bios: bool) -> Result<GuestMemory> {
+ let arch_mem_regions = arch_memory_regions(mem_size, has_bios);
+ let mem = GuestMemory::new(&arch_mem_regions).map_err(Error::SetupGuestMemory)?;
+ Ok(mem)
+ }
+
+ /// The creates the interrupt controller device and optionally returns the fd for it.
+ /// Some architectures may not have a separate descriptor for the interrupt
+ /// controller, so they would return None even on success.
+ ///
+ /// # Arguments
+ ///
+ /// * `vm` - the vm object
+ fn create_irq_chip(_vm: &kvm::Vm) -> Result<Option<File>> {
+ // Unfortunately X86 and ARM have to do this in completely different order
+ // X86 needs to create the irq chip before creating cpus and
+ // ARM needs to do it afterwards.
+ Ok(None)
+ }
+
+ /// This returns the first page frame number for use by the balloon driver.
+ ///
+ /// # Arguments
+ ///
+ /// * `mem_size` - the size in bytes of physical ram for the guest
+ fn get_base_dev_pfn(mem_size: u64) -> u64 {
+ // Put device memory at a 2MB boundary after physical memory or 4gb, whichever is greater.
+ const MB: u64 = 1024 * 1024;
+ const GB: u64 = 1024 * MB;
+ let mem_size_round_2mb = (mem_size + 2 * MB - 1) / (2 * MB) * (2 * MB);
+ std::cmp::max(mem_size_round_2mb, 4 * GB) / sys_util::pagesize() as u64
+ }
+
+ /// This returns a minimal kernel command for this architecture
+ fn get_base_linux_cmdline(stdio_serial_num: Option<u8>) -> kernel_cmdline::Cmdline {
+ let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE as usize);
+ if stdio_serial_num.is_some() {
+ let tty_string = get_serial_tty_string(stdio_serial_num.unwrap());
+ cmdline.insert("console", &tty_string).unwrap();
+ }
+ cmdline.insert_str("noacpi reboot=k panic=-1").unwrap();
+
+ cmdline
+ }
+
+ /// Returns a system resource allocator.
+ fn get_resource_allocator(mem_size: u64, gpu_allocation: bool) -> SystemAllocator {
+ const MMIO_BASE: u64 = 0xe0000000;
+ let device_addr_start = Self::get_base_dev_pfn(mem_size) * sys_util::pagesize() as u64;
+ SystemAllocator::builder()
+ .add_io_addresses(0xc000, 0x10000)
+ .add_mmio_addresses(MMIO_BASE, 0x100000)
+ .add_device_addresses(device_addr_start, u64::max_value() - device_addr_start)
+ .create_allocator(X86_64_IRQ_BASE, gpu_allocation)
+ .unwrap()
+ }
+
+ /// Sets up the IO bus for this platform
+ ///
+ /// # Arguments
+ ///
+ /// * - `vm` the vm object
+ /// * - `split_irqchip`: whether to use a split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
+ /// * - `exit_evt` - the event fd object which should receive exit events
+ /// * - `mem_size` - the size in bytes of physical ram for the guest
+ fn setup_io_bus(
+ vm: &mut Vm,
+ split_irqchip: bool,
+ exit_evt: EventFd,
+ pci: Option<Arc<Mutex<devices::PciConfigIo>>>,
+ mem_size: u64,
+ ) -> Result<(devices::Bus)> {
+ struct NoDevice;
+ impl devices::BusDevice for NoDevice {
+ fn debug_label(&self) -> String {
+ "no device".to_owned()
+ }
+ }
+
+ let mut io_bus = devices::Bus::new();
+
+ let mem_gap_start = FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE;
+ let mem_below_4g = std::cmp::min(mem_gap_start, mem_size);
+ let mem_above_4g = mem_size.saturating_sub(FIRST_ADDR_PAST_32BITS);
+
+ io_bus
+ .insert(
+ Arc::new(Mutex::new(devices::Cmos::new(mem_below_4g, mem_above_4g))),
+ 0x70,
+ 0x2,
+ false,
+ )
+ .unwrap();
+ io_bus
+ .insert(
+ Arc::new(Mutex::new(devices::I8042Device::new(
+ exit_evt.try_clone().map_err(Error::CloneEventFd)?,
+ ))),
+ 0x061,
+ 0x4,
+ false,
+ )
+ .unwrap();
+
+ let nul_device = Arc::new(Mutex::new(NoDevice));
+ if split_irqchip {
+ let pit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
+ let pit = Arc::new(Mutex::new(
+ devices::Pit::new(
+ pit_evt.try_clone().map_err(Error::CloneEventFd)?,
+ Arc::new(Mutex::new(Clock::new())),
+ )
+ .map_err(Error::CreatePitDevice)?,
+ ));
+ // Reserve from 0x40 to 0x61 (the speaker).
+ io_bus.insert(pit.clone(), 0x040, 0x22, false).unwrap();
+ vm.register_irqfd(&pit_evt, 0)
+ .map_err(Error::RegisterIrqfd)?;
+ } else {
+ io_bus
+ .insert(nul_device.clone(), 0x040, 0x8, false)
+ .unwrap(); // ignore pit
+ }
+
+ 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
+
+ if let Some(pci_root) = pci {
+ io_bus.insert(pci_root, 0xcf8, 0x8, false).unwrap();
+ } else {
+ // ignore pci.
+ io_bus
+ .insert(nul_device.clone(), 0xcf8, 0x8, false)
+ .unwrap();
+ }
+
+ Ok(io_bus)
+ }
+
+ /// Sets up the serial devices for this platform. Returns the serial port number and serial
+ /// device to be used for stdout
+ ///
+ /// # Arguments
+ ///
+ /// * - `vm` the vm object
+ /// * - `io_bus` the I/O bus to add the devices to
+ /// * - `serial_parmaters` - definitions for how the serial devices should be configured
+ fn setup_serial_devices(
+ vm: &mut Vm,
+ io_bus: &mut devices::Bus,
+ serial_parameters: &BTreeMap<u8, SerialParameters>,
+ ) -> Result<(Option<u8>, Option<Arc<Mutex<devices::Serial>>>)> {
+ let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
+ let com_evt_2_4 = EventFd::new().map_err(Error::CreateEventFd)?;
+
+ let (stdio_serial_num, stdio_serial) =
+ arch::add_serial_devices(io_bus, &com_evt_1_3, &com_evt_2_4, &serial_parameters)
+ .map_err(Error::CreateSerialDevices)?;
+
+ vm.register_irqfd(&com_evt_1_3, X86_64_SERIAL_1_3_IRQ)
+ .map_err(Error::RegisterIrqfd)?;
+ vm.register_irqfd(&com_evt_2_4, X86_64_SERIAL_2_4_IRQ)
+ .map_err(Error::RegisterIrqfd)?;
+
+ Ok((stdio_serial_num, stdio_serial))
+ }
+
+ /// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
+ ///
+ /// # Arguments
+ ///
+ /// * `guest_mem` - The memory to be used by the guest.
+ /// * `kernel_load_offset` - Offset in bytes from `guest_mem` at which the
+ /// kernel starts.
+ /// * `kvm` - The /dev/kvm object that created vcpu.
+ /// * `vm` - The VM object associated with this VCPU.
+ /// * `vcpu` - The VCPU object to configure.
+ /// * `cpu_id` - The id of the given `vcpu`.
+ /// * `num_cpus` - Number of virtual CPUs the guest will have.
+ fn configure_vcpu(
+ guest_mem: &GuestMemory,
+ kvm: &Kvm,
+ _vm: &Vm,
+ vcpu: &Vcpu,
+ cpu_id: u64,
+ num_cpus: u64,
+ ) -> Result<()> {
+ let kernel_load_addr = GuestAddress(KERNEL_START_OFFSET);
+ cpuid::setup_cpuid(kvm, vcpu, cpu_id, num_cpus).map_err(Error::SetupCpuid)?;
+ regs::setup_msrs(vcpu).map_err(Error::SetupMsrs)?;
+ let kernel_end = guest_mem
+ .checked_offset(kernel_load_addr, KERNEL_64BIT_ENTRY_OFFSET)
+ .ok_or(Error::KernelOffsetPastEnd)?;
+ regs::setup_regs(
+ vcpu,
+ (kernel_end).offset() as u64,
+ BOOT_STACK_POINTER as u64,
+ ZERO_PAGE_OFFSET as u64,
+ )
+ .map_err(Error::SetupRegs)?;
+ regs::setup_fpu(vcpu).map_err(Error::SetupFpu)?;
+ regs::setup_sregs(guest_mem, vcpu).map_err(Error::SetupSregs)?;
+ interrupts::set_lint(vcpu).map_err(Error::SetLint)?;
+ Ok(())
+ }
+}
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn regions_lt_4gb_nobios() {
+ let regions = arch_memory_regions(1u64 << 29, /* has_bios */ false);
+ assert_eq!(1, regions.len());
+ assert_eq!(GuestAddress(0), regions[0].0);
+ assert_eq!(1u64 << 29, regions[0].1);
+ }
+
+ #[test]
+ fn regions_gt_4gb_nobios() {
+ let regions = arch_memory_regions((1u64 << 32) + 0x8000, /* has_bios */ false);
+ assert_eq!(2, regions.len());
+ assert_eq!(GuestAddress(0), regions[0].0);
+ assert_eq!(GuestAddress(1u64 << 32), regions[1].0);
+ }
+
+ #[test]
+ fn regions_lt_4gb_bios() {
+ let regions = arch_memory_regions(1u64 << 29, /* has_bios */ true);
+ assert_eq!(2, regions.len());
+ assert_eq!(GuestAddress(0), regions[0].0);
+ assert_eq!(1u64 << 29, regions[0].1);
+ assert_eq!(GuestAddress(BIOS_START), regions[1].0);
+ assert_eq!(BIOS_LEN as u64, regions[1].1);
+ }
+
+ #[test]
+ fn regions_gt_4gb_bios() {
+ let regions = arch_memory_regions((1u64 << 32) + 0x8000, /* has_bios */ true);
+ assert_eq!(2, regions.len());
+ assert_eq!(GuestAddress(0), regions[0].0);
+ assert_eq!(GuestAddress(BIOS_START), regions[1].0);
+ }
+}
diff --git a/x86_64/src/mpspec.rs b/x86_64/src/mpspec.rs
new file mode 100644
index 0000000..ab7af51
--- /dev/null
+++ b/x86_64/src/mpspec.rs
@@ -0,0 +1,832 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* automatically generated by rust-bindgen */
+
+pub const MPC_SIGNATURE: &'static [u8; 5usize] = b"PCMP\x00";
+pub const MP_PROCESSOR: ::std::os::raw::c_uint = 0;
+pub const MP_BUS: ::std::os::raw::c_uint = 1;
+pub const MP_IOAPIC: ::std::os::raw::c_uint = 2;
+pub const MP_INTSRC: ::std::os::raw::c_uint = 3;
+pub const MP_LINTSRC: ::std::os::raw::c_uint = 4;
+pub const MP_TRANSLATION: ::std::os::raw::c_uint = 192;
+pub const CPU_ENABLED: ::std::os::raw::c_uint = 1;
+pub const CPU_BOOTPROCESSOR: ::std::os::raw::c_uint = 2;
+pub const CPU_STEPPING_MASK: ::std::os::raw::c_uint = 15;
+pub const CPU_MODEL_MASK: ::std::os::raw::c_uint = 240;
+pub const CPU_FAMILY_MASK: ::std::os::raw::c_uint = 3840;
+pub const BUSTYPE_EISA: &'static [u8; 5usize] = b"EISA\x00";
+pub const BUSTYPE_ISA: &'static [u8; 4usize] = b"ISA\x00";
+pub const BUSTYPE_INTERN: &'static [u8; 7usize] = b"INTERN\x00";
+pub const BUSTYPE_MCA: &'static [u8; 4usize] = b"MCA\x00";
+pub const BUSTYPE_VL: &'static [u8; 3usize] = b"VL\x00";
+pub const BUSTYPE_PCI: &'static [u8; 4usize] = b"PCI\x00";
+pub const BUSTYPE_PCMCIA: &'static [u8; 7usize] = b"PCMCIA\x00";
+pub const BUSTYPE_CBUS: &'static [u8; 5usize] = b"CBUS\x00";
+pub const BUSTYPE_CBUSII: &'static [u8; 7usize] = b"CBUSII\x00";
+pub const BUSTYPE_FUTURE: &'static [u8; 7usize] = b"FUTURE\x00";
+pub const BUSTYPE_MBI: &'static [u8; 4usize] = b"MBI\x00";
+pub const BUSTYPE_MBII: &'static [u8; 5usize] = b"MBII\x00";
+pub const BUSTYPE_MPI: &'static [u8; 4usize] = b"MPI\x00";
+pub const BUSTYPE_MPSA: &'static [u8; 5usize] = b"MPSA\x00";
+pub const BUSTYPE_NUBUS: &'static [u8; 6usize] = b"NUBUS\x00";
+pub const BUSTYPE_TC: &'static [u8; 3usize] = b"TC\x00";
+pub const BUSTYPE_VME: &'static [u8; 4usize] = b"VME\x00";
+pub const BUSTYPE_XPRESS: &'static [u8; 7usize] = b"XPRESS\x00";
+pub const MPC_APIC_USABLE: ::std::os::raw::c_uint = 1;
+pub const MP_IRQDIR_DEFAULT: ::std::os::raw::c_uint = 0;
+pub const MP_IRQDIR_HIGH: ::std::os::raw::c_uint = 1;
+pub const MP_IRQDIR_LOW: ::std::os::raw::c_uint = 3;
+pub const MP_APIC_ALL: ::std::os::raw::c_uint = 255;
+pub const MPC_OEM_SIGNATURE: &'static [u8; 5usize] = b"_OEM\x00";
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct mpf_intel {
+ pub signature: [::std::os::raw::c_char; 4usize],
+ pub physptr: ::std::os::raw::c_uint,
+ pub length: ::std::os::raw::c_uchar,
+ pub specification: ::std::os::raw::c_uchar,
+ pub checksum: ::std::os::raw::c_uchar,
+ pub feature1: ::std::os::raw::c_uchar,
+ pub feature2: ::std::os::raw::c_uchar,
+ pub feature3: ::std::os::raw::c_uchar,
+ pub feature4: ::std::os::raw::c_uchar,
+ pub feature5: ::std::os::raw::c_uchar,
+}
+#[test]
+fn bindgen_test_layout_mpf_intel() {
+ assert_eq!(
+ ::std::mem::size_of::<mpf_intel>(),
+ 16usize,
+ concat!("Size of: ", stringify!(mpf_intel))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<mpf_intel>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(mpf_intel))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).signature as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(signature)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).physptr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(physptr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).length as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(length)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).specification as *const _ as usize },
+ 9usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(specification)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).checksum as *const _ as usize },
+ 10usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(checksum)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).feature1 as *const _ as usize },
+ 11usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(feature1)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).feature2 as *const _ as usize },
+ 12usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(feature2)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).feature3 as *const _ as usize },
+ 13usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(feature3)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).feature4 as *const _ as usize },
+ 14usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(feature4)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpf_intel)).feature5 as *const _ as usize },
+ 15usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpf_intel),
+ "::",
+ stringify!(feature5)
+ )
+ );
+}
+impl Clone for mpf_intel {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct mpc_table {
+ pub signature: [::std::os::raw::c_char; 4usize],
+ pub length: ::std::os::raw::c_ushort,
+ pub spec: ::std::os::raw::c_char,
+ pub checksum: ::std::os::raw::c_char,
+ pub oem: [::std::os::raw::c_char; 8usize],
+ pub productid: [::std::os::raw::c_char; 12usize],
+ pub oemptr: ::std::os::raw::c_uint,
+ pub oemsize: ::std::os::raw::c_ushort,
+ pub oemcount: ::std::os::raw::c_ushort,
+ pub lapic: ::std::os::raw::c_uint,
+ pub reserved: ::std::os::raw::c_uint,
+}
+#[test]
+fn bindgen_test_layout_mpc_table() {
+ assert_eq!(
+ ::std::mem::size_of::<mpc_table>(),
+ 44usize,
+ concat!("Size of: ", stringify!(mpc_table))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<mpc_table>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(mpc_table))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).signature as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(signature)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).length as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(length)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).spec as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(spec)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).checksum as *const _ as usize },
+ 7usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(checksum)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).oem as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(oem)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).productid as *const _ as usize },
+ 16usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(productid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).oemptr as *const _ as usize },
+ 28usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(oemptr)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).oemsize as *const _ as usize },
+ 32usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(oemsize)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).oemcount as *const _ as usize },
+ 34usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(oemcount)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).lapic as *const _ as usize },
+ 36usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(lapic)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_table)).reserved as *const _ as usize },
+ 40usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_table),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Clone for mpc_table {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct mpc_cpu {
+ pub type_: ::std::os::raw::c_uchar,
+ pub apicid: ::std::os::raw::c_uchar,
+ pub apicver: ::std::os::raw::c_uchar,
+ pub cpuflag: ::std::os::raw::c_uchar,
+ pub cpufeature: ::std::os::raw::c_uint,
+ pub featureflag: ::std::os::raw::c_uint,
+ pub reserved: [::std::os::raw::c_uint; 2usize],
+}
+#[test]
+fn bindgen_test_layout_mpc_cpu() {
+ assert_eq!(
+ ::std::mem::size_of::<mpc_cpu>(),
+ 20usize,
+ concat!("Size of: ", stringify!(mpc_cpu))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<mpc_cpu>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(mpc_cpu))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_cpu)).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_cpu),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_cpu)).apicid as *const _ as usize },
+ 1usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_cpu),
+ "::",
+ stringify!(apicid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_cpu)).apicver as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_cpu),
+ "::",
+ stringify!(apicver)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_cpu)).cpuflag as *const _ as usize },
+ 3usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_cpu),
+ "::",
+ stringify!(cpuflag)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_cpu)).cpufeature as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_cpu),
+ "::",
+ stringify!(cpufeature)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_cpu)).featureflag as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_cpu),
+ "::",
+ stringify!(featureflag)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_cpu)).reserved as *const _ as usize },
+ 12usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_cpu),
+ "::",
+ stringify!(reserved)
+ )
+ );
+}
+impl Clone for mpc_cpu {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct mpc_bus {
+ pub type_: ::std::os::raw::c_uchar,
+ pub busid: ::std::os::raw::c_uchar,
+ pub bustype: [::std::os::raw::c_uchar; 6usize],
+}
+#[test]
+fn bindgen_test_layout_mpc_bus() {
+ assert_eq!(
+ ::std::mem::size_of::<mpc_bus>(),
+ 8usize,
+ concat!("Size of: ", stringify!(mpc_bus))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<mpc_bus>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(mpc_bus))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_bus)).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_bus),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_bus)).busid as *const _ as usize },
+ 1usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_bus),
+ "::",
+ stringify!(busid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_bus)).bustype as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_bus),
+ "::",
+ stringify!(bustype)
+ )
+ );
+}
+impl Clone for mpc_bus {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct mpc_ioapic {
+ pub type_: ::std::os::raw::c_uchar,
+ pub apicid: ::std::os::raw::c_uchar,
+ pub apicver: ::std::os::raw::c_uchar,
+ pub flags: ::std::os::raw::c_uchar,
+ pub apicaddr: ::std::os::raw::c_uint,
+}
+#[test]
+fn bindgen_test_layout_mpc_ioapic() {
+ assert_eq!(
+ ::std::mem::size_of::<mpc_ioapic>(),
+ 8usize,
+ concat!("Size of: ", stringify!(mpc_ioapic))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<mpc_ioapic>(),
+ 4usize,
+ concat!("Alignment of ", stringify!(mpc_ioapic))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_ioapic)).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_ioapic),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_ioapic)).apicid as *const _ as usize },
+ 1usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_ioapic),
+ "::",
+ stringify!(apicid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_ioapic)).apicver as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_ioapic),
+ "::",
+ stringify!(apicver)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_ioapic)).flags as *const _ as usize },
+ 3usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_ioapic),
+ "::",
+ stringify!(flags)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_ioapic)).apicaddr as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_ioapic),
+ "::",
+ stringify!(apicaddr)
+ )
+ );
+}
+impl Clone for mpc_ioapic {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct mpc_intsrc {
+ pub type_: ::std::os::raw::c_uchar,
+ pub irqtype: ::std::os::raw::c_uchar,
+ pub irqflag: ::std::os::raw::c_ushort,
+ pub srcbus: ::std::os::raw::c_uchar,
+ pub srcbusirq: ::std::os::raw::c_uchar,
+ pub dstapic: ::std::os::raw::c_uchar,
+ pub dstirq: ::std::os::raw::c_uchar,
+}
+#[test]
+fn bindgen_test_layout_mpc_intsrc() {
+ assert_eq!(
+ ::std::mem::size_of::<mpc_intsrc>(),
+ 8usize,
+ concat!("Size of: ", stringify!(mpc_intsrc))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<mpc_intsrc>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(mpc_intsrc))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_intsrc)).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_intsrc),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_intsrc)).irqtype as *const _ as usize },
+ 1usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_intsrc),
+ "::",
+ stringify!(irqtype)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_intsrc)).irqflag as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_intsrc),
+ "::",
+ stringify!(irqflag)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_intsrc)).srcbus as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_intsrc),
+ "::",
+ stringify!(srcbus)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_intsrc)).srcbusirq as *const _ as usize },
+ 5usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_intsrc),
+ "::",
+ stringify!(srcbusirq)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_intsrc)).dstapic as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_intsrc),
+ "::",
+ stringify!(dstapic)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_intsrc)).dstirq as *const _ as usize },
+ 7usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_intsrc),
+ "::",
+ stringify!(dstirq)
+ )
+ );
+}
+impl Clone for mpc_intsrc {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub const mp_irq_source_types_mp_INT: mp_irq_source_types = 0;
+pub const mp_irq_source_types_mp_NMI: mp_irq_source_types = 1;
+pub const mp_irq_source_types_mp_SMI: mp_irq_source_types = 2;
+pub const mp_irq_source_types_mp_ExtINT: mp_irq_source_types = 3;
+pub type mp_irq_source_types = ::std::os::raw::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct mpc_lintsrc {
+ pub type_: ::std::os::raw::c_uchar,
+ pub irqtype: ::std::os::raw::c_uchar,
+ pub irqflag: ::std::os::raw::c_ushort,
+ pub srcbusid: ::std::os::raw::c_uchar,
+ pub srcbusirq: ::std::os::raw::c_uchar,
+ pub destapic: ::std::os::raw::c_uchar,
+ pub destapiclint: ::std::os::raw::c_uchar,
+}
+#[test]
+fn bindgen_test_layout_mpc_lintsrc() {
+ assert_eq!(
+ ::std::mem::size_of::<mpc_lintsrc>(),
+ 8usize,
+ concat!("Size of: ", stringify!(mpc_lintsrc))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<mpc_lintsrc>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(mpc_lintsrc))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_lintsrc)).type_ as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_lintsrc),
+ "::",
+ stringify!(type_)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_lintsrc)).irqtype as *const _ as usize },
+ 1usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_lintsrc),
+ "::",
+ stringify!(irqtype)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_lintsrc)).irqflag as *const _ as usize },
+ 2usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_lintsrc),
+ "::",
+ stringify!(irqflag)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_lintsrc)).srcbusid as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_lintsrc),
+ "::",
+ stringify!(srcbusid)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_lintsrc)).srcbusirq as *const _ as usize },
+ 5usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_lintsrc),
+ "::",
+ stringify!(srcbusirq)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_lintsrc)).destapic as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_lintsrc),
+ "::",
+ stringify!(destapic)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_lintsrc)).destapiclint as *const _ as usize },
+ 7usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_lintsrc),
+ "::",
+ stringify!(destapiclint)
+ )
+ );
+}
+impl Clone for mpc_lintsrc {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct mpc_oemtable {
+ pub signature: [::std::os::raw::c_char; 4usize],
+ pub length: ::std::os::raw::c_ushort,
+ pub rev: ::std::os::raw::c_char,
+ pub checksum: ::std::os::raw::c_char,
+ pub mpc: [::std::os::raw::c_char; 8usize],
+}
+#[test]
+fn bindgen_test_layout_mpc_oemtable() {
+ assert_eq!(
+ ::std::mem::size_of::<mpc_oemtable>(),
+ 16usize,
+ concat!("Size of: ", stringify!(mpc_oemtable))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<mpc_oemtable>(),
+ 2usize,
+ concat!("Alignment of ", stringify!(mpc_oemtable))
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_oemtable)).signature as *const _ as usize },
+ 0usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_oemtable),
+ "::",
+ stringify!(signature)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_oemtable)).length as *const _ as usize },
+ 4usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_oemtable),
+ "::",
+ stringify!(length)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_oemtable)).rev as *const _ as usize },
+ 6usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_oemtable),
+ "::",
+ stringify!(rev)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_oemtable)).checksum as *const _ as usize },
+ 7usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_oemtable),
+ "::",
+ stringify!(checksum)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(0 as *const mpc_oemtable)).mpc as *const _ as usize },
+ 8usize,
+ concat!(
+ "Alignment of field: ",
+ stringify!(mpc_oemtable),
+ "::",
+ stringify!(mpc)
+ )
+ );
+}
+impl Clone for mpc_oemtable {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub const mp_bustype_MP_BUS_ISA: mp_bustype = 1;
+pub const mp_bustype_MP_BUS_EISA: mp_bustype = 2;
+pub const mp_bustype_MP_BUS_PCI: mp_bustype = 3;
+pub type mp_bustype = ::std::os::raw::c_uint;
diff --git a/x86_64/src/mptable.rs b/x86_64/src/mptable.rs
new file mode 100644
index 0000000..8b754bd
--- /dev/null
+++ b/x86_64/src/mptable.rs
@@ -0,0 +1,448 @@
+// Copyright 2017 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::fmt::{self, Display};
+use std::mem;
+use std::result;
+use std::slice;
+
+use libc::c_char;
+
+use data_model::VolatileMemory;
+use devices::PciInterruptPin;
+use sys_util::{GuestAddress, GuestMemory};
+
+use crate::mpspec::*;
+
+#[derive(Debug)]
+pub enum Error {
+ /// There was too little guest memory to store the entire MP table.
+ NotEnoughMemory,
+ /// The MP table has too little address space to be stored.
+ AddressOverflow,
+ /// Failure while zeroing out the memory for the MP table.
+ Clear,
+ /// Failure to write the MP floating pointer.
+ WriteMpfIntel,
+ /// Failure to write MP CPU entry.
+ WriteMpcCpu,
+ /// Failure to write MP ioapic entry.
+ WriteMpcIoapic,
+ /// Failure to write MP bus entry.
+ WriteMpcBus,
+ /// Failure to write MP interrupt source entry.
+ WriteMpcIntsrc,
+ /// Failure to write MP local interrupt source entry.
+ WriteMpcLintsrc,
+ /// Failure to write MP table header.
+ WriteMpcTable,
+}
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ let description = match self {
+ NotEnoughMemory => "There was too little guest memory to store the MP table",
+ AddressOverflow => "The MP table has too little address space to be stored",
+ Clear => "Failure while zeroing out the memory for the MP table",
+ WriteMpfIntel => "Failure to write the MP floating pointer",
+ WriteMpcCpu => "Failure to write MP CPU entry",
+ WriteMpcIoapic => "Failure to write MP ioapic entry",
+ WriteMpcBus => "Failure to write MP bus entry",
+ WriteMpcIntsrc => "Failure to write MP interrupt source entry",
+ WriteMpcLintsrc => "Failure to write MP local interrupt source entry",
+ WriteMpcTable => "Failure to write MP table header",
+ };
+
+ write!(f, "MPTable error: {}", description)
+ }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+// Convenience macro for making arrays of diverse character types.
+macro_rules! char_array {
+ ($t:ty; $( $c:expr ),*) => ( [ $( $c as $t ),* ] )
+}
+
+// Most of these variables are sourced from the Intel MP Spec 1.4.
+const SMP_MAGIC_IDENT: [c_char; 4] = char_array!(c_char; '_', 'M', 'P', '_');
+const MPC_SIGNATURE: [c_char; 4] = char_array!(c_char; 'P', 'C', 'M', 'P');
+const MPC_SPEC: i8 = 4;
+const MPC_OEM: [c_char; 8] = char_array!(c_char; 'C', 'R', 'O', 'S', 'V', 'M', ' ', ' ');
+const MPC_PRODUCT_ID: [c_char; 12] = ['0' as c_char; 12];
+const BUS_TYPE_ISA: [u8; 6] = char_array!(u8; 'I', 'S', 'A', ' ', ' ', ' ');
+const BUS_TYPE_PCI: [u8; 6] = char_array!(u8; 'P', 'C', 'I', ' ', ' ', ' ');
+const IO_APIC_DEFAULT_PHYS_BASE: u32 = 0xfec00000; // source: linux/arch/x86/include/asm/apicdef.h
+const APIC_DEFAULT_PHYS_BASE: u32 = 0xfee00000; // source: linux/arch/x86/include/asm/apicdef.h
+const APIC_VERSION: u8 = 0x14;
+const CPU_STEPPING: u32 = 0x600;
+const CPU_FEATURE_APIC: u32 = 0x200;
+const CPU_FEATURE_FPU: u32 = 0x001;
+const MPTABLE_START: u64 = 0x400 * 639; // Last 1k of Linux's 640k base RAM.
+
+fn compute_checksum<T: Copy>(v: &T) -> u8 {
+ // Safe because we are only reading the bytes within the size of the `T` reference `v`.
+ let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
+ let mut checksum: u8 = 0;
+ for i in v_slice {
+ checksum = checksum.wrapping_add(*i);
+ }
+ checksum
+}
+
+fn mpf_intel_compute_checksum(v: &mpf_intel) -> u8 {
+ let checksum = compute_checksum(v).wrapping_sub(v.checksum);
+ (!checksum).wrapping_add(1)
+}
+
+fn compute_mp_size(num_cpus: u8) -> usize {
+ mem::size_of::<mpf_intel>()
+ + mem::size_of::<mpc_table>()
+ + mem::size_of::<mpc_cpu>() * (num_cpus as usize)
+ + mem::size_of::<mpc_ioapic>()
+ + mem::size_of::<mpc_bus>() * 2
+ + mem::size_of::<mpc_intsrc>()
+ + mem::size_of::<mpc_intsrc>() * 16
+ + mem::size_of::<mpc_lintsrc>() * 2
+}
+
+/// Performs setup of the MP table for the given `num_cpus`.
+pub fn setup_mptable(
+ mem: &GuestMemory,
+ num_cpus: u8,
+ pci_irqs: Vec<(u32, PciInterruptPin)>,
+) -> Result<()> {
+ const PCI_BUS_ID: u8 = 0;
+ const ISA_BUS_ID: u8 = 1;
+
+ // Used to keep track of the next base pointer into the MP table.
+ let mut base_mp = GuestAddress(MPTABLE_START);
+
+ let mp_size = compute_mp_size(num_cpus);
+
+ // The checked_add here ensures the all of the following base_mp.unchecked_add's will be without
+ // overflow.
+ if let Some(end_mp) = base_mp.checked_add(mp_size as u64 - 1) {
+ if !mem.address_in_range(end_mp) {
+ return Err(Error::NotEnoughMemory);
+ }
+ } else {
+ return Err(Error::AddressOverflow);
+ }
+
+ mem.get_slice(base_mp.0, mp_size as u64)
+ .map_err(|_| Error::Clear)?
+ .write_bytes(0);
+
+ {
+ let size = mem::size_of::<mpf_intel>();
+ let mut mpf_intel = mpf_intel::default();
+ mpf_intel.signature = SMP_MAGIC_IDENT;
+ mpf_intel.length = 1;
+ mpf_intel.specification = 4;
+ mpf_intel.physptr = (base_mp.offset() + mem::size_of::<mpf_intel>() as u64) as u32;
+ mpf_intel.checksum = mpf_intel_compute_checksum(&mpf_intel);
+ mem.write_obj_at_addr(mpf_intel, base_mp)
+ .map_err(|_| Error::WriteMpfIntel)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ }
+
+ // We set the location of the mpc_table here but we can't fill it out until we have the length
+ // of the entire table later.
+ let table_base = base_mp;
+ base_mp = base_mp.unchecked_add(mem::size_of::<mpc_table>() as u64);
+
+ let mut checksum: u8 = 0;
+ let ioapicid: u8 = num_cpus + 1;
+
+ for cpu_id in 0..num_cpus {
+ let size = mem::size_of::<mpc_cpu>();
+ let mut mpc_cpu = mpc_cpu::default();
+ mpc_cpu.type_ = MP_PROCESSOR as u8;
+ mpc_cpu.apicid = cpu_id;
+ mpc_cpu.apicver = APIC_VERSION;
+ mpc_cpu.cpuflag = CPU_ENABLED as u8
+ | if cpu_id == 0 {
+ CPU_BOOTPROCESSOR as u8
+ } else {
+ 0
+ };
+ mpc_cpu.cpufeature = CPU_STEPPING;
+ mpc_cpu.featureflag = CPU_FEATURE_APIC | CPU_FEATURE_FPU;
+ mem.write_obj_at_addr(mpc_cpu, base_mp)
+ .map_err(|_| Error::WriteMpcCpu)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_cpu));
+ }
+ {
+ let size = mem::size_of::<mpc_ioapic>();
+ let mut mpc_ioapic = mpc_ioapic::default();
+ mpc_ioapic.type_ = MP_IOAPIC as u8;
+ mpc_ioapic.apicid = ioapicid;
+ mpc_ioapic.apicver = APIC_VERSION;
+ mpc_ioapic.flags = MPC_APIC_USABLE as u8;
+ mpc_ioapic.apicaddr = IO_APIC_DEFAULT_PHYS_BASE;
+ mem.write_obj_at_addr(mpc_ioapic, base_mp)
+ .map_err(|_| Error::WriteMpcIoapic)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_ioapic));
+ }
+ {
+ let size = mem::size_of::<mpc_bus>();
+ let mut mpc_bus = mpc_bus::default();
+ mpc_bus.type_ = MP_BUS as u8;
+ mpc_bus.busid = PCI_BUS_ID;
+ mpc_bus.bustype = BUS_TYPE_PCI;
+ mem.write_obj_at_addr(mpc_bus, base_mp)
+ .map_err(|_| Error::WriteMpcBus)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_bus));
+ }
+ {
+ let size = mem::size_of::<mpc_bus>();
+ let mut mpc_bus = mpc_bus::default();
+ mpc_bus.type_ = MP_BUS as u8;
+ mpc_bus.busid = ISA_BUS_ID;
+ mpc_bus.bustype = BUS_TYPE_ISA;
+ mem.write_obj_at_addr(mpc_bus, base_mp)
+ .map_err(|_| Error::WriteMpcBus)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_bus));
+ }
+ {
+ let size = mem::size_of::<mpc_intsrc>();
+ let mut mpc_intsrc = mpc_intsrc::default();
+ mpc_intsrc.type_ = MP_INTSRC as u8;
+ mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
+ mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
+ mpc_intsrc.srcbus = ISA_BUS_ID;
+ mpc_intsrc.srcbusirq = 0;
+ mpc_intsrc.dstapic = 0;
+ mpc_intsrc.dstirq = 0;
+ mem.write_obj_at_addr(mpc_intsrc, base_mp)
+ .map_err(|_| Error::WriteMpcIntsrc)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
+ }
+ // Per kvm_setup_default_irq_routing() in kernel
+ for i in 0..5 {
+ let size = mem::size_of::<mpc_intsrc>();
+ let mut mpc_intsrc = mpc_intsrc::default();
+ mpc_intsrc.type_ = MP_INTSRC as u8;
+ mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
+ mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
+ mpc_intsrc.srcbus = ISA_BUS_ID;
+ mpc_intsrc.srcbusirq = i;
+ mpc_intsrc.dstapic = ioapicid;
+ mpc_intsrc.dstirq = i;
+ mem.write_obj_at_addr(mpc_intsrc, base_mp)
+ .map_err(|_| Error::WriteMpcIntsrc)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
+ }
+ // Insert PCI interrupts after platform IRQs.
+ for (i, pci_irq) in pci_irqs.iter().enumerate() {
+ let size = mem::size_of::<mpc_intsrc>();
+ let mut mpc_intsrc = mpc_intsrc::default();
+ mpc_intsrc.type_ = MP_INTSRC as u8;
+ mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
+ mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
+ mpc_intsrc.srcbus = PCI_BUS_ID;
+ mpc_intsrc.srcbusirq = (pci_irq.0 as u8 + 1) << 2 | pci_irq.1.to_mask() as u8;
+ mpc_intsrc.dstapic = ioapicid;
+ mpc_intsrc.dstirq = 5 + i as u8;
+ mem.write_obj_at_addr(mpc_intsrc, base_mp)
+ .map_err(|_| Error::WriteMpcIntsrc)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
+ }
+ // Finally insert ISA interrupts.
+ for i in 5 + pci_irqs.len()..16 {
+ let size = mem::size_of::<mpc_intsrc>();
+ let mut mpc_intsrc = mpc_intsrc::default();
+ mpc_intsrc.type_ = MP_INTSRC as u8;
+ mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
+ mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
+ mpc_intsrc.srcbus = ISA_BUS_ID;
+ mpc_intsrc.srcbusirq = i as u8;
+ mpc_intsrc.dstapic = ioapicid;
+ mpc_intsrc.dstirq = i as u8;
+ mem.write_obj_at_addr(mpc_intsrc, base_mp)
+ .map_err(|_| Error::WriteMpcIntsrc)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
+ }
+ {
+ let size = mem::size_of::<mpc_lintsrc>();
+ let mut mpc_lintsrc = mpc_lintsrc::default();
+ mpc_lintsrc.type_ = MP_LINTSRC as u8;
+ mpc_lintsrc.irqtype = mp_irq_source_types_mp_ExtINT as u8;
+ mpc_lintsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
+ mpc_lintsrc.srcbusid = ISA_BUS_ID;
+ mpc_lintsrc.srcbusirq = 0;
+ mpc_lintsrc.destapic = 0;
+ mpc_lintsrc.destapiclint = 0;
+ mem.write_obj_at_addr(mpc_lintsrc, base_mp)
+ .map_err(|_| Error::WriteMpcLintsrc)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_lintsrc));
+ }
+ {
+ let size = mem::size_of::<mpc_lintsrc>();
+ let mut mpc_lintsrc = mpc_lintsrc::default();
+ mpc_lintsrc.type_ = MP_LINTSRC as u8;
+ mpc_lintsrc.irqtype = mp_irq_source_types_mp_NMI as u8;
+ mpc_lintsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
+ mpc_lintsrc.srcbusid = ISA_BUS_ID;
+ mpc_lintsrc.srcbusirq = 0;
+ mpc_lintsrc.destapic = 0xFF; // Per SeaBIOS
+ mpc_lintsrc.destapiclint = 1;
+ mem.write_obj_at_addr(mpc_lintsrc, base_mp)
+ .map_err(|_| Error::WriteMpcLintsrc)?;
+ base_mp = base_mp.unchecked_add(size as u64);
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_lintsrc));
+ }
+
+ // At this point we know the size of the mp_table.
+ let table_end = base_mp;
+
+ {
+ let mut mpc_table = mpc_table::default();
+ mpc_table.signature = MPC_SIGNATURE;
+ mpc_table.length = table_end.offset_from(table_base) as u16;
+ mpc_table.spec = MPC_SPEC;
+ mpc_table.oem = MPC_OEM;
+ mpc_table.productid = MPC_PRODUCT_ID;
+ mpc_table.lapic = APIC_DEFAULT_PHYS_BASE;
+ checksum = checksum.wrapping_add(compute_checksum(&mpc_table));
+ mpc_table.checksum = (!checksum).wrapping_add(1) as i8;
+ mem.write_obj_at_addr(mpc_table, table_base)
+ .map_err(|_| Error::WriteMpcTable)?;
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use sys_util::pagesize;
+
+ fn compute_page_aligned_mp_size(num_cpus: u8) -> u64 {
+ let mp_size = compute_mp_size(num_cpus);
+ let pg_size = pagesize();
+ (mp_size + pg_size - (mp_size % pg_size)) as u64
+ }
+
+ fn table_entry_size(type_: u8) -> usize {
+ match type_ as u32 {
+ MP_PROCESSOR => mem::size_of::<mpc_cpu>(),
+ MP_BUS => mem::size_of::<mpc_bus>(),
+ MP_IOAPIC => mem::size_of::<mpc_ioapic>(),
+ MP_INTSRC => mem::size_of::<mpc_intsrc>(),
+ MP_LINTSRC => mem::size_of::<mpc_lintsrc>(),
+ _ => panic!("unrecognized mpc table entry type: {}", type_),
+ }
+ }
+
+ #[test]
+ fn bounds_check() {
+ let num_cpus = 4;
+ let mem = GuestMemory::new(&[(
+ GuestAddress(MPTABLE_START),
+ compute_page_aligned_mp_size(num_cpus),
+ )])
+ .unwrap();
+
+ setup_mptable(&mem, num_cpus, Vec::new()).unwrap();
+ }
+
+ #[test]
+ fn bounds_check_fails() {
+ let num_cpus = 255;
+ let mem = GuestMemory::new(&[(GuestAddress(MPTABLE_START), 0x1000)]).unwrap();
+
+ assert!(setup_mptable(&mem, num_cpus, Vec::new()).is_err());
+ }
+
+ #[test]
+ fn mpf_intel_checksum() {
+ let num_cpus = 1;
+ let mem = GuestMemory::new(&[(
+ GuestAddress(MPTABLE_START),
+ compute_page_aligned_mp_size(num_cpus),
+ )])
+ .unwrap();
+
+ setup_mptable(&mem, num_cpus, Vec::new()).unwrap();
+
+ let mpf_intel = mem.read_obj_from_addr(GuestAddress(MPTABLE_START)).unwrap();
+
+ assert_eq!(mpf_intel_compute_checksum(&mpf_intel), mpf_intel.checksum);
+ }
+
+ #[test]
+ fn mpc_table_checksum() {
+ let num_cpus = 4;
+ let mem = GuestMemory::new(&[(
+ GuestAddress(MPTABLE_START),
+ compute_page_aligned_mp_size(num_cpus),
+ )])
+ .unwrap();
+
+ setup_mptable(&mem, num_cpus, Vec::new()).unwrap();
+
+ let mpf_intel: mpf_intel = mem.read_obj_from_addr(GuestAddress(MPTABLE_START)).unwrap();
+ let mpc_offset = GuestAddress(mpf_intel.physptr as u64);
+ let mpc_table: mpc_table = mem.read_obj_from_addr(mpc_offset).unwrap();
+
+ let mut buf = vec![0; mpc_table.length as usize];
+ mem.read_at_addr(&mut buf[..], mpc_offset).unwrap();
+ let mut sum: u8 = 0;
+ for &v in &buf {
+ sum = sum.wrapping_add(v);
+ }
+
+ assert_eq!(sum, 0);
+ }
+
+ #[test]
+ fn cpu_entry_count() {
+ const MAX_CPUS: u8 = 0xff;
+ let mem = GuestMemory::new(&[(
+ GuestAddress(MPTABLE_START),
+ compute_page_aligned_mp_size(MAX_CPUS),
+ )])
+ .unwrap();
+
+ for i in 0..MAX_CPUS {
+ setup_mptable(&mem, i, Vec::new()).unwrap();
+
+ let mpf_intel: mpf_intel = mem.read_obj_from_addr(GuestAddress(MPTABLE_START)).unwrap();
+ let mpc_offset = GuestAddress(mpf_intel.physptr as u64);
+ let mpc_table: mpc_table = mem.read_obj_from_addr(mpc_offset).unwrap();
+ let mpc_end = mpc_offset.checked_add(mpc_table.length as u64).unwrap();
+
+ let mut entry_offset = mpc_offset
+ .checked_add(mem::size_of::<mpc_table>() as u64)
+ .unwrap();
+ let mut cpu_count = 0;
+ while entry_offset < mpc_end {
+ let entry_type: u8 = mem.read_obj_from_addr(entry_offset).unwrap();
+ entry_offset = entry_offset
+ .checked_add(table_entry_size(entry_type) as u64)
+ .unwrap();
+ assert!(entry_offset <= mpc_end);
+ if entry_type as u32 == MP_PROCESSOR {
+ cpu_count += 1;
+ }
+ }
+ assert_eq!(cpu_count, i);
+ }
+ }
+}
diff --git a/x86_64/src/msr_index.rs b/x86_64/src/msr_index.rs
new file mode 100644
index 0000000..f057835
--- /dev/null
+++ b/x86_64/src/msr_index.rs
@@ -0,0 +1,543 @@
+// Copyright 2017 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.
+
+/*
+ * automatically generated by rust-bindgen
+ * From upstream linux msr-index.h at commit:
+ * 806276b7f07a39a1cc3f38bb1ef5c573d4594a38
+ */
+
+pub const MSR_EFER: ::std::os::raw::c_uint = 0xc0000080;
+pub const MSR_STAR: ::std::os::raw::c_uint = 0xc0000081;
+pub const MSR_LSTAR: ::std::os::raw::c_uint = 0xc0000082;
+pub const MSR_CSTAR: ::std::os::raw::c_uint = 0xc0000083;
+pub const MSR_SYSCALL_MASK: ::std::os::raw::c_uint = 0xc0000084;
+pub const MSR_FS_BASE: ::std::os::raw::c_uint = 0xc0000100;
+pub const MSR_GS_BASE: ::std::os::raw::c_uint = 0xc0000101;
+pub const MSR_KERNEL_GS_BASE: ::std::os::raw::c_uint = 0xc0000102;
+pub const MSR_TSC_AUX: ::std::os::raw::c_uint = 0xc0000103;
+pub const _EFER_SCE: ::std::os::raw::c_uint = 0x00000000;
+pub const _EFER_LME: ::std::os::raw::c_uint = 0x00000008;
+pub const _EFER_LMA: ::std::os::raw::c_uint = 0x0000000a;
+pub const _EFER_NX: ::std::os::raw::c_uint = 0x0000000b;
+pub const _EFER_SVME: ::std::os::raw::c_uint = 0x0000000c;
+pub const _EFER_LMSLE: ::std::os::raw::c_uint = 0x0000000d;
+pub const _EFER_FFXSR: ::std::os::raw::c_uint = 0x0000000e;
+pub const EFER_SCE: ::std::os::raw::c_uint = 0x00000001;
+pub const EFER_LME: ::std::os::raw::c_uint = 0x00000100;
+pub const EFER_LMA: ::std::os::raw::c_uint = 0x00000400;
+pub const EFER_NX: ::std::os::raw::c_uint = 0x00000800;
+pub const EFER_SVME: ::std::os::raw::c_uint = 0x00001000;
+pub const EFER_LMSLE: ::std::os::raw::c_uint = 0x00002000;
+pub const EFER_FFXSR: ::std::os::raw::c_uint = 0x00004000;
+pub const MSR_PPIN_CTL: ::std::os::raw::c_uint = 0x0000004e;
+pub const MSR_PPIN: ::std::os::raw::c_uint = 0x0000004f;
+pub const MSR_IA32_PERFCTR0: ::std::os::raw::c_uint = 0x000000c1;
+pub const MSR_IA32_PERFCTR1: ::std::os::raw::c_uint = 0x000000c2;
+pub const MSR_FSB_FREQ: ::std::os::raw::c_uint = 0x000000cd;
+pub const MSR_PLATFORM_INFO: ::std::os::raw::c_uint = 0x000000ce;
+pub const MSR_PKG_CST_CONFIG_CONTROL: ::std::os::raw::c_uint = 0x000000e2;
+pub const NHM_C3_AUTO_DEMOTE: ::std::os::raw::c_uint = 0x02000000;
+pub const NHM_C1_AUTO_DEMOTE: ::std::os::raw::c_uint = 0x04000000;
+pub const ATM_LNC_C6_AUTO_DEMOTE: ::std::os::raw::c_uint = 0x02000000;
+pub const SNB_C1_AUTO_UNDEMOTE: ::std::os::raw::c_uint = 0x08000000;
+pub const SNB_C3_AUTO_UNDEMOTE: ::std::os::raw::c_uint = 0x10000000;
+pub const MSR_MTRRcap: ::std::os::raw::c_uint = 0x000000fe;
+pub const MSR_IA32_BBL_CR_CTL: ::std::os::raw::c_uint = 0x00000119;
+pub const MSR_IA32_BBL_CR_CTL3: ::std::os::raw::c_uint = 0x0000011e;
+pub const MSR_IA32_SYSENTER_CS: ::std::os::raw::c_uint = 0x00000174;
+pub const MSR_IA32_SYSENTER_ESP: ::std::os::raw::c_uint = 0x00000175;
+pub const MSR_IA32_SYSENTER_EIP: ::std::os::raw::c_uint = 0x00000176;
+pub const MSR_IA32_MCG_CAP: ::std::os::raw::c_uint = 0x00000179;
+pub const MSR_IA32_MCG_STATUS: ::std::os::raw::c_uint = 0x0000017a;
+pub const MSR_IA32_MCG_CTL: ::std::os::raw::c_uint = 0x0000017b;
+pub const MSR_IA32_MCG_EXT_CTL: ::std::os::raw::c_uint = 0x000004d0;
+pub const MSR_OFFCORE_RSP_0: ::std::os::raw::c_uint = 0x000001a6;
+pub const MSR_OFFCORE_RSP_1: ::std::os::raw::c_uint = 0x000001a7;
+pub const MSR_TURBO_RATIO_LIMIT: ::std::os::raw::c_uint = 0x000001ad;
+pub const MSR_TURBO_RATIO_LIMIT1: ::std::os::raw::c_uint = 0x000001ae;
+pub const MSR_TURBO_RATIO_LIMIT2: ::std::os::raw::c_uint = 0x000001af;
+pub const MSR_LBR_SELECT: ::std::os::raw::c_uint = 0x000001c8;
+pub const MSR_LBR_TOS: ::std::os::raw::c_uint = 0x000001c9;
+pub const MSR_LBR_NHM_FROM: ::std::os::raw::c_uint = 0x00000680;
+pub const MSR_LBR_NHM_TO: ::std::os::raw::c_uint = 0x000006c0;
+pub const MSR_LBR_CORE_FROM: ::std::os::raw::c_uint = 0x00000040;
+pub const MSR_LBR_CORE_TO: ::std::os::raw::c_uint = 0x00000060;
+pub const MSR_LBR_INFO_0: ::std::os::raw::c_uint = 0x00000dc0;
+pub const LBR_INFO_CYCLES: ::std::os::raw::c_uint = 0x0000ffff;
+pub const MSR_IA32_PEBS_ENABLE: ::std::os::raw::c_uint = 0x000003f1;
+pub const MSR_IA32_DS_AREA: ::std::os::raw::c_uint = 0x00000600;
+pub const MSR_IA32_PERF_CAPABILITIES: ::std::os::raw::c_uint = 0x00000345;
+pub const MSR_PEBS_LD_LAT_THRESHOLD: ::std::os::raw::c_uint = 0x000003f6;
+pub const MSR_IA32_RTIT_CTL: ::std::os::raw::c_uint = 0x00000570;
+pub const MSR_IA32_RTIT_STATUS: ::std::os::raw::c_uint = 0x00000571;
+pub const MSR_IA32_RTIT_ADDR0_A: ::std::os::raw::c_uint = 0x00000580;
+pub const MSR_IA32_RTIT_ADDR0_B: ::std::os::raw::c_uint = 0x00000581;
+pub const MSR_IA32_RTIT_ADDR1_A: ::std::os::raw::c_uint = 0x00000582;
+pub const MSR_IA32_RTIT_ADDR1_B: ::std::os::raw::c_uint = 0x00000583;
+pub const MSR_IA32_RTIT_ADDR2_A: ::std::os::raw::c_uint = 0x00000584;
+pub const MSR_IA32_RTIT_ADDR2_B: ::std::os::raw::c_uint = 0x00000585;
+pub const MSR_IA32_RTIT_ADDR3_A: ::std::os::raw::c_uint = 0x00000586;
+pub const MSR_IA32_RTIT_ADDR3_B: ::std::os::raw::c_uint = 0x00000587;
+pub const MSR_IA32_RTIT_CR3_MATCH: ::std::os::raw::c_uint = 0x00000572;
+pub const MSR_IA32_RTIT_OUTPUT_BASE: ::std::os::raw::c_uint = 0x00000560;
+pub const MSR_IA32_RTIT_OUTPUT_MASK: ::std::os::raw::c_uint = 0x00000561;
+pub const MSR_MTRRfix64K_00000: ::std::os::raw::c_uint = 0x00000250;
+pub const MSR_MTRRfix16K_80000: ::std::os::raw::c_uint = 0x00000258;
+pub const MSR_MTRRfix16K_A0000: ::std::os::raw::c_uint = 0x00000259;
+pub const MSR_MTRRfix4K_C0000: ::std::os::raw::c_uint = 0x00000268;
+pub const MSR_MTRRfix4K_C8000: ::std::os::raw::c_uint = 0x00000269;
+pub const MSR_MTRRfix4K_D0000: ::std::os::raw::c_uint = 0x0000026a;
+pub const MSR_MTRRfix4K_D8000: ::std::os::raw::c_uint = 0x0000026b;
+pub const MSR_MTRRfix4K_E0000: ::std::os::raw::c_uint = 0x0000026c;
+pub const MSR_MTRRfix4K_E8000: ::std::os::raw::c_uint = 0x0000026d;
+pub const MSR_MTRRfix4K_F0000: ::std::os::raw::c_uint = 0x0000026e;
+pub const MSR_MTRRfix4K_F8000: ::std::os::raw::c_uint = 0x0000026f;
+pub const MSR_MTRRdefType: ::std::os::raw::c_uint = 0x000002ff;
+pub const MSR_IA32_CR_PAT: ::std::os::raw::c_uint = 0x00000277;
+pub const MSR_IA32_DEBUGCTLMSR: ::std::os::raw::c_uint = 0x000001d9;
+pub const MSR_IA32_LASTBRANCHFROMIP: ::std::os::raw::c_uint = 0x000001db;
+pub const MSR_IA32_LASTBRANCHTOIP: ::std::os::raw::c_uint = 0x000001dc;
+pub const MSR_IA32_LASTINTFROMIP: ::std::os::raw::c_uint = 0x000001dd;
+pub const MSR_IA32_LASTINTTOIP: ::std::os::raw::c_uint = 0x000001de;
+pub const DEBUGCTLMSR_LBR: ::std::os::raw::c_uint = 0x00000001;
+pub const DEBUGCTLMSR_BTF: ::std::os::raw::c_uint = 0x00000002;
+pub const DEBUGCTLMSR_TR: ::std::os::raw::c_uint = 0x00000040;
+pub const DEBUGCTLMSR_BTS: ::std::os::raw::c_uint = 0x00000080;
+pub const DEBUGCTLMSR_BTINT: ::std::os::raw::c_uint = 0x00000100;
+pub const DEBUGCTLMSR_BTS_OFF_OS: ::std::os::raw::c_uint = 0x00000200;
+pub const DEBUGCTLMSR_BTS_OFF_USR: ::std::os::raw::c_uint = 0x00000400;
+pub const DEBUGCTLMSR_FREEZE_LBRS_ON_PMI: ::std::os::raw::c_uint = 0x00000800;
+pub const MSR_PEBS_FRONTEND: ::std::os::raw::c_uint = 0x000003f7;
+pub const MSR_IA32_POWER_CTL: ::std::os::raw::c_uint = 0x000001fc;
+pub const MSR_IA32_MC0_CTL: ::std::os::raw::c_uint = 0x00000400;
+pub const MSR_IA32_MC0_STATUS: ::std::os::raw::c_uint = 0x00000401;
+pub const MSR_IA32_MC0_ADDR: ::std::os::raw::c_uint = 0x00000402;
+pub const MSR_IA32_MC0_MISC: ::std::os::raw::c_uint = 0x00000403;
+pub const MSR_PKG_C3_RESIDENCY: ::std::os::raw::c_uint = 0x000003f8;
+pub const MSR_PKG_C6_RESIDENCY: ::std::os::raw::c_uint = 0x000003f9;
+pub const MSR_ATOM_PKG_C6_RESIDENCY: ::std::os::raw::c_uint = 0x000003fa;
+pub const MSR_PKG_C7_RESIDENCY: ::std::os::raw::c_uint = 0x000003fa;
+pub const MSR_CORE_C3_RESIDENCY: ::std::os::raw::c_uint = 0x000003fc;
+pub const MSR_CORE_C6_RESIDENCY: ::std::os::raw::c_uint = 0x000003fd;
+pub const MSR_CORE_C7_RESIDENCY: ::std::os::raw::c_uint = 0x000003fe;
+pub const MSR_KNL_CORE_C6_RESIDENCY: ::std::os::raw::c_uint = 0x000003ff;
+pub const MSR_PKG_C2_RESIDENCY: ::std::os::raw::c_uint = 0x0000060d;
+pub const MSR_PKG_C8_RESIDENCY: ::std::os::raw::c_uint = 0x00000630;
+pub const MSR_PKG_C9_RESIDENCY: ::std::os::raw::c_uint = 0x00000631;
+pub const MSR_PKG_C10_RESIDENCY: ::std::os::raw::c_uint = 0x00000632;
+pub const MSR_PKGC3_IRTL: ::std::os::raw::c_uint = 0x0000060a;
+pub const MSR_PKGC6_IRTL: ::std::os::raw::c_uint = 0x0000060b;
+pub const MSR_PKGC7_IRTL: ::std::os::raw::c_uint = 0x0000060c;
+pub const MSR_PKGC8_IRTL: ::std::os::raw::c_uint = 0x00000633;
+pub const MSR_PKGC9_IRTL: ::std::os::raw::c_uint = 0x00000634;
+pub const MSR_PKGC10_IRTL: ::std::os::raw::c_uint = 0x00000635;
+pub const MSR_RAPL_POWER_UNIT: ::std::os::raw::c_uint = 0x00000606;
+pub const MSR_PKG_POWER_LIMIT: ::std::os::raw::c_uint = 0x00000610;
+pub const MSR_PKG_ENERGY_STATUS: ::std::os::raw::c_uint = 0x00000611;
+pub const MSR_PKG_PERF_STATUS: ::std::os::raw::c_uint = 0x00000613;
+pub const MSR_PKG_POWER_INFO: ::std::os::raw::c_uint = 0x00000614;
+pub const MSR_DRAM_POWER_LIMIT: ::std::os::raw::c_uint = 0x00000618;
+pub const MSR_DRAM_ENERGY_STATUS: ::std::os::raw::c_uint = 0x00000619;
+pub const MSR_DRAM_PERF_STATUS: ::std::os::raw::c_uint = 0x0000061b;
+pub const MSR_DRAM_POWER_INFO: ::std::os::raw::c_uint = 0x0000061c;
+pub const MSR_PP0_POWER_LIMIT: ::std::os::raw::c_uint = 0x00000638;
+pub const MSR_PP0_ENERGY_STATUS: ::std::os::raw::c_uint = 0x00000639;
+pub const MSR_PP0_POLICY: ::std::os::raw::c_uint = 0x0000063a;
+pub const MSR_PP0_PERF_STATUS: ::std::os::raw::c_uint = 0x0000063b;
+pub const MSR_PP1_POWER_LIMIT: ::std::os::raw::c_uint = 0x00000640;
+pub const MSR_PP1_ENERGY_STATUS: ::std::os::raw::c_uint = 0x00000641;
+pub const MSR_PP1_POLICY: ::std::os::raw::c_uint = 0x00000642;
+pub const MSR_CONFIG_TDP_NOMINAL: ::std::os::raw::c_uint = 0x00000648;
+pub const MSR_CONFIG_TDP_LEVEL_1: ::std::os::raw::c_uint = 0x00000649;
+pub const MSR_CONFIG_TDP_LEVEL_2: ::std::os::raw::c_uint = 0x0000064a;
+pub const MSR_CONFIG_TDP_CONTROL: ::std::os::raw::c_uint = 0x0000064b;
+pub const MSR_TURBO_ACTIVATION_RATIO: ::std::os::raw::c_uint = 0x0000064c;
+pub const MSR_PLATFORM_ENERGY_STATUS: ::std::os::raw::c_uint = 0x0000064d;
+pub const MSR_PKG_WEIGHTED_CORE_C0_RES: ::std::os::raw::c_uint = 0x00000658;
+pub const MSR_PKG_ANY_CORE_C0_RES: ::std::os::raw::c_uint = 0x00000659;
+pub const MSR_PKG_ANY_GFXE_C0_RES: ::std::os::raw::c_uint = 0x0000065a;
+pub const MSR_PKG_BOTH_CORE_GFXE_C0_RES: ::std::os::raw::c_uint = 0x0000065b;
+pub const MSR_CORE_C1_RES: ::std::os::raw::c_uint = 0x00000660;
+pub const MSR_MODULE_C6_RES_MS: ::std::os::raw::c_uint = 0x00000664;
+pub const MSR_CC6_DEMOTION_POLICY_CONFIG: ::std::os::raw::c_uint = 0x00000668;
+pub const MSR_MC6_DEMOTION_POLICY_CONFIG: ::std::os::raw::c_uint = 0x00000669;
+pub const MSR_ATOM_CORE_RATIOS: ::std::os::raw::c_uint = 0x0000066a;
+pub const MSR_ATOM_CORE_VIDS: ::std::os::raw::c_uint = 0x0000066b;
+pub const MSR_ATOM_CORE_TURBO_RATIOS: ::std::os::raw::c_uint = 0x0000066c;
+pub const MSR_ATOM_CORE_TURBO_VIDS: ::std::os::raw::c_uint = 0x0000066d;
+pub const MSR_CORE_PERF_LIMIT_REASONS: ::std::os::raw::c_uint = 0x00000690;
+pub const MSR_GFX_PERF_LIMIT_REASONS: ::std::os::raw::c_uint = 0x000006b0;
+pub const MSR_RING_PERF_LIMIT_REASONS: ::std::os::raw::c_uint = 0x000006b1;
+pub const MSR_PPERF: ::std::os::raw::c_uint = 0x0000064e;
+pub const MSR_PERF_LIMIT_REASONS: ::std::os::raw::c_uint = 0x0000064f;
+pub const MSR_PM_ENABLE: ::std::os::raw::c_uint = 0x00000770;
+pub const MSR_HWP_CAPABILITIES: ::std::os::raw::c_uint = 0x00000771;
+pub const MSR_HWP_REQUEST_PKG: ::std::os::raw::c_uint = 0x00000772;
+pub const MSR_HWP_INTERRUPT: ::std::os::raw::c_uint = 0x00000773;
+pub const MSR_HWP_REQUEST: ::std::os::raw::c_uint = 0x00000774;
+pub const MSR_HWP_STATUS: ::std::os::raw::c_uint = 0x00000777;
+pub const HWP_BASE_BIT: ::std::os::raw::c_uint = 0x00000080;
+pub const HWP_NOTIFICATIONS_BIT: ::std::os::raw::c_uint = 0x00000100;
+pub const HWP_ACTIVITY_WINDOW_BIT: ::std::os::raw::c_uint = 0x00000200;
+pub const HWP_ENERGY_PERF_PREFERENCE_BIT: ::std::os::raw::c_uint = 0x00000400;
+pub const HWP_PACKAGE_LEVEL_REQUEST_BIT: ::std::os::raw::c_uint = 0x00000800;
+pub const MSR_AMD64_MC0_MASK: ::std::os::raw::c_uint = 0xc0010044;
+pub const MSR_IA32_MC0_CTL2: ::std::os::raw::c_uint = 0x00000280;
+pub const MSR_P6_PERFCTR0: ::std::os::raw::c_uint = 0x000000c1;
+pub const MSR_P6_PERFCTR1: ::std::os::raw::c_uint = 0x000000c2;
+pub const MSR_P6_EVNTSEL0: ::std::os::raw::c_uint = 0x00000186;
+pub const MSR_P6_EVNTSEL1: ::std::os::raw::c_uint = 0x00000187;
+pub const MSR_KNC_PERFCTR0: ::std::os::raw::c_uint = 0x00000020;
+pub const MSR_KNC_PERFCTR1: ::std::os::raw::c_uint = 0x00000021;
+pub const MSR_KNC_EVNTSEL0: ::std::os::raw::c_uint = 0x00000028;
+pub const MSR_KNC_EVNTSEL1: ::std::os::raw::c_uint = 0x00000029;
+pub const MSR_IA32_PMC0: ::std::os::raw::c_uint = 0x000004c1;
+pub const MSR_AMD64_PATCH_LEVEL: ::std::os::raw::c_uint = 0x0000008b;
+pub const MSR_AMD64_TSC_RATIO: ::std::os::raw::c_uint = 0xc0000104;
+pub const MSR_AMD64_NB_CFG: ::std::os::raw::c_uint = 0xc001001f;
+pub const MSR_AMD64_PATCH_LOADER: ::std::os::raw::c_uint = 0xc0010020;
+pub const MSR_AMD64_OSVW_ID_LENGTH: ::std::os::raw::c_uint = 0xc0010140;
+pub const MSR_AMD64_OSVW_STATUS: ::std::os::raw::c_uint = 0xc0010141;
+pub const MSR_AMD64_LS_CFG: ::std::os::raw::c_uint = 0xc0011020;
+pub const MSR_AMD64_DC_CFG: ::std::os::raw::c_uint = 0xc0011022;
+pub const MSR_AMD64_BU_CFG2: ::std::os::raw::c_uint = 0xc001102a;
+pub const MSR_AMD64_IBSFETCHCTL: ::std::os::raw::c_uint = 0xc0011030;
+pub const MSR_AMD64_IBSFETCHLINAD: ::std::os::raw::c_uint = 0xc0011031;
+pub const MSR_AMD64_IBSFETCHPHYSAD: ::std::os::raw::c_uint = 0xc0011032;
+pub const MSR_AMD64_IBSFETCH_REG_COUNT: ::std::os::raw::c_uint = 0x00000003;
+pub const MSR_AMD64_IBSFETCH_REG_MASK: ::std::os::raw::c_uint = 0x00000007;
+pub const MSR_AMD64_IBSOPCTL: ::std::os::raw::c_uint = 0xc0011033;
+pub const MSR_AMD64_IBSOPRIP: ::std::os::raw::c_uint = 0xc0011034;
+pub const MSR_AMD64_IBSOPDATA: ::std::os::raw::c_uint = 0xc0011035;
+pub const MSR_AMD64_IBSOPDATA2: ::std::os::raw::c_uint = 0xc0011036;
+pub const MSR_AMD64_IBSOPDATA3: ::std::os::raw::c_uint = 0xc0011037;
+pub const MSR_AMD64_IBSDCLINAD: ::std::os::raw::c_uint = 0xc0011038;
+pub const MSR_AMD64_IBSDCPHYSAD: ::std::os::raw::c_uint = 0xc0011039;
+pub const MSR_AMD64_IBSOP_REG_COUNT: ::std::os::raw::c_uint = 0x00000007;
+pub const MSR_AMD64_IBSOP_REG_MASK: ::std::os::raw::c_uint = 0x0000007f;
+pub const MSR_AMD64_IBSCTL: ::std::os::raw::c_uint = 0xc001103a;
+pub const MSR_AMD64_IBSBRTARGET: ::std::os::raw::c_uint = 0xc001103b;
+pub const MSR_AMD64_IBSOPDATA4: ::std::os::raw::c_uint = 0xc001103d;
+pub const MSR_AMD64_IBS_REG_COUNT_MAX: ::std::os::raw::c_uint = 0x00000008;
+pub const MSR_F17H_IRPERF: ::std::os::raw::c_uint = 0xc00000e9;
+pub const MSR_F16H_L2I_PERF_CTL: ::std::os::raw::c_uint = 0xc0010230;
+pub const MSR_F16H_L2I_PERF_CTR: ::std::os::raw::c_uint = 0xc0010231;
+pub const MSR_F16H_DR1_ADDR_MASK: ::std::os::raw::c_uint = 0xc0011019;
+pub const MSR_F16H_DR2_ADDR_MASK: ::std::os::raw::c_uint = 0xc001101a;
+pub const MSR_F16H_DR3_ADDR_MASK: ::std::os::raw::c_uint = 0xc001101b;
+pub const MSR_F16H_DR0_ADDR_MASK: ::std::os::raw::c_uint = 0xc0011027;
+pub const MSR_F15H_PERF_CTL: ::std::os::raw::c_uint = 0xc0010200;
+pub const MSR_F15H_PERF_CTR: ::std::os::raw::c_uint = 0xc0010201;
+pub const MSR_F15H_NB_PERF_CTL: ::std::os::raw::c_uint = 0xc0010240;
+pub const MSR_F15H_NB_PERF_CTR: ::std::os::raw::c_uint = 0xc0010241;
+pub const MSR_F15H_PTSC: ::std::os::raw::c_uint = 0xc0010280;
+pub const MSR_F15H_IC_CFG: ::std::os::raw::c_uint = 0xc0011021;
+pub const MSR_FAM10H_MMIO_CONF_BASE: ::std::os::raw::c_uint = 0xc0010058;
+pub const FAM10H_MMIO_CONF_ENABLE: ::std::os::raw::c_uint = 0x00000001;
+pub const FAM10H_MMIO_CONF_BUSRANGE_MASK: ::std::os::raw::c_uint = 0x0000000f;
+pub const FAM10H_MMIO_CONF_BUSRANGE_SHIFT: ::std::os::raw::c_uint = 0x00000002;
+pub const FAM10H_MMIO_CONF_BASE_MASK: ::std::os::raw::c_uint = 0x0fffffff;
+pub const FAM10H_MMIO_CONF_BASE_SHIFT: ::std::os::raw::c_uint = 0x00000014;
+pub const MSR_FAM10H_NODE_ID: ::std::os::raw::c_uint = 0xc001100c;
+pub const MSR_K8_TOP_MEM1: ::std::os::raw::c_uint = 0xc001001a;
+pub const MSR_K8_TOP_MEM2: ::std::os::raw::c_uint = 0xc001001d;
+pub const MSR_K8_SYSCFG: ::std::os::raw::c_uint = 0xc0010010;
+pub const MSR_K8_INT_PENDING_MSG: ::std::os::raw::c_uint = 0xc0010055;
+pub const K8_INTP_C1E_ACTIVE_MASK: ::std::os::raw::c_uint = 0x18000000;
+pub const MSR_K8_TSEG_ADDR: ::std::os::raw::c_uint = 0xc0010112;
+pub const MSR_K8_TSEG_MASK: ::std::os::raw::c_uint = 0xc0010113;
+pub const K8_MTRRFIXRANGE_DRAM_ENABLE: ::std::os::raw::c_uint = 0x00040000;
+pub const K8_MTRRFIXRANGE_DRAM_MODIFY: ::std::os::raw::c_uint = 0x00080000;
+pub const K8_MTRR_RDMEM_WRMEM_MASK: ::std::os::raw::c_uint = 0x18181818;
+pub const MSR_K7_EVNTSEL0: ::std::os::raw::c_uint = 0xc0010000;
+pub const MSR_K7_PERFCTR0: ::std::os::raw::c_uint = 0xc0010004;
+pub const MSR_K7_EVNTSEL1: ::std::os::raw::c_uint = 0xc0010001;
+pub const MSR_K7_PERFCTR1: ::std::os::raw::c_uint = 0xc0010005;
+pub const MSR_K7_EVNTSEL2: ::std::os::raw::c_uint = 0xc0010002;
+pub const MSR_K7_PERFCTR2: ::std::os::raw::c_uint = 0xc0010006;
+pub const MSR_K7_EVNTSEL3: ::std::os::raw::c_uint = 0xc0010003;
+pub const MSR_K7_PERFCTR3: ::std::os::raw::c_uint = 0xc0010007;
+pub const MSR_K7_CLK_CTL: ::std::os::raw::c_uint = 0xc001001b;
+pub const MSR_K7_HWCR: ::std::os::raw::c_uint = 0xc0010015;
+pub const MSR_K7_FID_VID_CTL: ::std::os::raw::c_uint = 0xc0010041;
+pub const MSR_K7_FID_VID_STATUS: ::std::os::raw::c_uint = 0xc0010042;
+pub const MSR_K6_WHCR: ::std::os::raw::c_uint = 0xc0000082;
+pub const MSR_K6_UWCCR: ::std::os::raw::c_uint = 0xc0000085;
+pub const MSR_K6_EPMR: ::std::os::raw::c_uint = 0xc0000086;
+pub const MSR_K6_PSOR: ::std::os::raw::c_uint = 0xc0000087;
+pub const MSR_K6_PFIR: ::std::os::raw::c_uint = 0xc0000088;
+pub const MSR_IDT_FCR1: ::std::os::raw::c_uint = 0x00000107;
+pub const MSR_IDT_FCR2: ::std::os::raw::c_uint = 0x00000108;
+pub const MSR_IDT_FCR3: ::std::os::raw::c_uint = 0x00000109;
+pub const MSR_IDT_FCR4: ::std::os::raw::c_uint = 0x0000010a;
+pub const MSR_IDT_MCR0: ::std::os::raw::c_uint = 0x00000110;
+pub const MSR_IDT_MCR1: ::std::os::raw::c_uint = 0x00000111;
+pub const MSR_IDT_MCR2: ::std::os::raw::c_uint = 0x00000112;
+pub const MSR_IDT_MCR3: ::std::os::raw::c_uint = 0x00000113;
+pub const MSR_IDT_MCR4: ::std::os::raw::c_uint = 0x00000114;
+pub const MSR_IDT_MCR5: ::std::os::raw::c_uint = 0x00000115;
+pub const MSR_IDT_MCR6: ::std::os::raw::c_uint = 0x00000116;
+pub const MSR_IDT_MCR7: ::std::os::raw::c_uint = 0x00000117;
+pub const MSR_IDT_MCR_CTRL: ::std::os::raw::c_uint = 0x00000120;
+pub const MSR_VIA_FCR: ::std::os::raw::c_uint = 0x00001107;
+pub const MSR_VIA_LONGHAUL: ::std::os::raw::c_uint = 0x0000110a;
+pub const MSR_VIA_RNG: ::std::os::raw::c_uint = 0x0000110b;
+pub const MSR_VIA_BCR2: ::std::os::raw::c_uint = 0x00001147;
+pub const MSR_TMTA_LONGRUN_CTRL: ::std::os::raw::c_uint = 0x80868010;
+pub const MSR_TMTA_LONGRUN_FLAGS: ::std::os::raw::c_uint = 0x80868011;
+pub const MSR_TMTA_LRTI_READOUT: ::std::os::raw::c_uint = 0x80868018;
+pub const MSR_TMTA_LRTI_VOLT_MHZ: ::std::os::raw::c_uint = 0x8086801a;
+pub const MSR_IA32_P5_MC_ADDR: ::std::os::raw::c_uint = 0x00000000;
+pub const MSR_IA32_P5_MC_TYPE: ::std::os::raw::c_uint = 0x00000001;
+pub const MSR_IA32_TSC: ::std::os::raw::c_uint = 0x00000010;
+pub const MSR_IA32_PLATFORM_ID: ::std::os::raw::c_uint = 0x00000017;
+pub const MSR_IA32_EBL_CR_POWERON: ::std::os::raw::c_uint = 0x0000002a;
+pub const MSR_EBC_FREQUENCY_ID: ::std::os::raw::c_uint = 0x0000002c;
+pub const MSR_SMI_COUNT: ::std::os::raw::c_uint = 0x00000034;
+pub const MSR_IA32_FEATURE_CONTROL: ::std::os::raw::c_uint = 0x0000003a;
+pub const MSR_IA32_TSC_ADJUST: ::std::os::raw::c_uint = 0x0000003b;
+pub const MSR_IA32_BNDCFGS: ::std::os::raw::c_uint = 0x00000d90;
+pub const MSR_IA32_XSS: ::std::os::raw::c_uint = 0x00000da0;
+pub const FEATURE_CONTROL_LOCKED: ::std::os::raw::c_uint = 0x00000001;
+pub const FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX: ::std::os::raw::c_uint = 0x00000002;
+pub const FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX: ::std::os::raw::c_uint = 0x00000004;
+pub const FEATURE_CONTROL_LMCE: ::std::os::raw::c_uint = 0x00100000;
+pub const MSR_IA32_APICBASE: ::std::os::raw::c_uint = 0x0000001b;
+pub const MSR_IA32_APICBASE_BSP: ::std::os::raw::c_uint = 0x00000100;
+pub const MSR_IA32_APICBASE_ENABLE: ::std::os::raw::c_uint = 0x00000800;
+pub const MSR_IA32_APICBASE_BASE: ::std::os::raw::c_uint = 0xfffff000;
+pub const MSR_IA32_TSCDEADLINE: ::std::os::raw::c_uint = 0x000006e0;
+pub const MSR_IA32_UCODE_WRITE: ::std::os::raw::c_uint = 0x00000079;
+pub const MSR_IA32_UCODE_REV: ::std::os::raw::c_uint = 0x0000008b;
+pub const MSR_IA32_SMM_MONITOR_CTL: ::std::os::raw::c_uint = 0x0000009b;
+pub const MSR_IA32_SMBASE: ::std::os::raw::c_uint = 0x0000009e;
+pub const MSR_IA32_PERF_STATUS: ::std::os::raw::c_uint = 0x00000198;
+pub const MSR_IA32_PERF_CTL: ::std::os::raw::c_uint = 0x00000199;
+pub const INTEL_PERF_CTL_MASK: ::std::os::raw::c_uint = 0x0000ffff;
+pub const MSR_AMD_PSTATE_DEF_BASE: ::std::os::raw::c_uint = 0xc0010064;
+pub const MSR_AMD_PERF_STATUS: ::std::os::raw::c_uint = 0xc0010063;
+pub const MSR_AMD_PERF_CTL: ::std::os::raw::c_uint = 0xc0010062;
+pub const MSR_IA32_MPERF: ::std::os::raw::c_uint = 0x000000e7;
+pub const MSR_IA32_APERF: ::std::os::raw::c_uint = 0x000000e8;
+pub const MSR_IA32_THERM_CONTROL: ::std::os::raw::c_uint = 0x0000019a;
+pub const MSR_IA32_THERM_INTERRUPT: ::std::os::raw::c_uint = 0x0000019b;
+pub const THERM_INT_HIGH_ENABLE: ::std::os::raw::c_uint = 0x00000001;
+pub const THERM_INT_LOW_ENABLE: ::std::os::raw::c_uint = 0x00000002;
+pub const THERM_INT_PLN_ENABLE: ::std::os::raw::c_uint = 0x01000000;
+pub const MSR_IA32_THERM_STATUS: ::std::os::raw::c_uint = 0x0000019c;
+pub const THERM_STATUS_PROCHOT: ::std::os::raw::c_uint = 0x00000001;
+pub const THERM_STATUS_POWER_LIMIT: ::std::os::raw::c_uint = 0x00000400;
+pub const MSR_THERM2_CTL: ::std::os::raw::c_uint = 0x0000019d;
+pub const MSR_THERM2_CTL_TM_SELECT: ::std::os::raw::c_uint = 0x00010000;
+pub const MSR_IA32_MISC_ENABLE: ::std::os::raw::c_uint = 0x000001a0;
+pub const MSR_IA32_TEMPERATURE_TARGET: ::std::os::raw::c_uint = 0x000001a2;
+pub const MSR_MISC_FEATURE_CONTROL: ::std::os::raw::c_uint = 0x000001a4;
+pub const MSR_MISC_PWR_MGMT: ::std::os::raw::c_uint = 0x000001aa;
+pub const MSR_IA32_ENERGY_PERF_BIAS: ::std::os::raw::c_uint = 0x000001b0;
+pub const ENERGY_PERF_BIAS_PERFORMANCE: ::std::os::raw::c_uint = 0x00000000;
+pub const ENERGY_PERF_BIAS_NORMAL: ::std::os::raw::c_uint = 0x00000006;
+pub const ENERGY_PERF_BIAS_POWERSAVE: ::std::os::raw::c_uint = 0x0000000f;
+pub const MSR_IA32_PACKAGE_THERM_STATUS: ::std::os::raw::c_uint = 0x000001b1;
+pub const PACKAGE_THERM_STATUS_PROCHOT: ::std::os::raw::c_uint = 0x00000001;
+pub const PACKAGE_THERM_STATUS_POWER_LIMIT: ::std::os::raw::c_uint = 0x00000400;
+pub const MSR_IA32_PACKAGE_THERM_INTERRUPT: ::std::os::raw::c_uint = 0x000001b2;
+pub const PACKAGE_THERM_INT_HIGH_ENABLE: ::std::os::raw::c_uint = 0x00000001;
+pub const PACKAGE_THERM_INT_LOW_ENABLE: ::std::os::raw::c_uint = 0x00000002;
+pub const PACKAGE_THERM_INT_PLN_ENABLE: ::std::os::raw::c_uint = 0x01000000;
+pub const THERM_INT_THRESHOLD0_ENABLE: ::std::os::raw::c_uint = 0x00008000;
+pub const THERM_SHIFT_THRESHOLD0: ::std::os::raw::c_uint = 0x00000008;
+pub const THERM_MASK_THRESHOLD0: ::std::os::raw::c_uint = 0x00007f00;
+pub const THERM_INT_THRESHOLD1_ENABLE: ::std::os::raw::c_uint = 0x00800000;
+pub const THERM_SHIFT_THRESHOLD1: ::std::os::raw::c_uint = 0x00000010;
+pub const THERM_MASK_THRESHOLD1: ::std::os::raw::c_uint = 0x007f0000;
+pub const THERM_STATUS_THRESHOLD0: ::std::os::raw::c_uint = 0x00000040;
+pub const THERM_LOG_THRESHOLD0: ::std::os::raw::c_uint = 0x00000080;
+pub const THERM_STATUS_THRESHOLD1: ::std::os::raw::c_uint = 0x00000100;
+pub const THERM_LOG_THRESHOLD1: ::std::os::raw::c_uint = 0x00000200;
+pub const MSR_IA32_MISC_ENABLE_FAST_STRING_BIT: ::std::os::raw::c_uint = 0x00000000;
+pub const MSR_IA32_MISC_ENABLE_FAST_STRING: ::std::os::raw::c_uint = 0x00000001;
+pub const MSR_IA32_MISC_ENABLE_TCC_BIT: ::std::os::raw::c_uint = 0x00000001;
+pub const MSR_IA32_MISC_ENABLE_TCC: ::std::os::raw::c_uint = 0x00000002;
+pub const MSR_IA32_MISC_ENABLE_EMON_BIT: ::std::os::raw::c_uint = 0x00000007;
+pub const MSR_IA32_MISC_ENABLE_EMON: ::std::os::raw::c_uint = 0x00000080;
+pub const MSR_IA32_MISC_ENABLE_BTS_UNAVAIL_BIT: ::std::os::raw::c_uint = 0x0000000b;
+pub const MSR_IA32_MISC_ENABLE_BTS_UNAVAIL: ::std::os::raw::c_uint = 0x00000800;
+pub const MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL_BIT: ::std::os::raw::c_uint = 0x0000000c;
+pub const MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL: ::std::os::raw::c_uint = 0x00001000;
+pub const MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP_BIT: ::std::os::raw::c_uint = 0x00000010;
+pub const MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP: ::std::os::raw::c_uint = 0x00010000;
+pub const MSR_IA32_MISC_ENABLE_MWAIT_BIT: ::std::os::raw::c_uint = 0x00000012;
+pub const MSR_IA32_MISC_ENABLE_MWAIT: ::std::os::raw::c_uint = 0x00040000;
+pub const MSR_IA32_MISC_ENABLE_LIMIT_CPUID_BIT: ::std::os::raw::c_uint = 0x00000016;
+pub const MSR_IA32_MISC_ENABLE_LIMIT_CPUID: ::std::os::raw::c_uint = 0x00400000;
+pub const MSR_IA32_MISC_ENABLE_XTPR_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000017;
+pub const MSR_IA32_MISC_ENABLE_XTPR_DISABLE: ::std::os::raw::c_uint = 0x00800000;
+pub const MSR_IA32_MISC_ENABLE_XD_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000022;
+pub const MSR_IA32_MISC_ENABLE_XD_DISABLE: ::std::os::raw::c_ulonglong = 0x400000000;
+pub const MSR_IA32_MISC_ENABLE_X87_COMPAT_BIT: ::std::os::raw::c_uint = 0x00000002;
+pub const MSR_IA32_MISC_ENABLE_X87_COMPAT: ::std::os::raw::c_uint = 0x00000004;
+pub const MSR_IA32_MISC_ENABLE_TM1_BIT: ::std::os::raw::c_uint = 0x00000003;
+pub const MSR_IA32_MISC_ENABLE_TM1: ::std::os::raw::c_uint = 0x00000008;
+pub const MSR_IA32_MISC_ENABLE_SPLIT_LOCK_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000004;
+pub const MSR_IA32_MISC_ENABLE_SPLIT_LOCK_DISABLE: ::std::os::raw::c_uint = 0x00000010;
+pub const MSR_IA32_MISC_ENABLE_L3CACHE_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000006;
+pub const MSR_IA32_MISC_ENABLE_L3CACHE_DISABLE: ::std::os::raw::c_uint = 0x00000040;
+pub const MSR_IA32_MISC_ENABLE_SUPPRESS_LOCK_BIT: ::std::os::raw::c_uint = 0x00000008;
+pub const MSR_IA32_MISC_ENABLE_SUPPRESS_LOCK: ::std::os::raw::c_uint = 0x00000100;
+pub const MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000009;
+pub const MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE: ::std::os::raw::c_uint = 0x00000200;
+pub const MSR_IA32_MISC_ENABLE_FERR_BIT: ::std::os::raw::c_uint = 0x0000000a;
+pub const MSR_IA32_MISC_ENABLE_FERR: ::std::os::raw::c_uint = 0x00000400;
+pub const MSR_IA32_MISC_ENABLE_FERR_MULTIPLEX_BIT: ::std::os::raw::c_uint = 0x0000000a;
+pub const MSR_IA32_MISC_ENABLE_FERR_MULTIPLEX: ::std::os::raw::c_uint = 0x00000400;
+pub const MSR_IA32_MISC_ENABLE_TM2_BIT: ::std::os::raw::c_uint = 0x0000000d;
+pub const MSR_IA32_MISC_ENABLE_TM2: ::std::os::raw::c_uint = 0x00002000;
+pub const MSR_IA32_MISC_ENABLE_ADJ_PREF_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000013;
+pub const MSR_IA32_MISC_ENABLE_ADJ_PREF_DISABLE: ::std::os::raw::c_uint = 0x00080000;
+pub const MSR_IA32_MISC_ENABLE_SPEEDSTEP_LOCK_BIT: ::std::os::raw::c_uint = 0x00000014;
+pub const MSR_IA32_MISC_ENABLE_SPEEDSTEP_LOCK: ::std::os::raw::c_uint = 0x00100000;
+pub const MSR_IA32_MISC_ENABLE_L1D_CONTEXT_BIT: ::std::os::raw::c_uint = 0x00000018;
+pub const MSR_IA32_MISC_ENABLE_L1D_CONTEXT: ::std::os::raw::c_uint = 0x01000000;
+pub const MSR_IA32_MISC_ENABLE_DCU_PREF_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000025;
+pub const MSR_IA32_MISC_ENABLE_DCU_PREF_DISABLE: ::std::os::raw::c_ulonglong = 0x2000000000;
+pub const MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000026;
+pub const MSR_IA32_MISC_ENABLE_TURBO_DISABLE: ::std::os::raw::c_ulonglong = 0x4000000000;
+pub const MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE_BIT: ::std::os::raw::c_uint = 0x00000027;
+pub const MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE: ::std::os::raw::c_ulonglong = 0x8000000000;
+pub const MSR_MISC_FEATURE_ENABLES: ::std::os::raw::c_uint = 0x00000140;
+pub const MSR_MISC_FEATURE_ENABLES_RING3MWAIT_BIT: ::std::os::raw::c_uint = 0x00000001;
+pub const MSR_IA32_TSC_DEADLINE: ::std::os::raw::c_uint = 0x000006e0;
+pub const MSR_IA32_MCG_EAX: ::std::os::raw::c_uint = 0x00000180;
+pub const MSR_IA32_MCG_EBX: ::std::os::raw::c_uint = 0x00000181;
+pub const MSR_IA32_MCG_ECX: ::std::os::raw::c_uint = 0x00000182;
+pub const MSR_IA32_MCG_EDX: ::std::os::raw::c_uint = 0x00000183;
+pub const MSR_IA32_MCG_ESI: ::std::os::raw::c_uint = 0x00000184;
+pub const MSR_IA32_MCG_EDI: ::std::os::raw::c_uint = 0x00000185;
+pub const MSR_IA32_MCG_EBP: ::std::os::raw::c_uint = 0x00000186;
+pub const MSR_IA32_MCG_ESP: ::std::os::raw::c_uint = 0x00000187;
+pub const MSR_IA32_MCG_EFLAGS: ::std::os::raw::c_uint = 0x00000188;
+pub const MSR_IA32_MCG_EIP: ::std::os::raw::c_uint = 0x00000189;
+pub const MSR_IA32_MCG_RESERVED: ::std::os::raw::c_uint = 0x0000018a;
+pub const MSR_P4_BPU_PERFCTR0: ::std::os::raw::c_uint = 0x00000300;
+pub const MSR_P4_BPU_PERFCTR1: ::std::os::raw::c_uint = 0x00000301;
+pub const MSR_P4_BPU_PERFCTR2: ::std::os::raw::c_uint = 0x00000302;
+pub const MSR_P4_BPU_PERFCTR3: ::std::os::raw::c_uint = 0x00000303;
+pub const MSR_P4_MS_PERFCTR0: ::std::os::raw::c_uint = 0x00000304;
+pub const MSR_P4_MS_PERFCTR1: ::std::os::raw::c_uint = 0x00000305;
+pub const MSR_P4_MS_PERFCTR2: ::std::os::raw::c_uint = 0x00000306;
+pub const MSR_P4_MS_PERFCTR3: ::std::os::raw::c_uint = 0x00000307;
+pub const MSR_P4_FLAME_PERFCTR0: ::std::os::raw::c_uint = 0x00000308;
+pub const MSR_P4_FLAME_PERFCTR1: ::std::os::raw::c_uint = 0x00000309;
+pub const MSR_P4_FLAME_PERFCTR2: ::std::os::raw::c_uint = 0x0000030a;
+pub const MSR_P4_FLAME_PERFCTR3: ::std::os::raw::c_uint = 0x0000030b;
+pub const MSR_P4_IQ_PERFCTR0: ::std::os::raw::c_uint = 0x0000030c;
+pub const MSR_P4_IQ_PERFCTR1: ::std::os::raw::c_uint = 0x0000030d;
+pub const MSR_P4_IQ_PERFCTR2: ::std::os::raw::c_uint = 0x0000030e;
+pub const MSR_P4_IQ_PERFCTR3: ::std::os::raw::c_uint = 0x0000030f;
+pub const MSR_P4_IQ_PERFCTR4: ::std::os::raw::c_uint = 0x00000310;
+pub const MSR_P4_IQ_PERFCTR5: ::std::os::raw::c_uint = 0x00000311;
+pub const MSR_P4_BPU_CCCR0: ::std::os::raw::c_uint = 0x00000360;
+pub const MSR_P4_BPU_CCCR1: ::std::os::raw::c_uint = 0x00000361;
+pub const MSR_P4_BPU_CCCR2: ::std::os::raw::c_uint = 0x00000362;
+pub const MSR_P4_BPU_CCCR3: ::std::os::raw::c_uint = 0x00000363;
+pub const MSR_P4_MS_CCCR0: ::std::os::raw::c_uint = 0x00000364;
+pub const MSR_P4_MS_CCCR1: ::std::os::raw::c_uint = 0x00000365;
+pub const MSR_P4_MS_CCCR2: ::std::os::raw::c_uint = 0x00000366;
+pub const MSR_P4_MS_CCCR3: ::std::os::raw::c_uint = 0x00000367;
+pub const MSR_P4_FLAME_CCCR0: ::std::os::raw::c_uint = 0x00000368;
+pub const MSR_P4_FLAME_CCCR1: ::std::os::raw::c_uint = 0x00000369;
+pub const MSR_P4_FLAME_CCCR2: ::std::os::raw::c_uint = 0x0000036a;
+pub const MSR_P4_FLAME_CCCR3: ::std::os::raw::c_uint = 0x0000036b;
+pub const MSR_P4_IQ_CCCR0: ::std::os::raw::c_uint = 0x0000036c;
+pub const MSR_P4_IQ_CCCR1: ::std::os::raw::c_uint = 0x0000036d;
+pub const MSR_P4_IQ_CCCR2: ::std::os::raw::c_uint = 0x0000036e;
+pub const MSR_P4_IQ_CCCR3: ::std::os::raw::c_uint = 0x0000036f;
+pub const MSR_P4_IQ_CCCR4: ::std::os::raw::c_uint = 0x00000370;
+pub const MSR_P4_IQ_CCCR5: ::std::os::raw::c_uint = 0x00000371;
+pub const MSR_P4_ALF_ESCR0: ::std::os::raw::c_uint = 0x000003ca;
+pub const MSR_P4_ALF_ESCR1: ::std::os::raw::c_uint = 0x000003cb;
+pub const MSR_P4_BPU_ESCR0: ::std::os::raw::c_uint = 0x000003b2;
+pub const MSR_P4_BPU_ESCR1: ::std::os::raw::c_uint = 0x000003b3;
+pub const MSR_P4_BSU_ESCR0: ::std::os::raw::c_uint = 0x000003a0;
+pub const MSR_P4_BSU_ESCR1: ::std::os::raw::c_uint = 0x000003a1;
+pub const MSR_P4_CRU_ESCR0: ::std::os::raw::c_uint = 0x000003b8;
+pub const MSR_P4_CRU_ESCR1: ::std::os::raw::c_uint = 0x000003b9;
+pub const MSR_P4_CRU_ESCR2: ::std::os::raw::c_uint = 0x000003cc;
+pub const MSR_P4_CRU_ESCR3: ::std::os::raw::c_uint = 0x000003cd;
+pub const MSR_P4_CRU_ESCR4: ::std::os::raw::c_uint = 0x000003e0;
+pub const MSR_P4_CRU_ESCR5: ::std::os::raw::c_uint = 0x000003e1;
+pub const MSR_P4_DAC_ESCR0: ::std::os::raw::c_uint = 0x000003a8;
+pub const MSR_P4_DAC_ESCR1: ::std::os::raw::c_uint = 0x000003a9;
+pub const MSR_P4_FIRM_ESCR0: ::std::os::raw::c_uint = 0x000003a4;
+pub const MSR_P4_FIRM_ESCR1: ::std::os::raw::c_uint = 0x000003a5;
+pub const MSR_P4_FLAME_ESCR0: ::std::os::raw::c_uint = 0x000003a6;
+pub const MSR_P4_FLAME_ESCR1: ::std::os::raw::c_uint = 0x000003a7;
+pub const MSR_P4_FSB_ESCR0: ::std::os::raw::c_uint = 0x000003a2;
+pub const MSR_P4_FSB_ESCR1: ::std::os::raw::c_uint = 0x000003a3;
+pub const MSR_P4_IQ_ESCR0: ::std::os::raw::c_uint = 0x000003ba;
+pub const MSR_P4_IQ_ESCR1: ::std::os::raw::c_uint = 0x000003bb;
+pub const MSR_P4_IS_ESCR0: ::std::os::raw::c_uint = 0x000003b4;
+pub const MSR_P4_IS_ESCR1: ::std::os::raw::c_uint = 0x000003b5;
+pub const MSR_P4_ITLB_ESCR0: ::std::os::raw::c_uint = 0x000003b6;
+pub const MSR_P4_ITLB_ESCR1: ::std::os::raw::c_uint = 0x000003b7;
+pub const MSR_P4_IX_ESCR0: ::std::os::raw::c_uint = 0x000003c8;
+pub const MSR_P4_IX_ESCR1: ::std::os::raw::c_uint = 0x000003c9;
+pub const MSR_P4_MOB_ESCR0: ::std::os::raw::c_uint = 0x000003aa;
+pub const MSR_P4_MOB_ESCR1: ::std::os::raw::c_uint = 0x000003ab;
+pub const MSR_P4_MS_ESCR0: ::std::os::raw::c_uint = 0x000003c0;
+pub const MSR_P4_MS_ESCR1: ::std::os::raw::c_uint = 0x000003c1;
+pub const MSR_P4_PMH_ESCR0: ::std::os::raw::c_uint = 0x000003ac;
+pub const MSR_P4_PMH_ESCR1: ::std::os::raw::c_uint = 0x000003ad;
+pub const MSR_P4_RAT_ESCR0: ::std::os::raw::c_uint = 0x000003bc;
+pub const MSR_P4_RAT_ESCR1: ::std::os::raw::c_uint = 0x000003bd;
+pub const MSR_P4_SAAT_ESCR0: ::std::os::raw::c_uint = 0x000003ae;
+pub const MSR_P4_SAAT_ESCR1: ::std::os::raw::c_uint = 0x000003af;
+pub const MSR_P4_SSU_ESCR0: ::std::os::raw::c_uint = 0x000003be;
+pub const MSR_P4_SSU_ESCR1: ::std::os::raw::c_uint = 0x000003bf;
+pub const MSR_P4_TBPU_ESCR0: ::std::os::raw::c_uint = 0x000003c2;
+pub const MSR_P4_TBPU_ESCR1: ::std::os::raw::c_uint = 0x000003c3;
+pub const MSR_P4_TC_ESCR0: ::std::os::raw::c_uint = 0x000003c4;
+pub const MSR_P4_TC_ESCR1: ::std::os::raw::c_uint = 0x000003c5;
+pub const MSR_P4_U2L_ESCR0: ::std::os::raw::c_uint = 0x000003b0;
+pub const MSR_P4_U2L_ESCR1: ::std::os::raw::c_uint = 0x000003b1;
+pub const MSR_P4_PEBS_MATRIX_VERT: ::std::os::raw::c_uint = 0x000003f2;
+pub const MSR_CORE_PERF_FIXED_CTR0: ::std::os::raw::c_uint = 0x00000309;
+pub const MSR_CORE_PERF_FIXED_CTR1: ::std::os::raw::c_uint = 0x0000030a;
+pub const MSR_CORE_PERF_FIXED_CTR2: ::std::os::raw::c_uint = 0x0000030b;
+pub const MSR_CORE_PERF_FIXED_CTR_CTRL: ::std::os::raw::c_uint = 0x0000038d;
+pub const MSR_CORE_PERF_GLOBAL_STATUS: ::std::os::raw::c_uint = 0x0000038e;
+pub const MSR_CORE_PERF_GLOBAL_CTRL: ::std::os::raw::c_uint = 0x0000038f;
+pub const MSR_CORE_PERF_GLOBAL_OVF_CTRL: ::std::os::raw::c_uint = 0x00000390;
+pub const MSR_GEODE_BUSCONT_CONF0: ::std::os::raw::c_uint = 0x00001900;
+pub const MSR_IA32_VMX_BASIC: ::std::os::raw::c_uint = 0x00000480;
+pub const MSR_IA32_VMX_PINBASED_CTLS: ::std::os::raw::c_uint = 0x00000481;
+pub const MSR_IA32_VMX_PROCBASED_CTLS: ::std::os::raw::c_uint = 0x00000482;
+pub const MSR_IA32_VMX_EXIT_CTLS: ::std::os::raw::c_uint = 0x00000483;
+pub const MSR_IA32_VMX_ENTRY_CTLS: ::std::os::raw::c_uint = 0x00000484;
+pub const MSR_IA32_VMX_MISC: ::std::os::raw::c_uint = 0x00000485;
+pub const MSR_IA32_VMX_CR0_FIXED0: ::std::os::raw::c_uint = 0x00000486;
+pub const MSR_IA32_VMX_CR0_FIXED1: ::std::os::raw::c_uint = 0x00000487;
+pub const MSR_IA32_VMX_CR4_FIXED0: ::std::os::raw::c_uint = 0x00000488;
+pub const MSR_IA32_VMX_CR4_FIXED1: ::std::os::raw::c_uint = 0x00000489;
+pub const MSR_IA32_VMX_VMCS_ENUM: ::std::os::raw::c_uint = 0x0000048a;
+pub const MSR_IA32_VMX_PROCBASED_CTLS2: ::std::os::raw::c_uint = 0x0000048b;
+pub const MSR_IA32_VMX_EPT_VPID_CAP: ::std::os::raw::c_uint = 0x0000048c;
+pub const MSR_IA32_VMX_TRUE_PINBASED_CTLS: ::std::os::raw::c_uint = 0x0000048d;
+pub const MSR_IA32_VMX_TRUE_PROCBASED_CTLS: ::std::os::raw::c_uint = 0x0000048e;
+pub const MSR_IA32_VMX_TRUE_EXIT_CTLS: ::std::os::raw::c_uint = 0x0000048f;
+pub const MSR_IA32_VMX_TRUE_ENTRY_CTLS: ::std::os::raw::c_uint = 0x00000490;
+pub const MSR_IA32_VMX_VMFUNC: ::std::os::raw::c_uint = 0x00000491;
+pub const VMX_BASIC_VMCS_SIZE_SHIFT: ::std::os::raw::c_uint = 0x00000020;
+pub const VMX_BASIC_TRUE_CTLS: ::std::os::raw::c_ulonglong = 0x80000000000000;
+pub const VMX_BASIC_64: ::std::os::raw::c_ulonglong = 0x1000000000000;
+pub const VMX_BASIC_MEM_TYPE_SHIFT: ::std::os::raw::c_uint = 0x00000032;
+pub const VMX_BASIC_MEM_TYPE_MASK: ::std::os::raw::c_ulonglong = 0x3c000000000000;
+pub const VMX_BASIC_MEM_TYPE_WB: ::std::os::raw::c_uint = 0x00000006;
+pub const VMX_BASIC_INOUT: ::std::os::raw::c_ulonglong = 0x40000000000000;
+pub const MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS: ::std::os::raw::c_uint = 0x20000000;
+pub const MSR_IA32_VMX_MISC_PREEMPTION_TIMER_SCALE: ::std::os::raw::c_uint = 0x0000001f;
+pub const MSR_VM_CR: ::std::os::raw::c_uint = 0xc0010114;
+pub const MSR_VM_IGNNE: ::std::os::raw::c_uint = 0xc0010115;
+pub const MSR_VM_HSAVE_PA: ::std::os::raw::c_uint = 0xc0010117;
diff --git a/x86_64/src/regs.rs b/x86_64/src/regs.rs
new file mode 100644
index 0000000..8879a0c
--- /dev/null
+++ b/x86_64/src/regs.rs
@@ -0,0 +1,370 @@
+// Copyright 2017 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::alloc::Layout;
+use std::fmt::{self, Display};
+use std::{mem, result};
+
+use assertions::const_assert;
+use kvm;
+use kvm_sys::kvm_fpu;
+use kvm_sys::kvm_msr_entry;
+use kvm_sys::kvm_msrs;
+use kvm_sys::kvm_regs;
+use kvm_sys::kvm_sregs;
+use sys_util::{self, GuestAddress, GuestMemory, LayoutAllocation};
+
+use crate::gdt;
+
+#[derive(Debug)]
+pub enum Error {
+ /// Setting up msrs failed.
+ MsrIoctlFailed(sys_util::Error),
+ /// Failed to configure the FPU.
+ FpuIoctlFailed(sys_util::Error),
+ /// Failed to get sregs for this cpu.
+ GetSRegsIoctlFailed(sys_util::Error),
+ /// Failed to set base registers for this cpu.
+ SettingRegistersIoctl(sys_util::Error),
+ /// Failed to set sregs for this cpu.
+ SetSRegsIoctlFailed(sys_util::Error),
+ /// Writing the GDT to RAM failed.
+ WriteGDTFailure,
+ /// Writing the IDT to RAM failed.
+ WriteIDTFailure,
+ /// Writing PML4 to RAM failed.
+ WritePML4Address,
+ /// Writing PDPTE to RAM failed.
+ WritePDPTEAddress,
+ /// Writing PDE to RAM failed.
+ WritePDEAddress,
+}
+pub type Result<T> = result::Result<T, Error>;
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ MsrIoctlFailed(e) => write!(f, "setting up msrs failed: {}", e),
+ FpuIoctlFailed(e) => write!(f, "failed to configure the FPU: {}", e),
+ GetSRegsIoctlFailed(e) => write!(f, "failed to get sregs for this cpu: {}", e),
+ SettingRegistersIoctl(e) => {
+ write!(f, "failed to set base registers for this cpu: {}", e)
+ }
+ SetSRegsIoctlFailed(e) => write!(f, "failed to set sregs for this cpu: {}", e),
+ WriteGDTFailure => write!(f, "writing the GDT to RAM failed"),
+ WriteIDTFailure => write!(f, "writing the IDT to RAM failed"),
+ WritePML4Address => write!(f, "writing PML4 to RAM failed"),
+ WritePDPTEAddress => write!(f, "writing PDPTE to RAM failed"),
+ WritePDEAddress => write!(f, "writing PDE to RAM failed"),
+ }
+ }
+}
+
+fn create_msr_entries() -> Vec<kvm_msr_entry> {
+ let mut entries = Vec::<kvm_msr_entry>::new();
+
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_IA32_SYSENTER_CS,
+ data: 0x0,
+ ..Default::default()
+ });
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_IA32_SYSENTER_ESP,
+ data: 0x0,
+ ..Default::default()
+ });
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_IA32_SYSENTER_EIP,
+ data: 0x0,
+ ..Default::default()
+ });
+ // x86_64 specific msrs, we only run on x86_64 not x86
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_STAR,
+ data: 0x0,
+ ..Default::default()
+ });
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_CSTAR,
+ data: 0x0,
+ ..Default::default()
+ });
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_KERNEL_GS_BASE,
+ data: 0x0,
+ ..Default::default()
+ });
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_SYSCALL_MASK,
+ data: 0x0,
+ ..Default::default()
+ });
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_LSTAR,
+ data: 0x0,
+ ..Default::default()
+ });
+ // end of x86_64 specific code
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_IA32_TSC,
+ data: 0x0,
+ ..Default::default()
+ });
+ entries.push(kvm_msr_entry {
+ index: crate::msr_index::MSR_IA32_MISC_ENABLE,
+ data: crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64,
+ ..Default::default()
+ });
+
+ entries
+}
+
+/// Configure Model specific registers for x86
+///
+/// # Arguments
+///
+/// * `vcpu` - Structure for the vcpu that holds the vcpu fd.
+pub fn setup_msrs(vcpu: &kvm::Vcpu) -> Result<()> {
+ const SIZE_OF_MSRS: usize = mem::size_of::<kvm_msrs>();
+ const SIZE_OF_ENTRY: usize = mem::size_of::<kvm_msr_entry>();
+ const ALIGN_OF_MSRS: usize = mem::align_of::<kvm_msrs>();
+ const ALIGN_OF_ENTRY: usize = mem::align_of::<kvm_msr_entry>();
+ const_assert!(ALIGN_OF_MSRS >= ALIGN_OF_ENTRY);
+
+ let entry_vec = create_msr_entries();
+ let size = SIZE_OF_MSRS + entry_vec.len() * SIZE_OF_ENTRY;
+ let layout = Layout::from_size_align(size, ALIGN_OF_MSRS).expect("impossible layout");
+ let mut allocation = LayoutAllocation::zeroed(layout);
+
+ // Safe to obtain an exclusive reference because there are no other
+ // references to the allocation yet and all-zero is a valid bit pattern.
+ let msrs = unsafe { allocation.as_mut::<kvm_msrs>() };
+
+ unsafe {
+ // Mapping the unsized array to a slice is unsafe becase the length isn't known. Providing
+ // the length used to create the struct guarantees the entire slice is valid.
+ let entries: &mut [kvm_msr_entry] = msrs.entries.as_mut_slice(entry_vec.len());
+ entries.copy_from_slice(&entry_vec);
+ }
+ msrs.nmsrs = entry_vec.len() as u32;
+
+ vcpu.set_msrs(msrs).map_err(Error::MsrIoctlFailed)?;
+
+ Ok(())
+
+ // msrs allocation is deallocated.
+}
+
+/// Configure FPU registers for x86
+///
+/// # Arguments
+///
+/// * `vcpu` - Structure for the vcpu that holds the vcpu fd.
+pub fn setup_fpu(vcpu: &kvm::Vcpu) -> Result<()> {
+ let fpu: kvm_fpu = kvm_fpu {
+ fcw: 0x37f,
+ mxcsr: 0x1f80,
+ ..Default::default()
+ };
+
+ vcpu.set_fpu(&fpu).map_err(Error::FpuIoctlFailed)?;
+
+ Ok(())
+}
+
+/// Configure base registers for x86
+///
+/// # Arguments
+///
+/// * `vcpu` - Structure for the vcpu that holds the vcpu fd.
+/// * `boot_ip` - Starting instruction pointer.
+/// * `boot_sp` - Starting stack pointer.
+/// * `boot_si` - Must point to zero page address per Linux ABI.
+pub fn setup_regs(vcpu: &kvm::Vcpu, boot_ip: u64, boot_sp: u64, boot_si: u64) -> Result<()> {
+ let regs: kvm_regs = kvm_regs {
+ rflags: 0x0000000000000002u64,
+ rip: boot_ip,
+ rsp: boot_sp,
+ rbp: boot_sp,
+ rsi: boot_si,
+ ..Default::default()
+ };
+
+ vcpu.set_regs(®s).map_err(Error::SettingRegistersIoctl)?;
+
+ Ok(())
+}
+
+const X86_CR0_PE: u64 = 0x1;
+const X86_CR0_PG: u64 = 0x80000000;
+const X86_CR4_PAE: u64 = 0x20;
+
+const EFER_LME: u64 = 0x100;
+const EFER_LMA: u64 = 0x400;
+
+const BOOT_GDT_OFFSET: u64 = 0x500;
+const BOOT_IDT_OFFSET: u64 = 0x520;
+
+const BOOT_GDT_MAX: usize = 4;
+
+fn write_gdt_table(table: &[u64], guest_mem: &GuestMemory) -> Result<()> {
+ let boot_gdt_addr = GuestAddress(BOOT_GDT_OFFSET);
+ for (index, entry) in table.iter().enumerate() {
+ let addr = guest_mem
+ .checked_offset(boot_gdt_addr, (index * mem::size_of::<u64>()) as u64)
+ .ok_or(Error::WriteGDTFailure)?;
+ guest_mem
+ .write_obj_at_addr(*entry, addr)
+ .map_err(|_| Error::WriteGDTFailure)?;
+ }
+ Ok(())
+}
+
+fn write_idt_value(val: u64, guest_mem: &GuestMemory) -> Result<()> {
+ let boot_idt_addr = GuestAddress(BOOT_IDT_OFFSET);
+ guest_mem
+ .write_obj_at_addr(val, boot_idt_addr)
+ .map_err(|_| Error::WriteIDTFailure)
+}
+
+fn configure_segments_and_sregs(mem: &GuestMemory, sregs: &mut kvm_sregs) -> Result<()> {
+ let gdt_table: [u64; BOOT_GDT_MAX as usize] = [
+ gdt::gdt_entry(0, 0, 0), // NULL
+ gdt::gdt_entry(0xa09b, 0, 0xfffff), // CODE
+ gdt::gdt_entry(0xc093, 0, 0xfffff), // DATA
+ gdt::gdt_entry(0x808b, 0, 0xfffff), // TSS
+ ];
+
+ let code_seg = gdt::kvm_segment_from_gdt(gdt_table[1], 1);
+ let data_seg = gdt::kvm_segment_from_gdt(gdt_table[2], 2);
+ let tss_seg = gdt::kvm_segment_from_gdt(gdt_table[3], 3);
+
+ // Write segments
+ write_gdt_table(&gdt_table[..], mem)?;
+ sregs.gdt.base = BOOT_GDT_OFFSET as u64;
+ sregs.gdt.limit = mem::size_of_val(&gdt_table) as u16 - 1;
+
+ write_idt_value(0, mem)?;
+ sregs.idt.base = BOOT_IDT_OFFSET as u64;
+ sregs.idt.limit = mem::size_of::<u64>() as u16 - 1;
+
+ sregs.cs = code_seg;
+ sregs.ds = data_seg;
+ sregs.es = data_seg;
+ sregs.fs = data_seg;
+ sregs.gs = data_seg;
+ sregs.ss = data_seg;
+ sregs.tr = tss_seg;
+
+ /* 64-bit protected mode */
+ sregs.cr0 |= X86_CR0_PE;
+ sregs.efer |= EFER_LME;
+
+ Ok(())
+}
+
+fn setup_page_tables(mem: &GuestMemory, sregs: &mut kvm_sregs) -> Result<()> {
+ // Puts PML4 right after zero page but aligned to 4k.
+ let boot_pml4_addr = GuestAddress(0x9000);
+ let boot_pdpte_addr = GuestAddress(0xa000);
+ let boot_pde_addr = GuestAddress(0xb000);
+
+ // Entry covering VA [0..512GB)
+ mem.write_obj_at_addr(boot_pdpte_addr.offset() as u64 | 0x03, boot_pml4_addr)
+ .map_err(|_| Error::WritePML4Address)?;
+
+ // Entry covering VA [0..1GB)
+ mem.write_obj_at_addr(boot_pde_addr.offset() as u64 | 0x03, boot_pdpte_addr)
+ .map_err(|_| Error::WritePDPTEAddress)?;
+
+ // 512 2MB entries together covering VA [0..1GB). Note we are assuming
+ // CPU supports 2MB pages (/proc/cpuinfo has 'pse'). All modern CPUs do.
+ for i in 0..512 {
+ mem.write_obj_at_addr((i << 21) + 0x83u64, boot_pde_addr.unchecked_add(i * 8))
+ .map_err(|_| Error::WritePDEAddress)?;
+ }
+ sregs.cr3 = boot_pml4_addr.offset() as u64;
+ sregs.cr4 |= X86_CR4_PAE;
+ sregs.cr0 |= X86_CR0_PG;
+ sregs.efer |= EFER_LMA; // Long mode is active. Must be auto-enabled with CR0_PG.
+ Ok(())
+}
+
+/// Configures the segment registers and system page tables for a given CPU.
+///
+/// # Arguments
+///
+/// * `mem` - The memory that will be passed to the guest.
+/// * `vcpu_fd` - The FD returned from the KVM_CREATE_VCPU ioctl.
+pub fn setup_sregs(mem: &GuestMemory, vcpu: &kvm::Vcpu) -> Result<()> {
+ let mut sregs: kvm_sregs = vcpu.get_sregs().map_err(Error::GetSRegsIoctlFailed)?;
+
+ configure_segments_and_sregs(mem, &mut sregs)?;
+ setup_page_tables(mem, &mut sregs)?; // TODO(dgreid) - Can this be done once per system instead?
+
+ vcpu.set_sregs(&sregs).map_err(Error::SetSRegsIoctlFailed)?;
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use sys_util::{GuestAddress, GuestMemory};
+
+ fn create_guest_mem() -> GuestMemory {
+ GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap()
+ }
+
+ fn read_u64(gm: &GuestMemory, offset: u64) -> u64 {
+ let read_addr = GuestAddress(offset);
+ gm.read_obj_from_addr(read_addr).unwrap()
+ }
+
+ #[test]
+ fn segments_and_sregs() {
+ let mut sregs: kvm_sregs = Default::default();
+ let gm = create_guest_mem();
+ configure_segments_and_sregs(&gm, &mut sregs).unwrap();
+
+ assert_eq!(0x0, read_u64(&gm, BOOT_GDT_OFFSET));
+ assert_eq!(0xaf9b000000ffff, read_u64(&gm, BOOT_GDT_OFFSET + 8));
+ assert_eq!(0xcf93000000ffff, read_u64(&gm, BOOT_GDT_OFFSET + 16));
+ assert_eq!(0x8f8b000000ffff, read_u64(&gm, BOOT_GDT_OFFSET + 24));
+ assert_eq!(0x0, read_u64(&gm, BOOT_IDT_OFFSET));
+
+ assert_eq!(0, sregs.cs.base);
+ assert_eq!(0xfffff, sregs.ds.limit);
+ assert_eq!(0x10, sregs.es.selector);
+ assert_eq!(1, sregs.fs.present);
+ assert_eq!(1, sregs.gs.g);
+ assert_eq!(0, sregs.ss.avl);
+ assert_eq!(0, sregs.tr.base);
+ assert_eq!(0xfffff, sregs.tr.limit);
+ assert_eq!(0, sregs.tr.avl);
+ assert_eq!(X86_CR0_PE, sregs.cr0);
+ assert_eq!(EFER_LME, sregs.efer);
+ }
+
+ #[test]
+ fn page_tables() {
+ let mut sregs: kvm_sregs = Default::default();
+ let gm = create_guest_mem();
+ setup_page_tables(&gm, &mut sregs).unwrap();
+
+ assert_eq!(0xa003, read_u64(&gm, 0x9000));
+ assert_eq!(0xb003, read_u64(&gm, 0xa000));
+ for i in 0..512 {
+ assert_eq!((i << 21) + 0x83u64, read_u64(&gm, 0xb000 + i * 8));
+ }
+
+ assert_eq!(0x9000, sregs.cr3);
+ assert_eq!(X86_CR4_PAE, sregs.cr4);
+ assert_eq!(X86_CR0_PG, sregs.cr0);
+ }
+}
diff --git a/x86_64/src/smbios.rs b/x86_64/src/smbios.rs
new file mode 100644
index 0000000..0ae3851
--- /dev/null
+++ b/x86_64/src/smbios.rs
@@ -0,0 +1,247 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::fmt::{self, Display};
+use std::mem;
+use std::result;
+use std::slice;
+
+use data_model::DataInit;
+use sys_util::{GuestAddress, GuestMemory};
+
+#[derive(Debug)]
+pub enum Error {
+ /// There was too little guest memory to store the entire SMBIOS table.
+ NotEnoughMemory,
+ /// The SMBIOS table has too little address space to be stored.
+ AddressOverflow,
+ /// Failure while zeroing out the memory for the SMBIOS table.
+ Clear,
+ /// Failure to write SMBIOS entrypoint structure
+ WriteSmbiosEp,
+ /// Failure to write additional data to memory
+ WriteData,
+}
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ let description = match self {
+ NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
+ AddressOverflow => "The SMBIOS table has too little address space to be stored",
+ Clear => "Failure while zeroing out the memory for the SMBIOS table",
+ WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
+ WriteData => "Failure to write additional data to memory",
+ };
+
+ write!(f, "SMBIOS error: {}", description)
+ }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+const SMBIOS_START: u64 = 0xf0000; // First possible location per the spec.
+
+// Constants sourced from SMBIOS Spec 3.2.0.
+const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
+const BIOS_INFORMATION: u8 = 0;
+const SYSTEM_INFORMATION: u8 = 1;
+const PCI_SUPPORTED: u64 = 1 << 7;
+const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
+
+fn compute_checksum<T: Copy>(v: &T) -> u8 {
+ // Safe because we are only reading the bytes within the size of the `T` reference `v`.
+ let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
+ let mut checksum: u8 = 0;
+ for i in v_slice.iter() {
+ checksum = checksum.wrapping_add(*i);
+ }
+ (!checksum).wrapping_add(1)
+}
+
+#[repr(packed)]
+#[derive(Default, Copy)]
+pub struct Smbios30Entrypoint {
+ pub signature: [u8; 5usize],
+ pub checksum: u8,
+ pub length: u8,
+ pub majorver: u8,
+ pub minorver: u8,
+ pub docrev: u8,
+ pub revision: u8,
+ pub reserved: u8,
+ pub max_size: u32,
+ pub physptr: u64,
+}
+unsafe impl data_model::DataInit for Smbios30Entrypoint {}
+
+impl Clone for Smbios30Entrypoint {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+#[repr(packed)]
+#[derive(Default, Copy)]
+pub struct SmbiosBiosInfo {
+ pub typ: u8,
+ pub length: u8,
+ pub handle: u16,
+ pub vendor: u8,
+ pub version: u8,
+ pub start_addr: u16,
+ pub release_date: u8,
+ pub rom_size: u8,
+ pub characteristics: u64,
+ pub characteristics_ext1: u8,
+ pub characteristics_ext2: u8,
+}
+
+impl Clone for SmbiosBiosInfo {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+unsafe impl data_model::DataInit for SmbiosBiosInfo {}
+
+#[repr(packed)]
+#[derive(Default, Copy)]
+pub struct SmbiosSysInfo {
+ pub typ: u8,
+ pub length: u8,
+ pub handle: u16,
+ pub manufacturer: u8,
+ pub product_name: u8,
+ pub version: u8,
+ pub serial_number: u8,
+ pub uuid: [u8; 16usize],
+ pub wake_up_type: u8,
+ pub sku: u8,
+ pub family: u8,
+}
+
+impl Clone for SmbiosSysInfo {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+unsafe impl data_model::DataInit for SmbiosSysInfo {}
+
+fn write_and_incr<T: DataInit>(
+ mem: &GuestMemory,
+ val: T,
+ mut curptr: GuestAddress,
+) -> Result<GuestAddress> {
+ mem.write_obj_at_addr(val, curptr)
+ .map_err(|_| Error::WriteData)?;
+ curptr = curptr
+ .checked_add(mem::size_of::<T>() as u64)
+ .ok_or(Error::NotEnoughMemory)?;
+ Ok(curptr)
+}
+
+fn write_string(mem: &GuestMemory, val: &str, mut curptr: GuestAddress) -> Result<GuestAddress> {
+ for c in val.as_bytes().iter() {
+ curptr = write_and_incr(mem, *c, curptr)?;
+ }
+ curptr = write_and_incr(mem, 0 as u8, curptr)?;
+ Ok(curptr)
+}
+
+pub fn setup_smbios(mem: &GuestMemory) -> Result<()> {
+ let physptr = GuestAddress(SMBIOS_START)
+ .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
+ .ok_or(Error::NotEnoughMemory)?;
+ let mut curptr = physptr;
+ let mut handle = 0;
+
+ {
+ handle += 1;
+ let mut smbios_biosinfo = SmbiosBiosInfo::default();
+ smbios_biosinfo.typ = BIOS_INFORMATION;
+ smbios_biosinfo.length = mem::size_of::<SmbiosBiosInfo>() as u8;
+ smbios_biosinfo.handle = handle;
+ smbios_biosinfo.vendor = 1; // First string written in this section
+ smbios_biosinfo.version = 2; // Second string written in this section
+ smbios_biosinfo.characteristics = PCI_SUPPORTED;
+ smbios_biosinfo.characteristics_ext2 = IS_VIRTUAL_MACHINE;
+ curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
+ curptr = write_string(mem, "crosvm", curptr)?;
+ curptr = write_string(mem, "0", curptr)?;
+ curptr = write_and_incr(mem, 0 as u8, curptr)?;
+ }
+
+ {
+ handle += 1;
+ let mut smbios_sysinfo = SmbiosSysInfo::default();
+ smbios_sysinfo.typ = SYSTEM_INFORMATION;
+ smbios_sysinfo.length = mem::size_of::<SmbiosSysInfo>() as u8;
+ smbios_sysinfo.handle = handle;
+ smbios_sysinfo.manufacturer = 1; // First string written in this section
+ smbios_sysinfo.product_name = 2; // Second string written in this section
+ curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
+ curptr = write_string(mem, "ChromiumOS", curptr)?;
+ curptr = write_string(mem, "crosvm", curptr)?;
+ curptr = write_and_incr(mem, 0 as u8, curptr)?;
+ }
+
+ {
+ let mut smbios_ep = Smbios30Entrypoint::default();
+ smbios_ep.signature = *SM3_MAGIC_IDENT;
+ smbios_ep.length = mem::size_of::<Smbios30Entrypoint>() as u8;
+ // SMBIOS rev 3.2.0
+ smbios_ep.majorver = 0x03;
+ smbios_ep.minorver = 0x02;
+ smbios_ep.docrev = 0x00;
+ smbios_ep.revision = 0x01; // SMBIOS 3.0
+ smbios_ep.max_size = curptr.offset_from(physptr) as u32;
+ smbios_ep.physptr = physptr.offset();
+ smbios_ep.checksum = compute_checksum(&smbios_ep);
+ mem.write_obj_at_addr(smbios_ep, GuestAddress(SMBIOS_START))
+ .map_err(|_| Error::WriteSmbiosEp)?;
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn struct_size() {
+ assert_eq!(
+ mem::size_of::<Smbios30Entrypoint>(),
+ 0x18usize,
+ concat!("Size of: ", stringify!(Smbios30Entrypoint))
+ );
+ assert_eq!(
+ mem::size_of::<SmbiosBiosInfo>(),
+ 0x14usize,
+ concat!("Size of: ", stringify!(SmbiosBiosInfo))
+ );
+ assert_eq!(
+ mem::size_of::<SmbiosSysInfo>(),
+ 0x1busize,
+ concat!("Size of: ", stringify!(SmbiosSysInfo))
+ );
+ }
+
+ #[test]
+ fn entrypoint_checksum() {
+ let mem = GuestMemory::new(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
+
+ setup_smbios(&mem).unwrap();
+
+ let smbios_ep: Smbios30Entrypoint =
+ mem.read_obj_from_addr(GuestAddress(SMBIOS_START)).unwrap();
+
+ assert_eq!(compute_checksum(&smbios_ep), 0);
+ }
+}