aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederick Mayle <fmayle@google.com>2024-04-16 22:44:01 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-04-16 22:44:01 +0000
commit61c9a84871f705327e37883d4a637b7297766c43 (patch)
tree717f3c1a4923e084a4cfde51ca9162b4a75f2994
parentefb7605ac161ba257b8e442e1421e2ce4e161460 (diff)
parent2a9a3407346fd09aa3ddf89d320e4700211cee5a (diff)
downloadcrosvm-61c9a84871f705327e37883d4a637b7297766c43.tar.gz
Merge changes from topic "fmayle-crosvm-merge-04122024" into main
* changes: UPSTREAM: base: fix musl build Merge remote-tracking branch 'aosp/upstream-main'
-rw-r--r--Android.bp4
-rw-r--r--Cargo.lock203
-rw-r--r--Cargo.toml5
-rw-r--r--aarch64/Android.bp3
-rw-r--r--acpi_tables/Android.bp3
-rwxr-xr-xandroid-merge-2-cargo-embargo.sh6
-rw-r--r--arch/Android.bp5
-rw-r--r--arch/Cargo.toml1
-rw-r--r--arch/src/lib.rs3
-rw-r--r--arch/src/sys/linux.rs2
-rw-r--r--argh_helpers/Android.bp3
-rw-r--r--audio_util/Android.bp3
-rw-r--r--base/Android.bp3
-rw-r--r--base/base_event_token_derive/Android.bp3
-rw-r--r--base/src/lib.rs1
-rw-r--r--base/src/mmap.rs143
-rw-r--r--base/src/periodic_logger.rs232
-rw-r--r--base/src/sys/unix/descriptor.rs27
-rw-r--r--base/src/sys/windows/tube.rs12
-rw-r--r--bit_field/Android.bp3
-rw-r--r--bit_field/bit_field_derive/Android.bp3
-rw-r--r--broker_ipc/Android.bp3
-rw-r--r--broker_ipc/src/lib.rs6
-rw-r--r--cargo_embargo.json3
-rw-r--r--common/audio_streams/Android.bp3
-rw-r--r--common/balloon_control/Android.bp3
-rw-r--r--common/data_model/Android.bp3
-rw-r--r--common/sync/Android.bp3
-rw-r--r--cros_async/Android.bp3
-rw-r--r--cros_async/Cargo.toml4
-rw-r--r--cros_async/src/executor.rs150
-rw-r--r--cros_async/src/io_source.rs8
-rw-r--r--cros_async/src/lib.rs8
-rw-r--r--cros_async/src/sys/linux.rs2
-rw-r--r--cros_async/src/sys/linux/error.rs5
-rw-r--r--cros_async/src/sys/linux/tokio_source.rs405
-rw-r--r--cros_async/src/sys/windows.rs2
-rw-r--r--cros_async/src/sys/windows/error.rs5
-rw-r--r--cros_async/src/sys/windows/executor.rs24
-rw-r--r--cros_async/src/sys/windows/handle_executor.rs2
-rw-r--r--cros_async/src/sys/windows/tokio_source.rs291
-rw-r--r--cros_async/src/tokio_executor.rs170
-rw-r--r--cros_fdt/Android.bp3
-rw-r--r--cros_tracing/Android.bp3
-rw-r--r--cros_tracing_types/Android.bp3
-rw-r--r--crosvm_cli/Android.bp3
-rw-r--r--crosvm_control/Android.bp3
-rw-r--r--devices/Android.bp6
-rw-r--r--devices/Cargo.toml2
-rw-r--r--devices/src/acpi.rs27
-rw-r--r--devices/src/cmos.rs33
-rw-r--r--devices/src/pci/pci_configuration.rs14
-rw-r--r--devices/src/pci/pci_root.rs4
-rw-r--r--devices/src/serial_device.rs1
-rw-r--r--devices/src/virtio/async_device.rs22
-rw-r--r--devices/src/virtio/block/mod.rs46
-rw-r--r--devices/src/virtio/block/sys/windows.rs23
-rw-r--r--devices/src/virtio/console/asynchronous.rs10
-rw-r--r--devices/src/virtio/gpu/mod.rs6
-rw-r--r--devices/src/virtio/gpu/parameters.rs8
-rw-r--r--devices/src/virtio/gpu/virtio_gpu.rs11
-rw-r--r--devices/src/virtio/pvclock.rs10
-rw-r--r--devices/src/virtio/vhost/user/device/block/sys/windows.rs2
-rw-r--r--devices/src/virtio/vhost/user/device/fs/sys/linux.rs1
-rw-r--r--devices/src/virtio/vhost/user/device/gpu/sys/linux.rs9
-rw-r--r--devices/src/virtio/vhost/user/device/gpu/sys/windows.rs4
-rw-r--r--devices/src/virtio/vhost/user/device/handler.rs13
-rw-r--r--devices/src/virtio/vhost/user/device/wl.rs7
-rw-r--r--devices/src/virtio/video/decoder/backend/mod.rs14
-rw-r--r--devices/src/virtio/video/resource.rs13
-rw-r--r--devices/src/virtio/video/worker.rs9
-rw-r--r--devices/src/virtio/virtio_pci_device.rs10
-rw-r--r--devices/src/virtio/wl.rs57
-rw-r--r--devices/tests/irqchip/userspace.rs21
-rw-r--r--disk/Android.bp3
-rw-r--r--disk/src/qcow/mod.rs64
-rw-r--r--e2e_tests/fixture/src/vm.rs22
-rw-r--r--e2e_tests/guest_under_test/rootfs/readclock/Android.bp3
-rw-r--r--e2e_tests/tests/pci_hotplug.rs2
-rw-r--r--e2e_tests/tests/suspend_resume.rs4
-rw-r--r--e2e_tests/tests/vsock.rs10
-rw-r--r--fuse/Android.bp3
-rw-r--r--gpu_display/Android.bp4
-rw-r--r--gpu_display/examples/simple.rs5
-rw-r--r--gpu_display/examples/simple_open.rs5
-rw-r--r--gpu_display/src/gpu_display_android.rs5
-rw-r--r--gpu_display/src/gpu_display_stub.rs5
-rw-r--r--gpu_display/src/gpu_display_win/mod.rs42
-rw-r--r--gpu_display/src/gpu_display_win/surface.rs38
-rw-r--r--gpu_display/src/gpu_display_win/window_manager.rs2
-rw-r--r--gpu_display/src/gpu_display_win/window_procedure_thread.rs54
-rw-r--r--gpu_display/src/gpu_display_wl.rs5
-rw-r--r--gpu_display/src/gpu_display_x.rs5
-rw-r--r--gpu_display/src/lib.rs12
-rw-r--r--gpu_display/src/sys/windows.rs4
-rw-r--r--hypervisor/Android.bp55
-rw-r--r--hypervisor/src/aarch64.rs6
-rw-r--r--hypervisor/src/geniezone/mod.rs8
-rw-r--r--hypervisor/src/gunyah/aarch64.rs8
-rw-r--r--hypervisor/src/haxm/vcpu.rs182
-rw-r--r--hypervisor/src/kvm/aarch64.rs38
-rw-r--r--hypervisor/src/kvm/x86_64.rs229
-rw-r--r--hypervisor/src/whpx/vcpu.rs274
-rw-r--r--hypervisor/src/x86_64.rs170
-rw-r--r--hypervisor/tests/hypervisor_virtualization.rs111
-rw-r--r--hypervisor/tests/kvm/x86_64.rs60
-rw-r--r--hypervisor/tests/tsc_offsets.rs7
-rw-r--r--infra/README.recipes.md32
-rw-r--r--infra/config/generated/realms.cfg4
-rwxr-xr-xinfra/config/main.star1
-rw-r--r--infra/config/recipes.cfg4
-rw-r--r--io_uring/Android.bp3
-rw-r--r--jail/Android.bp3
-rw-r--r--kernel_cmdline/Android.bp3
-rw-r--r--kernel_loader/Android.bp3
-rw-r--r--kvm/Android.bp3
-rw-r--r--kvm_sys/Android.bp3
-rw-r--r--libcras_stub/Android.bp3
-rw-r--r--linux_input_sys/Android.bp3
-rw-r--r--metrics/Android.bp5
-rw-r--r--metrics/Cargo.toml1
-rw-r--r--metrics/src/controller.rs17
-rw-r--r--metrics/src/lib.rs2
-rw-r--r--metrics/src/sys.rs1
-rw-r--r--metrics_events/Android.bp26
-rw-r--r--metrics_events/Cargo.toml14
-rw-r--r--metrics_events/src/event_types.rs54
-rw-r--r--metrics_events/src/lib.rs10
-rw-r--r--metrics_events/src/sys.rs (renamed from vendor/generic/metrics/src/sys.rs)0
-rw-r--r--metrics_events/src/sys/windows.rs76
-rw-r--r--net_sys/Android.bp3
-rw-r--r--net_util/Android.bp3
-rw-r--r--power_monitor/Android.bp3
-rw-r--r--protos/Android.bp3
-rw-r--r--resources/Android.bp3
-rw-r--r--riscv64/Android.bp3
-rw-r--r--rutabaga_gfx/Android.bp3
-rw-r--r--serde_keyvalue/Android.bp3
-rw-r--r--serde_keyvalue/serde_keyvalue_derive/Android.bp3
-rw-r--r--src/crosvm/cmdline.rs14
-rw-r--r--src/crosvm/gpu_config.rs122
-rw-r--r--src/crosvm/sys/linux.rs37
-rw-r--r--src/crosvm/sys/linux/jail_warden.rs2
-rw-r--r--src/crosvm/sys/windows/broker.rs25
-rw-r--r--src/sys/windows/main.rs8
-rw-r--r--swap/Android.bp4
-rw-r--r--swap/Cargo.toml1
-rw-r--r--swap/src/controller.rs1
-rw-r--r--swap/src/processes.rs28
-rw-r--r--swap/src/userfaultfd.rs3
-rw-r--r--third_party/vmm_vhost/Android.bp3
-rwxr-xr-xtools/chromeos/merge_bot5
-rw-r--r--tools/impl/cros_container/Dockerfile2
-rw-r--r--usb_sys/Android.bp3
-rw-r--r--usb_util/Android.bp3
-rw-r--r--vendor/generic/anti_tamper/Android.bp3
-rw-r--r--vendor/generic/broker_ipc/Android.bp3
-rw-r--r--vendor/generic/crash_report/Android.bp3
-rw-r--r--vendor/generic/crypto/Android.bp3
-rw-r--r--vendor/generic/metrics/Android.bp7
-rw-r--r--vendor/generic/metrics/Cargo.toml10
-rw-r--r--vendor/generic/metrics/build.rs33
-rw-r--r--vendor/generic/metrics/protos/event_details.proto105
-rw-r--r--vendor/generic/metrics/src/client.rs14
-rw-r--r--vendor/generic/metrics/src/event_types.rs110
-rw-r--r--vendor/generic/metrics/src/lib.rs10
-rw-r--r--vendor/generic/metrics/src/metrics_requests.rs49
-rw-r--r--vendor/generic/metrics/src/out/event_details.rs1408
-rw-r--r--vendor/generic/metrics/src/out/generated.rs2
-rw-r--r--vendor/generic/metrics/src/out/mod.rs3
-rw-r--r--vendor/generic/metrics/src/periodic_logger.rs2
-rw-r--r--vendor/generic/metrics/src/request_handler.rs6
-rw-r--r--vendor/generic/metrics/src/sys/windows.rs24
-rw-r--r--vendor/generic/metrics_events/Android.bp20
-rw-r--r--vendor/generic/metrics_events/Cargo.toml8
-rw-r--r--vendor/generic/metrics_events/src/lib.rs18
-rw-r--r--vendor/generic/vm_control/Android.bp3
-rw-r--r--vfio_sys/Android.bp3
-rw-r--r--vhost/Android.bp3
-rw-r--r--virtio_sys/Android.bp3
-rw-r--r--vm_control/Android.bp3
-rw-r--r--vm_memory/Android.bp3
-rw-r--r--win_audio/src/win_audio_impl/mod.rs14
-rw-r--r--win_audio/src/win_audio_impl/wave_format.rs205
-rw-r--r--x86_64/Android.bp3
-rw-r--r--x86_64/src/lib.rs26
-rw-r--r--x86_64/src/regs.rs124
-rw-r--r--x86_64/tests/integration/main.rs13
188 files changed, 3336 insertions, 3081 deletions
diff --git a/Android.bp b/Android.bp
index 5c237099a..77c2eebaa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,5 +1,7 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
+// Content before the first "rust_*" or "genrule" module is preserved.
package {
default_applicable_licenses: ["external_crosvm_license"],
diff --git a/Cargo.lock b/Cargo.lock
index c579fa3bf..59faa9fa6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -38,6 +38,21 @@ dependencies = [
]
[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -82,6 +97,7 @@ dependencies = [
"jail",
"kernel_cmdline",
"libc",
+ "metrics",
"minijail",
"power_monitor",
"remain",
@@ -216,6 +232,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
name = "balloon_control"
version = "0.1.0"
dependencies = [
@@ -638,6 +669,7 @@ dependencies = [
"sync",
"tempfile",
"thiserror",
+ "tokio",
"win_util",
"winapi",
]
@@ -1323,6 +1355,12 @@ dependencies = [
]
[[package]]
+name = "gimli"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+
+[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1801,6 +1839,7 @@ dependencies = [
"base",
"cfg-if",
"chrono",
+ "metrics_events",
"metrics_generic",
"serde",
"sync",
@@ -1808,19 +1847,33 @@ dependencies = [
]
[[package]]
-name = "metrics_generic"
+name = "metrics_events"
version = "0.1.0"
dependencies = [
"anyhow",
- "base",
"cfg-if",
- "proto_build_tools",
- "protobuf",
+ "metrics_events_generic",
"serde",
"win_util",
]
[[package]]
+name = "metrics_events_generic"
+version = "0.1.0"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "metrics_generic"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "base",
+ "metrics_events",
+]
+
+[[package]]
name = "minijail"
version = "0.2.3"
dependencies = [
@@ -1845,6 +1898,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
+name = "miniz_oxide"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
name = "named-lock"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1965,6 +2038,15 @@ dependencies = [
]
[[package]]
+name = "object"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "once_cell"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2106,9 +2188,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
-version = "0.2.9"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pin-utils"
@@ -2409,6 +2491,12 @@ dependencies = [
]
[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2575,6 +2663,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
+name = "socket2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2597,6 +2695,7 @@ dependencies = [
"jail",
"libc",
"libtest-mimic",
+ "metrics",
"num_cpus",
"once_cell",
"remain",
@@ -2738,6 +2837,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
+name = "tokio"
+version = "1.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
+dependencies = [
+ "autocfg",
+ "backtrace",
+ "libc",
+ "mio",
+ "num_cpus",
+ "pin-project-lite",
+ "socket2",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3125,12 +3240,12 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
- "windows_aarch64_gnullvm",
+ "windows_aarch64_gnullvm 0.42.1",
"windows_aarch64_msvc 0.42.1",
"windows_i686_gnu 0.42.1",
"windows_i686_msvc 0.42.1",
"windows_x86_64_gnu 0.42.1",
- "windows_x86_64_gnullvm",
+ "windows_x86_64_gnullvm 0.42.1",
"windows_x86_64_msvc 0.42.1",
]
@@ -3140,7 +3255,16 @@ version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
- "windows-targets",
+ "windows-targets 0.42.1",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
]
[[package]]
@@ -3149,22 +3273,43 @@ version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
dependencies = [
- "windows_aarch64_gnullvm",
+ "windows_aarch64_gnullvm 0.42.1",
"windows_aarch64_msvc 0.42.1",
"windows_i686_gnu 0.42.1",
"windows_i686_msvc 0.42.1",
"windows_x86_64_gnu 0.42.1",
- "windows_x86_64_gnullvm",
+ "windows_x86_64_gnullvm 0.42.1",
"windows_x86_64_msvc 0.42.1",
]
[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
name = "windows_aarch64_msvc"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3177,6 +3322,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
name = "windows_i686_gnu"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3189,6 +3340,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
name = "windows_i686_msvc"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3201,6 +3358,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
name = "windows_x86_64_gnu"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3213,12 +3376,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
name = "windows_x86_64_msvc"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3231,6 +3406,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
name = "wio"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index c3967f8d4..1e3b7a841 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -72,6 +72,7 @@ members = [
"linux_input_sys",
"media/ffmpeg",
"metrics",
+ "metrics_events",
"net_sys",
"net_util",
"power_monitor",
@@ -170,6 +171,9 @@ swap = ["aarch64/swap", "arch/swap", "devices/swap", "vm_control/swap", "x86_64/
## Enables collection of VM statistics.
stats = ["devices/stats"]
+## Supports tokio as an asynchronous executor.
+tokio = ["cros_async/tokio"]
+
## Enables USB host device passthrough via an emulated XHCI controller.
## USB is supported only on unix/linux. The feature is a no-op on windows.
usb = ["devices/usb"]
@@ -347,6 +351,7 @@ all-default = [
"registered_events",
"slirp",
"swap",
+ "tokio",
"trace_marker",
"vaapi",
"video-decoder",
diff --git a/aarch64/Android.bp b/aarch64/Android.bp
index a4c3ac791..e51fc5cc0 100644
--- a/aarch64/Android.bp
+++ b/aarch64/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/acpi_tables/Android.bp b/acpi_tables/Android.bp
index ae09cfb5c..f39bca670 100644
--- a/acpi_tables/Android.bp
+++ b/acpi_tables/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/android-merge-2-cargo-embargo.sh b/android-merge-2-cargo-embargo.sh
index f5f1759b3..ceaaa1ded 100755
--- a/android-merge-2-cargo-embargo.sh
+++ b/android-merge-2-cargo-embargo.sh
@@ -40,7 +40,7 @@ done
#
# TODO: Consider using android's prebuilt rust binaries. Currently doesn't work
# because they try to incorrectly use system clang and llvm.
-RUST_TOOLCHAIN="1.68.2"
+RUST_TOOLCHAIN="1.73.0"
rustup which --toolchain $RUST_TOOLCHAIN cargo || \
rustup toolchain install $RUST_TOOLCHAIN
CARGO_BIN="$(dirname $(rustup which --toolchain $RUST_TOOLCHAIN cargo))"
@@ -67,7 +67,3 @@ fi
# cargo_embargo runs. This didn't happen with cargo2android.py because it
# ignored the lock file.
git restore Cargo.lock
-
-# Fix workstation specific path in "metrics" crate's generated files.
-# TODO(b/232150148): Find a better solution for protobuf generated files.
-sed --in-place 's/path = ".*\/out/path = "./' vendor/generic/metrics/src/out/generated.rs
diff --git a/arch/Android.bp b/arch/Android.bp
index 4c3e68495..738e27584 100644
--- a/arch/Android.bp
+++ b/arch/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
@@ -44,6 +45,7 @@ rust_test {
"libjail",
"libkernel_cmdline",
"liblibc",
+ "libmetrics",
"libminijail_rust",
"libpower_monitor",
"libresources",
@@ -88,6 +90,7 @@ rust_library {
"libjail",
"libkernel_cmdline",
"liblibc",
+ "libmetrics",
"libminijail_rust",
"libpower_monitor",
"libresources",
diff --git a/arch/Cargo.toml b/arch/Cargo.toml
index f362ff4b9..8fb29995d 100644
--- a/arch/Cargo.toml
+++ b/arch/Cargo.toml
@@ -25,6 +25,7 @@ hypervisor = { path = "../hypervisor" }
jail = { path = "../jail" }
kernel_cmdline = { path = "../kernel_cmdline" }
libc = "*"
+metrics = { path = "../metrics" }
resources = { path = "../resources" }
remain = "*"
serde = { version = "*", features = [ "derive"] }
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index 5c26e3b9d..de7a19320 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -740,6 +740,7 @@ pub fn configure_pci_device<V: VmArch, Vcpu: VcpuArch>(
let mut keep_rds = device.keep_rds();
syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
device
.register_device_capabilities()
@@ -821,6 +822,7 @@ pub fn generate_virtio_mmio_bus(
let mut keep_rds = device.keep_rds();
syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
let irq_num = resources
.allocate_irq()
@@ -1148,6 +1150,7 @@ pub fn generate_pci_root(
let mut keep_rds = device.keep_rds();
syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
keep_rds.append(&mut vm.get_memory().as_raw_descriptors());
let ranges = io_ranges.remove(&dev_idx).unwrap_or_default();
diff --git a/arch/src/sys/linux.rs b/arch/src/sys/linux.rs
index 4289aea5a..f536535f6 100644
--- a/arch/src/sys/linux.rs
+++ b/arch/src/sys/linux.rs
@@ -93,6 +93,7 @@ pub fn add_goldfish_battery(
let mut keep_rds = goldfish_bat.keep_rds();
syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
mmio_bus
.insert(
Arc::new(Mutex::new(
@@ -188,6 +189,7 @@ pub fn generate_platform_bus(
let mut keep_rds = device.keep_rds();
syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
let irqs = device
.get_platform_irqs()
diff --git a/argh_helpers/Android.bp b/argh_helpers/Android.bp
index 95ec35255..33024d115 100644
--- a/argh_helpers/Android.bp
+++ b/argh_helpers/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/audio_util/Android.bp b/audio_util/Android.bp
index afc89376f..4000343d6 100644
--- a/audio_util/Android.bp
+++ b/audio_util/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/base/Android.bp b/base/Android.bp
index 1ed8cb7da..4a871fb0e 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/base/base_event_token_derive/Android.bp b/base/base_event_token_derive/Android.bp
index 92a8f2b51..7f9b0862d 100644
--- a/base/base_event_token_derive/Android.bp
+++ b/base/base_event_token_derive/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/base/src/lib.rs b/base/src/lib.rs
index e23f2876e..e7b77f9c9 100644
--- a/base/src/lib.rs
+++ b/base/src/lib.rs
@@ -15,6 +15,7 @@ mod file_traits;
mod iobuf;
mod mmap;
mod notifiers;
+mod periodic_logger;
mod shm;
pub mod syslog;
pub mod test_utils;
diff --git a/base/src/mmap.rs b/base/src/mmap.rs
index 6654d078e..8095135eb 100644
--- a/base/src/mmap.rs
+++ b/base/src/mmap.rs
@@ -13,6 +13,7 @@ use std::ptr::write_unaligned;
use std::ptr::write_volatile;
use std::sync::atomic::fence;
use std::sync::atomic::Ordering;
+use std::sync::OnceLock;
use remain::sorted;
use serde::Deserialize;
@@ -29,6 +30,44 @@ use crate::VolatileMemoryError;
use crate::VolatileMemoryResult;
use crate::VolatileSlice;
+static CACHELINE_SIZE: OnceLock<usize> = OnceLock::new();
+
+#[allow(unused_assignments)]
+fn get_cacheline_size_once() -> usize {
+ let mut assume_reason: &str = "unknown";
+ cfg_if::cfg_if! {
+ if #[cfg(all(any(target_os = "android", target_os = "linux"), not(target_env = "musl")))] {
+ // SAFETY:
+ // Safe because we check the return value for errors or unsupported requests
+ let linesize = unsafe { libc::sysconf(libc::_SC_LEVEL1_DCACHE_LINESIZE) };
+ if linesize > 0 {
+ return linesize as usize;
+ } else {
+ assume_reason = "sysconf cacheline size query failed";
+ }
+ } else {
+ assume_reason = "cacheline size query not implemented for platform/arch";
+ }
+ }
+
+ let assumed_size = 64;
+ log::debug!(
+ "assuming cacheline_size={}; reason: {}.",
+ assumed_size,
+ assume_reason
+ );
+ assumed_size
+}
+
+/// Returns the system's effective cacheline size (e.g. the granularity at which arch-specific
+/// cacheline management, such as with the clflush instruction, is expected to occur).
+#[inline(always)]
+fn get_cacheline_size() -> usize {
+ let size = *CACHELINE_SIZE.get_or_init(get_cacheline_size_once);
+ assert!(size > 0);
+ size
+}
+
#[sorted]
#[derive(Debug, thiserror::Error)]
pub enum Error {
@@ -44,6 +83,8 @@ pub enum Error {
InvalidOffset,
#[error("requested memory range spans past the end of the region: offset={0} count={1} region_size={2}")]
InvalidRange(usize, usize, usize),
+ #[error("operation is not implemented on platform/architecture: {0}")]
+ NotImplemented(&'static str),
#[error("requested memory is not page aligned")]
NotPageAligned,
#[error("failed to read from file to memory: {0}")]
@@ -132,6 +173,27 @@ pub struct MemoryMapping {
pub(crate) _file_descriptor: Option<SafeDescriptor>,
}
+#[inline(always)]
+unsafe fn flush_one(_addr: *const u8) -> Result<()> {
+ cfg_if::cfg_if! {
+ if #[cfg(target_arch = "x86_64")] {
+ // As per table 11-7 of the SDM, processors are not required to
+ // snoop UC mappings, so flush the target to memory.
+ // SAFETY: assumes that the caller has supplied a valid address.
+ unsafe { core::arch::x86_64::_mm_clflush(_addr) };
+ Ok(())
+ } else if #[cfg(target_arch = "aarch64")] {
+ // Data cache clean by VA to PoC.
+ std::arch::asm!("DC CVAC, {x}", x = in(reg) _addr);
+ Ok(())
+ } else if #[cfg(target_arch = "arm")] {
+ Err(Error::NotImplemented("Userspace cannot flush to PoC"))
+ } else {
+ Err(Error::NotImplemented("Cache flush not implemented"))
+ }
+ }
+}
+
impl MemoryMapping {
pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> {
match self.mapping.size().checked_sub(offset) {
@@ -294,42 +356,63 @@ impl MemoryMapping {
self.mapping.msync()
}
- /// Flush memory which the guest may be accessing through an uncached mapping.
+ /// Flush a region of the MemoryMapping from the system's caching hierarchy.
+ /// There are several uses for flushing:
///
- /// Reads via an uncached mapping can bypass the cache and directly access main
- /// memory. This is outside the memory model of Rust, which means that even with
- /// proper synchronization, guest reads via an uncached mapping might not see
- /// updates from the host. As such, it is necessary to perform architectural
- /// cache maintainance to flush the host writes to main memory.
+ /// * Cached memory which the guest may be reading through an uncached mapping:
///
- /// Note that this does not support writable uncached guest mappings, as doing so
- /// requires invalidating the cache, not flushing the cache.
+ /// Guest reads via an uncached mapping can bypass the cache and directly access main
+ /// memory. This is outside the memory model of Rust, which means that even with proper
+ /// synchronization, guest reads via an uncached mapping might not see updates from the
+ /// host. As such, it is necessary to perform architectural cache maintainance to flush the
+ /// host writes to main memory.
+ ///
+ /// Note that this does not support writable uncached guest mappings, as doing so
+ /// requires invalidating the cache, not flushing the cache.
+ ///
+ /// * Uncached memory which the guest may be writing through a cached mapping:
+ ///
+ /// Guest writes via a cached mapping of a host's uncached memory may never make it to
+ /// system/device memory prior to being read. In such cases, explicit flushing of the cached
+ /// writes is necessary, since other managers of the host's uncached mapping (e.g. DRM) see
+ /// no need to flush, as they believe all writes would explicitly bypass the caches.
///
/// Currently only supported on x86_64 and aarch64. Cannot be supported on 32-bit arm.
- pub fn flush_uncached_guest_mapping(&self, offset: usize) {
- if offset > self.mapping.size() {
- return;
+ pub fn flush_region(&self, offset: usize, len: usize) -> Result<()> {
+ let addr: *const u8 = self.as_ptr();
+ let size = self.size();
+
+ // disallow overflow/wrapping ranges and subregion extending beyond mapped range
+ if usize::MAX - size < addr as usize || offset >= size || size - offset < len {
+ return Err(Error::InvalidRange(offset, len, size));
}
- // SAFETY: We checked that offset is within the mapping, and flushing
- // the cache doesn't affect any rust safety properties.
- unsafe {
- #[allow(unused)]
- let target = self.mapping.as_ptr().add(offset);
- cfg_if::cfg_if! {
- if #[cfg(target_arch = "x86_64")] {
- // As per table 11-7 of the SDM, processors are not required to
- // snoop UC mappings, so flush the target to memory.
- core::arch::x86_64::_mm_clflush(target);
- } else if #[cfg(target_arch = "aarch64")] {
- // Data cache clean by VA to PoC.
- std::arch::asm!("DC CVAC, {x}", x = in(reg) target);
- } else if #[cfg(target_arch = "arm")] {
- panic!("Userspace cannot flush to PoC");
- } else {
- unimplemented!("Cache flush not implemented")
- }
- }
+
+ // SAFETY:
+ // Safe because already validated that `next` will be an address in the mapping:
+ // * mapped region is non-wrapping
+ // * subregion is bounded within the mapped region
+ let mut next: *const u8 = unsafe { addr.add(offset) };
+
+ let cacheline_size = get_cacheline_size();
+ let cacheline_count = len.div_ceil(cacheline_size);
+
+ for _ in 0..cacheline_count {
+ // SAFETY:
+ // Safe because `next` is guaranteed to be within the mapped region (see earlier
+ // validations), and flushing the cache doesn't affect any rust safety properties.
+ unsafe { flush_one(next)? };
+
+ // SAFETY:
+ // Safe because we never use next if it goes out of the mapped region or overflows its
+ // storage type (based on earlier validations and the loop bounds).
+ next = unsafe { next.add(cacheline_size) };
}
+ Ok(())
+ }
+
+ /// Flush all backing memory for a mapping in an arch-specific manner (see `flush_region()`).
+ pub fn flush_all(&self) -> Result<()> {
+ self.flush_region(0, self.size())
}
}
diff --git a/base/src/periodic_logger.rs b/base/src/periodic_logger.rs
new file mode 100644
index 000000000..7fd1a045d
--- /dev/null
+++ b/base/src/periodic_logger.rs
@@ -0,0 +1,232 @@
+// Copyright 2024 The ChromiumOS Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(b/318439696): Remove once it is used
+#![allow(dead_code)]
+
+use std::collections::HashMap;
+use std::fmt::Write;
+use std::sync::atomic::AtomicU32;
+use std::sync::atomic::Ordering;
+use std::sync::Arc;
+use std::sync::RwLock;
+use std::time::Duration;
+
+use thiserror::Error as ThisError;
+
+use crate::EventToken;
+use crate::Timer;
+use crate::TimerTrait;
+use crate::WaitContext;
+use crate::WorkerThread;
+
+/// Utility class that helps count and log high frequency events periodically.
+pub struct PeriodicLogger {
+ // Name that is printed out to differentiate between other `PeriodicLogger`s
+ name: String,
+ // Interval to log
+ interval: Duration,
+ // Map of event counters that are periodically logged
+ counters: Arc<RwLock<HashMap<String, AtomicU32>>>,
+ // The periodic logger thread
+ worker_thread: Option<WorkerThread<Result<(), PeriodicLoggerError>>>,
+}
+
+impl PeriodicLogger {
+ pub fn new(name: String, interval: Duration) -> Self {
+ PeriodicLogger {
+ name,
+ interval,
+ counters: Arc::new(RwLock::new(HashMap::new())),
+ worker_thread: None,
+ }
+ }
+
+ /// Add a new event item to be counted.
+ pub fn add_counter_item(&self, name: String) -> Result<(), PeriodicLoggerError> {
+ // This write lock will likely be acquired infrequently.
+ let mut counters_write_lock = self
+ .counters
+ .write()
+ .map_err(|e| PeriodicLoggerError::WriteLockError(e.to_string()))?;
+
+ if counters_write_lock.contains_key(&name) {
+ return Err(PeriodicLoggerError::CounterAlreadyExist(name));
+ }
+
+ counters_write_lock.insert(name, AtomicU32::new(0));
+ Ok(())
+ }
+
+ /// Increment event counter by an `amount`
+ pub fn increment_counter(&self, name: String, amount: u32) -> Result<(), PeriodicLoggerError> {
+ match self.counters.read() {
+ Ok(counters_map) => {
+ if let Some(atomic_counter) = counters_map.get(&name) {
+ atomic_counter.fetch_add(amount, Ordering::Relaxed);
+ Ok(())
+ } else {
+ Err(PeriodicLoggerError::CounterDoesNotExist(name))
+ }
+ }
+ Err(e) => Err(PeriodicLoggerError::ReadLockError(e.to_string())),
+ }
+ }
+
+ /// Starts a thread that will log the count of events within a `self.interval` time period.
+ /// All counters will be reset to 0 after logging.
+ pub fn start_logging_thread(&mut self) -> Result<(), PeriodicLoggerError> {
+ if self.worker_thread.is_some() {
+ return Err(PeriodicLoggerError::ThreadAlreadyStarted);
+ }
+
+ #[derive(EventToken)]
+ enum Token {
+ Exit,
+ PeriodicLog,
+ }
+
+ let cloned_counter = self.counters.clone();
+ let interval_copy = self.interval;
+ let name_copy = self.name.clone();
+ self.worker_thread = Some(WorkerThread::start(
+ format!("PeriodicLogger_{}", self.name),
+ move |kill_evt| {
+ let mut timer = Timer::new().map_err(PeriodicLoggerError::TimerNewError)?;
+ timer
+ .reset(interval_copy, Some(interval_copy))
+ .map_err(PeriodicLoggerError::TimerResetError)?;
+
+ let wait_ctx = WaitContext::build_with(&[
+ (&kill_evt, Token::Exit),
+ (&timer, Token::PeriodicLog),
+ ])
+ .map_err(PeriodicLoggerError::WaitContextBuildError)?;
+
+ 'outer: loop {
+ let events = wait_ctx.wait().expect("wait failed");
+ for event in events.iter().filter(|e| e.is_readable) {
+ match event.token {
+ Token::Exit => {
+ break 'outer;
+ }
+ Token::PeriodicLog => {
+ let counter_map = cloned_counter.read().map_err(|e| {
+ PeriodicLoggerError::ReadLockError(e.to_string())
+ })?;
+
+ let mut logged_string =
+ format!("{} {:?}:", name_copy, interval_copy);
+ for (counter_name, counter_value) in counter_map.iter() {
+ let value = counter_value.swap(0, Ordering::Relaxed);
+ let _ =
+ write!(logged_string, "\n {}: {}", counter_name, value);
+ }
+
+ // Log all counters
+ crate::info!("{}", logged_string);
+ }
+ }
+ }
+ }
+ Ok(())
+ },
+ ));
+
+ Ok(())
+ }
+}
+
+#[derive(Debug, ThisError, PartialEq)]
+pub enum PeriodicLoggerError {
+ #[error("Periodic logger thread already started.")]
+ ThreadAlreadyStarted,
+ #[error("Failed to acquire write lock: {0}")]
+ WriteLockError(String),
+ #[error("Failed to acquire read lock: {0}")]
+ ReadLockError(String),
+ #[error("Counter already exists: {0}")]
+ CounterAlreadyExist(String),
+ #[error("Counter does not exist: {0}")]
+ CounterDoesNotExist(String),
+ #[error("Failed to build WaitContext: {0}")]
+ WaitContextBuildError(crate::Error),
+ #[error("Failed to wait on WaitContext: {0}")]
+ WaitContextWaitError(crate::Error),
+ #[error("Failed to reset Timer: {0}")]
+ TimerResetError(crate::Error),
+ #[error("Failed initialize Timer: {0}")]
+ TimerNewError(crate::Error),
+}
+
+#[cfg(test)]
+mod tests {
+ use std::thread;
+
+ use super::*;
+
+ #[test]
+ fn periodic_add() {
+ let periodic_logger = PeriodicLogger::new("test".to_string(), Duration::from_secs(3));
+ periodic_logger
+ .add_counter_item("counter_1".to_string())
+ .unwrap();
+ periodic_logger
+ .increment_counter("counter_1".to_string(), 2)
+ .unwrap();
+ periodic_logger
+ .increment_counter("counter_1".to_string(), 5)
+ .unwrap();
+
+ assert_eq!(periodic_logger.counters.read().unwrap().len(), 1);
+ assert_eq!(
+ periodic_logger
+ .counters
+ .read()
+ .unwrap()
+ .get("counter_1")
+ .unwrap()
+ .load(Ordering::Relaxed),
+ 7
+ );
+ }
+
+ #[test]
+ fn worker_thread_cannot_start_twice() {
+ let mut periodic_logger = PeriodicLogger::new("test".to_string(), Duration::from_secs(3));
+ assert!(periodic_logger.start_logging_thread().is_ok());
+ assert!(periodic_logger.start_logging_thread().is_err());
+ }
+
+ #[test]
+ fn add_same_counter_item_twice_return_err() {
+ let periodic_logger = PeriodicLogger::new("test".to_string(), Duration::from_secs(3));
+ assert!(periodic_logger
+ .add_counter_item("counter_1".to_string())
+ .is_ok());
+ assert_eq!(
+ periodic_logger.add_counter_item("counter_1".to_string()),
+ Err(PeriodicLoggerError::CounterAlreadyExist(
+ "counter_1".to_string()
+ ))
+ );
+ }
+
+ /// Ignored because this is intended to be ran locally
+ #[ignore]
+ #[test]
+ fn periodic_logger_smoke_test() {
+ let mut periodic_logger = PeriodicLogger::new("test".to_string(), Duration::from_secs(3));
+ periodic_logger
+ .add_counter_item("counter_1".to_string())
+ .unwrap();
+
+ periodic_logger.start_logging_thread().unwrap();
+ periodic_logger
+ .increment_counter("counter_1".to_string(), 5)
+ .unwrap();
+
+ thread::sleep(Duration::from_secs(5));
+ }
+}
diff --git a/base/src/sys/unix/descriptor.rs b/base/src/sys/unix/descriptor.rs
index 26a9cd2c3..fd97efe6e 100644
--- a/base/src/sys/unix/descriptor.rs
+++ b/base/src/sys/unix/descriptor.rs
@@ -32,24 +32,25 @@ pub type RawDescriptor = RawFd;
pub const INVALID_DESCRIPTOR: RawDescriptor = -1;
-/// Clones `descriptor`, returning a new `RawDescriptor` that refers to the same open file
-/// description as `descriptor`. The cloned descriptor will have the `FD_CLOEXEC` flag set but will
-/// not share any other file descriptor flags with `descriptor`.
-pub fn clone_descriptor(descriptor: &dyn AsRawDescriptor) -> Result<RawDescriptor> {
- clone_fd(&descriptor.as_raw_descriptor())
+/// Clones `descriptor`, returning a new `SafeDescriptor` that refers to the same file
+/// `descriptor`. The cloned descriptor will have the `FD_CLOEXEC` flag set but will not share any
+/// other file descriptor flags with `descriptor`.
+pub fn clone_descriptor(descriptor: &(impl AsRawDescriptor + ?Sized)) -> Result<SafeDescriptor> {
+ clone_fd(descriptor.as_raw_descriptor())
}
-/// Clones `fd`, returning a new file descriptor that refers to the same open file description as
-/// `fd`. The cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file
-/// descriptor flags with `fd`.
-fn clone_fd(fd: &dyn AsRawFd) -> Result<RawFd> {
+/// Clones `fd`, returning a new file descriptor that refers to the same open file as `fd`. The
+/// cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file descriptor
+/// flags with `fd`.
+fn clone_fd(fd: RawFd) -> Result<SafeDescriptor> {
// SAFETY:
// Safe because this doesn't modify any memory and we check the return value.
- let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) };
+ let ret = unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) };
if ret < 0 {
errno_result()
} else {
- Ok(ret)
+ // SAFETY: We just dup'd the FD and so have exclusive access.
+ Ok(unsafe { SafeDescriptor::from_raw_descriptor(ret) })
}
}
@@ -108,9 +109,7 @@ impl TryFrom<&dyn AsRawFd> for SafeDescriptor {
type Error = std::io::Error;
fn try_from(fd: &dyn AsRawFd) -> std::result::Result<Self, Self::Error> {
- Ok(SafeDescriptor {
- descriptor: clone_fd(fd)?,
- })
+ Ok(clone_fd(fd.as_raw_fd())?)
}
}
diff --git a/base/src/sys/windows/tube.rs b/base/src/sys/windows/tube.rs
index ed61c2f39..b65350837 100644
--- a/base/src/sys/windows/tube.rs
+++ b/base/src/sys/windows/tube.rs
@@ -384,6 +384,18 @@ impl AsRawDescriptor for RecvTube {
}
}
+impl CloseNotifier for SendTube {
+ fn get_close_notifier(&self) -> &dyn AsRawDescriptor {
+ self.0.get_close_notifier()
+ }
+}
+
+impl CloseNotifier for RecvTube {
+ fn get_close_notifier(&self) -> &dyn AsRawDescriptor {
+ self.0.get_close_notifier()
+ }
+}
+
/// A request to duplicate a handle to a target process.
#[derive(Serialize, Deserialize, Debug)]
pub struct DuplicateHandleRequest {
diff --git a/bit_field/Android.bp b/bit_field/Android.bp
index d6f35fb37..3873a8fa5 100644
--- a/bit_field/Android.bp
+++ b/bit_field/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/bit_field/bit_field_derive/Android.bp b/bit_field/bit_field_derive/Android.bp
index 34c8592b0..fb5b59d15 100644
--- a/bit_field/bit_field_derive/Android.bp
+++ b/bit_field/bit_field_derive/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/broker_ipc/Android.bp b/broker_ipc/Android.bp
index d78d4688a..85409085e 100644
--- a/broker_ipc/Android.bp
+++ b/broker_ipc/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/broker_ipc/src/lib.rs b/broker_ipc/src/lib.rs
index 4f396615a..71a7313ee 100644
--- a/broker_ipc/src/lib.rs
+++ b/broker_ipc/src/lib.rs
@@ -17,7 +17,7 @@ use base::EnabledHighResTimer;
use base::FromRawDescriptor;
use base::IntoRawDescriptor;
use base::SafeDescriptor;
-use base::Tube;
+use base::SendTube;
#[cfg(feature = "process-invariants")]
pub use broker_ipc_product::init_broker_process_invariants;
use broker_ipc_product::init_child_crash_reporting;
@@ -33,7 +33,7 @@ use serde::Serialize;
pub struct CommonChildStartupArgs {
log_args: LogArgs,
syslog_file: Option<SafeDescriptor>,
- metrics_tube: Option<Tube>,
+ metrics_tube: Option<SendTube>,
product_attrs: ProductAttributes,
}
@@ -44,7 +44,7 @@ impl CommonChildStartupArgs {
syslog_path: Option<PathBuf>,
#[cfg(feature = "crash-report")] _crash_attrs: crash_report::CrashReportAttributes,
#[cfg(feature = "process-invariants")] _process_invariants: EmulatorProcessInvariants,
- metrics_tube: Option<Tube>,
+ metrics_tube: Option<SendTube>,
) -> anyhow::Result<Self> {
Ok(Self {
log_args: log_args.clone(),
diff --git a/cargo_embargo.json b/cargo_embargo.json
index 377728074..777ce8b6f 100644
--- a/cargo_embargo.json
+++ b/cargo_embargo.json
@@ -191,9 +191,6 @@
"kvm_sys": {
"no_presubmit": true
},
- "metrics": {
- "copy_out": true
- },
"net_util": {
"no_presubmit": true
},
diff --git a/common/audio_streams/Android.bp b/common/audio_streams/Android.bp
index 1eed2c763..9d535c076 100644
--- a/common/audio_streams/Android.bp
+++ b/common/audio_streams/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/common/balloon_control/Android.bp b/common/balloon_control/Android.bp
index e6ddd56d8..561c23b2c 100644
--- a/common/balloon_control/Android.bp
+++ b/common/balloon_control/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/common/data_model/Android.bp b/common/data_model/Android.bp
index 78d8ba2d3..461e626ce 100644
--- a/common/data_model/Android.bp
+++ b/common/data_model/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/common/sync/Android.bp b/common/sync/Android.bp
index d683033ff..af6260cc9 100644
--- a/common/sync/Android.bp
+++ b/common/sync/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/cros_async/Android.bp b/cros_async/Android.bp
index 6acddafbb..a74446941 100644
--- a/cros_async/Android.bp
+++ b/cros_async/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/cros_async/Cargo.toml b/cros_async/Cargo.toml
index 3db488892..fb25c40ad 100644
--- a/cros_async/Cargo.toml
+++ b/cros_async/Cargo.toml
@@ -4,6 +4,9 @@ version = "0.1.1"
authors = ["The ChromiumOS Authors"]
edition = "2021"
+[features]
+tokio = ["dep:tokio"]
+
[dependencies]
async-trait = "0.1.36"
async-task = "4"
@@ -23,6 +26,7 @@ anyhow = "1.0"
serde = "*"
serde_keyvalue = { path = "../serde_keyvalue", features = ["argh_derive"] } # provided by ebuild
static_assertions = "1.1"
+tokio = { version = "1.29.1", optional = true, features = ["net", "rt-multi-thread"] }
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
io_uring = { path = "../io_uring" } # provided by ebuild
diff --git a/cros_async/src/executor.rs b/cros_async/src/executor.rs
index 2f1e7166b..35e409561 100644
--- a/cros_async/src/executor.rs
+++ b/cros_async/src/executor.rs
@@ -13,6 +13,10 @@ use base::AsRawDescriptors;
#[cfg(any(target_os = "android", target_os = "linux"))]
use base::RawDescriptor;
use once_cell::sync::OnceCell;
+use serde::Deserialize;
+use serde_keyvalue::argh::FromArgValue;
+use serde_keyvalue::ErrorKind;
+use serde_keyvalue::KeyValueDeserializer;
use crate::common_executor;
use crate::common_executor::RawExecutor;
@@ -25,19 +29,18 @@ use crate::AsyncResult;
use crate::IntoAsync;
use crate::IoSource;
-#[derive(
- Clone,
- Copy,
- Debug,
- PartialEq,
- Eq,
- serde::Serialize,
- serde::Deserialize,
- serde_keyvalue::FromKeyValues,
-)]
-#[serde(deny_unknown_fields, rename_all = "kebab-case", untagged)]
+cfg_if::cfg_if! {
+ if #[cfg(feature = "tokio")] {
+ use crate::tokio_executor::TokioExecutor;
+ use crate::tokio_executor::TokioTaskHandle;
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ExecutorKind {
SysVariants(ExecutorKindSys),
+ #[cfg(feature = "tokio")]
+ Tokio,
}
impl From<ExecutorKindSys> for ExecutorKind {
@@ -46,16 +49,9 @@ impl From<ExecutorKindSys> for ExecutorKind {
}
}
-// TODO: schuffelen - Remove after adding a platform-independent Executor
-impl From<ExecutorKind> for ExecutorKindSys {
- fn from(e: ExecutorKind) -> ExecutorKindSys {
- match e {
- ExecutorKind::SysVariants(inner) => inner,
- }
- }
-}
-
-/// If set, [`Executor::new()`] is created with `ExecutorKindSys` of `DEFAULT_EXECUTOR_KIND`.
+/// If set, [`ExecutorKind::default()`] returns the value of `DEFAULT_EXECUTOR_KIND`.
+/// If not set, [`ExecutorKind::default()`] returns a statically-chosen default value, and
+/// [`ExecutorKind::default()`] initializes `DEFAULT_EXECUTOR_KIND` with that value.
static DEFAULT_EXECUTOR_KIND: OnceCell<ExecutorKind> = OnceCell::new();
impl Default for ExecutorKind {
@@ -82,6 +78,76 @@ pub enum SetDefaultExecutorKindError {
UringUnavailable(linux::uring_executor::Error),
}
+impl FromArgValue for ExecutorKind {
+ fn from_arg_value(value: &str) -> std::result::Result<ExecutorKind, String> {
+ // `from_arg_value` returns a `String` as error, but our deserializer API defines its own
+ // error type. Perform parsing from a closure so we can easily map returned errors.
+ let builder = move || {
+ let mut des = KeyValueDeserializer::from(value);
+
+ let kind: ExecutorKind = match (des.parse_identifier()?, des.next_char()) {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ("epoll", None) => ExecutorKindSys::Fd.into(),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ("uring", None) => ExecutorKindSys::Uring.into(),
+ #[cfg(windows)]
+ ("handle", None) => ExecutorKindSys::Handle.into(),
+ #[cfg(windows)]
+ ("overlapped", None) => ExecutorKindSys::Overlapped { concurrency: None }.into(),
+ #[cfg(windows)]
+ ("overlapped", Some(',')) => {
+ if des.parse_identifier()? != "concurrency" {
+ let kind = ErrorKind::SerdeError("expected `concurrency`".to_string());
+ return Err(des.error_here(kind));
+ }
+ if des.next_char() != Some('=') {
+ return Err(des.error_here(ErrorKind::ExpectedEqual));
+ }
+ let concurrency = des.parse_number()?;
+ ExecutorKindSys::Overlapped {
+ concurrency: Some(concurrency),
+ }
+ .into()
+ }
+ #[cfg(feature = "tokio")]
+ ("tokio", None) => ExecutorKind::Tokio,
+ (_identifier, _next) => {
+ let kind = ErrorKind::SerdeError("unexpected kind".to_string());
+ return Err(des.error_here(kind));
+ }
+ };
+ des.finish()?;
+ Ok(kind)
+ };
+
+ builder().map_err(|e| e.to_string())
+ }
+}
+
+impl serde::Serialize for ExecutorKind {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ match self {
+ ExecutorKind::SysVariants(sv) => sv.serialize(serializer),
+ #[cfg(feature = "tokio")]
+ ExecutorKind::Tokio => "tokio".serialize(serializer),
+ }
+ }
+}
+
+impl<'de> Deserialize<'de> for ExecutorKind {
+ fn deserialize<D>(deserializer: D) -> Result<ExecutorKind, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ base::error!("ExecutorKind::deserialize");
+ let string = String::deserialize(deserializer)?;
+ ExecutorKind::from_arg_value(&string).map_err(serde::de::Error::custom)
+ }
+}
+
/// Reference to a task managed by the executor.
///
/// Dropping a `TaskHandle` attempts to cancel the associated task. Call `detach` to allow it to
@@ -95,6 +161,8 @@ pub enum TaskHandle<R> {
Uring(common_executor::RawTaskHandle<linux::UringReactor, R>),
#[cfg(windows)]
Handle(common_executor::RawTaskHandle<windows::HandleReactor, R>),
+ #[cfg(feature = "tokio")]
+ Tokio(TokioTaskHandle<R>),
}
impl<R: Send + 'static> TaskHandle<R> {
@@ -106,6 +174,8 @@ impl<R: Send + 'static> TaskHandle<R> {
TaskHandle::Uring(u) => u.detach(),
#[cfg(windows)]
TaskHandle::Handle(h) => h.detach(),
+ #[cfg(feature = "tokio")]
+ TaskHandle::Tokio(t) => t.detach(),
}
}
@@ -119,6 +189,8 @@ impl<R: Send + 'static> TaskHandle<R> {
TaskHandle::Uring(u) => u.cancel().await,
#[cfg(windows)]
TaskHandle::Handle(h) => h.cancel().await,
+ #[cfg(feature = "tokio")]
+ TaskHandle::Tokio(t) => t.cancel().await,
}
}
}
@@ -134,6 +206,8 @@ impl<R: 'static> Future for TaskHandle<R> {
TaskHandle::Uring(u) => Pin::new(u).poll(cx),
#[cfg(windows)]
TaskHandle::Handle(h) => Pin::new(h).poll(cx),
+ #[cfg(feature = "tokio")]
+ TaskHandle::Tokio(t) => Pin::new(t).poll(cx),
}
}
}
@@ -265,6 +339,8 @@ pub enum Executor {
Handle(Arc<RawExecutor<windows::HandleReactor>>),
#[cfg(windows)]
Overlapped(Arc<RawExecutor<windows::HandleReactor>>),
+ #[cfg(feature = "tokio")]
+ Tokio(TokioExecutor),
}
impl Executor {
@@ -287,21 +363,15 @@ impl Executor {
Executor::Handle(RawExecutor::new()?)
}
#[cfg(windows)]
- ExecutorKind::SysVariants(ExecutorKindSys::Overlapped) => {
- Executor::Overlapped(RawExecutor::new()?)
- }
- })
- }
-
- /// Create a new `Executor` of the given `ExecutorKind`.
- pub fn with_kind_and_concurrency(kind: ExecutorKind, _concurrency: u32) -> AsyncResult<Self> {
- Ok(match kind {
- #[cfg(windows)]
- ExecutorKind::SysVariants(ExecutorKindSys::Overlapped) => {
- let reactor = windows::HandleReactor::new_with(_concurrency)?;
+ ExecutorKind::SysVariants(ExecutorKindSys::Overlapped { concurrency }) => {
+ let reactor = match concurrency {
+ Some(concurrency) => windows::HandleReactor::new_with(concurrency)?,
+ None => windows::HandleReactor::new()?,
+ };
Executor::Overlapped(RawExecutor::new_with(reactor)?)
}
- _ => Executor::with_executor_kind(kind)?,
+ #[cfg(feature = "tokio")]
+ ExecutorKind::Tokio => Executor::Tokio(TokioExecutor::new()?),
})
}
@@ -341,6 +411,8 @@ impl Executor {
Executor::Handle(ex) => ex.async_from(f),
#[cfg(windows)]
Executor::Overlapped(ex) => ex.async_from(f),
+ #[cfg(feature = "tokio")]
+ Executor::Tokio(ex) => ex.async_from(f),
}
}
@@ -402,6 +474,8 @@ impl Executor {
Executor::Handle(ex) => ex.spawn(f),
#[cfg(windows)]
Executor::Overlapped(ex) => ex.spawn(f),
+ #[cfg(feature = "tokio")]
+ Executor::Tokio(ex) => ex.spawn(f),
}
}
@@ -446,6 +520,8 @@ impl Executor {
Executor::Handle(ex) => ex.spawn_local(f),
#[cfg(windows)]
Executor::Overlapped(ex) => ex.spawn_local(f),
+ #[cfg(feature = "tokio")]
+ Executor::Tokio(ex) => ex.spawn_local(f),
}
}
@@ -492,6 +568,8 @@ impl Executor {
Executor::Handle(ex) => ex.spawn_blocking(f),
#[cfg(windows)]
Executor::Overlapped(ex) => ex.spawn_blocking(f),
+ #[cfg(feature = "tokio")]
+ Executor::Tokio(ex) => ex.spawn_blocking(f),
}
}
@@ -569,6 +647,8 @@ impl Executor {
Executor::Handle(ex) => ex.run_until(f),
#[cfg(windows)]
Executor::Overlapped(ex) => ex.run_until(f),
+ #[cfg(feature = "tokio")]
+ Executor::Tokio(ex) => ex.run_until(f),
}
}
}
@@ -579,6 +659,8 @@ impl AsRawDescriptors for Executor {
match self {
Executor::Fd(ex) => ex.as_raw_descriptors(),
Executor::Uring(ex) => ex.as_raw_descriptors(),
+ #[cfg(feature = "tokio")]
+ Executor::Tokio(ex) => ex.as_raw_descriptors(),
}
}
}
diff --git a/cros_async/src/io_source.rs b/cros_async/src/io_source.rs
index efc4c03b5..b46b2aa0e 100644
--- a/cros_async/src/io_source.rs
+++ b/cros_async/src/io_source.rs
@@ -10,6 +10,8 @@ use base::AsRawDescriptor;
use crate::sys::linux::PollSource;
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::sys::linux::UringSource;
+#[cfg(feature = "tokio")]
+use crate::sys::platform::tokio_source::TokioSource;
#[cfg(windows)]
use crate::sys::windows::HandleSource;
#[cfg(windows)]
@@ -29,6 +31,8 @@ pub enum IoSource<F: base::AsRawDescriptor> {
Handle(HandleSource<F>),
#[cfg(windows)]
Overlapped(OverlappedSource<F>),
+ #[cfg(feature = "tokio")]
+ Tokio(TokioSource<F>),
}
static_assertions::assert_impl_all!(IoSource<std::fs::File>: Send, Sync);
@@ -47,6 +51,8 @@ macro_rules! await_on_inner {
IoSource::Handle(x) => HandleSource::$method(x, $($args),*).await,
#[cfg(windows)]
IoSource::Overlapped(x) => OverlappedSource::$method(x, $($args),*).await,
+ #[cfg(feature = "tokio")]
+ IoSource::Tokio(x) => TokioSource::$method(x, $($args),*).await,
}
};
}
@@ -65,6 +71,8 @@ macro_rules! on_inner {
IoSource::Handle(x) => HandleSource::$method(x, $($args),*),
#[cfg(windows)]
IoSource::Overlapped(x) => OverlappedSource::$method(x, $($args),*),
+ #[cfg(feature = "tokio")]
+ IoSource::Tokio(x) => TokioSource::$method(x, $($args),*),
}
};
}
diff --git a/cros_async/src/lib.rs b/cros_async/src/lib.rs
index 70ac127d1..071317d8b 100644
--- a/cros_async/src/lib.rs
+++ b/cros_async/src/lib.rs
@@ -57,6 +57,7 @@
mod async_types;
pub mod audio_streams_async;
mod blocking;
+mod common_executor;
mod complete;
mod event;
mod executor;
@@ -67,10 +68,9 @@ mod queue;
mod select;
pub mod sync;
pub mod sys;
-#[cfg(any(target_os = "android", target_os = "linux"))]
-pub use sys::linux::uring_executor::is_uring_stable;
-mod common_executor;
mod timer;
+#[cfg(feature = "tokio")]
+mod tokio_executor;
mod waker;
use std::future::Future;
@@ -105,6 +105,8 @@ pub use mem::MemRegionIter;
pub use mem::VecIoWrapper;
use remain::sorted;
pub use select::SelectResult;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use sys::linux::uring_executor::is_uring_stable;
use thiserror::Error as ThisError;
pub use timer::TimerAsync;
diff --git a/cros_async/src/sys/linux.rs b/cros_async/src/sys/linux.rs
index fb1948c39..fcaf87ec9 100644
--- a/cros_async/src/sys/linux.rs
+++ b/cros_async/src/sys/linux.rs
@@ -9,6 +9,8 @@ pub mod executor;
pub mod fd_executor;
pub mod poll_source;
mod timer;
+#[cfg(feature = "tokio")]
+pub mod tokio_source;
pub mod uring_executor;
pub mod uring_source;
diff --git a/cros_async/src/sys/linux/error.rs b/cros_async/src/sys/linux/error.rs
index 48e492cb3..83e946d3c 100644
--- a/cros_async/src/sys/linux/error.rs
+++ b/cros_async/src/sys/linux/error.rs
@@ -9,6 +9,9 @@ use std::io;
pub enum AsyncErrorSys {
#[error("Poll source error: {0}")]
Poll(#[from] super::poll_source::Error),
+ #[cfg(feature = "tokio")]
+ #[error("Tokio source error: {0}")]
+ Tokio(#[from] super::tokio_source::Error),
#[error("Uring source error: {0}")]
Uring(#[from] super::uring_executor::Error),
}
@@ -17,6 +20,8 @@ impl From<AsyncErrorSys> for io::Error {
fn from(err: AsyncErrorSys) -> Self {
match err {
AsyncErrorSys::Poll(e) => e.into(),
+ #[cfg(feature = "tokio")]
+ AsyncErrorSys::Tokio(e) => e.into(),
AsyncErrorSys::Uring(e) => e.into(),
}
}
diff --git a/cros_async/src/sys/linux/tokio_source.rs b/cros_async/src/sys/linux/tokio_source.rs
new file mode 100644
index 000000000..4a8471296
--- /dev/null
+++ b/cros_async/src/sys/linux/tokio_source.rs
@@ -0,0 +1,405 @@
+// Copyright 2024 The ChromiumOS Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::io;
+use std::os::fd::AsRawFd;
+use std::os::fd::OwnedFd;
+use std::os::fd::RawFd;
+use std::sync::Arc;
+
+use base::add_fd_flags;
+use base::clone_descriptor;
+use base::linux::fallocate;
+use base::linux::FallocateMode;
+use base::AsRawDescriptor;
+use base::VolatileSlice;
+use tokio::io::unix::AsyncFd;
+
+use crate::mem::MemRegion;
+use crate::AsyncError;
+use crate::AsyncResult;
+use crate::BackingMemory;
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("Failed to copy the FD for the polling context: '{0}'")]
+ DuplicatingFd(base::Error),
+ #[error("Failed to punch hole in file: '{0}'.")]
+ Fallocate(base::Error),
+ #[error("Failed to fdatasync: '{0}'")]
+ Fdatasync(io::Error),
+ #[error("Failed to fsync: '{0}'")]
+ Fsync(io::Error),
+ #[error("Failed to join task: '{0}'")]
+ Join(tokio::task::JoinError),
+ #[error("Cannot wait on file descriptor")]
+ NonWaitable,
+ #[error("Failed to read: '{0}'")]
+ Read(io::Error),
+ #[error("Failed to set nonblocking: '{0}'")]
+ SettingNonBlocking(base::Error),
+ #[error("Tokio Async FD error: '{0}'")]
+ TokioAsyncFd(io::Error),
+ #[error("Failed to write: '{0}'")]
+ Write(io::Error),
+}
+
+impl From<Error> for io::Error {
+ fn from(e: Error) -> Self {
+ use Error::*;
+ match e {
+ DuplicatingFd(e) => e.into(),
+ Fallocate(e) => e.into(),
+ Fdatasync(e) => e,
+ Fsync(e) => e,
+ Join(e) => io::Error::new(io::ErrorKind::Other, e),
+ NonWaitable => io::Error::new(io::ErrorKind::Other, e),
+ Read(e) => e,
+ SettingNonBlocking(e) => e.into(),
+ TokioAsyncFd(e) => e,
+ Write(e) => e,
+ }
+ }
+}
+
+enum FdType {
+ Async(AsyncFd<Arc<OwnedFd>>),
+ Blocking(Arc<OwnedFd>),
+}
+
+impl AsRawFd for FdType {
+ fn as_raw_fd(&self) -> RawFd {
+ match self {
+ FdType::Async(async_fd) => async_fd.as_raw_fd(),
+ FdType::Blocking(blocking) => blocking.as_raw_fd(),
+ }
+ }
+}
+
+impl From<Error> for AsyncError {
+ fn from(e: Error) -> AsyncError {
+ AsyncError::SysVariants(e.into())
+ }
+}
+
+fn do_fdatasync(raw: Arc<OwnedFd>) -> io::Result<()> {
+ let fd = raw.as_raw_fd();
+ // SAFETY: we partially own `raw`
+ match unsafe { libc::fdatasync(fd) } {
+ 0 => Ok(()),
+ _ => Err(io::Error::last_os_error()),
+ }
+}
+
+fn do_fsync(raw: Arc<OwnedFd>) -> io::Result<()> {
+ let fd = raw.as_raw_fd();
+ // SAFETY: we partially own `raw`
+ match unsafe { libc::fsync(fd) } {
+ 0 => Ok(()),
+ _ => Err(io::Error::last_os_error()),
+ }
+}
+
+fn do_read_to_mem(
+ raw: Arc<OwnedFd>,
+ file_offset: Option<u64>,
+ io_vecs: &Vec<VolatileSlice>,
+) -> io::Result<usize> {
+ let ptr = io_vecs.as_ptr() as *const libc::iovec;
+ let len = io_vecs.len() as i32;
+ let fd = raw.as_raw_fd();
+ let res = match file_offset {
+ // SAFETY: we partially own `raw`, `io_vecs` is validated
+ Some(off) => unsafe { libc::preadv64(fd, ptr, len, off as libc::off64_t) },
+ // SAFETY: we partially own `raw`, `io_vecs` is validated
+ None => unsafe { libc::readv(fd, ptr, len) },
+ };
+ match res {
+ r if r >= 0 => Ok(res as usize),
+ _ => Err(io::Error::last_os_error()),
+ }
+}
+fn do_read_to_vec(
+ raw: Arc<OwnedFd>,
+ file_offset: Option<u64>,
+ vec: &mut Vec<u8>,
+) -> io::Result<usize> {
+ let fd = raw.as_raw_fd();
+ let ptr = vec.as_mut_ptr() as *mut libc::c_void;
+ let res = match file_offset {
+ // SAFETY: we partially own `raw`, `ptr` has space up to vec.len()
+ Some(off) => unsafe { libc::pread64(fd, ptr, vec.len(), off as libc::off64_t) },
+ // SAFETY: we partially own `raw`, `ptr` has space up to vec.len()
+ None => unsafe { libc::read(fd, ptr, vec.len()) },
+ };
+ match res {
+ r if r >= 0 => Ok(res as usize),
+ _ => Err(io::Error::last_os_error()),
+ }
+}
+
+fn do_write_from_vec(
+ raw: Arc<OwnedFd>,
+ file_offset: Option<u64>,
+ vec: &Vec<u8>,
+) -> io::Result<usize> {
+ let fd = raw.as_raw_fd();
+ let ptr = vec.as_ptr() as *const libc::c_void;
+ let res = match file_offset {
+ // SAFETY: we partially own `raw`, `ptr` has data up to vec.len()
+ Some(off) => unsafe { libc::pwrite64(fd, ptr, vec.len(), off as libc::off64_t) },
+ // SAFETY: we partially own `raw`, `ptr` has data up to vec.len()
+ None => unsafe { libc::write(fd, ptr, vec.len()) },
+ };
+ match res {
+ r if r >= 0 => Ok(res as usize),
+ _ => Err(io::Error::last_os_error()),
+ }
+}
+
+fn do_write_from_mem(
+ raw: Arc<OwnedFd>,
+ file_offset: Option<u64>,
+ io_vecs: &Vec<VolatileSlice>,
+) -> io::Result<usize> {
+ let ptr = io_vecs.as_ptr() as *const libc::iovec;
+ let len = io_vecs.len() as i32;
+ let fd = raw.as_raw_fd();
+ let res = match file_offset {
+ // SAFETY: we partially own `raw`, `io_vecs` is validated
+ Some(off) => unsafe { libc::pwritev64(fd, ptr, len, off as libc::off64_t) },
+ // SAFETY: we partially own `raw`, `io_vecs` is validated
+ None => unsafe { libc::writev(fd, ptr, len) },
+ };
+ match res {
+ r if r >= 0 => Ok(res as usize),
+ _ => Err(io::Error::last_os_error()),
+ }
+}
+
+pub struct TokioSource<T> {
+ fd: FdType,
+ inner: T,
+ runtime: tokio::runtime::Handle,
+}
+impl<T: AsRawDescriptor> TokioSource<T> {
+ pub fn new(inner: T, runtime: tokio::runtime::Handle) -> Result<TokioSource<T>, Error> {
+ let _guard = runtime.enter(); // Required for AsyncFd
+ let safe_fd = clone_descriptor(&inner).map_err(Error::DuplicatingFd)?;
+ let fd_arc: Arc<OwnedFd> = Arc::new(safe_fd.into());
+ let fd = match AsyncFd::new(fd_arc.clone()) {
+ Ok(async_fd) => {
+ add_fd_flags(async_fd.get_ref().as_raw_descriptor(), libc::O_NONBLOCK)
+ .map_err(Error::SettingNonBlocking)?;
+ FdType::Async(async_fd)
+ }
+ Err(e) if e.kind() == io::ErrorKind::PermissionDenied => FdType::Blocking(fd_arc),
+ Err(e) => return Err(Error::TokioAsyncFd(e)),
+ };
+ Ok(TokioSource { fd, inner, runtime })
+ }
+
+ pub fn as_source(&self) -> &T {
+ &self.inner
+ }
+
+ pub fn as_source_mut(&mut self) -> &mut T {
+ &mut self.inner
+ }
+
+ fn clone_fd(&self) -> Arc<OwnedFd> {
+ match &self.fd {
+ FdType::Async(async_fd) => async_fd.get_ref().clone(),
+ FdType::Blocking(blocking) => blocking.clone(),
+ }
+ }
+
+ pub async fn fdatasync(&self) -> AsyncResult<()> {
+ let fd = self.clone_fd();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || do_fdatasync(fd))
+ .await
+ .map_err(Error::Join)?
+ .map_err(Error::Fdatasync)?)
+ }
+
+ pub async fn fsync(&self) -> AsyncResult<()> {
+ let fd = self.clone_fd();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || do_fsync(fd))
+ .await
+ .map_err(Error::Join)?
+ .map_err(Error::Fsync)?)
+ }
+
+ pub fn into_source(self) -> T {
+ self.inner
+ }
+
+ pub async fn read_to_vec(
+ &self,
+ file_offset: Option<u64>,
+ mut vec: Vec<u8>,
+ ) -> AsyncResult<(usize, Vec<u8>)> {
+ Ok(match &self.fd {
+ FdType::Async(async_fd) => {
+ let res = async_fd
+ .async_io(tokio::io::Interest::READABLE, |fd| {
+ do_read_to_vec(fd.clone(), file_offset, &mut vec)
+ })
+ .await
+ .map_err(AsyncError::Io)?;
+ (res, vec)
+ }
+ FdType::Blocking(blocking) => {
+ let fd = blocking.clone();
+ self.runtime
+ .spawn_blocking(move || {
+ let size = do_read_to_vec(fd, file_offset, &mut vec)?;
+ Ok((size, vec))
+ })
+ .await
+ .map_err(Error::Join)?
+ .map_err(Error::Read)?
+ }
+ })
+ }
+
+ pub async fn read_to_mem(
+ &self,
+ file_offset: Option<u64>,
+ mem: Arc<dyn BackingMemory + Send + Sync>,
+ mem_offsets: impl IntoIterator<Item = MemRegion>,
+ ) -> AsyncResult<usize> {
+ let mem_offsets_vec: Vec<MemRegion> = mem_offsets.into_iter().collect();
+ Ok(match &self.fd {
+ FdType::Async(async_fd) => {
+ let iovecs = mem_offsets_vec
+ .into_iter()
+ .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok())
+ .collect::<Vec<VolatileSlice>>();
+ async_fd
+ .async_io(tokio::io::Interest::READABLE, |fd| {
+ do_read_to_mem(fd.clone(), file_offset, &iovecs)
+ })
+ .await
+ .map_err(AsyncError::Io)?
+ }
+ FdType::Blocking(blocking) => {
+ let fd = blocking.clone();
+ self.runtime
+ .spawn_blocking(move || {
+ let iovecs = mem_offsets_vec
+ .into_iter()
+ .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok())
+ .collect::<Vec<VolatileSlice>>();
+ do_read_to_mem(fd, file_offset, &iovecs)
+ })
+ .await
+ .map_err(Error::Join)?
+ .map_err(Error::Read)?
+ }
+ })
+ }
+
+ pub async fn punch_hole(&self, file_offset: u64, len: u64) -> AsyncResult<()> {
+ let fd = self.clone_fd();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || fallocate(&*fd, FallocateMode::PunchHole, file_offset, len))
+ .await
+ .map_err(Error::Join)?
+ .map_err(Error::Fallocate)?)
+ }
+
+ pub async fn wait_readable(&self) -> AsyncResult<()> {
+ match &self.fd {
+ FdType::Async(async_fd) => async_fd
+ .readable()
+ .await
+ .map_err(crate::AsyncError::Io)?
+ .retain_ready(),
+ FdType::Blocking(_) => return Err(Error::NonWaitable.into()),
+ }
+ Ok(())
+ }
+
+ pub async fn write_from_mem(
+ &self,
+ file_offset: Option<u64>,
+ mem: Arc<dyn BackingMemory + Send + Sync>,
+ mem_offsets: impl IntoIterator<Item = MemRegion>,
+ ) -> AsyncResult<usize> {
+ let mem_offsets_vec: Vec<MemRegion> = mem_offsets.into_iter().collect();
+ Ok(match &self.fd {
+ FdType::Async(async_fd) => {
+ let iovecs = mem_offsets_vec
+ .into_iter()
+ .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok())
+ .collect::<Vec<VolatileSlice>>();
+ async_fd
+ .async_io(tokio::io::Interest::WRITABLE, |fd| {
+ do_write_from_mem(fd.clone(), file_offset, &iovecs)
+ })
+ .await
+ .map_err(AsyncError::Io)?
+ }
+ FdType::Blocking(blocking) => {
+ let fd = blocking.clone();
+ self.runtime
+ .spawn_blocking(move || {
+ let iovecs = mem_offsets_vec
+ .into_iter()
+ .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok())
+ .collect::<Vec<VolatileSlice>>();
+ do_write_from_mem(fd, file_offset, &iovecs.clone())
+ })
+ .await
+ .map_err(Error::Join)?
+ .map_err(Error::Read)?
+ }
+ })
+ }
+
+ pub async fn write_from_vec(
+ &self,
+ file_offset: Option<u64>,
+ vec: Vec<u8>,
+ ) -> AsyncResult<(usize, Vec<u8>)> {
+ Ok(match &self.fd {
+ FdType::Async(async_fd) => {
+ let res = async_fd
+ .async_io(tokio::io::Interest::WRITABLE, |fd| {
+ do_write_from_vec(fd.clone(), file_offset, &vec)
+ })
+ .await
+ .map_err(AsyncError::Io)?;
+ (res, vec)
+ }
+ FdType::Blocking(blocking) => {
+ let fd = blocking.clone();
+ self.runtime
+ .spawn_blocking(move || {
+ let size = do_write_from_vec(fd.clone(), file_offset, &vec)?;
+ Ok((size, vec))
+ })
+ .await
+ .map_err(Error::Join)?
+ .map_err(Error::Read)?
+ }
+ })
+ }
+
+ pub async fn write_zeroes_at(&self, file_offset: u64, len: u64) -> AsyncResult<()> {
+ let fd = self.clone_fd();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || fallocate(&*fd, FallocateMode::ZeroRange, file_offset, len))
+ .await
+ .map_err(Error::Join)?
+ .map_err(Error::Fallocate)?)
+ }
+}
diff --git a/cros_async/src/sys/windows.rs b/cros_async/src/sys/windows.rs
index b040309dd..315426f33 100644
--- a/cros_async/src/sys/windows.rs
+++ b/cros_async/src/sys/windows.rs
@@ -11,6 +11,8 @@ pub mod handle_source;
mod io_completion_port;
pub mod overlapped_source;
mod timer;
+#[cfg(feature = "tokio")]
+pub mod tokio_source;
pub mod wait_for_handle;
pub use error::AsyncErrorSys;
diff --git a/cros_async/src/sys/windows/error.rs b/cros_async/src/sys/windows/error.rs
index f0437457a..74c4c04dc 100644
--- a/cros_async/src/sys/windows/error.rs
+++ b/cros_async/src/sys/windows/error.rs
@@ -13,6 +13,9 @@ pub enum AsyncErrorSys {
HandleSource(#[from] super::handle_source::Error),
#[error("An error with a handle source: {0}")]
OverlappedSource(#[from] super::overlapped_source::Error),
+ #[cfg(feature = "tokio")]
+ #[error("Tokio source error: {0}")]
+ Tokio(#[from] super::tokio_source::Error),
}
impl From<AsyncErrorSys> for io::Error {
@@ -21,6 +24,8 @@ impl From<AsyncErrorSys> for io::Error {
AsyncErrorSys::HandleExecutor(e) => e.into(),
AsyncErrorSys::HandleSource(e) => e.into(),
AsyncErrorSys::OverlappedSource(e) => e.into(),
+ #[cfg(feature = "tokio")]
+ AsyncErrorSys::Tokio(e) => e.into(),
}
}
}
diff --git a/cros_async/src/sys/windows/executor.rs b/cros_async/src/sys/windows/executor.rs
index 3d9053b22..31ba4bfa5 100644
--- a/cros_async/src/sys/windows/executor.rs
+++ b/cros_async/src/sys/windows/executor.rs
@@ -2,15 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-use serde::Deserialize;
-use serde::Serialize;
-
/// An enum to express the kind of the backend of `Executor`
-#[derive(
- Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, serde_keyvalue::FromKeyValues,
-)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Deserialize, serde_keyvalue::FromKeyValues)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub enum ExecutorKindSys {
Handle,
- Overlapped,
+ Overlapped { concurrency: Option<u32> },
+}
+
+impl serde::Serialize for ExecutorKindSys {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.serialize_str(&match self {
+ ExecutorKindSys::Handle => "handle".to_string(),
+ ExecutorKindSys::Overlapped { concurrency: None } => "overlapped".to_string(),
+ ExecutorKindSys::Overlapped {
+ concurrency: Some(n),
+ } => format!("overlapped,concurrency={}", n),
+ })
+ }
}
diff --git a/cros_async/src/sys/windows/handle_executor.rs b/cros_async/src/sys/windows/handle_executor.rs
index 614730801..c5c4fe522 100644
--- a/cros_async/src/sys/windows/handle_executor.rs
+++ b/cros_async/src/sys/windows/handle_executor.rs
@@ -95,7 +95,7 @@ impl HandleReactor {
})
}
- fn new() -> Result<Self> {
+ pub fn new() -> Result<Self> {
Self::new_with(DEFAULT_IO_CONCURRENCY)
}
diff --git a/cros_async/src/sys/windows/tokio_source.rs b/cros_async/src/sys/windows/tokio_source.rs
new file mode 100644
index 000000000..676edb7c2
--- /dev/null
+++ b/cros_async/src/sys/windows/tokio_source.rs
@@ -0,0 +1,291 @@
+// Copyright 2022 The ChromiumOS Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::fs::File;
+use std::io;
+use std::io::Read;
+use std::io::Seek;
+use std::io::SeekFrom;
+use std::io::Write;
+use std::mem::ManuallyDrop;
+use std::sync::Arc;
+
+use base::AsRawDescriptor;
+use base::FileReadWriteAtVolatile;
+use base::FileReadWriteVolatile;
+use base::FromRawDescriptor;
+use base::PunchHole;
+use base::VolatileSlice;
+use base::WriteZeroesAt;
+use smallvec::SmallVec;
+use sync::Mutex;
+
+use crate::mem::MemRegion;
+use crate::AsyncError;
+use crate::AsyncResult;
+use crate::BackingMemory;
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("An error occurred trying to seek: {0}.")]
+ IoSeekError(io::Error),
+ #[error("An error occurred trying to read: {0}.")]
+ IoReadError(io::Error),
+ #[error("An error occurred trying to write: {0}.")]
+ IoWriteError(io::Error),
+ #[error("An error occurred trying to flush: {0}.")]
+ IoFlushError(io::Error),
+ #[error("An error occurred trying to punch hole: {0}.")]
+ IoPunchHoleError(io::Error),
+ #[error("An error occurred trying to write zeroes: {0}.")]
+ IoWriteZeroesError(io::Error),
+ #[error("Failed to join task: '{0}'")]
+ Join(tokio::task::JoinError),
+ #[error("An error occurred trying to duplicate source handles: {0}.")]
+ HandleDuplicationFailed(io::Error),
+ #[error("An error occurred trying to wait on source handles: {0}.")]
+ HandleWaitFailed(base::Error),
+ #[error("An error occurred trying to get a VolatileSlice into BackingMemory: {0}.")]
+ BackingMemoryVolatileSliceFetchFailed(crate::mem::Error),
+ #[error("TokioSource is gone, so no handles are available to fulfill the IO request.")]
+ NoTokioSource,
+ #[error("Operation on TokioSource is cancelled.")]
+ OperationCancelled,
+ #[error("Operation on TokioSource was aborted (unexpected).")]
+ OperationAborted,
+}
+
+impl From<Error> for AsyncError {
+ fn from(e: Error) -> AsyncError {
+ AsyncError::SysVariants(e.into())
+ }
+}
+
+impl From<Error> for io::Error {
+ fn from(e: Error) -> Self {
+ use Error::*;
+ match e {
+ IoSeekError(e) => e,
+ IoReadError(e) => e,
+ IoWriteError(e) => e,
+ IoFlushError(e) => e,
+ IoPunchHoleError(e) => e,
+ IoWriteZeroesError(e) => e,
+ Join(e) => io::Error::new(io::ErrorKind::Other, e),
+ HandleDuplicationFailed(e) => e,
+ HandleWaitFailed(e) => e.into(),
+ BackingMemoryVolatileSliceFetchFailed(e) => io::Error::new(io::ErrorKind::Other, e),
+ NoTokioSource => io::Error::new(io::ErrorKind::Other, NoTokioSource),
+ OperationCancelled => io::Error::new(io::ErrorKind::Interrupted, OperationCancelled),
+ OperationAborted => io::Error::new(io::ErrorKind::Interrupted, OperationAborted),
+ }
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+pub struct TokioSource<T: AsRawDescriptor> {
+ source: Option<T>,
+ source_file: Arc<Mutex<Option<ManuallyDrop<File>>>>,
+ runtime: tokio::runtime::Handle,
+}
+
+impl<T: AsRawDescriptor> TokioSource<T> {
+ pub(crate) fn new(source: T, runtime: tokio::runtime::Handle) -> Result<TokioSource<T>> {
+ let descriptor = source.as_raw_descriptor();
+ // SAFETY: The Drop implementation makes sure `source` outlives `source_file`.
+ let source_file = unsafe { ManuallyDrop::new(File::from_raw_descriptor(descriptor)) };
+ Ok(Self {
+ source: Some(source),
+ source_file: Arc::new(Mutex::new(Some(source_file))),
+ runtime,
+ })
+ }
+ #[inline]
+ fn get_slices(
+ mem: &Arc<dyn BackingMemory + Send + Sync>,
+ mem_offsets: Vec<MemRegion>,
+ ) -> Result<SmallVec<[VolatileSlice<'_>; 16]>> {
+ mem_offsets
+ .into_iter()
+ .map(|region| {
+ mem.get_volatile_slice(region)
+ .map_err(Error::BackingMemoryVolatileSliceFetchFailed)
+ })
+ .collect::<Result<SmallVec<[VolatileSlice; 16]>>>()
+ }
+ pub fn as_source(&self) -> &T {
+ self.source.as_ref().unwrap()
+ }
+ pub fn as_source_mut(&mut self) -> &mut T {
+ self.source.as_mut().unwrap()
+ }
+ pub async fn fdatasync(&self) -> AsyncResult<()> {
+ // TODO(b/282003931): Fall back to regular fsync.
+ self.fsync().await
+ }
+ pub async fn fsync(&self) -> AsyncResult<()> {
+ let source_file = self.source_file.clone();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || {
+ source_file
+ .lock()
+ .as_mut()
+ .ok_or(Error::OperationCancelled)?
+ .flush()
+ .map_err(Error::IoFlushError)
+ })
+ .await
+ .map_err(Error::Join)??)
+ }
+ pub fn into_source(mut self) -> T {
+ self.source_file.lock().take();
+ self.source.take().unwrap()
+ }
+ pub async fn punch_hole(&self, file_offset: u64, len: u64) -> AsyncResult<()> {
+ let source_file = self.source_file.clone();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || {
+ source_file
+ .lock()
+ .as_mut()
+ .ok_or(Error::OperationCancelled)?
+ .punch_hole(file_offset, len)
+ .map_err(Error::IoPunchHoleError)
+ })
+ .await
+ .map_err(Error::Join)??)
+ }
+ pub async fn read_to_mem(
+ &self,
+ file_offset: Option<u64>,
+ mem: Arc<dyn BackingMemory + Send + Sync>,
+ mem_offsets: impl IntoIterator<Item = MemRegion>,
+ ) -> AsyncResult<usize> {
+ let mem_offsets = mem_offsets.into_iter().collect();
+ let source_file = self.source_file.clone();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || {
+ let mut file_lock = source_file.lock();
+ let file = file_lock.as_mut().ok_or(Error::OperationCancelled)?;
+ let memory_slices = Self::get_slices(&mem, mem_offsets)?;
+ match file_offset {
+ Some(file_offset) => file
+ .read_vectored_at_volatile(memory_slices.as_slice(), file_offset)
+ .map_err(Error::IoReadError),
+ None => file
+ .read_vectored_volatile(memory_slices.as_slice())
+ .map_err(Error::IoReadError),
+ }
+ })
+ .await
+ .map_err(Error::Join)??)
+ }
+ pub async fn read_to_vec(
+ &self,
+ file_offset: Option<u64>,
+ mut vec: Vec<u8>,
+ ) -> AsyncResult<(usize, Vec<u8>)> {
+ let source_file = self.source_file.clone();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || {
+ let mut file_lock = source_file.lock();
+ let file = file_lock.as_mut().ok_or(Error::OperationCancelled)?;
+ if let Some(file_offset) = file_offset {
+ file.seek(SeekFrom::Start(file_offset))
+ .map_err(Error::IoSeekError)?;
+ }
+ Ok::<(usize, Vec<u8>), Error>((
+ file.read(vec.as_mut_slice()).map_err(Error::IoReadError)?,
+ vec,
+ ))
+ })
+ .await
+ .map_err(Error::Join)??)
+ }
+ pub async fn wait_readable(&self) -> AsyncResult<()> {
+ unimplemented!();
+ }
+ pub async fn wait_for_handle(&self) -> AsyncResult<()> {
+ let waiter = super::wait_for_handle::WaitForHandle::new(self.source.as_ref().unwrap());
+ Ok(waiter.await?)
+ }
+ pub async fn write_from_mem(
+ &self,
+ file_offset: Option<u64>,
+ mem: Arc<dyn BackingMemory + Send + Sync>,
+ mem_offsets: impl IntoIterator<Item = MemRegion>,
+ ) -> AsyncResult<usize> {
+ let mem_offsets = mem_offsets.into_iter().collect();
+ let source_file = self.source_file.clone();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || {
+ let mut file_lock = source_file.lock();
+ let file = file_lock.as_mut().ok_or(Error::OperationCancelled)?;
+ let memory_slices = Self::get_slices(&mem, mem_offsets)?;
+ match file_offset {
+ Some(file_offset) => file
+ .write_vectored_at_volatile(memory_slices.as_slice(), file_offset)
+ .map_err(Error::IoWriteError),
+ None => file
+ .write_vectored_volatile(memory_slices.as_slice())
+ .map_err(Error::IoWriteError),
+ }
+ })
+ .await
+ .map_err(Error::Join)??)
+ }
+ pub async fn write_from_vec(
+ &self,
+ file_offset: Option<u64>,
+ vec: Vec<u8>,
+ ) -> AsyncResult<(usize, Vec<u8>)> {
+ let source_file = self.source_file.clone();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || {
+ let mut file_lock = source_file.lock();
+ let file = file_lock.as_mut().ok_or(Error::OperationCancelled)?;
+ if let Some(file_offset) = file_offset {
+ file.seek(SeekFrom::Start(file_offset))
+ .map_err(Error::IoSeekError)?;
+ }
+ Ok::<(usize, Vec<u8>), Error>((
+ file.write(vec.as_slice()).map_err(Error::IoWriteError)?,
+ vec,
+ ))
+ })
+ .await
+ .map_err(Error::Join)??)
+ }
+ pub async fn write_zeroes_at(&self, file_offset: u64, len: u64) -> AsyncResult<()> {
+ let source_file = self.source_file.clone();
+ Ok(self
+ .runtime
+ .spawn_blocking(move || {
+ // ZeroRange calls `punch_hole` which doesn't extend the File size if it needs to.
+ // Will fix if it becomes a problem.
+ source_file
+ .lock()
+ .as_mut()
+ .ok_or(Error::OperationCancelled)?
+ .write_zeroes_at(file_offset, len as usize)
+ .map_err(Error::IoWriteZeroesError)
+ .map(|_| ())
+ })
+ .await
+ .map_err(Error::Join)??)
+ }
+}
+impl<T: AsRawDescriptor> Drop for TokioSource<T> {
+ fn drop(&mut self) {
+ let mut source_file = self.source_file.lock();
+ source_file.take();
+ }
+}
diff --git a/cros_async/src/tokio_executor.rs b/cros_async/src/tokio_executor.rs
new file mode 100644
index 000000000..b84fccadb
--- /dev/null
+++ b/cros_async/src/tokio_executor.rs
@@ -0,0 +1,170 @@
+// Copyright 2023 The ChromiumOS Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::future::Future;
+use std::pin::Pin;
+use std::sync::Arc;
+use std::sync::OnceLock;
+
+use base::AsRawDescriptors;
+use base::RawDescriptor;
+use tokio::runtime::Runtime;
+use tokio::task::LocalSet;
+
+use crate::sys::platform::tokio_source::TokioSource;
+use crate::AsyncError;
+use crate::AsyncResult;
+use crate::ExecutorTrait;
+use crate::IntoAsync;
+use crate::IoSource;
+use crate::TaskHandle;
+
+mod send_wrapper {
+ use std::thread;
+
+ #[derive(Clone)]
+ pub(super) struct SendWrapper<T> {
+ instance: T,
+ thread_id: thread::ThreadId,
+ }
+
+ impl<T> SendWrapper<T> {
+ pub(super) fn new(instance: T) -> SendWrapper<T> {
+ SendWrapper {
+ instance,
+ thread_id: thread::current().id(),
+ }
+ }
+ }
+
+ // SAFETY: panics when the value is accessed on the wrong thread.
+ unsafe impl<T> Send for SendWrapper<T> {}
+ // SAFETY: panics when the value is accessed on the wrong thread.
+ unsafe impl<T> Sync for SendWrapper<T> {}
+
+ impl<T> Drop for SendWrapper<T> {
+ fn drop(&mut self) {
+ if self.thread_id != thread::current().id() {
+ panic!("SendWrapper value was dropped on the wrong thread");
+ }
+ }
+ }
+
+ impl<T> std::ops::Deref for SendWrapper<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ if self.thread_id != thread::current().id() {
+ panic!("SendWrapper value was accessed on the wrong thread");
+ }
+ &self.instance
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct TokioExecutor {
+ runtime: Arc<Runtime>,
+ local_set: Arc<OnceLock<send_wrapper::SendWrapper<LocalSet>>>,
+}
+
+impl TokioExecutor {
+ pub fn new() -> AsyncResult<Self> {
+ Ok(TokioExecutor {
+ runtime: Arc::new(Runtime::new().map_err(AsyncError::Io)?),
+ local_set: Arc::new(OnceLock::new()),
+ })
+ }
+}
+
+impl ExecutorTrait for TokioExecutor {
+ fn async_from<'a, F: IntoAsync + 'a>(&self, f: F) -> AsyncResult<IoSource<F>> {
+ Ok(IoSource::Tokio(TokioSource::new(
+ f,
+ self.runtime.handle().clone(),
+ )?))
+ }
+
+ fn run_until<F: Future>(&self, f: F) -> AsyncResult<F::Output> {
+ let local_set = self
+ .local_set
+ .get_or_init(|| send_wrapper::SendWrapper::new(LocalSet::new()));
+ Ok(self
+ .runtime
+ .block_on(async { local_set.run_until(f).await }))
+ }
+
+ fn spawn<F>(&self, f: F) -> TaskHandle<F::Output>
+ where
+ F: Future + Send + 'static,
+ F::Output: Send + 'static,
+ {
+ TaskHandle::Tokio(TokioTaskHandle {
+ join_handle: Some(self.runtime.spawn(f)),
+ })
+ }
+
+ fn spawn_blocking<F, R>(&self, f: F) -> TaskHandle<R>
+ where
+ F: FnOnce() -> R + Send + 'static,
+ R: Send + 'static,
+ {
+ TaskHandle::Tokio(TokioTaskHandle {
+ join_handle: Some(self.runtime.spawn_blocking(f)),
+ })
+ }
+
+ fn spawn_local<F>(&self, f: F) -> TaskHandle<F::Output>
+ where
+ F: Future + 'static,
+ F::Output: 'static,
+ {
+ let local_set = self
+ .local_set
+ .get_or_init(|| send_wrapper::SendWrapper::new(LocalSet::new()));
+ TaskHandle::Tokio(TokioTaskHandle {
+ join_handle: Some(local_set.spawn_local(f)),
+ })
+ }
+}
+
+impl AsRawDescriptors for TokioExecutor {
+ fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
+ todo!();
+ }
+}
+
+pub struct TokioTaskHandle<T> {
+ join_handle: Option<tokio::task::JoinHandle<T>>,
+}
+impl<R> TokioTaskHandle<R> {
+ pub async fn cancel(mut self) -> Option<R> {
+ match self.join_handle.take() {
+ Some(handle) => {
+ handle.abort();
+ handle.await.ok()
+ }
+ None => None,
+ }
+ }
+ pub fn detach(mut self) {
+ self.join_handle.take();
+ }
+}
+impl<R: 'static> Future for TokioTaskHandle<R> {
+ type Output = R;
+ fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context) -> std::task::Poll<Self::Output> {
+ let self_mut = self.get_mut();
+ Pin::new(self_mut.join_handle.as_mut().unwrap())
+ .poll(cx)
+ .map(|v| v.unwrap())
+ }
+}
+impl<T> std::ops::Drop for TokioTaskHandle<T> {
+ fn drop(&mut self) {
+ if let Some(handle) = self.join_handle.take() {
+ handle.abort()
+ }
+ }
+}
diff --git a/cros_fdt/Android.bp b/cros_fdt/Android.bp
index c99b584b0..73c4d15ae 100644
--- a/cros_fdt/Android.bp
+++ b/cros_fdt/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/cros_tracing/Android.bp b/cros_tracing/Android.bp
index ed18c8dcd..b132421c1 100644
--- a/cros_tracing/Android.bp
+++ b/cros_tracing/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/cros_tracing_types/Android.bp b/cros_tracing_types/Android.bp
index ca29fd7ec..fb9a25ba5 100644
--- a/cros_tracing_types/Android.bp
+++ b/cros_tracing_types/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/crosvm_cli/Android.bp b/crosvm_cli/Android.bp
index 3ce8499db..ba356984c 100644
--- a/crosvm_cli/Android.bp
+++ b/crosvm_cli/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/crosvm_control/Android.bp b/crosvm_control/Android.bp
index bf87a302b..abed499c9 100644
--- a/crosvm_control/Android.bp
+++ b/crosvm_control/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/devices/Android.bp b/devices/Android.bp
index 26c7ecc78..efed696f7 100644
--- a/devices/Android.bp
+++ b/devices/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
@@ -67,6 +68,7 @@ rust_test {
"liblibc",
"liblinux_input_sys",
"libmemoffset",
+ "libmetrics",
"libminijail_rust",
"libnamed_lock",
"libnet_sys",
@@ -159,6 +161,7 @@ rust_test {
"liblibc",
"liblinux_input_sys",
"libmemoffset",
+ "libmetrics",
"libminijail_rust",
"libnamed_lock",
"libnet_sys",
@@ -243,6 +246,7 @@ rust_library {
"liblibc",
"liblinux_input_sys",
"libmemoffset",
+ "libmetrics",
"libminijail_rust",
"libnet_sys",
"libnet_util",
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index 6524dd7de..de3b798c4 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -66,6 +66,7 @@ libc = "*"
libvda = { path = "../media/libvda", optional = true }
linux_input_sys = { path = "../linux_input_sys" }
memoffset = { version = "0.6" }
+metrics = { path = "../metrics" }
net_util = { path = "../net_util" }
num-traits = "0.2"
once_cell = "1.7.2"
@@ -102,7 +103,6 @@ vhost = { path = "../vhost" }
[target.'cfg(windows)'.dependencies]
broker_ipc = { path = "../broker_ipc" }
-metrics = { path = "../metrics" }
tube_transporter = { path = "../tube_transporter" }
win_audio = { path = "../win_audio"}
win_util = { path = "../win_util"}
diff --git a/devices/src/acpi.rs b/devices/src/acpi.rs
index e4657dcca..7244e0290 100644
--- a/devices/src/acpi.rs
+++ b/devices/src/acpi.rs
@@ -7,6 +7,7 @@ use std::str::FromStr;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
+use std::time::Instant;
use acpi_tables::aml;
use acpi_tables::aml::Aml;
@@ -23,6 +24,8 @@ use base::Tube;
use base::VmEventType;
use base::WaitContext;
use base::WorkerThread;
+use metrics::log_metric;
+use metrics::MetricEventType;
use serde::Deserialize;
use serde::Serialize;
use sync::Mutex;
@@ -663,38 +666,50 @@ pub struct PmWakeupEvent {
active: AtomicBool,
vm_control_tube: Arc<Mutex<Tube>>,
pm_config: Arc<Mutex<PmConfig>>,
- debug_label: String,
+ metrics_event: MetricEventType,
+ armed_time: Arc<Mutex<Instant>>,
}
impl PmWakeupEvent {
pub fn new(
vm_control_tube: Arc<Mutex<Tube>>,
pm_config: Arc<Mutex<PmConfig>>,
- debug_label: String,
+ metrics_event: MetricEventType,
) -> Self {
Self {
active: AtomicBool::new(false),
vm_control_tube,
pm_config,
- debug_label,
+ metrics_event,
+ // Not actually armed, but simpler than wrapping with an Option.
+ armed_time: Arc::new(Mutex::new(Instant::now())),
}
}
pub fn trigger_wakeup(&self) -> anyhow::Result<()> {
if self.active.load(Ordering::SeqCst) && self.pm_config.lock().should_trigger_pme() {
+ let elapsed = self.armed_time.lock().elapsed().as_millis();
+ log_metric(
+ self.metrics_event.clone(),
+ elapsed.try_into().unwrap_or(i64::MAX),
+ );
+
let tube = self.vm_control_tube.lock();
tube.send(&VmRequest::Gpe(PM_WAKEUP_GPIO))
- .with_context(|| format!("{} failed to send pme", self.debug_label))?;
+ .with_context(|| format!("{:?} failed to send pme", self.metrics_event))?;
match tube.recv::<VmResponse>() {
Ok(VmResponse::Ok) => (),
- e => bail!("{} pme failure {:?}", self.debug_label, e),
+ e => bail!("{:?} pme failure {:?}", self.metrics_event, e),
}
}
Ok(())
}
pub fn set_active(&self, active: bool) {
- self.active.store(active, Ordering::SeqCst)
+ self.active.store(active, Ordering::SeqCst);
+ if active {
+ *self.armed_time.lock() = Instant::now();
+ }
}
}
diff --git a/devices/src/cmos.rs b/devices/src/cmos.rs
index 0c44ac1b0..0f99c1615 100644
--- a/devices/src/cmos.rs
+++ b/devices/src/cmos.rs
@@ -5,6 +5,7 @@
use std::cmp::min;
use std::sync::Arc;
use std::time::Duration;
+use std::time::Instant;
use anyhow::anyhow;
use anyhow::Context;
@@ -24,6 +25,8 @@ use chrono::Datelike;
use chrono::TimeZone;
use chrono::Timelike;
use chrono::Utc;
+use metrics::log_metric;
+use metrics::MetricEventType;
use serde::Deserialize;
use serde::Serialize;
use sync::Mutex;
@@ -86,17 +89,35 @@ pub struct Cmos {
alarm_fn: Option<AlarmFn>,
#[serde(skip_serializing)] // skip serializing the worker thread
worker: Option<WorkerThread<AlarmFn>>,
+ #[serde(skip_serializing)] // skip serializing the armed time
+ armed_time: Option<Arc<Mutex<Instant>>>,
}
struct AlarmFn {
irq: IrqEdgeEvent,
vm_control: Tube,
+ armed_time: Arc<Mutex<Instant>>,
}
impl AlarmFn {
+ fn new(irq: IrqEdgeEvent, vm_control: Tube) -> Self {
+ Self {
+ irq,
+ vm_control,
+ // Not actually armed, but simpler than wrapping with an Option.
+ armed_time: Arc::new(Mutex::new(Instant::now())),
+ }
+ }
+
fn fire(&self) -> anyhow::Result<()> {
self.irq.trigger().context("failed to trigger irq")?;
+ let elapsed = self.armed_time.lock().elapsed().as_millis();
+ log_metric(
+ MetricEventType::RtcWakeup,
+ elapsed.try_into().unwrap_or(i64::MAX),
+ );
+
// The Linux kernel expects wakeups to come via ACPI when ACPI is enabled. There's
// no real way to determine that here, so just send this unconditionally.
self.vm_control
@@ -125,7 +146,7 @@ impl Cmos {
mem_below_4g,
mem_above_4g,
now_fn,
- Some(AlarmFn { irq, vm_control }),
+ Some(AlarmFn::new(irq, vm_control)),
)
}
@@ -153,6 +174,7 @@ impl Cmos {
data[0x5c] = (high_mem >> 8) as u8;
data[0x5d] = (high_mem >> 16) as u8;
+ let armed_time = alarm_fn.as_ref().map(|a| a.armed_time.clone());
Ok(Cmos {
index: 0,
data,
@@ -161,6 +183,7 @@ impl Cmos {
alarm_time: None,
alarm_fn,
worker: None,
+ armed_time,
})
}
@@ -202,6 +225,9 @@ impl Cmos {
if self.alarm_fn.is_some() {
self.spawn_worker();
}
+ if let Some(armed_time) = self.armed_time.as_ref() {
+ *armed_time.lock() = Instant::now();
+ }
let duration = target
.signed_duration_since(now)
@@ -690,10 +716,7 @@ mod tests {
fn cmos_sleep_wake() {
// 2000-01-02T03:04:05+00:00
let now_fn = || timestamp_to_datetime(946782245);
- let alarm_fn = AlarmFn {
- irq: IrqEdgeEvent::new().unwrap(),
- vm_control: Tube::pair().unwrap().0,
- };
+ let alarm_fn = AlarmFn::new(IrqEdgeEvent::new().unwrap(), Tube::pair().unwrap().0);
let mut cmos = Cmos::new_inner(1024, 0, now_fn, Some(alarm_fn)).unwrap();
// A date later this year
diff --git a/devices/src/pci/pci_configuration.rs b/devices/src/pci/pci_configuration.rs
index 3559082e2..e7a1b9149 100644
--- a/devices/src/pci/pci_configuration.rs
+++ b/devices/src/pci/pci_configuration.rs
@@ -833,7 +833,12 @@ impl PciConfiguration {
.write_obj_volatile(value, reg_offset)
.expect("bad register offset");
}
- mmio_mapping.flush_uncached_guest_mapping(reg_offset)
+ if let Err(err) = mmio_mapping.flush_region(reg_offset, 4) {
+ error!(
+ "failed to flush write to pci mmio register ({}): {}",
+ reg_idx, err
+ );
+ }
}
}
@@ -1017,7 +1022,12 @@ impl PciCapMapping {
mapping
.write_obj_volatile(new_val, offset)
.expect("memcpy failed");
- mapping.flush_uncached_guest_mapping(offset);
+ if let Err(err) = mapping.flush_region(offset, 4) {
+ error!(
+ "failed to flush write to pci cap in mmio register ({}): {}",
+ reg_idx, err
+ );
+ }
}
}
diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs
index a029bae9a..b6d8d035c 100644
--- a/devices/src/pci/pci_root.rs
+++ b/devices/src/pci/pci_root.rs
@@ -579,7 +579,9 @@ impl PciRootMmioState {
mapping
.write_obj_volatile(val, reg_offset)
.expect("memcpy failed");
- mapping.flush_uncached_guest_mapping(reg_offset);
+ if let Err(err) = mapping.flush_region(reg_offset, 4) {
+ error!("failed to flush write to mfd bit: {}", err);
+ }
}
}
}
diff --git a/devices/src/serial_device.rs b/devices/src/serial_device.rs
index 20454b522..dd4771161 100644
--- a/devices/src/serial_device.rs
+++ b/devices/src/serial_device.rs
@@ -188,6 +188,7 @@ impl SerialParameters {
let evt = evt.try_clone().map_err(Error::CloneEvent)?;
keep_rds.push(evt.as_raw_descriptor());
cros_tracing::push_descriptors!(keep_rds);
+ metrics::push_descriptors(keep_rds);
let input: Option<Box<dyn SerialInput>> = if let Some(input_path) = &self.input {
let input_path = input_path.as_path();
diff --git a/devices/src/virtio/async_device.rs b/devices/src/virtio/async_device.rs
index 5e38324c2..ff9c45434 100644
--- a/devices/src/virtio/async_device.rs
+++ b/devices/src/virtio/async_device.rs
@@ -78,6 +78,7 @@ impl<T: 'static> AsyncQueueState<T> {
///
/// Returns `true` if the queue was running, `false` if it wasn't.
pub fn stop(&mut self) -> AsyncResult<bool> {
+ // TODO: schuffelen - All callers should use stop_async instead.
match std::mem::replace(self, AsyncQueueState::Broken) {
AsyncQueueState::Running((task, ex, handle)) => {
// Abort the task and run it to completion to retrieve the queue's resource.
@@ -92,4 +93,25 @@ impl<T: 'static> AsyncQueueState<T> {
}
}
}
+ /// Stops a previously started queue.
+ ///
+ /// The executor on which the task has been started will be run if needed in order to retrieve
+ /// the queue's resource.
+ ///
+ /// Returns `true` if the queue was running, `false` if it wasn't.
+ pub async fn stop_async(&mut self) -> AsyncResult<bool> {
+ match std::mem::replace(self, AsyncQueueState::Broken) {
+ AsyncQueueState::Running((task, _, handle)) => {
+ // Abort the task and run it to completion to retrieve the queue's resource.
+ handle.abort();
+ let resource = task.await;
+ *self = AsyncQueueState::Stopped(resource);
+ Ok(true)
+ }
+ state => {
+ *self = state;
+ Ok(false)
+ }
+ }
+ }
}
diff --git a/devices/src/virtio/block/mod.rs b/devices/src/virtio/block/mod.rs
index d52c4fe2b..b06f31115 100644
--- a/devices/src/virtio/block/mod.rs
+++ b/devices/src/virtio/block/mod.rs
@@ -102,6 +102,7 @@ pub struct DiskOption {
deserialize_with = "deserialize_disk_id"
)]
pub id: Option<[u8; DISK_ID_LEN]>,
+ // Deprecated: Use async_executor=overlapped[concurrency=N]"
// camel_case variant allowed for backward compatibility.
#[cfg(windows)]
#[serde(
@@ -451,6 +452,51 @@ mod tests {
pci_address: None,
}
);
+ let params = from_block_arg("/some/path.img,async-executor=overlapped").unwrap();
+ assert_eq!(
+ params,
+ DiskOption {
+ path: "/some/path.img".into(),
+ read_only: false,
+ root: false,
+ sparse: true,
+ direct: false,
+ block_size: 512,
+ id: None,
+ io_concurrency: NonZeroU32::new(1).unwrap(),
+ multiple_workers: false,
+ async_executor: Some(ExecutorKindSys::Overlapped { concurrency: None }.into()),
+ packed_queue: false,
+ bootindex: None,
+ pci_address: None,
+ }
+ );
+ let params =
+ from_block_arg("/some/path.img,async-executor=\"overlapped,concurrency=4\"")
+ .unwrap();
+ assert_eq!(
+ params,
+ DiskOption {
+ path: "/some/path.img".into(),
+ read_only: false,
+ root: false,
+ sparse: true,
+ direct: false,
+ block_size: 512,
+ id: None,
+ io_concurrency: NonZeroU32::new(1).unwrap(),
+ multiple_workers: false,
+ async_executor: Some(
+ ExecutorKindSys::Overlapped {
+ concurrency: Some(4)
+ }
+ .into()
+ ),
+ packed_queue: false,
+ bootindex: None,
+ pci_address: None,
+ }
+ );
}
// id
diff --git a/devices/src/virtio/block/sys/windows.rs b/devices/src/virtio/block/sys/windows.rs
index 4c215290f..7b66a1ab6 100644
--- a/devices/src/virtio/block/sys/windows.rs
+++ b/devices/src/virtio/block/sys/windows.rs
@@ -10,6 +10,7 @@ use anyhow::Context;
use base::warn;
use cros_async::sys::windows::ExecutorKindSys;
use cros_async::Executor;
+use cros_async::ExecutorKind;
use winapi::um::winbase::FILE_FLAG_NO_BUFFERING;
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
use winapi::um::winnt::FILE_SHARE_READ;
@@ -38,7 +39,13 @@ impl DiskOption {
flags |= FILE_FLAG_NO_BUFFERING;
}
- if self.async_executor == Some(ExecutorKindSys::Overlapped.into()) {
+ let is_overlapped = matches!(
+ self.async_executor,
+ Some(ExecutorKind::SysVariants(
+ ExecutorKindSys::Overlapped { .. }
+ ))
+ );
+ if is_overlapped {
warn!("Opening disk file for overlapped IO");
flags |= FILE_FLAG_OVERLAPPED;
}
@@ -50,10 +57,7 @@ impl DiskOption {
let file = open_option
.open(&self.path)
.context("Failed to open disk file")?;
- let image_type = disk::detect_image_type(
- &file,
- self.async_executor == Some(ExecutorKindSys::Overlapped.into()),
- )?;
+ let image_type = disk::detect_image_type(&file, is_overlapped)?;
Ok(disk::create_disk_file_of_type(
file,
self.sparse,
@@ -66,7 +70,12 @@ impl DiskOption {
impl BlockAsync {
pub fn create_executor(&self) -> Executor {
- Executor::with_kind_and_concurrency(self.executor_kind, self.io_concurrency)
- .expect("Failed to create an executor")
+ let mut kind = self.executor_kind;
+ if let ExecutorKind::SysVariants(ExecutorKindSys::Overlapped { concurrency }) = &mut kind {
+ if concurrency.is_none() {
+ *concurrency = Some(self.io_concurrency);
+ }
+ }
+ Executor::with_executor_kind(kind).expect("Failed to create an executor")
}
}
diff --git a/devices/src/virtio/console/asynchronous.rs b/devices/src/virtio/console/asynchronous.rs
index 46ca606b4..b98480b98 100644
--- a/devices/src/virtio/console/asynchronous.rs
+++ b/devices/src/virtio/console/asynchronous.rs
@@ -534,9 +534,15 @@ impl VirtioDevice for AsyncConsole {
async_utils::await_and_exit(&ex, kill_evt).await?;
let port = &mut console.port0;
if let Some(input) = port.input.as_mut() {
- input.stop().context("failed to stop rx queue")?;
+ input
+ .stop_async()
+ .await
+ .context("failed to stop rx queue")?;
}
- port.output.stop().context("failed to stop tx queue")?;
+ port.output
+ .stop_async()
+ .await
+ .context("failed to stop tx queue")?;
Ok(console)
})?
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index 911deb1da..c26bc0a67 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -68,6 +68,7 @@ pub use self::protocol::VIRTIO_GPU_F_FENCE_PASSING;
pub use self::protocol::VIRTIO_GPU_F_RESOURCE_BLOB;
pub use self::protocol::VIRTIO_GPU_F_RESOURCE_UUID;
pub use self::protocol::VIRTIO_GPU_F_VIRGL;
+pub use self::protocol::VIRTIO_GPU_MAX_SCANOUTS;
pub use self::protocol::VIRTIO_GPU_SHM_ID_HOST_VISIBLE;
use self::protocol::*;
use self::virtio_gpu::to_rutabaga_descriptor;
@@ -1118,7 +1119,7 @@ pub enum DisplayBackend {
Stub,
#[cfg(windows)]
/// Open a window using WinAPI.
- WinApi(WinDisplayProperties),
+ WinApi,
#[cfg(feature = "android_display")]
/// The display buffer is backed by an Android surface. The surface is set via an AIDL service
/// that the backend hosts. Currently, the AIDL service is registered to the service manager
@@ -1140,11 +1141,10 @@ impl DisplayBackend {
DisplayBackend::X(display) => GpuDisplay::open_x(display.as_deref()),
DisplayBackend::Stub => GpuDisplay::open_stub(),
#[cfg(windows)]
- DisplayBackend::WinApi(display_properties) => match wndproc_thread.take() {
+ DisplayBackend::WinApi => match wndproc_thread.take() {
Some(wndproc_thread) => GpuDisplay::open_winapi(
wndproc_thread,
/* win_metrics= */ None,
- display_properties.clone(),
gpu_display_wait_descriptor_ctrl,
None,
),
diff --git a/devices/src/virtio/gpu/parameters.rs b/devices/src/virtio/gpu/parameters.rs
index bbca096f7..3e97627b3 100644
--- a/devices/src/virtio/gpu/parameters.rs
+++ b/devices/src/virtio/gpu/parameters.rs
@@ -16,6 +16,7 @@ use vm_control::gpu::DisplayParameters;
use super::GpuMode;
use super::GpuWsi;
+use crate::virtio::gpu::VIRTIO_GPU_MAX_SCANOUTS;
use crate::PciAddress;
mod serde_capset_mask {
@@ -41,6 +42,8 @@ mod serde_capset_mask {
pub struct GpuParameters {
#[serde(rename = "backend")]
pub mode: GpuMode,
+ #[serde(default = "default_max_num_displays")]
+ pub max_num_displays: u32,
#[serde(rename = "displays")]
pub display_params: Vec<DisplayParameters>,
// `width` and `height` are supported for CLI backwards compatibility.
@@ -81,6 +84,7 @@ pub struct GpuParameters {
impl Default for GpuParameters {
fn default() -> Self {
GpuParameters {
+ max_num_displays: default_max_num_displays(),
display_params: vec![],
__width_compat: None,
__height_compat: None,
@@ -109,6 +113,10 @@ impl Default for GpuParameters {
}
}
+fn default_max_num_displays() -> u32 {
+ VIRTIO_GPU_MAX_SCANOUTS as u32
+}
+
#[cfg(test)]
mod tests {
use serde_json::*;
diff --git a/devices/src/virtio/gpu/virtio_gpu.rs b/devices/src/virtio/gpu/virtio_gpu.rs
index a296afbce..c453fcd07 100644
--- a/devices/src/virtio/gpu/virtio_gpu.rs
+++ b/devices/src/virtio/gpu/virtio_gpu.rs
@@ -46,6 +46,7 @@ use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD;
use serde::Deserialize;
use serde::Serialize;
use sync::Mutex;
+use vm_control::gpu::DisplayMode;
use vm_control::gpu::DisplayParameters;
use vm_control::gpu::GpuControlCommand;
use vm_control::gpu::GpuControlResult;
@@ -285,11 +286,17 @@ impl VirtioGpuScanout {
let mut display = display.borrow_mut();
+ let display_params =
+ self.display_params
+ .clone()
+ .unwrap_or(DisplayParameters::default_with_mode(DisplayMode::Windowed(
+ self.width,
+ self.height,
+ )));
let surface_id = display.create_surface(
self.parent_surface_id,
self.scanout_id,
- self.width,
- self.height,
+ &display_params,
self.scanout_type,
)?;
diff --git a/devices/src/virtio/pvclock.rs b/devices/src/virtio/pvclock.rs
index e4d23522f..a0820c5b4 100644
--- a/devices/src/virtio/pvclock.rs
+++ b/devices/src/virtio/pvclock.rs
@@ -553,7 +553,10 @@ impl PvClockWorker {
// SAFETY:
// Safe because _rdtsc takes no arguments, and we trust _rdtsc to not modify
// any other memory.
- unsafe { _rdtsc() } - suspend_time.tsc_value,
+ // NB: This calculation may wrap around, as TSC can be reset to zero when
+ // the device has resumed from the "deep" suspend state (it may not happen for
+ // s2idle cases). It also happens when the tsc value itself wraps.
+ unsafe { _rdtsc() }.wrapping_sub(suspend_time.tsc_value),
)
} else {
return Err(Error::new(libc::ENOTSUP))
@@ -561,7 +564,10 @@ impl PvClockWorker {
};
// update the total tsc delta during all suspends
- self.total_suspend_tsc_delta += this_suspend_tsc_delta;
+ // NB: This calculation may wrap around, as the suspend time can be bigger than u64 range.
+ self.total_suspend_tsc_delta = self
+ .total_suspend_tsc_delta
+ .wrapping_add(this_suspend_tsc_delta);
// save tsc_suspended_delta to shared memory
self.pvclock_shared_data
diff --git a/devices/src/virtio/vhost/user/device/block/sys/windows.rs b/devices/src/virtio/vhost/user/device/block/sys/windows.rs
index 058c823a7..0aa56707f 100644
--- a/devices/src/virtio/vhost/user/device/block/sys/windows.rs
+++ b/devices/src/virtio/vhost/user/device/block/sys/windows.rs
@@ -65,7 +65,7 @@ pub fn start_device(opts: Options) -> anyhow::Result<()> {
let _raise_timer_resolution =
enable_high_res_timers().context("failed to set timer resolution")?;
- info!("using {} IO handles.", disk_option.io_concurrency.get());
+ info!("using {:?} executor.", disk_option.async_executor);
let kind = disk_option
.async_executor
diff --git a/devices/src/virtio/vhost/user/device/fs/sys/linux.rs b/devices/src/virtio/vhost/user/device/fs/sys/linux.rs
index a00cbb5cb..7236bb1b8 100644
--- a/devices/src/virtio/vhost/user/device/fs/sys/linux.rs
+++ b/devices/src/virtio/vhost/user/device/fs/sys/linux.rs
@@ -104,6 +104,7 @@ pub fn start_device(opts: Options) -> anyhow::Result<()> {
base::syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
let pid = jail_and_fork(
keep_rds,
diff --git a/devices/src/virtio/vhost/user/device/gpu/sys/linux.rs b/devices/src/virtio/vhost/user/device/gpu/sys/linux.rs
index f58121d44..57df8cf47 100644
--- a/devices/src/virtio/vhost/user/device/gpu/sys/linux.rs
+++ b/devices/src/virtio/vhost/user/device/gpu/sys/linux.rs
@@ -12,7 +12,6 @@ use anyhow::Context;
use argh::FromArgs;
use base::clone_descriptor;
use base::error;
-use base::FromRawDescriptor;
use base::SafeDescriptor;
use base::Tube;
use base::UnixSeqpacketListener;
@@ -98,13 +97,7 @@ impl GpuBackend {
// Start handling the display.
let display = clone_descriptor(&*state.borrow_mut().display().borrow())
- .map(|fd| {
- AsyncWrapper::new(
- // SAFETY:
- // Safe because we just created this fd.
- unsafe { SafeDescriptor::from_raw_descriptor(fd) },
- )
- })
+ .map(AsyncWrapper::new)
.context("failed to clone inner WaitContext for gpu display")
.and_then(|ctx| {
self.ex
diff --git a/devices/src/virtio/vhost/user/device/gpu/sys/windows.rs b/devices/src/virtio/vhost/user/device/gpu/sys/windows.rs
index d37a564ee..f44562194 100644
--- a/devices/src/virtio/vhost/user/device/gpu/sys/windows.rs
+++ b/devices/src/virtio/vhost/user/device/gpu/sys/windows.rs
@@ -286,9 +286,7 @@ pub fn run_gpu_device_worker(
.push(GpuDisplayParameters::default());
}
- let display_backends = vec![virtio::DisplayBackend::WinApi(
- (&config.params.display_params[0]).into(),
- )];
+ let display_backends = vec![virtio::DisplayBackend::WinApi];
let mut gpu_params = config.params.clone();
diff --git a/devices/src/virtio/vhost/user/device/handler.rs b/devices/src/virtio/vhost/user/device/handler.rs
index 57e66b763..daa39a0c3 100644
--- a/devices/src/virtio/vhost/user/device/handler.rs
+++ b/devices/src/virtio/vhost/user/device/handler.rs
@@ -1142,11 +1142,12 @@ mod tests {
let vmm_bar = Arc::new(Barrier::new(2));
let dev_bar = vmm_bar.clone();
- let (tx, rx) = channel();
+ let (ready_tx, ready_rx) = channel();
+ let (shutdown_tx, shutdown_rx) = channel();
std::thread::spawn(move || {
// VMM side
- rx.recv().unwrap(); // Ensure the device is ready.
+ ready_rx.recv().unwrap(); // Ensure the device is ready.
let connection = test_helpers::connect(vmm);
@@ -1198,6 +1199,9 @@ mod tests {
println!("virtio_wake");
vmm_device.virtio_wake(None).unwrap();
+ println!("wait for shutdown signal");
+ shutdown_rx.recv().unwrap();
+
// The VMM side is supposed to stop before the device side.
println!("drop");
drop(vmm_device);
@@ -1210,7 +1214,7 @@ mod tests {
handler.as_mut().allow_backend_req = allow_backend_req;
// Notify listener is ready.
- tx.send(()).unwrap();
+ ready_tx.send(()).unwrap();
let mut req_handler = test_helpers::listen(dev, handler);
@@ -1285,8 +1289,11 @@ mod tests {
.expect("send_config_changed failed");
}
+ // Ask the client to shutdown, then wait to it to finish.
+ shutdown_tx.send(()).unwrap();
dev_bar.wait();
+ // Verify recv_header fails with `ClientExit` after the client has disconnected.
match req_handler.recv_header() {
Err(VhostError::ClientExit) => (),
r => panic!("expected Err(ClientExit) but got {:?}", r),
diff --git a/devices/src/virtio/vhost/user/device/wl.rs b/devices/src/virtio/vhost/user/device/wl.rs
index 0287a6708..0d3cf9b12 100644
--- a/devices/src/virtio/vhost/user/device/wl.rs
+++ b/devices/src/virtio/vhost/user/device/wl.rs
@@ -18,7 +18,6 @@ use argh::FromArgs;
use base::clone_descriptor;
use base::error;
use base::warn;
-use base::FromRawDescriptor;
use base::SafeDescriptor;
use base::Tube;
use base::UnixSeqpacket;
@@ -268,11 +267,7 @@ impl VhostUserDevice for WlBackend {
let queue_task = match idx {
0 => {
let wlstate_ctx = clone_descriptor(wlstate.borrow().wait_ctx())
- .map(|fd| {
- // SAFETY:
- // Safe because we just created this fd.
- AsyncWrapper::new(unsafe { SafeDescriptor::from_raw_descriptor(fd) })
- })
+ .map(AsyncWrapper::new)
.context("failed to clone inner WaitContext for WlState")
.and_then(|ctx| {
self.ex
diff --git a/devices/src/virtio/video/decoder/backend/mod.rs b/devices/src/virtio/video/decoder/backend/mod.rs
index 6e80f7626..cf8236e7d 100644
--- a/devices/src/virtio/video/decoder/backend/mod.rs
+++ b/devices/src/virtio/video/decoder/backend/mod.rs
@@ -153,10 +153,8 @@ pub enum DecoderEvent {
mod tests {
use std::time::Duration;
- use base::FromRawDescriptor;
use base::MappedRegion;
use base::MemoryMappingBuilder;
- use base::SafeDescriptor;
use base::SharedMemory;
use base::WaitContext;
@@ -233,11 +231,7 @@ mod tests {
#[allow(dead_code)]
pub fn build_object_handle(mem: &SharedMemory) -> GuestResourceHandle {
GuestResourceHandle::VirtioObject(VirtioObjectHandle {
- // SAFETY:
- // Safe because we are taking ownership of a just-duplicated FD.
- desc: unsafe {
- SafeDescriptor::from_raw_descriptor(base::clone_descriptor(mem).unwrap())
- },
+ desc: base::clone_descriptor(mem).unwrap(),
modifier: 0,
})
}
@@ -247,11 +241,7 @@ mod tests {
#[allow(dead_code)]
pub fn build_guest_mem_handle(mem: &SharedMemory) -> GuestResourceHandle {
GuestResourceHandle::GuestPages(GuestMemHandle {
- // SAFETY:
- // Safe because we are taking ownership of a just-duplicated FD.
- desc: unsafe {
- SafeDescriptor::from_raw_descriptor(base::clone_descriptor(mem).unwrap())
- },
+ desc: base::clone_descriptor(mem).unwrap(),
mem_areas: vec![GuestMemArea {
offset: 0,
length: mem.size() as usize,
diff --git a/devices/src/virtio/video/resource.rs b/devices/src/virtio/video/resource.rs
index 00202ab77..4d5192a30 100644
--- a/devices/src/virtio/video/resource.rs
+++ b/devices/src/virtio/video/resource.rs
@@ -232,11 +232,8 @@ impl GuestResource {
let guest_region = mem
.shm_region(GuestAddress(addr))
.map_err(GuestMemResourceCreationError::CantGetShmRegion)?;
- let desc = base::clone_descriptor(guest_region)
- .map_err(GuestMemResourceCreationError::DescriptorCloneError)?;
- // SAFETY:
- // Safe because we are the sole owner of the duplicated descriptor.
- unsafe { SafeDescriptor::from_raw_descriptor(desc) }
+ base::clone_descriptor(guest_region)
+ .map_err(GuestMemResourceCreationError::DescriptorCloneError)?
}
};
@@ -377,7 +374,6 @@ impl GuestResource {
#[cfg(test)]
mod tests {
use base::MappedRegion;
- use base::SafeDescriptor;
use base::SharedMemory;
use super::*;
@@ -415,10 +411,7 @@ mod tests {
// Create the `GuestMemHandle` we will try to map and retrieve the data from.
let mem_handle = GuestResourceHandle::GuestPages(GuestMemHandle {
- // SAFETY: descriptor is expected to be valid
- desc: unsafe {
- SafeDescriptor::from_raw_descriptor(base::clone_descriptor(&mem).unwrap())
- },
+ desc: base::clone_descriptor(&mem).unwrap(),
mem_areas: page_order
.iter()
.map(|&page| GuestMemArea {
diff --git a/devices/src/virtio/video/worker.rs b/devices/src/virtio/video/worker.rs
index 32bd15f15..aa40ef8fd 100644
--- a/devices/src/virtio/video/worker.rs
+++ b/devices/src/virtio/video/worker.rs
@@ -11,8 +11,6 @@ use base::clone_descriptor;
use base::error;
use base::info;
use base::Event;
-use base::FromRawDescriptor;
-use base::SafeDescriptor;
use base::WaitContext;
use cros_async::select3;
use cros_async::AsyncWrapper;
@@ -403,12 +401,7 @@ impl Worker {
let device_wait_ctx = WaitContext::new().map_err(Error::WaitContextCreationFailed)?;
let device_evt = ex
.async_from(AsyncWrapper::new(
- clone_descriptor(&device_wait_ctx)
- .map(|fd|
- // SAFETY:
- // Safe because we just created this fd.
- unsafe { SafeDescriptor::from_raw_descriptor(fd) })
- .map_err(Error::CloneDescriptorFailed)?,
+ clone_descriptor(&device_wait_ctx).map_err(Error::CloneDescriptorFailed)?,
))
.map_err(Error::EventAsyncCreationFailed)?;
diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs
index 101698243..7dbbc74bd 100644
--- a/devices/src/virtio/virtio_pci_device.rs
+++ b/devices/src/virtio/virtio_pci_device.rs
@@ -23,6 +23,8 @@ use data_model::Le32;
use hypervisor::Datamatch;
use hypervisor::MemCacheType;
use libc::ERANGE;
+#[cfg(target_arch = "x86_64")]
+use metrics::MetricEventType;
use resources::Alloc;
use resources::AllocOptions;
use resources::SystemAllocator;
@@ -548,7 +550,9 @@ impl VirtioPciDevice {
Some(PmWakeupEvent::new(
self.vm_control_tube.clone(),
self.pm_config.clone(),
- self.device.debug_label(),
+ MetricEventType::VirtioWakeup {
+ virtio_id: self.device.device_type() as u32,
+ },
)),
);
self.interrupt = Some(interrupt.clone());
@@ -1312,7 +1316,9 @@ impl Suspendable for VirtioPciDevice {
Some(PmWakeupEvent::new(
self.vm_control_tube.clone(),
self.pm_config.clone(),
- self.device.debug_label(),
+ MetricEventType::VirtioWakeup {
+ virtio_id: self.device.device_type() as u32,
+ },
)),
));
}
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs
index 22a9518e1..8f1e0d488 100644
--- a/devices/src/virtio/wl.rs
+++ b/devices/src/virtio/wl.rs
@@ -75,6 +75,10 @@ use base::EventType;
use base::FromRawDescriptor;
#[cfg(feature = "gpu")]
use base::IntoRawDescriptor;
+#[cfg(feature = "minigbm")]
+use base::MemoryMappingBuilder;
+#[cfg(feature = "minigbm")]
+use base::MmapError;
use base::Protection;
use base::RawDescriptor;
use base::Result;
@@ -93,6 +97,8 @@ use hypervisor::MemCacheType;
use libc::EBADF;
#[cfg(feature = "minigbm")]
use libc::EINVAL;
+#[cfg(feature = "minigbm")]
+use libc::ENOSYS;
use remain::sorted;
use resources::address_allocator::AddressAllocator;
use resources::AddressRange;
@@ -113,6 +119,10 @@ use rutabaga_gfx::RutabagaGralloc;
use rutabaga_gfx::RutabagaGrallocFlags;
#[cfg(feature = "minigbm")]
use rutabaga_gfx::RutabagaIntoRawDescriptor;
+#[cfg(feature = "minigbm")]
+use rutabaga_gfx::RUTABAGA_MAP_CACHE_CACHED;
+#[cfg(feature = "minigbm")]
+use rutabaga_gfx::RUTABAGA_MAP_CACHE_MASK;
use thiserror::Error as ThisError;
use vm_control::VmMemorySource;
use vm_memory::GuestAddress;
@@ -190,6 +200,10 @@ const VIRTIO_WL_VFD_DMABUF_SYNC_VALID_FLAG_MASK: u32 = 0x7;
#[cfg(feature = "minigbm")]
const DMA_BUF_IOCTL_BASE: c_uint = 0x62;
+#[cfg(feature = "minigbm")]
+const DMA_BUF_SYNC_WRITE: c_uint = 0x2;
+#[cfg(feature = "minigbm")]
+const DMA_BUF_SYNC_END: c_uint = 0x4;
#[cfg(feature = "minigbm")]
#[repr(C)]
@@ -797,6 +811,8 @@ struct WlVfd {
slot: Option<(u64 /* offset */, VmRequester)>,
#[cfg(feature = "minigbm")]
is_dmabuf: bool,
+ #[cfg(feature = "minigbm")]
+ map_info: u32,
fence: Option<File>,
is_fence: bool,
}
@@ -820,6 +836,25 @@ impl fmt::Debug for WlVfd {
}
}
+#[cfg(feature = "minigbm")]
+fn flush_shared_memory(shared_memory: &SharedMemory) -> Result<()> {
+ let mmap = match MemoryMappingBuilder::new(shared_memory.size as usize)
+ .from_shared_memory(shared_memory)
+ .build()
+ {
+ Ok(v) => v,
+ Err(_) => return Err(Error::new(EINVAL)),
+ };
+ if let Err(err) = mmap.flush_all() {
+ base::error!("failed to flush shared memory: {}", err);
+ return match err {
+ MmapError::NotImplemented(_) => Err(Error::new(ENOSYS)),
+ _ => Err(Error::new(EINVAL)),
+ };
+ }
+ Ok(())
+}
+
impl WlVfd {
fn connect<P: AsRef<Path>>(path: P) -> WlResult<WlVfd> {
let socket = UnixStream::connect(path).map_err(WlError::SocketConnect)?;
@@ -864,6 +899,7 @@ impl WlVfd {
vfd.guest_shared_memory = Some(vfd_shm);
vfd.slot = Some((offset, vm));
vfd.is_dmabuf = true;
+ vfd.map_info = reqs.map_info;
Ok((vfd, desc))
}
@@ -881,10 +917,25 @@ impl WlVfd {
// SAFETY:
// Safe as descriptor is a valid dmabuf and incorrect flags will return an error.
if unsafe { ioctl_with_ref(descriptor, DMA_BUF_IOCTL_SYNC(), &sync) } < 0 {
- Err(WlError::DmabufSync(io::Error::last_os_error()))
- } else {
- Ok(())
+ return Err(WlError::DmabufSync(io::Error::last_os_error()));
}
+
+ // virtio-wl kernel driver always maps dmabufs with WB memory type, regardless of
+ // the host memory type (which is wrong). However, to avoid changing the protocol,
+ // assume that all guest writes are cached and ensure clflush-like ops on all mapped
+ // cachelines if the host mapping is not cached.
+ const END_WRITE_MASK: u32 = DMA_BUF_SYNC_WRITE | DMA_BUF_SYNC_END;
+ if (flags & END_WRITE_MASK) == END_WRITE_MASK
+ && (self.map_info & RUTABAGA_MAP_CACHE_MASK) != RUTABAGA_MAP_CACHE_CACHED
+ {
+ if let Err(err) = flush_shared_memory(descriptor) {
+ base::warn!("failed to flush cached dmabuf mapping: {:?}", err);
+ return Err(WlError::DmabufSync(io::Error::from_raw_os_error(
+ err.errno(),
+ )));
+ }
+ }
+ Ok(())
}
None => Err(WlError::DmabufSync(io::Error::from_raw_os_error(EBADF))),
}
diff --git a/devices/tests/irqchip/userspace.rs b/devices/tests/irqchip/userspace.rs
index c4599bf11..7f521f691 100644
--- a/devices/tests/irqchip/userspace.rs
+++ b/devices/tests/irqchip/userspace.rs
@@ -4,6 +4,7 @@
#![cfg(target_arch = "x86_64")]
+use std::collections::BTreeMap;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
@@ -46,7 +47,6 @@ use hypervisor::IrqSource;
use hypervisor::Level;
use hypervisor::PicSelect;
use hypervisor::PitRWMode;
-use hypervisor::Register;
use hypervisor::Regs;
use hypervisor::Sregs;
use hypervisor::TriggerMode;
@@ -752,19 +752,19 @@ impl VcpuX86_64 for FakeVcpu {
fn set_debugregs(&self, _debugregs: &DebugRegs) -> Result<()> {
unimplemented!()
}
- fn get_xcrs(&self) -> Result<Vec<Register>> {
+ fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>> {
unimplemented!()
}
- fn set_xcrs(&self, _xcrs: &[Register]) -> Result<()> {
+ fn set_xcr(&self, _xcr_index: u32, _value: u64) -> Result<()> {
unimplemented!()
}
- fn get_msrs(&self, _msrs: &mut Vec<Register>) -> Result<()> {
+ fn get_msr(&self, _msr_index: u32) -> Result<u64> {
unimplemented!()
}
- fn get_all_msrs(&self) -> Result<Vec<Register>> {
+ fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>> {
unimplemented!()
}
- fn set_msrs(&self, _msrs: &[Register]) -> Result<()> {
+ fn set_msr(&self, _msr_index: u32, _value: u64) -> Result<()> {
unimplemented!()
}
fn set_cpuid(&self, _cpuid: &CpuId) -> Result<()> {
@@ -779,15 +779,6 @@ impl VcpuX86_64 for FakeVcpu {
fn set_guest_debug(&self, _addrs: &[GuestAddress], _enable_singlestep: bool) -> Result<()> {
unimplemented!()
}
- fn get_tsc_offset(&self) -> Result<u64> {
- unimplemented!()
- }
- fn set_tsc_offset(&self, _offset: u64) -> Result<()> {
- unimplemented!()
- }
- fn set_tsc_value(&self, _value: u64) -> Result<()> {
- unimplemented!()
- }
fn snapshot(&self) -> anyhow::Result<VcpuSnapshot> {
unimplemented!()
}
diff --git a/disk/Android.bp b/disk/Android.bp
index 4cbe5b38a..4e4b37f6d 100644
--- a/disk/Android.bp
+++ b/disk/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/disk/src/qcow/mod.rs b/disk/src/qcow/mod.rs
index e23a36274..96fbc0e45 100644
--- a/disk/src/qcow/mod.rs
+++ b/disk/src/qcow/mod.rs
@@ -287,10 +287,10 @@ impl QcowHeader {
}
// 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);
+ let num_clusters: u32 = size.div_ceil(u64::from(cluster_size)) as u32;
+ let num_l2_clusters: u32 = num_clusters.div_ceil(l2_size);
+ let l1_clusters: u32 = num_l2_clusters.div_ceil(cluster_size);
+ let header_clusters = (size_of::<QcowHeader>() as u32).div_ceil(cluster_size);
Ok(QcowHeader {
magic: QCOW_MAGIC,
version: 3,
@@ -317,10 +317,7 @@ impl QcowHeader {
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,
- )
+ (max_refcount_clusters * size_of::<u64>() as u32).div_ceil(cluster_size)
},
nb_snapshots: 0,
snapshots_offset: 0,
@@ -389,8 +386,8 @@ impl QcowHeader {
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);
+ let for_data = (u64::from(num_clusters) * refcount_bytes).div_ceil(u64::from(cluster_size));
+ let for_refcounts = (for_data * refcount_bytes).div_ceil(u64::from(cluster_size));
for_data + for_refcounts
}
@@ -532,10 +529,10 @@ impl QcowFile {
}
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);
+ let num_clusters = header.size.div_ceil(cluster_size);
+ let num_l2_clusters = num_clusters.div_ceil(l2_size);
+ let l1_clusters = num_l2_clusters.div_ceil(cluster_size);
+ let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size);
if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
return Err(Error::TooManyL1Entries(num_l2_clusters));
}
@@ -549,7 +546,7 @@ impl QcowFile {
.map_err(Error::ReadingHeader)?,
);
- let num_clusters = div_round_up_u64(header.size, cluster_size);
+ let num_clusters = header.size.div_ceil(cluster_size);
let refcount_clusters = max_refcount_clusters(
header.refcount_order,
cluster_size as u32,
@@ -737,7 +734,7 @@ impl QcowFile {
header: QcowHeader,
cluster_size: u64,
) -> Result<()> {
- let l1_clusters = div_round_up_u64(header.l1_size as u64, cluster_size);
+ let l1_clusters = u64::from(header.l1_size).div_ceil(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)?;
@@ -810,7 +807,7 @@ impl QcowFile {
refblock_clusters: u64,
pointers_per_cluster: u64,
) -> Result<Vec<u64>> {
- let refcount_table_entries = div_round_up_u64(refblock_clusters, pointers_per_cluster);
+ let refcount_table_entries = refblock_clusters.div_ceil(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 {
@@ -899,24 +896,21 @@ impl QcowFile {
.len();
let refcount_bits = 1u64 << header.refcount_order;
- let refcount_bytes = div_round_up_u64(refcount_bits, 8);
+ let refcount_bytes = refcount_bits.div_ceil(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 data_clusters = header.size.div_ceil(cluster_size);
+ let l2_clusters = data_clusters.div_ceil(pointers_per_cluster);
+ let l1_clusters = l2_clusters.div_ceil(cluster_size);
+ let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(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);
+ let refblock_clusters = max_valid_cluster_index.div_ceil(refcount_block_entries);
+ let reftable_clusters = refblock_clusters.div_ceil(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);
+ let refblocks_for_refs =
+ (refblock_clusters + reftable_clusters).div_ceil(refcount_block_entries);
+ let reftable_clusters_for_refs = refblocks_for_refs.div_ceil(refcount_block_entries);
max_valid_cluster_index += refblock_clusters + reftable_clusters;
max_valid_cluster_index += refblocks_for_refs + reftable_clusters_for_refs;
@@ -1630,16 +1624,6 @@ fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> {
Ok(())
}
-// Ceiling of the division of `dividend`/`divisor`.
-fn div_round_up_u64(dividend: u64, divisor: u64) -> u64 {
- dividend / divisor + u64::from(dividend % divisor != 0)
-}
-
-// Ceiling of the division of `dividend`/`divisor`.
-fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
- dividend / divisor + u32::from(dividend % divisor != 0)
-}
-
#[cfg(test)]
mod tests {
use std::fs::OpenOptions;
diff --git a/e2e_tests/fixture/src/vm.rs b/e2e_tests/fixture/src/vm.rs
index 483fd6ab0..ee5197832 100644
--- a/e2e_tests/fixture/src/vm.rs
+++ b/e2e_tests/fixture/src/vm.rs
@@ -406,7 +406,27 @@ impl TestVm {
TestVm::new_generic(TestVmSys::append_config_args, cfg, false)
}
- pub fn new_cold_restore(cfg: Config) -> Result<TestVm> {
+ /// Create `TestVm` from a snapshot, using `--restore` but NOT `--suspended`.
+ pub fn new_restore(cfg: Config) -> Result<TestVm> {
+ let mut vm = TestVm::new_generic_restore(TestVmSys::append_config_args, cfg, false)?;
+ // Send a resume request to wait for the restore to finish.
+ // We don't want to return from this function until the restore is complete, otherwise it
+ // will be difficult to differentiate between a slow restore and a slow response from the
+ // guest.
+ let vm = run_with_timeout(
+ move || {
+ vm.resume_full().expect("failed to resume after VM restore");
+ vm
+ },
+ Duration::from_secs(60),
+ )
+ .expect("VM restore timeout");
+
+ Ok(vm)
+ }
+
+ /// Create `TestVm` from a snapshot, using `--restore` AND `--suspended`.
+ pub fn new_restore_suspended(cfg: Config) -> Result<TestVm> {
TestVm::new_generic_restore(TestVmSys::append_config_args, cfg, false)
}
diff --git a/e2e_tests/guest_under_test/rootfs/readclock/Android.bp b/e2e_tests/guest_under_test/rootfs/readclock/Android.bp
index af297e004..0cc1cc5f2 100644
--- a/e2e_tests/guest_under_test/rootfs/readclock/Android.bp
+++ b/e2e_tests/guest_under_test/rootfs/readclock/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/e2e_tests/tests/pci_hotplug.rs b/e2e_tests/tests/pci_hotplug.rs
index 5e313cbb1..b0371f7d4 100644
--- a/e2e_tests/tests/pci_hotplug.rs
+++ b/e2e_tests/tests/pci_hotplug.rs
@@ -201,6 +201,7 @@ fn tap_hotplug_add_remove_add_impl() {
/// Checks tap hotplug works with a device added, removed, then added again.
#[test]
+#[ignore = "b/333090169 test is flaky"]
fn tap_hotplug_add_remove_add() {
call_test_with_sudo("tap_hotplug_add_remove_add_impl");
}
@@ -262,6 +263,7 @@ fn tap_hotplug_add_remove_rapid_add_impl() {
/// Checks tap hotplug works with a device added, removed, then rapidly added again.
#[test]
+#[ignore = "b/333090169 test is flaky"]
fn tap_hotplug_add_remove_rapid_add() {
call_test_with_sudo("tap_hotplug_add_remove_rapid_add_impl");
}
diff --git a/e2e_tests/tests/suspend_resume.rs b/e2e_tests/tests/suspend_resume.rs
index f893d2cf6..c814d39e8 100644
--- a/e2e_tests/tests/suspend_resume.rs
+++ b/e2e_tests/tests/suspend_resume.rs
@@ -119,7 +119,7 @@ fn suspend_resume_system(disabled_sandbox: bool) -> anyhow::Result<()> {
// shut down VM
drop(vm);
// Start up VM with cold restore.
- let mut vm = TestVm::new_cold_restore(new_config().extra_args(vec![
+ let mut vm = TestVm::new_restore_suspended(new_config().extra_args(vec![
"--restore".to_string(),
snap1_path.to_str().unwrap().to_string(),
"--suspended".to_string(),
@@ -213,7 +213,7 @@ fn snapshot_vhost_user() {
snap_path.to_str().unwrap().to_string(),
"--no-usb".to_string(),
]);
- let _vm = TestVm::new_cold_restore(config).unwrap();
+ let _vm = TestVm::new_restore(config).unwrap();
}
fn create_net_config(socket: &Path) -> VuConfig {
diff --git a/e2e_tests/tests/vsock.rs b/e2e_tests/tests/vsock.rs
index 64154289d..dfdb13c00 100644
--- a/e2e_tests/tests/vsock.rs
+++ b/e2e_tests/tests/vsock.rs
@@ -89,7 +89,7 @@ fn host_to_guest_snapshot_restore() {
])
.with_stdout_hardware("legacy-virtio-console");
drop(vm);
- vm = TestVm::new_cold_restore(config).unwrap();
+ vm = TestVm::new_restore(config).unwrap();
host_to_guest_connection(&mut vm, guest_cid, guest_port);
}
@@ -119,7 +119,7 @@ fn host_to_guest_disable_sandbox_snapshot_restore() {
])
.with_stdout_hardware("legacy-virtio-console");
drop(vm);
- vm = TestVm::new_cold_restore(config.disable_sandbox()).unwrap();
+ vm = TestVm::new_restore(config.disable_sandbox()).unwrap();
host_to_guest_connection(&mut vm, guest_cid, guest_port);
}
@@ -199,7 +199,7 @@ fn guest_to_host_snapshot_restore() {
])
.with_stdout_hardware("legacy-virtio-console");
drop(vm);
- vm = TestVm::new_cold_restore(config).unwrap();
+ vm = TestVm::new_restore(config).unwrap();
guest_to_host_connection(&mut vm, host_port);
}
@@ -230,7 +230,7 @@ fn guest_to_host_disable_sandbox_snapshot_restore() {
])
.with_stdout_hardware("legacy-virtio-console");
drop(vm);
- vm = TestVm::new_cold_restore(config.disable_sandbox()).unwrap();
+ vm = TestVm::new_restore(config.disable_sandbox()).unwrap();
guest_to_host_connection(&mut vm, host_port);
}
@@ -277,6 +277,7 @@ fn create_vu_config(cmd_type: CmdType, socket: &Path, cid: u32) -> VuConfig {
}
#[test]
+#[ignore = "b/333090069 test is flaky"]
fn vhost_user_host_to_guest() {
let guest_port = generate_vhost_port();
let guest_cid = generate_guest_cid();
@@ -295,6 +296,7 @@ fn vhost_user_host_to_guest() {
}
#[test]
+#[ignore = "b/333090069 test is flaky"]
fn vhost_user_host_to_guest_with_devices() {
let guest_port = generate_vhost_port();
let guest_cid = generate_guest_cid();
diff --git a/fuse/Android.bp b/fuse/Android.bp
index cb40a78d3..5d6c6fd4c 100644
--- a/fuse/Android.bp
+++ b/fuse/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/gpu_display/Android.bp b/gpu_display/Android.bp
index 741f34c7b..e96b71db4 100644
--- a/gpu_display/Android.bp
+++ b/gpu_display/Android.bp
@@ -1,5 +1,7 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
+// Content before the first "rust_*" or "genrule" module is preserved.
// cargo2android.py limitations:
// does not handle "-l dylib=wayland-client" yet
diff --git a/gpu_display/examples/simple.rs b/gpu_display/examples/simple.rs
index 9741dc44a..afc5b1f38 100644
--- a/gpu_display/examples/simple.rs
+++ b/gpu_display/examples/simple.rs
@@ -9,6 +9,8 @@ mod platform {
use anyhow::Context;
use anyhow::Result;
use gpu_display::*;
+ use vm_control::gpu::DisplayMode;
+ use vm_control::gpu::DisplayParameters;
pub fn run() -> Result<()> {
let mut disp = GpuDisplay::open_wayland(None::<&str>).context("open_wayland")?;
@@ -16,8 +18,7 @@ mod platform {
.create_surface(
None,
/* scanout_id= */ Some(0),
- 1280,
- 1024,
+ &DisplayParameters::default_with_mode(DisplayMode::Windowed(1280, 1024)),
SurfaceType::Scanout,
)
.context("create_surface")?;
diff --git a/gpu_display/examples/simple_open.rs b/gpu_display/examples/simple_open.rs
index 73bdef757..1bf96a068 100644
--- a/gpu_display/examples/simple_open.rs
+++ b/gpu_display/examples/simple_open.rs
@@ -8,6 +8,8 @@ use anyhow::Context;
use anyhow::Result;
use gpu_display::GpuDisplay;
use gpu_display::SurfaceType;
+use vm_control::gpu::DisplayMode;
+use vm_control::gpu::DisplayParameters;
fn run() -> Result<()> {
let mut disp = GpuDisplay::open_x(None::<&str>).context("open_x")?;
@@ -15,8 +17,7 @@ fn run() -> Result<()> {
.create_surface(
None,
/* scanout_id= */ Some(0),
- 1280,
- 1024,
+ &DisplayParameters::default_with_mode(DisplayMode::Windowed(1280, 1024)),
SurfaceType::Scanout,
)
.context("create_surface")?;
diff --git a/gpu_display/src/gpu_display_android.rs b/gpu_display/src/gpu_display_android.rs
index 9fcd5e7bc..5ccef070a 100644
--- a/gpu_display/src/gpu_display_android.rs
+++ b/gpu_display/src/gpu_display_android.rs
@@ -16,6 +16,7 @@ use base::AsRawDescriptor;
use base::Event;
use base::RawDescriptor;
use base::VolatileSlice;
+use vm_control::gpu::DisplayParameters;
use crate::DisplayT;
use crate::GpuDisplayError;
@@ -209,14 +210,14 @@ impl DisplayT for DisplayAndroid {
parent_surface_id: Option<u32>,
_surface_id: u32,
_scanout_id: Option<u32>,
- requested_width: u32,
- requested_height: u32,
+ display_params: &DisplayParameters,
_surf_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
if parent_surface_id.is_some() {
return Err(GpuDisplayError::Unsupported);
}
+ let (requested_width, requested_height) = display_params.get_virtual_display_size();
// SAFETY: context is an opaque handle.
let surface = NonNull::new(unsafe {
create_android_surface(self.context.0.as_ptr(), requested_width, requested_height)
diff --git a/gpu_display/src/gpu_display_stub.rs b/gpu_display/src/gpu_display_stub.rs
index 53403561c..123ab7487 100644
--- a/gpu_display/src/gpu_display_stub.rs
+++ b/gpu_display/src/gpu_display_stub.rs
@@ -6,6 +6,7 @@ use base::AsRawDescriptor;
use base::Event;
use base::RawDescriptor;
use base::VolatileSlice;
+use vm_control::gpu::DisplayParameters;
use crate::DisplayT;
use crate::GpuDisplayError;
@@ -103,14 +104,14 @@ impl DisplayT for DisplayStub {
parent_surface_id: Option<u32>,
_surface_id: u32,
_scanout_id: Option<u32>,
- width: u32,
- height: u32,
+ display_params: &DisplayParameters,
_surf_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
if parent_surface_id.is_some() {
return Err(GpuDisplayError::Unsupported);
}
+ let (width, height) = display_params.get_virtual_display_size();
Ok(Box::new(StubSurface {
width,
height,
diff --git a/gpu_display/src/gpu_display_win/mod.rs b/gpu_display/src/gpu_display_win/mod.rs
index ebe2dd737..f88e86435 100644
--- a/gpu_display/src/gpu_display_win/mod.rs
+++ b/gpu_display/src/gpu_display_win/mod.rs
@@ -36,14 +36,10 @@ use base::EventWaitResult;
use base::RawDescriptor;
use base::ReadNotifier;
use base::SendTube;
-use euclid::size2;
-use euclid::Size2D;
-use math_util::Size2DCheckedCast;
use metrics::sys::windows::Metrics;
pub use surface::Surface;
use sync::Mutex;
use sync::Waitable;
-use vm_control::gpu::DisplayMode;
use vm_control::gpu::DisplayParameters;
use vm_control::ModifyWaitContext;
use window_message_processor::DisplaySendToWndProc;
@@ -72,28 +68,6 @@ pub(crate) type ObjectId = NonZeroU32;
pub struct VirtualDisplaySpace;
pub struct HostWindowSpace;
-#[derive(Clone)]
-pub struct DisplayProperties {
- pub start_hidden: bool,
- pub is_fullscreen: bool,
- pub window_width: u32,
- pub window_height: u32,
-}
-
-impl From<&DisplayParameters> for DisplayProperties {
- fn from(params: &DisplayParameters) -> Self {
- let is_fullscreen = matches!(params.mode, DisplayMode::BorderlessFullScreen(_));
- let (window_width, window_height) = params.get_window_size();
-
- Self {
- start_hidden: params.hidden,
- is_fullscreen,
- window_width,
- window_height,
- }
- }
-}
-
pub enum VulkanDisplayWrapper {
Uninitialized,
#[cfg(feature = "vulkan_display")]
@@ -104,7 +78,6 @@ pub struct DisplayWin {
wndproc_thread: Rc<WindowProcedureThread>,
close_requested_event: Event,
win_metrics: Option<Weak<Metrics>>,
- display_properties: DisplayProperties,
is_surface_created: bool,
#[allow(dead_code)]
gpu_display_wait_descriptor_ctrl: SendTube,
@@ -118,7 +91,6 @@ impl DisplayWin {
pub fn new(
wndproc_thread: WindowProcedureThread,
win_metrics: Option<Weak<Metrics>>,
- display_properties: DisplayProperties,
gpu_display_wait_descriptor_ctrl: SendTube,
vulkan_display_create_params: Option<VulkanCreateParams>,
) -> Result<DisplayWin, GpuDisplayError> {
@@ -133,7 +105,6 @@ impl DisplayWin {
wndproc_thread: Rc::new(wndproc_thread),
close_requested_event,
win_metrics,
- display_properties,
is_surface_created: false,
gpu_display_wait_descriptor_ctrl,
event_device_wait_descriptor_requests: Vec::new(),
@@ -148,10 +119,10 @@ impl DisplayWin {
&mut self,
surface_id: u32,
scanout_id: u32,
- virtual_display_size: Size2D<i32, VirtualDisplaySpace>,
+ display_params: &DisplayParameters,
) -> Result<Arc<Mutex<VulkanDisplayWrapper>>> {
+ let display_params_clone = display_params.clone();
let metrics = self.win_metrics.clone();
- let display_properties = self.display_properties.clone();
#[cfg(feature = "vulkan_display")]
let vulkan_create_params = self.vulkan_display_create_params.clone();
// This function should not return until surface creation finishes. Besides, we would like
@@ -216,9 +187,8 @@ impl DisplayWin {
Surface::new(
surface_id,
window,
- &virtual_display_size,
metrics,
- &display_properties,
+ &display_params_clone,
display_event_dispatcher,
vulkan_display,
)
@@ -299,8 +269,7 @@ impl DisplayT for DisplayWin {
parent_surface_id: Option<u32>,
surface_id: u32,
scanout_id: Option<u32>,
- virtual_display_width: u32,
- virtual_display_height: u32,
+ display_params: &DisplayParameters,
surface_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
if parent_surface_id.is_some() {
@@ -316,7 +285,7 @@ impl DisplayT for DisplayWin {
let vulkan_display = match self.create_surface_internal(
surface_id,
scanout_id.expect("scanout id is required"),
- size2(virtual_display_width, virtual_display_height).checked_cast(),
+ display_params,
) {
Err(e) => {
error!("Failed to create surface: {:?}", e);
@@ -562,6 +531,7 @@ mod tests {
let wndproc_thread_builder = {
let mut wndproc_thread_builder = wndproc_thread_builder;
wndproc_thread_builder
+ .set_max_num_windows(1)
.set_display_tube(None)
.set_ime_tube(Some(_device_ime_tube));
wndproc_thread_builder
diff --git a/gpu_display/src/gpu_display_win/surface.rs b/gpu_display/src/gpu_display_win/surface.rs
index be5811825..6c7491f50 100644
--- a/gpu_display/src/gpu_display_win/surface.rs
+++ b/gpu_display/src/gpu_display_win/surface.rs
@@ -24,6 +24,8 @@ use euclid::Box2D;
use euclid::Size2D;
use metrics::sys::windows::Metrics;
use sync::Mutex;
+use vm_control::gpu::DisplayMode;
+use vm_control::gpu::DisplayParameters;
use win_util::keys_down;
use winapi::shared::minwindef::HIWORD;
use winapi::shared::minwindef::LOWORD;
@@ -48,10 +50,8 @@ use super::window_message_processor::SurfaceResources;
use super::window_message_processor::WindowMessage;
use super::window_message_processor::WindowPosMessage;
use super::window_message_processor::HANDLE_WINDOW_MESSAGE_TIMEOUT;
-use super::DisplayProperties;
use super::HostWindowSpace;
use super::MouseMode;
-use super::VirtualDisplaySpace;
use super::VulkanDisplayWrapper;
use crate::EventDeviceKind;
@@ -102,6 +102,29 @@ fn update_virtual_display_projection(
}
}
+#[allow(dead_code)]
+#[derive(Clone)]
+pub(crate) struct DisplayProperties {
+ pub start_hidden: bool,
+ pub is_fullscreen: bool,
+ pub window_width: u32,
+ pub window_height: u32,
+}
+
+impl From<&DisplayParameters> for DisplayProperties {
+ fn from(params: &DisplayParameters) -> Self {
+ let is_fullscreen = matches!(params.mode, DisplayMode::BorderlessFullScreen(_));
+ let (window_width, window_height) = params.get_window_size();
+
+ Self {
+ start_hidden: params.hidden,
+ is_fullscreen,
+ window_width,
+ window_height,
+ }
+ }
+}
+
pub struct Surface {
surface_id: u32,
mouse_input: MouseInputManager,
@@ -116,9 +139,8 @@ impl Surface {
pub fn new(
surface_id: u32,
window: &GuiWindow,
- virtual_display_size: &Size2D<i32, VirtualDisplaySpace>,
_metrics: Option<Weak<Metrics>>,
- display_properties: &DisplayProperties,
+ display_params: &DisplayParameters,
resources: SurfaceResources,
vulkan_display: Arc<Mutex<VulkanDisplayWrapper>>,
) -> Result<Self> {
@@ -130,8 +152,12 @@ impl Surface {
);
let initial_host_viewport_size = window.get_client_rect().context(CONTEXT_MESSAGE)?.size;
+ let virtual_display_size = {
+ let (width, height) = display_params.get_virtual_display_size();
+ size2(width, height).checked_cast()
+ };
let virtual_display_manager =
- VirtualDisplayManager::new(&initial_host_viewport_size, virtual_display_size);
+ VirtualDisplayManager::new(&initial_host_viewport_size, &virtual_display_size);
// This will make gfxstream initialize the child window to which it will render.
update_virtual_display_projection(
vulkan_display.lock(),
@@ -156,7 +182,7 @@ impl Surface {
mouse_input,
window_manager: WindowManager::new(
window,
- display_properties,
+ &display_params.into(),
initial_host_viewport_size,
gpu_main_display_tube.clone(),
)
diff --git a/gpu_display/src/gpu_display_win/window_manager.rs b/gpu_display/src/gpu_display_win/window_manager.rs
index 006718503..f9ca33d84 100644
--- a/gpu_display/src/gpu_display_win/window_manager.rs
+++ b/gpu_display/src/gpu_display_win/window_manager.rs
@@ -8,9 +8,9 @@ use anyhow::Result;
use base::Tube;
use super::math_util::Size;
+use super::surface::DisplayProperties;
use super::window::GuiWindow;
use super::window_message_processor::WindowPosMessage;
-use super::DisplayProperties;
pub(crate) struct NoopWindowManager {}
diff --git a/gpu_display/src/gpu_display_win/window_procedure_thread.rs b/gpu_display/src/gpu_display_win/window_procedure_thread.rs
index b84d69bf3..84253e762 100644
--- a/gpu_display/src/gpu_display_win/window_procedure_thread.rs
+++ b/gpu_display/src/gpu_display_win/window_procedure_thread.rs
@@ -62,11 +62,6 @@ use super::window_message_processor::*;
// The default app icon id, which is defined in crosvm-manifest.rc.
const APP_ICON_ID: u16 = 1;
-// The number of GUI windows to pre-create when booting KiwiVM. At most this
-// number of guest displays and host windows can be used concurrently.
-// TODO(b/314984693): The service or the config file should specify this number.
-const MAX_NUM_WINDOWS: usize = 1;
-
#[derive(Debug)]
enum MessageLoopState {
/// The initial state.
@@ -241,13 +236,14 @@ impl WindowProcedureThread {
// We don't implement Default for WindowProcedureThreadBuilder so that the builder function
// is the only way to create WindowProcedureThreadBuilder.
WindowProcedureThreadBuilder {
+ max_num_windows: 1,
display_tube: None,
#[cfg(feature = "kiwi")]
ime_tube: None,
}
}
- fn start_thread(gpu_main_display_tube: Option<Tube>) -> Result<Self> {
+ fn start_thread(max_num_windows: u32, gpu_main_display_tube: Option<Tube>) -> Result<Self> {
let (message_router_handle_sender, message_router_handle_receiver) = channel();
let message_loop_state = Arc::new(AtomicI32::new(MessageLoopState::NotStarted as i32));
let close_requested_event = Event::new().unwrap();
@@ -262,6 +258,7 @@ impl WindowProcedureThread {
.spawn(move || {
match close_requested_event_clone.try_clone() {
Ok(close_requested_event) => Self::run_message_loop(
+ max_num_windows,
message_router_handle_sender,
message_loop_state_clone,
gpu_main_display_tube,
@@ -337,6 +334,7 @@ impl WindowProcedureThread {
}
fn run_message_loop(
+ max_num_windows: u32,
message_router_handle_sender: Sender<Result<u32>>,
message_loop_state: Arc<AtomicI32>,
gpu_main_display_tube: Option<Tube>,
@@ -346,14 +344,16 @@ impl WindowProcedureThread {
// SAFETY:
// Safe because the dispatcher will take care of the lifetime of the `MessageOnlyWindow` and
// `GuiWindow` objects.
- match unsafe { Self::create_windows() }.and_then(|(message_router_window, gui_windows)| {
- WindowMessageDispatcher::new(
- message_router_window,
- gui_windows,
- gpu_main_display_tube.clone(),
- close_requested_event,
- )
- }) {
+ match unsafe { Self::create_windows(max_num_windows) }.and_then(
+ |(message_router_window, gui_windows)| {
+ WindowMessageDispatcher::new(
+ message_router_window,
+ gui_windows,
+ gpu_main_display_tube.clone(),
+ close_requested_event,
+ )
+ },
+ ) {
Ok(dispatcher) => {
info!("WndProc thread entering message loop");
message_loop_state.store(MessageLoopState::Running as i32, Ordering::SeqCst);
@@ -542,7 +542,7 @@ impl WindowProcedureThread {
/// # Safety
/// The owner of the returned window objects is responsible for dropping them before we finish
/// processing `WM_NCDESTROY`, because the window handle will become invalid afterwards.
- unsafe fn create_windows() -> Result<(MessageOnlyWindow, Vec<GuiWindow>)> {
+ unsafe fn create_windows(max_num_windows: u32) -> Result<(MessageOnlyWindow, Vec<GuiWindow>)> {
let message_router_window = MessageOnlyWindow::new(
/* class_name */
Self::get_window_class_name::<MessageOnlyWindow>()
@@ -559,10 +559,10 @@ impl WindowProcedureThread {
// window may use the background brush to clear the gfxstream window client area when
// drawing occurs. This caused the screen flickering issue during resizing.
// See b/197786842 for details.
- let mut gui_windows = Vec::with_capacity(MAX_NUM_WINDOWS);
- for scanout_id in 0..MAX_NUM_WINDOWS {
+ let mut gui_windows = Vec::with_capacity(max_num_windows as usize);
+ for scanout_id in 0..max_num_windows {
gui_windows.push(GuiWindow::new(
- scanout_id as u32,
+ scanout_id,
/* class_name */
Self::get_window_class_name::<GuiWindow>()
.with_context(|| {
@@ -650,12 +650,18 @@ unsafe impl Send for WindowProcedureThread {}
#[derive(Deserialize, Serialize)]
pub struct WindowProcedureThreadBuilder {
+ max_num_windows: u32,
display_tube: Option<Tube>,
#[cfg(feature = "kiwi")]
ime_tube: Option<Tube>,
}
impl WindowProcedureThreadBuilder {
+ pub fn set_max_num_windows(&mut self, max_num_windows: u32) -> &mut Self {
+ self.max_num_windows = max_num_windows;
+ self
+ }
+
pub fn set_display_tube(&mut self, display_tube: Option<Tube>) -> &mut Self {
self.display_tube = display_tube;
self
@@ -675,10 +681,16 @@ impl WindowProcedureThreadBuilder {
pub fn start_thread(self) -> Result<WindowProcedureThread> {
cfg_if::cfg_if! {
if #[cfg(feature = "kiwi")] {
- let ime_tube = self.ime_tube.ok_or_else(|| anyhow!("The ime tube is not set."))?;
- WindowProcedureThread::start_thread(self.display_tube, ime_tube)
+ let ime_tube = self
+ .ime_tube
+ .ok_or_else(|| anyhow!("The ime tube is not set."))?;
+ WindowProcedureThread::start_thread(
+ self.max_num_windows,
+ self.display_tube,
+ ime_tube,
+ )
} else {
- WindowProcedureThread::start_thread(None)
+ WindowProcedureThread::start_thread(self.max_num_windows, None)
}
}
}
diff --git a/gpu_display/src/gpu_display_wl.rs b/gpu_display/src/gpu_display_wl.rs
index 61a7f9b9b..c163ee7ed 100644
--- a/gpu_display/src/gpu_display_wl.rs
+++ b/gpu_display/src/gpu_display_wl.rs
@@ -32,6 +32,7 @@ use base::VolatileMemory;
use dwl::*;
use linux_input_sys::virtio_input_event;
use sync::Waitable;
+use vm_control::gpu::DisplayParameters;
use crate::DisplayExternalResourceImport;
use crate::DisplayT;
@@ -376,12 +377,12 @@ impl DisplayT for DisplayWl {
parent_surface_id: Option<u32>,
surface_id: u32,
scanout_id: Option<u32>,
- width: u32,
- height: u32,
+ display_params: &DisplayParameters,
surf_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
let parent_id = parent_surface_id.unwrap_or(0);
+ let (width, height) = display_params.get_virtual_display_size();
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);
diff --git a/gpu_display/src/gpu_display_x.rs b/gpu_display/src/gpu_display_x.rs
index 0cd720c94..f7560a1e9 100644
--- a/gpu_display/src/gpu_display_x.rs
+++ b/gpu_display/src/gpu_display_x.rs
@@ -34,6 +34,7 @@ use libc::IPC_CREAT;
use libc::IPC_PRIVATE;
use libc::IPC_RMID;
use linux_input_sys::virtio_input_event;
+use vm_control::gpu::DisplayParameters;
use crate::keycode_converter::KeycodeTranslator;
use crate::keycode_converter::KeycodeTypes;
@@ -699,8 +700,7 @@ impl DisplayT for DisplayX {
parent_surface_id: Option<u32>,
_surface_id: u32,
_scanout_id: Option<u32>,
- width: u32,
- height: u32,
+ display_params: &DisplayParameters,
_surf_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
if parent_surface_id.is_some() {
@@ -710,6 +710,7 @@ impl DisplayT for DisplayX {
// TODO(b/315870313): Add safety comment
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe {
+ let (width, height) = display_params.get_virtual_display_size();
let depth = xlib::XDefaultDepthOfScreen(self.screen.as_ptr()) as u32;
let black_pixel = xlib::XBlackPixelOfScreen(self.screen.as_ptr());
diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs
index 662575fed..49656fbb5 100644
--- a/gpu_display/src/lib.rs
+++ b/gpu_display/src/lib.rs
@@ -22,6 +22,7 @@ use serde::Deserialize;
use serde::Serialize;
use sync::Waitable;
use thiserror::Error;
+use vm_control::gpu::DisplayParameters;
use vm_control::gpu::MouseMode;
#[cfg(feature = "vulkan_display")]
use vulkano::VulkanLibrary;
@@ -47,8 +48,6 @@ pub mod vulkan;
pub use event_device::EventDevice;
pub use event_device::EventDeviceKind;
#[cfg(windows)]
-pub use gpu_display_win::DisplayProperties as WinDisplayProperties;
-#[cfg(windows)]
pub use gpu_display_win::WindowProcedureThread;
#[cfg(windows)]
pub use gpu_display_win::WindowProcedureThreadBuilder;
@@ -319,8 +318,7 @@ trait DisplayT: AsRawDescriptor {
parent_surface_id: Option<u32>,
surface_id: u32,
scanout_id: Option<u32>,
- width: u32,
- height: u32,
+ display_params: &DisplayParameters,
surf_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>>;
@@ -564,8 +562,7 @@ impl GpuDisplay {
&mut self,
parent_surface_id: Option<u32>,
scanout_id: Option<u32>,
- width: u32,
- height: u32,
+ display_params: &DisplayParameters,
surf_type: SurfaceType,
) -> GpuDisplayResult<u32> {
if let Some(parent_id) = parent_surface_id {
@@ -579,8 +576,7 @@ impl GpuDisplay {
parent_surface_id,
new_surface_id,
scanout_id,
- width,
- height,
+ display_params,
surf_type,
)?;
diff --git a/gpu_display/src/sys/windows.rs b/gpu_display/src/sys/windows.rs
index 96ad10693..bb9feb44c 100644
--- a/gpu_display/src/sys/windows.rs
+++ b/gpu_display/src/sys/windows.rs
@@ -11,7 +11,6 @@ use base::SendTube;
use base::WaitContext;
use metrics::sys::windows::Metrics;
-use crate::gpu_display_win::DisplayProperties;
use crate::gpu_display_win::DisplayWin;
use crate::DisplayEventToken;
use crate::DisplayT;
@@ -65,7 +64,6 @@ pub trait WinGpuDisplayExt {
fn open_winapi(
wndproc_thread: WindowProcedureThread,
win_metrics: Option<Weak<Metrics>>,
- display_properties: DisplayProperties,
gpu_display_wait_descriptor_ctrl: SendTube,
vulkan_display_create_params: Option<VulkanCreateParams>,
) -> GpuDisplayResult<GpuDisplay>;
@@ -75,14 +73,12 @@ impl WinGpuDisplayExt for GpuDisplay {
fn open_winapi(
wndproc_thread: WindowProcedureThread,
win_metrics: Option<Weak<Metrics>>,
- display_properties: DisplayProperties,
gpu_display_wait_descriptor_ctrl: SendTube,
vulkan_display_create_params: Option<VulkanCreateParams>,
) -> GpuDisplayResult<GpuDisplay> {
let display = DisplayWin::new(
wndproc_thread,
win_metrics,
- display_properties,
gpu_display_wait_descriptor_ctrl,
vulkan_display_create_params,
)?;
diff --git a/hypervisor/Android.bp b/hypervisor/Android.bp
index a4409ba10..896f91eb9 100644
--- a/hypervisor/Android.bp
+++ b/hypervisor/Android.bp
@@ -1,14 +1,11 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "external_crosvm_license"
- // to get the below license kinds:
- // SPDX-license-identifier-BSD
default_applicable_licenses: ["external_crosvm_license"],
+ default_team: "trendy_team_foundation_security_rust_pkvm_",
}
rust_test {
@@ -103,6 +100,52 @@ rust_test {
}
rust_test {
+ name: "hypervisor_test_tests_hypervisor_virtualization",
+ defaults: ["crosvm_inner_defaults"],
+ host_supported: true,
+ crate_name: "hypervisor_virtualization",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.1.0",
+ srcs: ["tests/hypervisor_virtualization.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: false,
+ },
+ edition: "2021",
+ features: [
+ "gdb",
+ "gdbstub",
+ "gdbstub_arch",
+ "geniezone",
+ "gunyah",
+ ],
+ rustlibs: [
+ "libanyhow",
+ "libbase_rust",
+ "libbit_field",
+ "libbitflags",
+ "libcros_fdt",
+ "libdata_model",
+ "libdowncast_rs",
+ "libfnv",
+ "libgdbstub",
+ "libgdbstub_arch",
+ "libhypervisor",
+ "libkvm",
+ "libkvm_sys",
+ "liblibc",
+ "libmemoffset",
+ "libonce_cell",
+ "libserde",
+ "libserde_json",
+ "libsync_rust",
+ "libvm_memory",
+ ],
+ proc_macros: ["libenumn"],
+}
+
+rust_test {
name: "hypervisor_test_tests_kvm_main",
defaults: ["crosvm_inner_defaults"],
host_supported: true,
diff --git a/hypervisor/src/aarch64.rs b/hypervisor/src/aarch64.rs
index 2caa0ae0c..c838196b3 100644
--- a/hypervisor/src/aarch64.rs
+++ b/hypervisor/src/aarch64.rs
@@ -114,6 +114,12 @@ pub trait VcpuAArch64: Vcpu {
/// Gets the value of a register on this VCPU.
fn get_one_reg(&self, reg_id: VcpuRegAArch64) -> Result<u64>;
+ /// Sets the value of a Neon vector register (V0-V31) on this VCPU.
+ fn set_vector_reg(&self, reg_num: u8, data: u128) -> Result<()>;
+
+ /// Gets the value of a Neon vector register (V0-V31) on this VCPU.
+ fn get_vector_reg(&self, reg_num: u8) -> Result<u128>;
+
/// Gets the value of MPIDR_EL1 on this VCPU.
fn get_mpidr(&self) -> Result<u64> {
const RES1: u64 = 1 << 31;
diff --git a/hypervisor/src/geniezone/mod.rs b/hypervisor/src/geniezone/mod.rs
index 07bdeeb2e..3440b6393 100644
--- a/hypervisor/src/geniezone/mod.rs
+++ b/hypervisor/src/geniezone/mod.rs
@@ -445,6 +445,14 @@ impl VcpuAArch64 for GeniezoneVcpu {
self.get_one_geniezone_reg_u64(GeniezoneVcpuRegister::from(reg_id))
}
+ fn set_vector_reg(&self, _reg_num: u8, _data: u128) -> Result<()> {
+ unimplemented!()
+ }
+
+ fn get_vector_reg(&self, _reg_num: u8) -> Result<u128> {
+ unimplemented!()
+ }
+
fn get_psci_version(&self) -> Result<PsciVersion> {
Ok(PSCI_0_2)
}
diff --git a/hypervisor/src/gunyah/aarch64.rs b/hypervisor/src/gunyah/aarch64.rs
index 3133d01ce..c40c36394 100644
--- a/hypervisor/src/gunyah/aarch64.rs
+++ b/hypervisor/src/gunyah/aarch64.rs
@@ -213,6 +213,14 @@ impl VcpuAArch64 for GunyahVcpu {
Err(Error::new(ENOTSUP))
}
+ fn set_vector_reg(&self, _reg_num: u8, _data: u128) -> Result<()> {
+ unimplemented!()
+ }
+
+ fn get_vector_reg(&self, _reg_num: u8) -> Result<u128> {
+ unimplemented!()
+ }
+
fn get_psci_version(&self) -> Result<PsciVersion> {
Ok(PSCI_0_2)
}
diff --git a/hypervisor/src/haxm/vcpu.rs b/hypervisor/src/haxm/vcpu.rs
index 91618999b..700cbb02f 100644
--- a/hypervisor/src/haxm/vcpu.rs
+++ b/hypervisor/src/haxm/vcpu.rs
@@ -5,6 +5,7 @@
use core::ffi::c_void;
use std::arch::x86_64::CpuidResult;
use std::cmp::min;
+use std::collections::BTreeMap;
use std::intrinsics::copy_nonoverlapping;
use std::mem::size_of;
@@ -27,9 +28,6 @@ use libc::EOPNOTSUPP;
use vm_memory::GuestAddress;
use super::*;
-use crate::get_tsc_offset_from_msr;
-use crate::set_tsc_offset_via_msr;
-use crate::set_tsc_value_via_msr;
use crate::CpuId;
use crate::CpuIdEntry;
use crate::DebugRegs;
@@ -38,7 +36,6 @@ use crate::Fpu;
use crate::HypervHypercall;
use crate::IoOperation;
use crate::IoParams;
-use crate::Register;
use crate::Regs;
use crate::Segment;
use crate::Sregs;
@@ -112,13 +109,7 @@ impl HaxmVcpu {
}
// Also read efer MSR
- let mut efer = vec![Register {
- id: IA32_EFER,
- value: 0,
- }];
-
- self.get_msrs(&mut efer)?;
- state._efer = efer[0].value as u32;
+ state._efer = self.get_msr(IA32_EFER)? as u32;
Ok(VcpuState { state })
}
@@ -131,12 +122,7 @@ impl HaxmVcpu {
}
// Also set efer MSR
- let efer = vec![Register {
- id: IA32_EFER,
- value: state.state._efer as u64,
- }];
-
- self.set_msrs(&efer)
+ self.set_msr(IA32_EFER, state.state._efer as u64)
}
}
@@ -481,78 +467,53 @@ impl VcpuX86_64 for HaxmVcpu {
}
/// Gets the VCPU extended control registers.
- fn get_xcrs(&self) -> Result<Vec<Register>> {
- // Haxm does not support setting XCRs
+ fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>> {
+ // Haxm does not support getting XCRs
Err(Error::new(libc::ENXIO))
}
- /// Sets the VCPU extended control registers.
- fn set_xcrs(&self, _xcrs: &[Register]) -> Result<()> {
+ /// Sets a VCPU extended control register.
+ fn set_xcr(&self, _xcr_index: u32, _value: u64) -> Result<()> {
// Haxm does not support setting XCRs
Err(Error::new(libc::ENXIO))
}
- /// Gets the model-specific registers. `msrs` specifies the MSR indexes to be queried, and
- /// on success contains their indexes and values.
- fn get_msrs(&self, msrs: &mut Vec<Register>) -> Result<()> {
- // HAX_VCPU_IOCTL_GET_MSRS only allows you to set HAX_MAX_MSR_ARRAY-1 msrs at a time
- // TODO (b/163811378): the fact that you can only set HAX_MAX_MSR_ARRAY-1 seems like a
- // bug with HAXM, since the entries array itself is HAX_MAX_MSR_ARRAY long
- for chunk in msrs.chunks_mut((HAX_MAX_MSR_ARRAY - 1) as usize) {
- let chunk_size = chunk.len();
- let hax_chunk: Vec<vmx_msr> = chunk.iter().map(vmx_msr::from).collect();
-
- let mut msr_data = hax_msr_data {
- nr_msr: chunk_size as u16,
- ..Default::default()
- };
-
- // Copy chunk into msr_data
- msr_data.entries[..chunk_size].copy_from_slice(&hax_chunk);
-
- // TODO(b/315998194): Add safety comment
- #[allow(clippy::undocumented_unsafe_blocks)]
- let ret = unsafe { ioctl_with_mut_ref(self, HAX_VCPU_IOCTL_GET_MSRS(), &mut msr_data) };
- if ret != 0 {
- return errno_result();
- }
+ /// Gets the value of one model-specific register.
+ fn get_msr(&self, msr_index: u32) -> Result<u64> {
+ let mut msr_data = hax_msr_data {
+ nr_msr: 1,
+ ..Default::default()
+ };
+ msr_data.entries[0].entry = u64::from(msr_index);
- // copy values we got from kernel
- for (i, item) in chunk.iter_mut().enumerate().take(chunk_size) {
- item.value = msr_data.entries[i].value;
- }
+ // TODO(b/315998194): Add safety comment
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ let ret = unsafe { ioctl_with_mut_ref(self, HAX_VCPU_IOCTL_GET_MSRS(), &mut msr_data) };
+ if ret != 0 {
+ return errno_result();
}
- Ok(())
+ Ok(msr_data.entries[0].value)
}
- fn get_all_msrs(&self) -> Result<Vec<Register>> {
+ fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>> {
Err(Error::new(EOPNOTSUPP))
}
- /// Sets the model-specific registers.
- fn set_msrs(&self, msrs: &[Register]) -> Result<()> {
- // HAX_VCPU_IOCTL_GET_MSRS only allows you to set HAX_MAX_MSR_ARRAY-1 msrs at a time
- // TODO (b/163811378): the fact that you can only set HAX_MAX_MSR_ARRAY-1 seems like a
- // bug with HAXM, since the entries array itself is HAX_MAX_MSR_ARRAY long
- for chunk in msrs.chunks((HAX_MAX_MSR_ARRAY - 1) as usize) {
- let chunk_size = chunk.len();
- let hax_chunk: Vec<vmx_msr> = chunk.iter().map(vmx_msr::from).collect();
-
- let mut msr_data = hax_msr_data {
- nr_msr: chunk_size as u16,
- ..Default::default()
- };
-
- // Copy chunk into msr_data
- msr_data.entries[..chunk_size].copy_from_slice(&hax_chunk);
-
- // TODO(b/315998194): Add safety comment
- #[allow(clippy::undocumented_unsafe_blocks)]
- let ret = unsafe { ioctl_with_mut_ref(self, HAX_VCPU_IOCTL_SET_MSRS(), &mut msr_data) };
- if ret != 0 {
- return errno_result();
- }
+ /// Sets the value of one model-specific register.
+ fn set_msr(&self, msr_index: u32, value: u64) -> Result<()> {
+ let mut msr_data = hax_msr_data {
+ nr_msr: 1,
+ ..Default::default()
+ };
+ msr_data.entries[0].entry = u64::from(msr_index);
+ msr_data.entries[0].value = value;
+
+ // TODO(b/315998194): Add safety comment
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ let ret = unsafe { ioctl_with_mut_ref(self, HAX_VCPU_IOCTL_SET_MSRS(), &mut msr_data) };
+ if ret != 0 {
+ return errno_result();
}
Ok(())
@@ -606,16 +567,6 @@ impl VcpuX86_64 for HaxmVcpu {
Err(Error::new(ENOENT))
}
- fn get_tsc_offset(&self) -> Result<u64> {
- // Use the default MSR-based implementation
- get_tsc_offset_from_msr(self)
- }
-
- fn set_tsc_offset(&self, offset: u64) -> Result<()> {
- // Use the default MSR-based implementation
- set_tsc_offset_via_msr(self, offset)
- }
-
fn restore_timekeeping(&self, _host_tsc_reference_moment: u64, tsc_offset: u64) -> Result<()> {
// HAXM sets TSC_OFFSET based on what we set TSC to; however, it does
// not yet handle syncing. This means it computes
@@ -630,10 +581,6 @@ impl VcpuX86_64 for HaxmVcpu {
// offset directly.)
self.set_tsc_offset(tsc_offset)
}
-
- fn set_tsc_value(&self, value: u64) -> Result<()> {
- set_tsc_value_via_msr(self, value)
- }
}
struct VcpuState {
@@ -1004,24 +951,6 @@ impl From<&CpuIdEntry> for hax_cpuid_entry {
}
}
-impl From<&vmx_msr> for Register {
- fn from(item: &vmx_msr) -> Register {
- Register {
- id: item.entry as u32,
- value: item.value,
- }
- }
-}
-
-impl From<&Register> for vmx_msr {
- fn from(item: &Register) -> vmx_msr {
- vmx_msr {
- entry: item.id as u64,
- value: item.value,
- }
- }
-}
-
// TODO(b:241252288): Enable tests disabled with dummy feature flag - enable_haxm_tests.
#[cfg(test)]
#[cfg(feature = "enable_haxm_tests")]
@@ -1063,42 +992,25 @@ mod tests {
}
#[test]
- fn set_many_msrs() {
+ fn set_msr() {
let haxm = Haxm::new().expect("failed to instantiate HAXM");
let mem =
GuestMemory::new(&[(GuestAddress(0), 0x1000)]).expect("failed to create guest memory");
let vm = HaxmVm::new(&haxm, mem).expect("failed to create vm");
let vcpu = vm.create_vcpu(0).expect("failed to create vcpu");
- let mut registers: Vec<Register> = Vec::new();
- for id in 0x300..0x3ff {
- registers.push(Register {
- id: 38,
- value: id as u64,
- });
- }
-
- vcpu.set_msrs(&registers).expect("failed to set registers");
+ vcpu.set_msr(38, 0x300).expect("failed to set MSR");
}
#[test]
- fn get_many_msrs() {
+ fn get_msr() {
let haxm = Haxm::new().expect("failed to instantiate HAXM");
let mem =
GuestMemory::new(&[(GuestAddress(0), 0x1000)]).expect("failed to create guest memory");
let vm = HaxmVm::new(&haxm, mem).expect("failed to create vm");
let vcpu = vm.create_vcpu(0).expect("failed to create vcpu");
- let mut registers: Vec<Register> = Vec::new();
- for id in 0x300..0x3ff {
- registers.push(Register {
- id: 38,
- value: id as u64,
- });
- }
-
- vcpu.get_msrs(&mut registers)
- .expect("failed to get registers");
+ let _value = vcpu.get_msr(38).expect("failed to get MSR");
}
#[test]
@@ -1148,21 +1060,17 @@ mod tests {
assert_eq!(sregs.efer, EFER_LMA | EFER_LME);
// IA32_EFER register value should match
- let mut efer_reg = vec![Register {
- id: IA32_EFER,
- value: 0,
- }];
- vcpu.get_msrs(&mut efer_reg).expect("failed to get msrs");
- assert_eq!(efer_reg[0].value, EFER_LMA | EFER_LME);
+ let efer = vcpu.get_msr(IA32_EFER).expect("failed to get msr");
+ assert_eq!(efer, EFER_LMA | EFER_LME);
// Enable SCE via set_msrs
- efer_reg[0].value |= EFER_SCE;
- vcpu.set_msrs(&efer_reg).expect("failed to set msrs");
+ vcpu.set_msr(IA32_EFER, efer | EFER_SCE)
+ .expect("failed to set msr");
// Verify that setting stuck
let sregs = vcpu.get_sregs().expect("failed to get sregs");
assert_eq!(sregs.efer, EFER_SCE | EFER_LME | EFER_LMA);
- vcpu.get_msrs(&mut efer_reg).expect("failed to get msrs");
- assert_eq!(efer_reg[0].value, EFER_SCE | EFER_LME | EFER_LMA);
+ let new_efer = vcpu.get_msr(IA32_EFER).expect("failed to get msrs");
+ assert_eq!(new_efer, EFER_SCE | EFER_LME | EFER_LMA);
}
}
diff --git a/hypervisor/src/kvm/aarch64.rs b/hypervisor/src/kvm/aarch64.rs
index 803bd6db4..5d1a2b8ed 100644
--- a/hypervisor/src/kvm/aarch64.rs
+++ b/hypervisor/src/kvm/aarch64.rs
@@ -257,6 +257,10 @@ impl KvmVcpu {
self.set_one_kvm_reg(kvm_reg_id, data.to_ne_bytes().as_slice())
}
+ fn set_one_kvm_reg_u128(&self, kvm_reg_id: KvmVcpuRegister, data: u128) -> Result<()> {
+ self.set_one_kvm_reg(kvm_reg_id, data.to_ne_bytes().as_slice())
+ }
+
fn set_one_kvm_reg(&self, kvm_reg_id: KvmVcpuRegister, data: &[u8]) -> Result<()> {
let onereg = kvm_one_reg {
id: kvm_reg_id.into(),
@@ -281,6 +285,12 @@ impl KvmVcpu {
Ok(u64::from_ne_bytes(bytes))
}
+ fn get_one_kvm_reg_u128(&self, kvm_reg_id: KvmVcpuRegister) -> Result<u128> {
+ let mut bytes = 0u128.to_ne_bytes();
+ self.get_one_kvm_reg(kvm_reg_id, bytes.as_mut_slice())?;
+ Ok(u128::from_ne_bytes(bytes))
+ }
+
fn get_one_kvm_reg(&self, kvm_reg_id: KvmVcpuRegister, data: &mut [u8]) -> Result<()> {
let onereg = kvm_one_reg {
id: kvm_reg_id.into(),
@@ -307,22 +317,12 @@ impl KvmVcpu {
self.set_one_kvm_reg(kvm_reg_id, data.to_ne_bytes().as_slice())
}
- fn set_one_kvm_reg_u128(&self, kvm_reg_id: KvmVcpuRegister, data: u128) -> Result<()> {
- self.set_one_kvm_reg(kvm_reg_id, data.to_ne_bytes().as_slice())
- }
-
fn get_one_kvm_reg_u32(&self, kvm_reg_id: KvmVcpuRegister) -> Result<u32> {
let mut bytes = 0u32.to_ne_bytes();
self.get_one_kvm_reg(kvm_reg_id, bytes.as_mut_slice())?;
Ok(u32::from_ne_bytes(bytes))
}
- fn get_one_kvm_reg_u128(&self, kvm_reg_id: KvmVcpuRegister) -> Result<u128> {
- let mut bytes = 0u128.to_ne_bytes();
- self.get_one_kvm_reg(kvm_reg_id, bytes.as_mut_slice())?;
- Ok(u128::from_ne_bytes(bytes))
- }
-
/// Retrieves the value of the currently active "version" of a multiplexed registers.
fn demux_register(&self, reg: &<GdbArch as Arch>::RegId) -> Result<Option<KvmVcpuRegister>> {
match *reg {
@@ -692,6 +692,20 @@ impl VcpuAArch64 for KvmVcpu {
self.get_one_kvm_reg_u64(KvmVcpuRegister::from(reg_id))
}
+ fn set_vector_reg(&self, reg_num: u8, data: u128) -> Result<()> {
+ if reg_num > 31 {
+ return Err(Error::new(EINVAL));
+ }
+ self.set_one_kvm_reg_u128(KvmVcpuRegister::V(reg_num), data)
+ }
+
+ fn get_vector_reg(&self, reg_num: u8) -> Result<u128> {
+ if reg_num > 31 {
+ return Err(Error::new(EINVAL));
+ }
+ self.get_one_kvm_reg_u128(KvmVcpuRegister::V(reg_num))
+ }
+
fn get_mpidr(&self) -> Result<u64> {
self.get_one_kvm_reg_u64(KvmVcpuRegister::MPIDR_EL1)
}
@@ -798,7 +812,7 @@ impl VcpuAArch64 for KvmVcpu {
self.set_one_kvm_reg_u64(KvmVcpuRegister::Pstate, pstate)?;
for (i, reg) in regs.v.iter().enumerate() {
let n = u8::try_from(i).expect("invalid Vn general purpose register index");
- self.set_one_kvm_reg_u128(KvmVcpuRegister::V(n), *reg)?;
+ self.set_vector_reg(n, *reg)?;
}
self.set_one_kvm_reg_u32(KvmVcpuRegister::Fpcr, regs.fpcr)?;
self.set_one_kvm_reg_u32(KvmVcpuRegister::Fpsr, regs.fpsr)?;
@@ -822,7 +836,7 @@ impl VcpuAArch64 for KvmVcpu {
regs.cpsr = self.get_one_kvm_reg_u64(KvmVcpuRegister::Pstate)? as u32;
for (i, reg) in regs.v.iter_mut().enumerate() {
let n = u8::try_from(i).expect("invalid Vn general purpose register index");
- *reg = self.get_one_kvm_reg_u128(KvmVcpuRegister::V(n))?;
+ *reg = self.get_vector_reg(n)?;
}
regs.fpcr = self.get_one_kvm_reg_u32(KvmVcpuRegister::Fpcr)?;
regs.fpsr = self.get_one_kvm_reg_u32(KvmVcpuRegister::Fpsr)?;
diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs
index 3ccb123c2..f9a3b2626 100644
--- a/hypervisor/src/kvm/x86_64.rs
+++ b/hypervisor/src/kvm/x86_64.rs
@@ -3,6 +3,7 @@
// found in the LICENSE file.
use std::arch::x86_64::CpuidResult;
+use std::collections::BTreeMap;
use base::errno_result;
use base::error;
@@ -30,10 +31,7 @@ use super::Config;
use super::Kvm;
use super::KvmVcpu;
use super::KvmVm;
-use crate::get_tsc_offset_from_msr;
use crate::host_phys_addr_bits;
-use crate::set_tsc_offset_via_msr;
-use crate::set_tsc_value_via_msr;
use crate::ClockState;
use crate::CpuId;
use crate::CpuIdEntry;
@@ -51,7 +49,6 @@ use crate::PicState;
use crate::PitChannelState;
use crate::PitState;
use crate::ProtectionType;
-use crate::Register;
use crate::Regs;
use crate::Segment;
use crate::Sregs;
@@ -883,25 +880,36 @@ impl VcpuX86_64 for KvmVcpu {
}
}
- fn get_xcrs(&self) -> Result<Vec<Register>> {
+ fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>> {
let mut regs: kvm_xcrs = Default::default();
// SAFETY:
// 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 ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_XCRS(), &mut regs) };
- if ret == 0 {
- Ok(from_kvm_xcrs(&regs))
- } else {
- errno_result()
+ if ret < 0 {
+ return errno_result();
}
+
+ Ok(regs
+ .xcrs
+ .iter()
+ .take(regs.nr_xcrs as usize)
+ .map(|kvm_xcr| (kvm_xcr.xcr, kvm_xcr.value))
+ .collect())
}
- fn set_xcrs(&self, xcrs: &[Register]) -> Result<()> {
- let xcrs = to_kvm_xcrs(xcrs);
+ fn set_xcr(&self, xcr_index: u32, value: u64) -> Result<()> {
+ let mut kvm_xcr = kvm_xcrs {
+ nr_xcrs: 1,
+ ..Default::default()
+ };
+ kvm_xcr.xcrs[0].xcr = xcr_index;
+ kvm_xcr.xcrs[0].value = value;
+
let ret = {
// SAFETY:
// Here we trust the kernel not to read past the end of the kvm_xcrs struct.
- unsafe { ioctl_with_ref(self, KVM_SET_XCRS(), &xcrs) }
+ unsafe { ioctl_with_ref(self, KVM_SET_XCRS(), &kvm_xcr) }
};
if ret == 0 {
Ok(())
@@ -910,78 +918,117 @@ impl VcpuX86_64 for KvmVcpu {
}
}
- fn get_msrs(&self, vec: &mut Vec<Register>) -> Result<()> {
- let msrs = to_kvm_msrs(vec);
+ fn get_msr(&self, msr_index: u32) -> Result<u64> {
+ let mut msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(1);
+ msrs[0].nmsrs = 1;
+
+ // SAFETY: We initialize a one-element array using `vec_with_array_field` above.
+ unsafe {
+ let msr_entries = msrs[0].entries.as_mut_slice(1);
+ msr_entries[0].index = msr_index;
+ }
+
let ret = {
// SAFETY:
// Here we trust the kernel not to read or write past the end of the kvm_msrs struct.
unsafe { ioctl_with_ref(self, KVM_GET_MSRS(), &msrs[0]) }
};
- // KVM_GET_MSRS actually returns the number of msr entries written.
if ret < 0 {
return errno_result();
}
+
+ // KVM_GET_MSRS returns the number of msr entries written.
+ if ret != 1 {
+ return Err(base::Error::new(libc::ENOENT));
+ }
+
// SAFETY:
// Safe because we trust the kernel to return the correct array length on success.
- let entries = unsafe {
- let count = ret as usize;
- assert!(count <= vec.len());
- msrs[0].entries.as_slice(count)
+ let value = unsafe {
+ let msr_entries = msrs[0].entries.as_slice(1);
+ msr_entries[0].data
};
- vec.truncate(0);
- vec.extend(entries.iter().map(|e| Register {
- id: e.index,
- value: e.data,
- }));
- Ok(())
+
+ Ok(value)
}
- fn get_all_msrs(&self) -> Result<Vec<Register>> {
- let mut msrs: Vec<_> = self
- .kvm
- .get_msr_index_list()?
- .into_iter()
- .map(|i| Register { id: i, value: 0 })
- .collect();
- let count = msrs.len();
- self.get_msrs(&mut msrs)?;
- if msrs.len() != count {
+ fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>> {
+ let msr_index_list = self.kvm.get_msr_index_list()?;
+ let mut kvm_msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(msr_index_list.len());
+ kvm_msrs[0].nmsrs = msr_index_list.len() as u32;
+ // SAFETY:
+ // 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.
+ unsafe {
+ kvm_msrs[0]
+ .entries
+ .as_mut_slice(msr_index_list.len())
+ .iter_mut()
+ .zip(msr_index_list.iter())
+ .for_each(|(msr_entry, msr_index)| msr_entry.index = *msr_index);
+ }
+
+ let ret = {
+ // SAFETY:
+ // Here we trust the kernel not to read or write past the end of the kvm_msrs struct.
+ unsafe { ioctl_with_ref(self, KVM_GET_MSRS(), &kvm_msrs[0]) }
+ };
+ if ret < 0 {
+ return errno_result();
+ }
+
+ // KVM_GET_MSRS returns the number of msr entries written.
+ let count = ret as usize;
+ if count != msr_index_list.len() {
error!(
"failed to get all MSRs: requested {}, got {}",
+ msr_index_list.len(),
count,
- msrs.len()
);
return Err(base::Error::new(libc::EPERM));
}
+
+ // SAFETY:
+ // Safe because we trust the kernel to return the correct array length on success.
+ let msrs = unsafe {
+ BTreeMap::from_iter(
+ kvm_msrs[0]
+ .entries
+ .as_slice(count)
+ .iter()
+ .map(|kvm_msr| (kvm_msr.index, kvm_msr.data)),
+ )
+ };
+
Ok(msrs)
}
- fn set_msrs(&self, vec: &[Register]) -> Result<()> {
- let msrs = to_kvm_msrs(vec);
+ fn set_msr(&self, msr_index: u32, value: u64) -> Result<()> {
+ let mut kvm_msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(1);
+ kvm_msrs[0].nmsrs = 1;
+
+ // SAFETY: We initialize a one-element array using `vec_with_array_field` above.
+ unsafe {
+ let msr_entries = kvm_msrs[0].entries.as_mut_slice(1);
+ msr_entries[0].index = msr_index;
+ msr_entries[0].data = value;
+ }
+
let ret = {
// SAFETY:
// Here we trust the kernel not to read past the end of the kvm_msrs struct.
- unsafe { ioctl_with_ref(self, KVM_SET_MSRS(), &msrs[0]) }
+ unsafe { ioctl_with_ref(self, KVM_SET_MSRS(), &kvm_msrs[0]) }
};
- // KVM_SET_MSRS actually returns the number of msr entries written.
if ret < 0 {
return errno_result();
}
- let num_set = ret as usize;
- if num_set != vec.len() {
- if let Some(register) = vec.get(num_set) {
- error!(
- "failed to set MSR {:#x?} to {:#x?}",
- register.id, register.value
- );
- } else {
- error!(
- "unexpected KVM_SET_MSRS return value {num_set} (nmsrs={})",
- vec.len()
- );
- }
+
+ // KVM_SET_MSRS returns the number of msr entries written.
+ if ret != 1 {
+ error!("failed to set MSR {:#x} to {:#x}", msr_index, value);
return Err(base::Error::new(libc::EPERM));
}
+
Ok(())
}
@@ -1049,10 +1096,6 @@ impl VcpuX86_64 for KvmVcpu {
Err(Error::new(ENXIO))
}
- fn set_tsc_value(&self, value: u64) -> Result<()> {
- set_tsc_value_via_msr(self, value)
- }
-
fn restore_timekeeping(&self, _host_tsc_reference_moment: u64, tsc_offset: u64) -> Result<()> {
// In theory, KVM requires no extra handling beyond restoring the TSC
// MSR, which happens separately because TSC is in the all MSR list for
@@ -1065,16 +1108,6 @@ impl VcpuX86_64 for KvmVcpu {
// research is required.)
self.set_tsc_offset(tsc_offset)
}
-
- fn get_tsc_offset(&self) -> Result<u64> {
- // Use the default MSR-based implementation
- get_tsc_offset_from_msr(self)
- }
-
- fn set_tsc_offset(&self, offset: u64) -> Result<()> {
- // Use the default MSR-based implementation
- set_tsc_offset_via_msr(self, offset)
- }
}
impl KvmVcpu {
@@ -1117,25 +1150,14 @@ impl KvmVcpu {
///
/// See the documentation for The kvm_run structure, and for KVM_GET_LAPIC.
pub fn get_apic_base(&self) -> Result<u64> {
- let mut apic_base = vec![Register {
- id: MSR_IA32_APICBASE,
- value: 0,
- }];
- self.get_msrs(&mut apic_base)?;
- match apic_base.get(0) {
- Some(base) => Ok(base.value),
- None => Err(Error::new(EIO)),
- }
+ self.get_msr(MSR_IA32_APICBASE)
}
/// X86 specific call to set the value of the APIC_BASE MSR.
///
/// See the documentation for The kvm_run structure, and for KVM_GET_LAPIC.
pub fn set_apic_base(&self, apic_base: u64) -> Result<()> {
- self.set_msrs(&[Register {
- id: MSR_IA32_APICBASE,
- value: apic_base,
- }])
+ self.set_msr(MSR_IA32_APICBASE, apic_base)
}
/// Call to get pending interrupts acknowledged by the APIC but not yet injected into the CPU.
@@ -1786,53 +1808,6 @@ impl From<&DebugRegs> for kvm_debugregs {
}
}
-fn from_kvm_xcrs(r: &kvm_xcrs) -> Vec<Register> {
- r.xcrs
- .iter()
- .take(r.nr_xcrs as usize)
- .map(|x| Register {
- id: x.xcr,
- value: x.value,
- })
- .collect()
-}
-
-fn to_kvm_xcrs(r: &[Register]) -> kvm_xcrs {
- let mut kvm = kvm_xcrs {
- nr_xcrs: r.len() as u32,
- ..Default::default()
- };
- for (i, &xcr) in r.iter().enumerate() {
- kvm.xcrs[i].xcr = xcr.id;
- kvm.xcrs[i].value = xcr.value;
- }
- kvm
-}
-
-fn to_kvm_msrs(vec: &[Register]) -> Vec<kvm_msrs> {
- let vec: Vec<kvm_msr_entry> = vec
- .iter()
- .map(|e| kvm_msr_entry {
- index: e.id,
- data: e.value,
- ..Default::default()
- })
- .collect();
-
- let mut msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(vec.len());
- // SAFETY:
- // 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.
- unsafe {
- msrs[0]
- .entries
- .as_mut_slice(vec.len())
- .copy_from_slice(&vec);
- }
- msrs[0].nmsrs = vec.len() as u32;
- msrs
-}
-
#[cfg(test)]
mod tests {
use super::*;
diff --git a/hypervisor/src/whpx/vcpu.rs b/hypervisor/src/whpx/vcpu.rs
index b3826ef13..fd3e93bb2 100644
--- a/hypervisor/src/whpx/vcpu.rs
+++ b/hypervisor/src/whpx/vcpu.rs
@@ -4,6 +4,7 @@
use core::ffi::c_void;
use std::arch::x86_64::CpuidResult;
+use std::collections::BTreeMap;
use std::convert::TryInto;
use std::mem::size_of;
use std::sync::Arc;
@@ -20,9 +21,6 @@ use windows::Win32::Foundation::WHV_E_INSUFFICIENT_BUFFER;
use super::types::*;
use super::*;
-use crate::get_tsc_offset_from_msr;
-use crate::set_tsc_offset_via_msr;
-use crate::set_tsc_value_via_msr;
use crate::CpuId;
use crate::CpuIdEntry;
use crate::DebugRegs;
@@ -30,7 +28,6 @@ use crate::Fpu;
use crate::HypervHypercall;
use crate::IoOperation;
use crate::IoParams;
-use crate::Register;
use crate::Regs;
use crate::Sregs;
use crate::Vcpu;
@@ -1083,83 +1080,71 @@ impl VcpuX86_64 for WhpxVcpu {
}
/// Gets the VCPU extended control registers.
- fn get_xcrs(&self) -> Result<Vec<Register>> {
- const REG_NAMES: [WHV_REGISTER_NAME; 1] = [WHV_REGISTER_NAME_WHvX64RegisterXCr0];
- let mut xcrs: [WHV_REGISTER_VALUE; 1] = Default::default();
+ fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>> {
+ const REG_NAME: WHV_REGISTER_NAME = WHV_REGISTER_NAME_WHvX64RegisterXCr0;
+ let mut reg_value = WHV_REGISTER_VALUE::default();
// safe because we have enough space for all the registers in whpx_regs
check_whpx!(unsafe {
WHvGetVirtualProcessorRegisters(
self.vm_partition.partition,
self.index,
- &REG_NAMES as *const WHV_REGISTER_NAME,
- REG_NAMES.len() as u32,
- xcrs.as_mut_ptr(),
+ &REG_NAME,
+ /* RegisterCount */ 1,
+ &mut reg_value,
)
})?;
- let reg = Register {
- id: 0, // whpx only supports xcr0
- // safe because the union value, reg64, is safe to pull out assuming
- // kernel filled in the xcrs properly.
- value: unsafe { xcrs[0].Reg64 },
- };
- Ok(vec![reg])
+
+ // safe because the union value, reg64, is safe to pull out assuming
+ // kernel filled in the xcrs properly.
+ let xcr0 = unsafe { reg_value.Reg64 };
+
+ // whpx only supports xcr0
+ let xcrs = BTreeMap::from([(0, xcr0)]);
+ Ok(xcrs)
}
- /// Sets the VCPU extended control registers.
- fn set_xcrs(&self, xcrs: &[Register]) -> Result<()> {
- const REG_NAMES: [WHV_REGISTER_NAME; 1] = [WHV_REGISTER_NAME_WHvX64RegisterXCr0];
- let whpx_xcrs = xcrs
- .iter()
- .filter_map(|reg| match reg.id {
- 0 => Some(WHV_REGISTER_VALUE { Reg64: reg.value }),
- _ => None,
- })
- .collect::<Vec<WHV_REGISTER_VALUE>>();
- if !whpx_xcrs.is_empty() {
- // safe because we have enough space for all the registers in whpx_xcrs
- check_whpx!(unsafe {
- WHvSetVirtualProcessorRegisters(
- self.vm_partition.partition,
- self.index,
- &REG_NAMES as *const WHV_REGISTER_NAME,
- REG_NAMES.len() as u32,
- whpx_xcrs.as_ptr(),
- )
- })
- } else {
+ /// Sets a VCPU extended control register.
+ fn set_xcr(&self, xcr_index: u32, value: u64) -> Result<()> {
+ if xcr_index != 0 {
// invalid xcr register provided
- Err(Error::new(ENXIO))
+ return Err(Error::new(EINVAL));
}
+
+ const REG_NAME: WHV_REGISTER_NAME = WHV_REGISTER_NAME_WHvX64RegisterXCr0;
+ let reg_value = WHV_REGISTER_VALUE { Reg64: value };
+ // safe because we have enough space for all the registers in whpx_xcrs
+ check_whpx!(unsafe {
+ WHvSetVirtualProcessorRegisters(
+ self.vm_partition.partition,
+ self.index,
+ &REG_NAME,
+ /* RegisterCount */ 1,
+ &reg_value,
+ )
+ })
}
- /// Gets the model-specific registers. `msrs` specifies the MSR indexes to be queried, and
- /// on success contains their indexes and values.
- fn get_msrs(&self, msrs: &mut Vec<Register>) -> Result<()> {
- let msr_names = get_msr_names(msrs);
- let mut buffer: Vec<WHV_REGISTER_VALUE> = vec![Default::default(); msr_names.len()];
+ /// Gets the value of a single model-specific register.
+ fn get_msr(&self, msr_index: u32) -> Result<u64> {
+ let msr_name = get_msr_name(msr_index).ok_or(Error::new(libc::ENOENT))?;
+ let mut msr_value = WHV_REGISTER_VALUE::default();
// safe because we have enough space for all the registers in whpx_regs
check_whpx!(unsafe {
WHvGetVirtualProcessorRegisters(
self.vm_partition.partition,
self.index,
- msr_names.as_ptr(),
- msr_names.len() as u32,
- buffer.as_mut_ptr(),
+ &msr_name,
+ /* RegisterCount */ 1,
+ &mut msr_value,
)
})?;
- msrs.retain(|&msr| VALID_MSRS.contains_key(&msr.id));
- if buffer.len() != msrs.len() {
- panic!("mismatch of valid whpx msr registers and returned registers");
- }
- for (i, msr) in msrs.iter_mut().enumerate() {
- // safe because Reg64 will be a valid union value for all msrs
- msr.value = unsafe { buffer[i].Reg64 };
- }
- Ok(())
+ // safe because Reg64 will be a valid union value
+ let value = unsafe { msr_value.Reg64 };
+ Ok(value)
}
- fn get_all_msrs(&self) -> Result<Vec<Register>> {
+ fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>> {
// Note that some members of VALID_MSRS cannot be fetched from WHPX with
// WHvGetVirtualProcessorRegisters per the HTLFS, so we enumerate all of
// permitted MSRs here.
@@ -1172,73 +1157,41 @@ impl VcpuX86_64 for WhpxVcpu {
// handled by the generic x86_64 VCPU snapshot/restore. Non snapshot
// consumers should use get/set_tsc_adjust to access the adjust register
// if needed.
- let mut registers = vec![
- Register {
- id: MSR_EFER,
- ..Default::default()
- },
- Register {
- id: MSR_KERNEL_GS_BASE,
- ..Default::default()
- },
- Register {
- id: MSR_APIC_BASE,
- ..Default::default()
- },
- Register {
- id: MSR_SYSENTER_CS,
- ..Default::default()
- },
- Register {
- id: MSR_SYSENTER_EIP,
- ..Default::default()
- },
- Register {
- id: MSR_SYSENTER_ESP,
- ..Default::default()
- },
- Register {
- id: MSR_STAR,
- ..Default::default()
- },
- Register {
- id: MSR_LSTAR,
- ..Default::default()
- },
- Register {
- id: MSR_CSTAR,
- ..Default::default()
- },
- Register {
- id: MSR_SFMASK,
- ..Default::default()
- },
+ const MSRS_TO_SAVE: &[u32] = &[
+ MSR_EFER,
+ MSR_KERNEL_GS_BASE,
+ MSR_APIC_BASE,
+ MSR_SYSENTER_CS,
+ MSR_SYSENTER_EIP,
+ MSR_SYSENTER_ESP,
+ MSR_STAR,
+ MSR_LSTAR,
+ MSR_CSTAR,
+ MSR_SFMASK,
];
- self.get_msrs(&mut registers)?;
- Ok(registers)
- }
- /// Sets the model-specific registers.
- fn set_msrs(&self, msrs: &[Register]) -> Result<()> {
- let msr_names = get_msr_names(msrs);
- let whpx_msrs = msrs
+ let registers = MSRS_TO_SAVE
.iter()
- .filter_map(|msr| {
- if VALID_MSRS.contains_key(&msr.id) {
- Some(WHV_REGISTER_VALUE { Reg64: msr.value })
- } else {
- None
- }
+ .map(|msr_index| {
+ let value = self.get_msr(*msr_index)?;
+ Ok((*msr_index, value))
})
- .collect::<Vec<WHV_REGISTER_VALUE>>();
+ .collect::<Result<BTreeMap<u32, u64>>>()?;
+ Ok(registers)
+ }
+
+ /// Sets the value of a single model-specific register.
+ fn set_msr(&self, msr_index: u32, value: u64) -> Result<()> {
+ let msr_name = get_msr_name(msr_index).ok_or(Error::new(libc::ENOENT))?;
+ let msr_value = WHV_REGISTER_VALUE { Reg64: value };
check_whpx!(unsafe {
WHvSetVirtualProcessorRegisters(
self.vm_partition.partition,
self.index,
- msr_names.as_ptr(),
- msr_names.len() as u32,
- whpx_msrs.as_ptr(),
+ &msr_name,
+ /* RegisterCount */ 1,
+ &msr_value,
)
})
}
@@ -1311,23 +1264,6 @@ impl VcpuX86_64 for WhpxVcpu {
Err(Error::new(ENOENT))
}
- fn get_tsc_offset(&self) -> Result<u64> {
- // Note: WHV_REGISTER_NAME_WHvX64RegisterTscVirtualOffset register appears to no longer be
- // supported, so we use the MSR path. (It also didn't work in 19H2 either, always returning
- // zero on get.)
- get_tsc_offset_from_msr(self)
- }
-
- fn set_tsc_offset(&self, offset: u64) -> Result<()> {
- // Note: WHV_REGISTER_NAME_WHvX64RegisterTscVirtualOffset register appears to no longer be
- // supported, so we use the MSR path.
- set_tsc_offset_via_msr(self, offset)
- }
-
- fn set_tsc_value(&self, value: u64) -> Result<()> {
- set_tsc_value_via_msr(self, value)
- }
-
fn restore_timekeeping(&self, host_tsc_reference_moment: u64, tsc_offset: u64) -> Result<()> {
// Set the guest TSC such that it has the same TSC_OFFSET as it did at
// the moment it was snapshotted. This is required for virtio-pvclock
@@ -1338,10 +1274,8 @@ impl VcpuX86_64 for WhpxVcpu {
}
}
-fn get_msr_names(msrs: &[Register]) -> Vec<WHV_REGISTER_NAME> {
- msrs.iter()
- .filter_map(|reg| VALID_MSRS.get(&reg.id).copied())
- .collect::<Vec<WHV_REGISTER_NAME>>()
+fn get_msr_name(msr_index: u32) -> Option<WHV_REGISTER_NAME> {
+ VALID_MSRS.get(&msr_index).copied()
}
// run calls are tested with the integration tests since the full vcpu needs to be setup for it.
@@ -1503,15 +1437,14 @@ mod tests {
return;
}
- let mut xcrs = vcpu.get_xcrs().unwrap();
- xcrs[0].value = 1;
- vcpu.set_xcrs(&xcrs).unwrap();
- let xcrs2 = vcpu.get_xcrs().unwrap();
- assert_eq!(xcrs[0].value, xcrs2[0].value);
+ vcpu.set_xcr(0, 1).unwrap();
+ let xcrs = vcpu.get_xcrs().unwrap();
+ let xcr0 = xcrs.get(&0).unwrap();
+ assert_eq!(*xcr0, 1);
}
#[test]
- fn set_msrs() {
+ fn set_msr() {
if !Whpx::is_enabled() {
return;
}
@@ -1521,21 +1454,14 @@ mod tests {
let vm = new_vm(cpu_count, mem);
let vcpu = vm.create_vcpu(0).expect("failed to create vcpu");
- let mut msrs = vec![Register {
- id: MSR_KERNEL_GS_BASE,
- value: 42,
- }];
- vcpu.set_msrs(&msrs).unwrap();
+ vcpu.set_msr(MSR_KERNEL_GS_BASE, 42).unwrap();
- msrs[0].value = 0;
- vcpu.get_msrs(&mut msrs).unwrap();
- assert_eq!(msrs.len(), 1);
- assert_eq!(msrs[0].id, MSR_KERNEL_GS_BASE);
- assert_eq!(msrs[0].value, 42);
+ let gs_base = vcpu.get_msr(MSR_KERNEL_GS_BASE).unwrap();
+ assert_eq!(gs_base, 42);
}
#[test]
- fn get_msrs() {
+ fn get_msr() {
if !Whpx::is_enabled() {
return;
}
@@ -1545,20 +1471,12 @@ mod tests {
let vm = new_vm(cpu_count, mem);
let vcpu = vm.create_vcpu(0).expect("failed to create vcpu");
- let mut msrs = vec![
- // This one should succeed
- Register {
- id: MSR_TSC,
- ..Default::default()
- },
- // This one will fail to fetch
- Register {
- id: MSR_TSC + 1,
- ..Default::default()
- },
- ];
- vcpu.get_msrs(&mut msrs).unwrap();
- assert_eq!(msrs.len(), 1);
+ // This one should succeed
+ let _value = vcpu.get_msr(MSR_TSC).unwrap();
+
+ // This one will fail to fetch
+ vcpu.get_msr(MSR_TSC + 1)
+ .expect_err("invalid MSR index should fail");
}
#[test]
@@ -1598,22 +1516,18 @@ mod tests {
assert_eq!(sregs.cr0 & X86_CR0_PG, X86_CR0_PG);
assert_eq!(sregs.cr4 & X86_CR4_PAE, X86_CR4_PAE);
- let mut efer_reg = vec![Register {
- id: MSR_EFER,
- value: 0,
- }];
- vcpu.get_msrs(&mut efer_reg).expect("failed to get msrs");
- assert_eq!(efer_reg[0].value, EFER_LMA | EFER_LME);
+ let efer = vcpu.get_msr(MSR_EFER).expect("failed to get msr");
+ assert_eq!(efer, EFER_LMA | EFER_LME);
// Enable SCE via set_msrs
- efer_reg[0].value |= EFER_SCE;
- vcpu.set_msrs(&efer_reg).expect("failed to set msrs");
+ vcpu.set_msr(MSR_EFER, efer | EFER_SCE)
+ .expect("failed to set msr");
// Verify that setting stuck
let sregs = vcpu.get_sregs().expect("failed to get sregs");
assert_eq!(sregs.efer, EFER_SCE | EFER_LME | EFER_LMA);
- vcpu.get_msrs(&mut efer_reg).expect("failed to get msrs");
- assert_eq!(efer_reg[0].value, EFER_SCE | EFER_LME | EFER_LMA);
+ let new_efer = vcpu.get_msr(MSR_EFER).expect("failed to get msr");
+ assert_eq!(new_efer, EFER_SCE | EFER_LME | EFER_LMA);
}
#[test]
@@ -1665,7 +1579,7 @@ mod tests {
// Our MSR buffer is init'ed to zeros in the registers. The APIC base will be non-zero, so
// by asserting that we know the MSR fetch actually did get us data.
- let apic_base = all_msrs.iter().find(|reg| reg.id == MSR_APIC_BASE).unwrap();
- assert_ne!(apic_base.value, 0);
+ let apic_base = all_msrs.get(&MSR_APIC_BASE).unwrap();
+ assert_ne!(*apic_base, 0);
}
}
diff --git a/hypervisor/src/x86_64.rs b/hypervisor/src/x86_64.rs
index 20a99c301..fb8a77489 100644
--- a/hypervisor/src/x86_64.rs
+++ b/hypervisor/src/x86_64.rs
@@ -5,9 +5,8 @@
use std::arch::x86_64::CpuidResult;
#[cfg(any(unix, feature = "haxm", feature = "whpx"))]
use std::arch::x86_64::__cpuid;
-#[cfg(any(unix, feature = "haxm", feature = "whpx"))]
use std::arch::x86_64::_rdtsc;
-use std::collections::HashMap;
+use std::collections::BTreeMap;
use std::collections::HashSet;
use anyhow::Context;
@@ -111,10 +110,10 @@ pub trait VcpuX86_64: Vcpu {
fn set_debugregs(&self, debugregs: &DebugRegs) -> Result<()>;
/// Gets the VCPU extended control registers.
- fn get_xcrs(&self) -> Result<Vec<Register>>;
+ fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>>;
- /// Sets the VCPU extended control registers.
- fn set_xcrs(&self, xcrs: &[Register]) -> Result<()>;
+ /// Sets a VCPU extended control register.
+ fn set_xcr(&self, xcr: u32, value: u64) -> Result<()>;
/// Gets the VCPU x87 FPU, MMX, XMM, YMM and MXCSR registers.
fn get_xsave(&self) -> Result<Xsave>;
@@ -130,15 +129,14 @@ pub trait VcpuX86_64: Vcpu {
/// snapshotting.
fn set_interrupt_state(&self, data: serde_json::Value) -> Result<()>;
- /// Gets the model-specific registers. `msrs` specifies the MSR indexes to be queried, and
- /// on success contains their indexes and values.
- fn get_msrs(&self, msrs: &mut Vec<Register>) -> Result<()>;
+ /// Gets a single model-specific register's value.
+ fn get_msr(&self, msr_index: u32) -> Result<u64>;
/// Gets the model-specific registers. Returns all the MSRs for the VCPU.
- fn get_all_msrs(&self) -> Result<Vec<Register>>;
+ fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>>;
- /// Sets the model-specific registers.
- fn set_msrs(&self, msrs: &[Register]) -> Result<()>;
+ /// Sets a single model-specific register's value.
+ fn set_msr(&self, msr_index: u32, value: u64) -> Result<()>;
/// Sets up the data returned by the CPUID instruction.
fn set_cpuid(&self, cpuid: &CpuId) -> Result<()>;
@@ -154,15 +152,62 @@ pub trait VcpuX86_64: Vcpu {
/// will then set the appropriate registers on the vcpu.
fn handle_cpuid(&mut self, entry: &CpuIdEntry) -> Result<()>;
- /// Get the guest->host TSC offset
- fn get_tsc_offset(&self) -> Result<u64>;
+ /// Gets the guest->host TSC offset.
+ ///
+ /// The default implementation uses [`VcpuX86_64::get_msr()`] to read the guest TSC.
+ fn get_tsc_offset(&self) -> Result<u64> {
+ // SAFETY:
+ // Safe because _rdtsc takes no arguments
+ let host_before_tsc = unsafe { _rdtsc() };
+
+ // get guest TSC value from our hypervisor
+ let guest_tsc = self.get_msr(crate::MSR_IA32_TSC)?;
+
+ // SAFETY:
+ // Safe because _rdtsc takes no arguments
+ let host_after_tsc = unsafe { _rdtsc() };
- /// Set the guest->host TSC offset
- fn set_tsc_offset(&self, offset: u64) -> Result<()>;
+ // Average the before and after host tsc to get the best value
+ let host_tsc = ((host_before_tsc as u128 + host_after_tsc as u128) / 2) as u64;
+
+ Ok(guest_tsc.wrapping_sub(host_tsc))
+ }
+
+ /// Sets the guest->host TSC offset.
+ ///
+ /// The default implementation uses [`VcpuX86_64::set_tsc_value()`] to set the TSC value.
+ ///
+ /// It sets TSC_OFFSET (VMCS / CB field) by setting the TSC MSR to the current
+ /// host TSC value plus the desired offset. We rely on the fact that hypervisors
+ /// determine the value of TSC_OFFSET by computing TSC_OFFSET = new_tsc_value
+ /// - _rdtsc() = _rdtsc() + offset - _rdtsc() ~= offset. Note that the ~= is
+ /// important: this is an approximate operation, because the two _rdtsc() calls
+ /// are separated by at least a few ticks.
+ ///
+ /// Note: TSC_OFFSET, host TSC, guest TSC, and TSC MSR are all different
+ /// concepts.
+ /// * When a guest executes rdtsc, the value (guest TSC) returned is host_tsc * TSC_MULTIPLIER +
+ /// TSC_OFFSET + TSC_ADJUST.
+ /// * The TSC MSR is a special MSR that when written to by the host, will cause TSC_OFFSET to be
+ /// set accordingly by the hypervisor.
+ /// * When the guest *writes* to TSC MSR, it actually changes the TSC_ADJUST MSR *for the
+ /// guest*. Generally this is only happens if the guest is trying to re-zero or synchronize
+ /// TSCs.
+ fn set_tsc_offset(&self, offset: u64) -> Result<()> {
+ // SAFETY: _rdtsc takes no arguments.
+ let host_tsc = unsafe { _rdtsc() };
+ self.set_tsc_value(host_tsc.wrapping_add(offset))
+ }
/// Sets the guest TSC exactly to the provided value.
- /// Required for snapshotting.
- fn set_tsc_value(&self, value: u64) -> Result<()>;
+ ///
+ /// The default implementation sets the guest's TSC by writing the value to the MSR directly.
+ ///
+ /// See [`VcpuX86_64::set_tsc_offset()`] for an explanation of how this value is actually read
+ /// by the guest after being set.
+ fn set_tsc_value(&self, value: u64) -> Result<()> {
+ self.set_msr(crate::MSR_IA32_TSC, value)
+ }
/// Some hypervisors require special handling to restore timekeeping when
/// a snapshot is restored. They are provided with a host TSC reference
@@ -213,19 +258,16 @@ pub trait VcpuX86_64: Vcpu {
self.set_regs(&snapshot.regs)?;
self.set_sregs(&snapshot.sregs)?;
self.set_debugregs(&snapshot.debug_regs)?;
- self.set_xcrs(&snapshot.xcrs)?;
-
- let mut msrs = HashMap::new();
- for reg in self.get_all_msrs()? {
- msrs.insert(reg.id, reg.value);
+ for (xcr_index, value) in &snapshot.xcrs {
+ self.set_xcr(*xcr_index, *value)?;
}
- for &msr in snapshot.msrs.iter() {
- if Some(&msr.value) == msrs.get(&msr.id) {
+ for (msr_index, value) in snapshot.msrs.iter() {
+ if self.get_msr(*msr_index) == Ok(*value) {
continue; // no need to set MSR since the values are the same.
}
- if let Err(e) = self.set_msrs(&[msr]) {
- if msr_allowlist.contains(&msr.id) {
+ if let Err(e) = self.set_msr(*msr_index, *value) {
+ if msr_allowlist.contains(msr_index) {
warn!(
"Failed to set MSR. MSR might not be supported in this kernel. Err: {}",
e
@@ -252,8 +294,8 @@ pub struct VcpuSnapshot {
regs: Regs,
sregs: Sregs,
debug_regs: DebugRegs,
- xcrs: Vec<Register>,
- msrs: Vec<Register>,
+ xcrs: BTreeMap<u32, u64>,
+ msrs: BTreeMap<u32, u64>,
xsave: Xsave,
hypervisor_data: serde_json::Value,
tsc_offset: u64,
@@ -264,69 +306,6 @@ impl_downcast!(VcpuX86_64);
// TSC MSR
pub const MSR_IA32_TSC: u32 = 0x00000010;
-/// Implementation of get_tsc_offset that uses VcpuX86_64::get_msrs.
-#[cfg(any(unix, feature = "haxm", feature = "whpx"))]
-pub(crate) fn get_tsc_offset_from_msr(vcpu: &impl VcpuX86_64) -> Result<u64> {
- let mut regs = vec![Register {
- id: crate::MSR_IA32_TSC,
- value: 0,
- }];
-
- // SAFETY:
- // Safe because _rdtsc takes no arguments
- let host_before_tsc = unsafe { _rdtsc() };
-
- // get guest TSC value from our hypervisor
- vcpu.get_msrs(&mut regs)?;
-
- // SAFETY:
- // Safe because _rdtsc takes no arguments
- let host_after_tsc = unsafe { _rdtsc() };
-
- // Average the before and after host tsc to get the best value
- let host_tsc = ((host_before_tsc as u128 + host_after_tsc as u128) / 2) as u64;
-
- Ok(regs[0].value.wrapping_sub(host_tsc))
-}
-
-/// Implementation of set_tsc_offset that uses VcpuX86_64::get_msrs.
-///
-/// It sets TSC_OFFSET (VMCS / CB field) by setting the TSC MSR to the current
-/// host TSC value plus the desired offset. We rely on the fact that hypervisors
-/// determine the value of TSC_OFFSET by computing TSC_OFFSET = new_tsc_value
-/// - _rdtsc() = _rdtsc() + offset - _rdtsc() ~= offset. Note that the ~= is
-/// important: this is an approximate operation, because the two _rdtsc() calls
-/// are separated by at least a few ticks.
-///
-/// Note: TSC_OFFSET, host TSC, guest TSC, and TSC MSR are all different
-/// concepts.
-/// * When a guest executes rdtsc, the value (guest TSC) returned is host_tsc * TSC_MULTIPLIER +
-/// TSC_OFFSET + TSC_ADJUST.
-/// * The TSC MSR is a special MSR that when written to by the host, will cause TSC_OFFSET to be set
-/// accordingly by the hypervisor.
-/// * When the guest *writes* to TSC MSR, it actually changes the TSC_ADJUST MSR *for the guest*.
-/// Generally this is only happens if the guest is trying to re-zero or synchronize TSCs.
-#[cfg(any(unix, feature = "haxm", feature = "whpx"))]
-pub(crate) fn set_tsc_offset_via_msr(vcpu: &impl VcpuX86_64, offset: u64) -> Result<()> {
- // SAFETY: _rdtsc takes no arguments.
- let host_tsc = unsafe { _rdtsc() };
- set_tsc_value_via_msr(vcpu, host_tsc.wrapping_add(offset))
-}
-
-/// Sets the guest's TSC by writing the value to the MSR directly. See
-/// [`set_tsc_offset_via_msr`] for an explanation of how this value is actually
-/// read by the guest after being set.
-#[cfg(any(unix, feature = "haxm", feature = "whpx"))]
-pub(crate) fn set_tsc_value_via_msr(vcpu: &impl VcpuX86_64, value: u64) -> Result<()> {
- let regs = vec![Register {
- id: crate::MSR_IA32_TSC,
- value,
- }];
-
- // set guest TSC value from our hypervisor
- vcpu.set_msrs(&regs)
-}
-
/// Gets host cpu max physical address bits.
#[cfg(any(unix, feature = "haxm", feature = "whpx"))]
pub(crate) fn host_phys_addr_bits() -> u8 {
@@ -355,7 +334,7 @@ pub struct VcpuInitX86_64 {
pub fpu: Fpu,
/// Machine-specific registers.
- pub msrs: Vec<Register>,
+ pub msrs: BTreeMap<u32, u64>,
}
/// Hold the CPU feature configurations that are needed to setup a vCPU.
@@ -1002,13 +981,6 @@ pub struct DebugRegs {
pub dr7: u64,
}
-/// State of one VCPU register. Currently used for MSRs and XCRs.
-#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
-pub struct Register {
- pub id: u32,
- pub value: u64,
-}
-
/// The hybrid type for intel hybrid CPU.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CpuHybridType {
diff --git a/hypervisor/tests/hypervisor_virtualization.rs b/hypervisor/tests/hypervisor_virtualization.rs
new file mode 100644
index 000000000..3a1440aaf
--- /dev/null
+++ b/hypervisor/tests/hypervisor_virtualization.rs
@@ -0,0 +1,111 @@
+// Copyright 2024 The ChromiumOS Authors
+// 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")]
+#![cfg(any(feature = "whpx", feature = "gvm", feature = "haxm", unix))]
+
+use hypervisor::*;
+use vm_memory::GuestAddress;
+use vm_memory::GuestMemory;
+
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_kvm_minimal_virtualization() {
+ use hypervisor::kvm::*;
+ test_minimal_virtualization(|guest_mem| {
+ let kvm = Kvm::new().expect("failed to create kvm");
+ let vm = KvmVm::new(&kvm, guest_mem, Default::default()).expect("failed to create vm");
+ (kvm, vm)
+ });
+}
+
+#[test]
+#[cfg(all(windows, feature = "haxm"))]
+fn test_haxm_minimal_virtualization() {
+ use hypervisor::haxm::*;
+ test_minimal_virtualization(|guest_mem| {
+ let haxm = Haxm::new().expect("failed to create haxm");
+ let vm = HaxmVm::new(&haxm, guest_mem).expect("failed to create vm");
+ (haxm, vm)
+ });
+}
+
+#[test]
+#[cfg(feature = "gvm")]
+fn test_gvm_minimal_virtualization() {
+ use hypervisor::gvm::*;
+ test_minimal_virtualization(|guest_mem| {
+ let gvm = Gvm::new().expect("failed to create gvm");
+ let vm = GvmVm::new(&gvm, guest_mem).expect("failed to create vm");
+ (gvm, vm)
+ });
+}
+
+#[test]
+#[cfg(all(windows, feature = "whpx"))]
+fn test_whpx_minimal_virtualization() {
+ use hypervisor::whpx::*;
+ test_minimal_virtualization(|guest_mem| {
+ let whpx = Whpx::new().expect("failed to create whpx");
+ let vm = WhpxVm::new(&whpx, 1, guest_mem, CpuId::new(0), false, None)
+ .expect("failed to create vm");
+ (whpx, vm)
+ });
+}
+
+// This runs a minimal program under virtualization.
+// It should require only the ability to execute instructions under virtualization, physical
+// memory, the ability to get and set some guest VM registers, and intercepting HLT.
+fn test_minimal_virtualization<CreateVm, HypervisorT, VmT>(create_vm: CreateVm)
+where
+ CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT),
+ HypervisorT: Hypervisor,
+ VmT: VmX86_64,
+{
+ /*
+ 0x0000000000000002: 01 D8 add ax, bx
+ 0x0000000000000002: F4 hlt
+ */
+
+ let code: [u8; 3] = [0x01, 0xd8, 0xf4];
+ let mem_size = 0x2000;
+ let load_addr = GuestAddress(0x1000);
+
+ let guest_mem =
+ GuestMemory::new(&[(GuestAddress(0), mem_size)]).expect("failed to create guest mem");
+ guest_mem
+ .write_at_addr(&code[..], load_addr)
+ .expect("failed to write to guest memory");
+
+ let (_, vm) = create_vm(guest_mem);
+ let mut vcpu = vm.create_vcpu(0).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 vcpu_regs = Regs {
+ rip: load_addr.offset(),
+ rflags: 2,
+ rax: 1,
+ rbx: 2,
+ ..Default::default()
+ };
+ vcpu.set_regs(&vcpu_regs).expect("set regs failed");
+
+ loop {
+ match vcpu.run().expect("run failed") {
+ VcpuExit::Hlt => {
+ break;
+ }
+ // Continue on external interrupt or signal
+ VcpuExit::Intr => continue,
+ r => panic!("unexpected exit reason: {:?}", r),
+ }
+ }
+
+ let regs: Regs = vcpu.get_regs().expect("failed to get regs");
+ assert_eq!(regs.rax, 3);
+}
diff --git a/hypervisor/tests/kvm/x86_64.rs b/hypervisor/tests/kvm/x86_64.rs
index c83d5de17..b8b45154a 100644
--- a/hypervisor/tests/kvm/x86_64.rs
+++ b/hypervisor/tests/kvm/x86_64.rs
@@ -25,7 +25,6 @@ use hypervisor::PitChannelState;
use hypervisor::PitRWMode;
use hypervisor::PitRWState;
use hypervisor::PitState;
-use hypervisor::Register;
use hypervisor::TriggerMode;
use hypervisor::Vm;
use hypervisor::VmCap;
@@ -365,68 +364,51 @@ fn xcrs() {
let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
let vm = KvmVm::new(&kvm, gm, Default::default()).unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
- let mut xcrs = vcpu.get_xcrs().unwrap();
- xcrs[0].value = 1;
- vcpu.set_xcrs(&xcrs).unwrap();
- let xcrs2 = vcpu.get_xcrs().unwrap();
- assert_eq!(xcrs[0].value, xcrs2[0].value);
+ vcpu.set_xcr(0, 1).unwrap();
+ let xcrs = vcpu.get_xcrs().unwrap();
+ let xcr0 = xcrs.get(&0).unwrap();
+ assert_eq!(*xcr0, 1);
}
#[test]
-fn get_msrs() {
+fn get_msr() {
let kvm = Kvm::new().unwrap();
let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
let vm = KvmVm::new(&kvm, gm, Default::default()).unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
- let mut msrs = vec![
- // This one should succeed
- Register {
- id: 0x0000011e,
- ..Default::default()
- },
- // This one will fail to fetch
- Register {
- id: 0xffffffff,
- ..Default::default()
- },
- ];
- vcpu.get_msrs(&mut msrs).unwrap();
- assert_eq!(msrs.len(), 1);
+
+ // This one should succeed
+ let _value = vcpu.get_msr(0x0000011e).unwrap();
+
+ // This one will fail to fetch
+ vcpu.get_msr(0xffffffff)
+ .expect_err("invalid MSR index should fail");
}
#[test]
-fn set_msrs() {
+fn set_msr() {
let kvm = Kvm::new().unwrap();
let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
let vm = KvmVm::new(&kvm, gm, Default::default()).unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
const MSR_TSC_AUX: u32 = 0xc0000103;
- let mut msrs = vec![Register {
- id: MSR_TSC_AUX,
- value: 42,
- }];
- vcpu.set_msrs(&msrs).unwrap();
-
- msrs[0].value = 0;
- vcpu.get_msrs(&mut msrs).unwrap();
- assert_eq!(msrs.len(), 1);
- assert_eq!(msrs[0].id, MSR_TSC_AUX);
- assert_eq!(msrs[0].value, 42);
+ vcpu.set_msr(MSR_TSC_AUX, 42).unwrap();
+ let msr_tsc_aux = vcpu.get_msr(MSR_TSC_AUX).unwrap();
+ assert_eq!(msr_tsc_aux, 42);
}
#[test]
-fn set_msrs_unsupported() {
+fn set_msr_unsupported() {
let kvm = Kvm::new().unwrap();
let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
let vm = KvmVm::new(&kvm, gm, Default::default()).unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
- let msrs = vec![Register {
- id: u32::MAX,
- value: u64::MAX,
- }];
- assert_eq!(vcpu.set_msrs(&msrs), Err(base::Error::new(libc::EPERM)));
+ assert_eq!(
+ vcpu.set_msr(u32::MAX, u64::MAX),
+ Err(base::Error::new(libc::EPERM))
+ );
}
#[test]
diff --git a/hypervisor/tests/tsc_offsets.rs b/hypervisor/tests/tsc_offsets.rs
index f27048617..d35e3272a 100644
--- a/hypervisor/tests/tsc_offsets.rs
+++ b/hypervisor/tests/tsc_offsets.rs
@@ -177,11 +177,8 @@ fn test_tsc_offset_run(
vcpu.set_regs(&vcpu_regs).expect("set regs failed");
if let Some(value) = set_msr {
- vcpu.set_msrs(&[Register {
- id: 0x00000010,
- value,
- }])
- .expect("set_msrs should not fail");
+ vcpu.set_msr(0x00000010, value)
+ .expect("set_msr should not fail");
}
if let Some(offset) = set_offset {
diff --git a/infra/README.recipes.md b/infra/README.recipes.md
index fd96b5720..d85ef1593 100644
--- a/infra/README.recipes.md
+++ b/infra/README.recipes.md
@@ -174,19 +174,19 @@ This recipe requires ambient luci authentication. To test locally run:
&mdash; **def [RunSteps](/infra/recipes/update_chromeos_merges.py#14)(api):**
-[depot_tools/recipe_modules/bot_update]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-bot_update
-[depot_tools/recipe_modules/depot_tools]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-depot_tools
-[depot_tools/recipe_modules/gclient]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-gclient
-[depot_tools/recipe_modules/git]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-git
-[depot_tools/recipe_modules/gsutil]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-gsutil
-[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-buildbucket
-[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-cipd
-[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-context
-[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-file
-[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-json
-[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-path
-[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-platform
-[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-properties
-[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-raw_io
-[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-step
-[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/recipe_engine/recipe_api.py#471
+[depot_tools/recipe_modules/bot_update]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-bot_update
+[depot_tools/recipe_modules/depot_tools]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-depot_tools
+[depot_tools/recipe_modules/gclient]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-gclient
+[depot_tools/recipe_modules/git]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-git
+[depot_tools/recipe_modules/gsutil]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-gsutil
+[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-buildbucket
+[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-cipd
+[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-context
+[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-file
+[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-json
+[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-path
+[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-platform
+[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-properties
+[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-raw_io
+[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-step
+[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/recipe_engine/recipe_api.py#471
diff --git a/infra/config/generated/realms.cfg b/infra/config/generated/realms.cfg
index 029481579..ad6a1a903 100644
--- a/infra/config/generated/realms.cfg
+++ b/infra/config/generated/realms.cfg
@@ -7,6 +7,10 @@
realms {
name: "@root"
bindings {
+ role: "role/buildbucket.owner"
+ principals: "group:mdb/crosvm-acl-luci-admin"
+ }
+ bindings {
role: "role/buildbucket.reader"
principals: "group:all"
}
diff --git a/infra/config/main.star b/infra/config/main.star
index 9885642a4..fa6f316f2 100755
--- a/infra/config/main.star
+++ b/infra/config/main.star
@@ -28,6 +28,7 @@ luci.project(
"role/swarming.poolOwner",
"role/swarming.poolUser",
"role/swarming.taskTriggerer",
+ "role/buildbucket.owner",
],
groups = "mdb/crosvm-acl-luci-admin",
),
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg
index fd728dca2..ec7d3746e 100644
--- a/infra/config/recipes.cfg
+++ b/infra/config/recipes.cfg
@@ -20,12 +20,12 @@
"deps": {
"depot_tools": {
"branch": "refs/heads/main",
- "revision": "e545830db2fb823da1217b142cb5be10bf45d575",
+ "revision": "5a584405cae6e1fd08a6054ce9ba61b645b4d591",
"url": "https://chromium.googlesource.com/chromium/tools/depot_tools.git"
},
"recipe_engine": {
"branch": "refs/heads/main",
- "revision": "3868908180614e35f4b57e321e5e6f1de6adf7dc",
+ "revision": "f967fa68fd4bd3e76140de7ffa449fa3a45a72f4",
"url": "https://chromium.googlesource.com/infra/luci/recipes-py.git"
}
},
diff --git a/io_uring/Android.bp b/io_uring/Android.bp
index 9786b3485..dc4efe19d 100644
--- a/io_uring/Android.bp
+++ b/io_uring/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/jail/Android.bp b/jail/Android.bp
index 765f02703..dd72dcb45 100644
--- a/jail/Android.bp
+++ b/jail/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/kernel_cmdline/Android.bp b/kernel_cmdline/Android.bp
index eb80e5895..7fcb23798 100644
--- a/kernel_cmdline/Android.bp
+++ b/kernel_cmdline/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/kernel_loader/Android.bp b/kernel_loader/Android.bp
index 0499835e6..d3fc0df56 100644
--- a/kernel_loader/Android.bp
+++ b/kernel_loader/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/kvm/Android.bp b/kvm/Android.bp
index 4d2ad431c..d87e60fcc 100644
--- a/kvm/Android.bp
+++ b/kvm/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/kvm_sys/Android.bp b/kvm_sys/Android.bp
index a0d53573d..e693e6b33 100644
--- a/kvm_sys/Android.bp
+++ b/kvm_sys/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/libcras_stub/Android.bp b/libcras_stub/Android.bp
index 3f8f7d43b..dd00bdabb 100644
--- a/libcras_stub/Android.bp
+++ b/libcras_stub/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/linux_input_sys/Android.bp b/linux_input_sys/Android.bp
index 3e8284e15..44ee82c5a 100644
--- a/linux_input_sys/Android.bp
+++ b/linux_input_sys/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/metrics/Android.bp b/metrics/Android.bp
index 87c2d10ff..275ec22f8 100644
--- a/metrics/Android.bp
+++ b/metrics/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
@@ -24,6 +25,7 @@ rust_library {
"libanyhow",
"libbase_rust",
"libcfg_if",
+ "libmetrics_events",
"libmetrics_generic",
"libserde",
"libsync_rust",
@@ -49,6 +51,7 @@ rust_test {
"libanyhow",
"libbase_rust",
"libcfg_if",
+ "libmetrics_events",
"libmetrics_generic",
"libserde",
"libsync_rust",
diff --git a/metrics/Cargo.toml b/metrics/Cargo.toml
index dfb9b5e39..078509cdd 100644
--- a/metrics/Cargo.toml
+++ b/metrics/Cargo.toml
@@ -10,6 +10,7 @@ base = { path = "../base" }
cfg-if = "*"
serde = { version = "1", features = ["derive"] }
sync = { path = "../common/sync" }
+metrics_events = { path = "../metrics_events" }
metrics_product = { path = "../vendor/generic/metrics", package = "metrics_generic" }
[target.'cfg(windows)'.dependencies]
diff --git a/metrics/src/controller.rs b/metrics/src/controller.rs
index c3eb6fd24..ac64e9269 100644
--- a/metrics/src/controller.rs
+++ b/metrics/src/controller.rs
@@ -6,16 +6,14 @@
use anyhow::Result;
use base::info;
-use base::warn;
use base::EventToken;
-use base::Tube;
+use base::RecvTube;
-use crate::MetricsRequest;
use crate::RequestHandler;
/// Runs the metrics controller.
pub struct MetricsController {
- pub(crate) agents: Vec<Tube>,
+ pub(crate) agents: Vec<RecvTube>,
handler: RequestHandler,
pub(crate) closed_tubes: usize,
}
@@ -30,7 +28,7 @@ pub(crate) enum MetricsControllerToken {
}
impl MetricsController {
- pub fn new(agents: Vec<Tube>) -> Self {
+ pub fn new(agents: Vec<RecvTube>) -> Self {
Self {
agents,
handler: RequestHandler::new(),
@@ -46,13 +44,8 @@ impl MetricsController {
}
/// Handles a tube that has indicated it has data ready to read.
- pub(crate) fn on_tube_readable(&self, client: &Tube) {
- match client.recv::<MetricsRequest>() {
- Ok(req) => self.handler.handle_request(req),
- Err(e) => {
- warn!("unexpected error receiving agent metrics request: {}", e)
- }
- }
+ pub(crate) fn on_tube_readable(&self, client: &RecvTube) {
+ self.handler.handle_tube_readable(client)
}
/// Handles a closed connection, and returns a bool indicating
diff --git a/metrics/src/lib.rs b/metrics/src/lib.rs
index dd561ca11..a996d82f3 100644
--- a/metrics/src/lib.rs
+++ b/metrics/src/lib.rs
@@ -16,7 +16,7 @@ mod local_stats;
pub mod sys;
pub use controller::MetricsController;
-pub use metrics_product::MetricEventType;
+pub use metrics_events::MetricEventType;
pub use metrics_product::*;
pub type RequestHandler = MetricsRequestHandler;
diff --git a/metrics/src/sys.rs b/metrics/src/sys.rs
index e0696cf17..64f09e4e2 100644
--- a/metrics/src/sys.rs
+++ b/metrics/src/sys.rs
@@ -6,6 +6,7 @@ cfg_if::cfg_if! {
if #[cfg(windows)] {
pub mod windows;
pub use windows::*;
+ pub use metrics_events::sys::windows::*;
} else if #[cfg(any(target_os = "android", target_os = "linux"))] {
pub(crate) mod linux;
}
diff --git a/metrics_events/Android.bp b/metrics_events/Android.bp
new file mode 100644
index 000000000..87a087eb8
--- /dev/null
+++ b/metrics_events/Android.bp
@@ -0,0 +1,26 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
+// Content before the first "rust_*" or "genrule" module is preserved.
+
+package {
+ default_applicable_licenses: ["external_crosvm_license"],
+}
+
+rust_library {
+ name: "libmetrics_events",
+ defaults: ["crosvm_inner_defaults"],
+ host_supported: true,
+ crate_name: "metrics_events",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.1.0",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libanyhow",
+ "libcfg_if",
+ "libmetrics_events_generic",
+ "libserde",
+ ],
+ aliases: ["metrics_events_generic:metrics_events_product"],
+}
diff --git a/metrics_events/Cargo.toml b/metrics_events/Cargo.toml
new file mode 100644
index 000000000..498b09a71
--- /dev/null
+++ b/metrics_events/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "metrics_events"
+version = "0.1.0"
+authors = ["The ChromiumOS Authors"]
+edition = "2021"
+
+[dependencies]
+anyhow = "*"
+cfg-if = "*"
+serde = { version = "1", features = ["derive"] }
+metrics_events_product = { path = "../vendor/generic/metrics_events", package = "metrics_events_generic" }
+
+[target.'cfg(windows)'.dependencies]
+win_util = { path = "../win_util" }
diff --git a/metrics_events/src/event_types.rs b/metrics_events/src/event_types.rs
new file mode 100644
index 000000000..f1fde88c4
--- /dev/null
+++ b/metrics_events/src/event_types.rs
@@ -0,0 +1,54 @@
+// Copyright 2024 The ChromiumOS Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use metrics_events_product::MetricEventType as VendorMetricEventType;
+use serde::Deserialize;
+use serde::Serialize;
+
+#[cfg(windows)]
+use crate::sys::windows::WaveFormatDetails;
+
+// TODO(mikehoyle): Create a way to generate these directly from the
+// proto for a single source-of-truth.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum MetricEventType {
+ CpuUsage,
+ MemoryUsage,
+ Fps,
+ JankyFps,
+ NetworkTxRate,
+ NetworkRxRate,
+ Interrupts,
+ FrameTime,
+ EmulatorGraphicsFreeze,
+ EmulatorGraphicsUnfreeze,
+ EmulatorGfxstreamVkAbortReason,
+ ChildProcessExit {
+ exit_code: u32,
+ #[cfg(windows)]
+ process_type: win_util::ProcessType,
+ },
+ ReadIo,
+ WriteIo,
+ #[cfg(windows)]
+ AudioFormatRequestOk(WaveFormatDetails),
+ #[cfg(windows)]
+ AudioFormatModifiedOk(WaveFormatDetails),
+ #[cfg(windows)]
+ AudioFormatFailed(WaveFormatDetails),
+ TscCoresOutOfSync,
+ NetworkTxRateSummarized,
+ NetworkRxRateSummarized,
+ DllLoaded(String),
+ GraphicsHangRenderThread,
+ GraphicsHangSyncThread,
+ AudioNoopStreamForced,
+ AudioPlaybackError,
+ RtcWakeup,
+ VirtioWakeup {
+ virtio_id: u32,
+ },
+ Other(i64),
+ Vendor(VendorMetricEventType),
+}
diff --git a/metrics_events/src/lib.rs b/metrics_events/src/lib.rs
new file mode 100644
index 000000000..3411cfb7d
--- /dev/null
+++ b/metrics_events/src/lib.rs
@@ -0,0 +1,10 @@
+// Copyright 2024 The ChromiumOS Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+mod event_types;
+pub mod sys;
+
+pub use event_types::MetricEventType;
+pub use metrics_events_product::MetricEventType as VendorMetricEventType;
+pub use metrics_events_product::RecordDetails;
diff --git a/vendor/generic/metrics/src/sys.rs b/metrics_events/src/sys.rs
index 10b70883f..10b70883f 100644
--- a/vendor/generic/metrics/src/sys.rs
+++ b/metrics_events/src/sys.rs
diff --git a/metrics_events/src/sys/windows.rs b/metrics_events/src/sys/windows.rs
new file mode 100644
index 000000000..0b972f988
--- /dev/null
+++ b/metrics_events/src/sys/windows.rs
@@ -0,0 +1,76 @@
+// Copyright 2024 The ChromiumOS Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use serde::Deserialize;
+use serde::Serialize;
+
+#[derive(Clone, Debug, Serialize, Deserialize, Default)]
+pub struct WaveFormatDetails {
+ // Format requested by WASAPI `GetMixFormat` system call.
+ pub requested: Option<WaveFormat>,
+ // Originally the requested wave format that's modified by the emulator. Only
+ // populated if the emulator decides the requested wave format should not be
+ // used.
+ pub modified: Option<WaveFormat>,
+ // Format that is valid and closest matching to the modified format, if the
+ // modified was rejected. Should only be populated if modified is also
+ // non-null and was rejected by WASAPI `IsFormatSupported` system call.
+ pub closest_matched: Option<WaveFormat>,
+}
+
+// Defines the format of waveformat audio data. This information is used by
+// WASAPI to determine how to process the audio playback data coming from the
+// emulator.
+//
+// The fields in the structure come from WAVEFORMATEXTENSIBLE of win32 api.
+// https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatextensible
+#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
+pub struct WaveFormat {
+ // Ex. 65534 (Maps to WAVE_FORMAT_EXTENSIBLE)
+ pub format_tag: i32,
+ // Number of channels.
+ pub channels: i32,
+ // Sample rate in Hz. Ex: 48000
+ pub samples_per_sec: i32,
+ // Required average data-transfer rate for the format tag. Usually this will
+ // be samples_per_sec * block_align, since the format tag is usually
+ // WAVE_FORMAT_IEEE_FLOAT or it's extensible and SubFormat is
+ // KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.
+ pub avg_bytes_per_sec: i32,
+ // Minimum atomic unit of data based on the format_tag. Usually this will
+ // just be bits_per_samples * channels.
+ pub block_align: i32,
+ // Bits used per sample. Must be a multiple of 8.
+ pub bits_per_sample: i32,
+ // Size in bytes of extra information appended to WAVEFORMATEX struct.
+ pub size_bytes: i32,
+
+ // The next fields are part of the WAVEFORMATEXTENSIBLE struct. They will only
+ // be non-null if format_tag is WAVE_FORMAT_EXTENSIBLE.
+
+ // Bit depth. Can be any value. Ex. bits_per_sample is 24,
+ // but samples is 20. Note: This value is a union, so it could mean something
+ // slightly different, but most likely won't. Refer to doc for more info.
+ pub samples: Option<i32>,
+ // Bitmask mapping channels in stream to speaker positions.
+ // Ex. 3 ( SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT )
+ pub channel_mask: Option<i64>,
+ // Similar to format_tag, but for WAVEFORMATEXTENSIBLE structs.
+ pub sub_format: Option<WaveFormatSubFormat>,
+}
+
+// Subformat GUID mapping:
+// https://github.com/retep998/winapi-rs/blob/2f76bdea3a79817ccfab496fbd1786d5a697387b/src/shared/ksmedia.rs
+#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
+pub enum WaveFormatSubFormat {
+ Invalid,
+ Analog,
+ Pcm,
+ IeeeFloat,
+ Drm,
+ ALaw,
+ MuLaw,
+ Adpcm,
+ Mpeg,
+}
diff --git a/net_sys/Android.bp b/net_sys/Android.bp
index c874f12dc..be808c61f 100644
--- a/net_sys/Android.bp
+++ b/net_sys/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/net_util/Android.bp b/net_util/Android.bp
index ded81b091..af80e9383 100644
--- a/net_util/Android.bp
+++ b/net_util/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/power_monitor/Android.bp b/power_monitor/Android.bp
index 84d59fd61..6dad7a4c2 100644
--- a/power_monitor/Android.bp
+++ b/power_monitor/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/protos/Android.bp b/protos/Android.bp
index 3014b47c8..3c72b0e76 100644
--- a/protos/Android.bp
+++ b/protos/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/resources/Android.bp b/resources/Android.bp
index 5ebf2cab7..a7c7354d6 100644
--- a/resources/Android.bp
+++ b/resources/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/riscv64/Android.bp b/riscv64/Android.bp
index bcf321061..d964f7925 100644
--- a/riscv64/Android.bp
+++ b/riscv64/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/rutabaga_gfx/Android.bp b/rutabaga_gfx/Android.bp
index 0c64f2ef2..b355aa2ae 100644
--- a/rutabaga_gfx/Android.bp
+++ b/rutabaga_gfx/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/serde_keyvalue/Android.bp b/serde_keyvalue/Android.bp
index c31364171..92080a453 100644
--- a/serde_keyvalue/Android.bp
+++ b/serde_keyvalue/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/serde_keyvalue/serde_keyvalue_derive/Android.bp b/serde_keyvalue/serde_keyvalue_derive/Android.bp
index 7d4376402..15d9f0f6e 100644
--- a/serde_keyvalue/serde_keyvalue_derive/Android.bp
+++ b/serde_keyvalue/serde_keyvalue_derive/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/src/crosvm/cmdline.rs b/src/crosvm/cmdline.rs
index 04f343dcd..c6cf27e38 100644
--- a/src/crosvm/cmdline.rs
+++ b/src/crosvm/cmdline.rs
@@ -289,7 +289,11 @@ pub struct MakeRTCommand {
#[derive(FromArgs)]
#[argh(subcommand, name = "resume")]
-/// Resumes the crosvm instance
+/// Resumes the crosvm instance. No-op if already running. When starting crosvm with `--restore`,
+/// this command can be used to wait until the restore is complete
+// Implementation note: All the restore work happens before crosvm becomes able to process incoming
+// commands, so really all commands can be used to wait for restore to complete, but few are side
+// effect free.
pub struct ResumeCommand {
#[argh(positional, arg_name = "VM_SOCKET")]
/// VM Socket path
@@ -1334,9 +1338,13 @@ pub struct RunCommand {
/// Possible key values:
/// backend=(2d|virglrenderer|gfxstream) - Which backend to
/// use for virtio-gpu (determining rendering protocol)
+ /// max_num_displays=INT - The maximum number of concurrent
+ /// virtual displays in this VM. This must not exceed
+ /// VIRTIO_GPU_MAX_SCANOUTS (i.e. 16).
/// displays=[[GpuDisplayParameters]] - The list of virtual
- /// displays to create. See the possible key values for
- /// GpuDisplayParameters in the section below.
+ /// displays to create when booting this VM. Displays may
+ /// be hotplugged after booting. See the possible key
+ /// values for GpuDisplayParameters in the section below.
/// context-types=LIST - The list of supported context
/// types, separated by ':' (default: no contexts enabled)
/// width=INT - The width of the virtual display connected
diff --git a/src/crosvm/gpu_config.rs b/src/crosvm/gpu_config.rs
index 650f4ae47..c8ef82184 100644
--- a/src/crosvm/gpu_config.rs
+++ b/src/crosvm/gpu_config.rs
@@ -2,10 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#[cfg(feature = "kiwi")]
-use base::warn;
-#[cfg(feature = "kiwi")]
-use battlestar::process_invariants;
+use devices::virtio::gpu::VIRTIO_GPU_MAX_SCANOUTS;
use devices::virtio::GpuDisplayMode;
use devices::virtio::GpuDisplayParameters;
#[cfg(feature = "gfxstream")]
@@ -103,37 +100,27 @@ pub(crate) fn validate_gpu_config(cfg: &mut Config) -> Result<(), String> {
));
}
+ if gpu_parameters.max_num_displays < 1
+ || gpu_parameters.max_num_displays > VIRTIO_GPU_MAX_SCANOUTS as u32
+ {
+ return Err(format!(
+ "`max_num_displays` must be in range [1, {}]",
+ VIRTIO_GPU_MAX_SCANOUTS
+ ));
+ }
+ if gpu_parameters.display_params.len() as u32 > gpu_parameters.max_num_displays {
+ return Err(format!(
+ "Provided more `display_params` ({}) than `max_num_displays` ({})",
+ gpu_parameters.display_params.len(),
+ gpu_parameters.max_num_displays
+ ));
+ }
+
// Add a default display if no display is specified.
if gpu_parameters.display_params.is_empty() {
gpu_parameters.display_params.push(Default::default());
}
- // Process invariants are not written to the static `PROCESS_INVARIANTS` yet, so instead of
- // calling `phenotype!(kiwi_emulator_feature, get_enable_4k_uhd_resolution)`, we have to
- // load it by ourselves.
- // TODO(b/276909432): The BSS should read the experiment flags and specify the virtual
- // display size, and then we can remove this workaround.
- #[cfg(feature = "kiwi")]
- let is_4k_uhd_enabled = match process_invariants::load_invariants(
- &cfg.process_invariants_data_handle,
- &cfg.process_invariants_data_size,
- ) {
- Ok(invariants) => invariants
- .get_flag_snapshot()
- .get_features()
- .kiwi_emulator_feature
- .clone()
- .unwrap_or_default()
- .get_enable_4k_uhd_resolution(),
- Err(e) => {
- warn!(
- "Failed to load process invariants, will not enable 4k UHD: {}",
- e
- );
- false
- }
- };
- #[cfg(not(feature = "kiwi"))]
let is_4k_uhd_enabled = false;
let (width, height) =
gpu_parameters.display_params[0].get_virtual_display_size_4k_uhd(is_4k_uhd_enabled);
@@ -172,6 +159,81 @@ mod tests {
}
#[test]
+ fn parse_gpu_options_max_num_displays() {
+ {
+ let gpu_params = parse_gpu_options("").unwrap();
+ assert_eq!(gpu_params.max_num_displays, VIRTIO_GPU_MAX_SCANOUTS as u32);
+ }
+ {
+ let gpu_params = parse_gpu_options("max-num-displays=5").unwrap();
+ assert_eq!(gpu_params.max_num_displays, 5);
+ }
+ {
+ let command = crate::crosvm::cmdline::RunCommand::from_args(
+ &[],
+ &["--gpu", "max-num-displays=0", "/dev/null"],
+ )
+ .unwrap();
+ assert!(Config::try_from(command).is_err());
+ }
+ {
+ let command = crate::crosvm::cmdline::RunCommand::from_args(
+ &[],
+ &[
+ "--gpu",
+ format!("max-num-displays={}", VIRTIO_GPU_MAX_SCANOUTS + 1).as_str(),
+ "/dev/null",
+ ],
+ )
+ .unwrap();
+ assert!(Config::try_from(command).is_err());
+ }
+ // TODO(b/332910955): Remove the single display restriction on Windows and enable this test.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ {
+ let command = crate::crosvm::cmdline::RunCommand::from_args(
+ &[],
+ &[
+ "--gpu",
+ "max-num-displays=1,displays=[[mode=windowed[1920,1080]],\
+ [mode=windowed[1280,720]]]",
+ "/dev/null",
+ ],
+ )
+ .unwrap();
+ assert!(Config::try_from(command).is_err());
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ {
+ let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
+ &[],
+ &[
+ "--gpu",
+ "max-num-displays=3,displays=[[mode=windowed[1920,1080]],\
+ [mode=windowed[1280,720]]]",
+ "/dev/null",
+ ],
+ )
+ .unwrap()
+ .try_into()
+ .unwrap();
+
+ let gpu_params = config.gpu_parameters.unwrap();
+
+ assert_eq!(gpu_params.max_num_displays, 3);
+ assert_eq!(gpu_params.display_params.len(), 2);
+ assert_eq!(
+ gpu_params.display_params[0].mode,
+ GpuDisplayMode::Windowed(1920, 1080)
+ );
+ assert_eq!(
+ gpu_params.display_params[1].mode,
+ GpuDisplayMode::Windowed(1280, 720)
+ );
+ }
+ }
+
+ #[test]
fn parse_gpu_options_mode() {
use devices::virtio::gpu::GpuMode;
diff --git a/src/crosvm/sys/linux.rs b/src/crosvm/sys/linux.rs
index 2bfdb442b..f94096cde 100644
--- a/src/crosvm/sys/linux.rs
+++ b/src/crosvm/sys/linux.rs
@@ -164,6 +164,7 @@ use jail_warden::JailWardenImpl;
#[cfg(feature = "pci-hotplug")]
use jail_warden::PermissiveJailWarden;
use libc;
+use metrics::MetricsController;
use minijail::Minijail;
#[cfg(feature = "pci-hotplug")]
use pci_hotplug_manager::PciHotPlugManager;
@@ -1681,6 +1682,10 @@ where
// access to those files will not be possible.
info!("crosvm entering multiprocess mode");
}
+
+ let (metrics_send, metrics_recv) = Tube::directional_pair().context("metrics tube")?;
+ metrics::initialize(metrics_send);
+
#[cfg(all(feature = "pci-hotplug", feature = "swap"))]
let swap_device_helper = match &swap_controller {
Some(swap_controller) => Some(swap_controller.create_device_helper()?),
@@ -2211,6 +2216,7 @@ where
guest_suspended_cvar,
#[cfg(feature = "pvclock")]
pvclock_host_tube,
+ metrics_recv,
)
}
@@ -3277,6 +3283,7 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
#[cfg(feature = "registered_events")] reg_evt_rdtube: RecvTube,
guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
#[cfg(feature = "pvclock")] pvclock_host_tube: Option<Tube>,
+ metrics_tube: RecvTube,
) -> Result<ExitState> {
#[derive(EventToken)]
enum Token {
@@ -3626,6 +3633,21 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
.context("static device setup complete")?;
}
+ let metrics_thread = if metrics::is_initialized() {
+ Some(
+ std::thread::Builder::new()
+ .name("metrics_thread".into())
+ .spawn(move || {
+ if let Err(e) = MetricsController::new(vec![metrics_tube]).run() {
+ error!("Metrics controller error: {:?}", e);
+ }
+ })
+ .context("metrics thread failed")?,
+ )
+ } else {
+ None
+ };
+
let mut exit_state = ExitState::Stop;
let mut pvpanic_code = PvPanicCode::Unknown;
#[cfg(feature = "registered_events")]
@@ -3955,6 +3977,20 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
// control sockets are closed when this function exits.
mem::drop(linux);
+ // Drop the hotplug manager to tell the warden process to exit before we try to join
+ // the metrics thread.
+ #[cfg(feature = "pci-hotplug")]
+ mem::drop(hotplug_manager);
+
+ // All our children should have exited by now, so closing our fd should
+ // terminate metrics. Then join so that everything gets flushed.
+ metrics::get_destructor().cleanup();
+ if let Some(metrics_thread) = metrics_thread {
+ if let Err(e) = metrics_thread.join() {
+ error!("failed to exit irq handler thread: {:?}", e);
+ }
+ }
+
stdin()
.set_canon_mode()
.expect("failed to restore canonical mode for terminal");
@@ -4400,6 +4436,7 @@ fn jail_and_start_vu_device<T: VirtioDeviceBuilder>(
base::syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
let jail_type = VirtioDeviceType::VhostUser;
diff --git a/src/crosvm/sys/linux/jail_warden.rs b/src/crosvm/sys/linux/jail_warden.rs
index 3cd9e3420..cee109ed6 100644
--- a/src/crosvm/sys/linux/jail_warden.rs
+++ b/src/crosvm/sys/linux/jail_warden.rs
@@ -88,6 +88,7 @@ impl JailWardenImpl {
let mut keep_rds = Vec::new();
syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
let (main_tube, worker_tube) = Tube::pair()?;
keep_rds.push(worker_tube.as_raw_descriptor());
#[cfg(feature = "swap")]
@@ -201,6 +202,7 @@ fn jail_worker_process(
let mut keep_rds = vec![];
syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
keep_rds.extend(pci_device.keep_rds());
let proxy_device_primitive = ChildProcIntf::new(
pci_device,
diff --git a/src/crosvm/sys/windows/broker.rs b/src/crosvm/sys/windows/broker.rs
index 55f8d287d..c5ba51821 100644
--- a/src/crosvm/sys/windows/broker.rs
+++ b/src/crosvm/sys/windows/broker.rs
@@ -102,8 +102,6 @@ use gpu_display::EventDevice;
use gpu_display::WindowProcedureThread;
#[cfg(feature = "gpu")]
use gpu_display::WindowProcedureThreadBuilder;
-use metrics::protos::event_details::EmulatorChildProcessExitDetails;
-use metrics::protos::event_details::RecordDetails;
use metrics::MetricEventType;
#[cfg(all(feature = "net", feature = "slirp"))]
use net_util::slirp::sys::windows::SlirpStartupConfig;
@@ -399,12 +397,10 @@ impl Drop for ChildCleanup {
if self.process_type != ProcessType::Metrics {
let exit_code = self.child.wait();
if let Ok(Some(exit_code)) = exit_code {
- let mut details = RecordDetails::new();
- let mut exit_details = EmulatorChildProcessExitDetails::new();
- exit_details.set_exit_code(exit_code as u32);
- exit_details.set_process_type(self.process_type.into());
- details.emulator_child_process_exit_details = Some(exit_details).into();
- metrics::log_event_with_details(MetricEventType::ChildProcessExit, &details);
+ metrics::log_event(MetricEventType::ChildProcessExit {
+ exit_code: exit_code as u32,
+ process_type: self.process_type,
+ });
} else {
error!(
"Failed to log exit code for process: {:?}, couldn't get exit code",
@@ -466,10 +462,11 @@ fn get_log_path(cfg: &Config, file_name: &str) -> Option<PathBuf> {
/// IMPORTANT NOTE: The metrics process must receive the client (second) end
/// of the Tube pair in order to allow the connection to be properly shut
/// down without data loss.
-fn metrics_tube_pair(metric_tubes: &mut Vec<Tube>) -> Result<Tube> {
+fn metrics_tube_pair(metric_tubes: &mut Vec<RecvTube>) -> Result<SendTube> {
// TODO(nkgold): as written, this Tube pair won't handle ancillary data properly because the
// PIDs are not set properly at each end; however, we don't plan to send ancillary data.
- let (t1, t2) = Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
+ let (t1, t2) =
+ Tube::directional_pair().exit_context(Exit::CreateTube, "failed to create tube")?;
metric_tubes.push(t2);
Ok(t1)
}
@@ -1193,7 +1190,7 @@ fn start_up_block_backends(
exit_events: &mut Vec<Event>,
wait_ctx: &mut WaitContext<Token>,
main_child: &mut ChildProcess,
- metric_tubes: &mut Vec<Tube>,
+ metric_tubes: &mut Vec<RecvTube>,
#[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants,
) -> Result<Vec<ChildProcess>> {
let mut block_children = Vec::new();
@@ -1421,7 +1418,7 @@ fn start_up_net_backend(
wait_ctx: &mut WaitContext<Token>,
cfg: &mut Config,
log_args: &LogArgs,
- metric_tubes: &mut Vec<Tube>,
+ metric_tubes: &mut Vec<RecvTube>,
#[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants,
) -> Result<(ChildProcess, ChildProcess)> {
let (host_pipe, guest_pipe) = named_pipes::pair_with_buffer_size(
@@ -1609,7 +1606,7 @@ fn start_up_snd(
main_child: &mut ChildProcess,
children: &mut HashMap<u32, ChildCleanup>,
wait_ctx: &mut WaitContext<Token>,
- metric_tubes: &mut Vec<Tube>,
+ metric_tubes: &mut Vec<RecvTube>,
#[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants,
) -> Result<ChildProcess> {
// Extract the backend config from the sound config, so it can run elsewhere.
@@ -1802,7 +1799,7 @@ fn start_up_gpu(
main_child: &mut ChildProcess,
children: &mut HashMap<u32, ChildCleanup>,
wait_ctx: &mut WaitContext<Token>,
- metric_tubes: &mut Vec<Tube>,
+ metric_tubes: &mut Vec<RecvTube>,
wndproc_thread_builder: WindowProcedureThreadBuilder,
#[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants,
) -> Result<ChildProcess> {
diff --git a/src/sys/windows/main.rs b/src/sys/windows/main.rs
index 44360777b..7775b5eb3 100644
--- a/src/sys/windows/main.rs
+++ b/src/sys/windows/main.rs
@@ -22,8 +22,6 @@ use broker_ipc::CommonChildStartupArgs;
use crosvm_cli::sys::windows::exit::Exit;
use crosvm_cli::sys::windows::exit::ExitContext;
use crosvm_cli::sys::windows::exit::ExitContextAnyhow;
-use metrics::protos::event_details::EmulatorDllDetails;
-use metrics::protos::event_details::RecordDetails;
use metrics::MetricEventType;
#[cfg(feature = "slirp")]
use net_util::slirp::sys::windows::SlirpStartupConfig;
@@ -110,11 +108,7 @@ pub fn sandbox_lower_token() -> Result<()> {
}
fn report_dll_loaded(dll_name: String) {
- let mut dll_load_details = EmulatorDllDetails::new();
- dll_load_details.set_dll_base_name(dll_name);
- let mut details = RecordDetails::new();
- details.emulator_dll_details = Some(dll_load_details).into();
- metrics::log_event_with_details(MetricEventType::DllLoaded, &details);
+ metrics::log_event(MetricEventType::DllLoaded(dll_name));
}
pub fn get_library_watcher(
diff --git a/swap/Android.bp b/swap/Android.bp
index 1dc7288e3..742feb72a 100644
--- a/swap/Android.bp
+++ b/swap/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
@@ -27,6 +28,7 @@ rust_library {
"libcros_tracing",
"libjail",
"liblibc",
+ "libmetrics",
"libnum_cpus",
"libonce_cell",
"libserde",
diff --git a/swap/Cargo.toml b/swap/Cargo.toml
index 3833a6e69..86d7e5587 100644
--- a/swap/Cargo.toml
+++ b/swap/Cargo.toml
@@ -22,6 +22,7 @@ base = { path = "../base" }
cfg-if = "*"
cros_tracing = { path = "../cros_tracing" }
jail = { path = "../jail" }
+metrics = { path = "../metrics" }
num_cpus = "*"
once_cell = "*"
remain = "*"
diff --git a/swap/src/controller.rs b/swap/src/controller.rs
index 8322ddb9a..928c2ac19 100644
--- a/swap/src/controller.rs
+++ b/swap/src/controller.rs
@@ -197,6 +197,7 @@ impl SwapController {
syslog::push_descriptors(&mut keep_rds);
cros_tracing::push_descriptors!(&mut keep_rds);
+ metrics::push_descriptors(&mut keep_rds);
keep_rds.extend(guest_memory.as_raw_descriptors());
keep_rds.extend(uffd_factory.as_raw_descriptors());
diff --git a/swap/src/processes.rs b/swap/src/processes.rs
index 0d8916999..b0fef61fc 100644
--- a/swap/src/processes.rs
+++ b/swap/src/processes.rs
@@ -6,11 +6,13 @@
use std::fs::read_to_string;
use std::num::ParseIntError;
+use std::path::Path;
use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
use anyhow::anyhow;
+use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
use base::linux::getpid;
@@ -41,13 +43,20 @@ pub struct ProcessesGuard {
///
/// This must be called from the main process.
pub fn freeze_child_processes(monitor_pid: Pid) -> Result<ProcessesGuard> {
- let guard = ProcessesGuard {
+ let mut guard = ProcessesGuard {
pids: load_descendants(getpid(), monitor_pid)?,
};
- guard.stop_the_world().context("stop the world")?;
+ for _ in 0..3 {
+ guard.stop_the_world().context("stop the world")?;
+ let pids_after = load_descendants(getpid(), monitor_pid)?;
+ if pids_after == guard.pids {
+ return Ok(guard);
+ }
+ guard.pids = pids_after;
+ }
- Ok(guard)
+ bail!("new processes forked while freezing");
}
impl ProcessesGuard {
@@ -139,10 +148,9 @@ fn parse_process_state(text: &str) -> Option<char> {
chars.next()
}
-fn wait_process_stopped(pid: Pid) -> Result<()> {
- let process_stat_path = format!("/proc/{}/stat", pid);
+fn wait_for_task_stopped(task_path: &Path) -> Result<()> {
for _ in 0..10 {
- let stat = read_to_string(&process_stat_path).context("read process status")?;
+ let stat = read_to_string(task_path.join("stat")).context("read process status")?;
if let Some(state) = parse_process_state(&stat) {
if state == 'T' {
return Ok(());
@@ -153,6 +161,14 @@ fn wait_process_stopped(pid: Pid) -> Result<()> {
Err(anyhow!("time out"))
}
+fn wait_process_stopped(pid: Pid) -> Result<()> {
+ let all_tasks = std::fs::read_dir(format!("/proc/{}/task", pid)).context("read tasks")?;
+ for task in all_tasks {
+ wait_for_task_stopped(&task.context("read task entry")?.path()).context("wait for task")?;
+ }
+ Ok(())
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/swap/src/userfaultfd.rs b/swap/src/userfaultfd.rs
index 41b7c41ba..10b9df6e1 100644
--- a/swap/src/userfaultfd.rs
+++ b/swap/src/userfaultfd.rs
@@ -25,6 +25,7 @@ use base::linux::MemoryMappingUnix;
use base::AsRawDescriptor;
use base::AsRawDescriptors;
use base::FromRawDescriptor;
+use base::IntoRawDescriptor;
use base::MappedRegion;
use base::MemoryMapping;
use base::MemoryMappingBuilder;
@@ -396,7 +397,7 @@ impl Userfaultfd {
pub fn try_clone(&self) -> Result<Self> {
let dup_desc = base::clone_descriptor(self).map_err(Error::Clone)?;
// SAFETY: no one owns dup_desc.
- let uffd = unsafe { Self::from_raw_descriptor(dup_desc) };
+ let uffd = Self::from(unsafe { Uffd::from_raw_fd(dup_desc.into_raw_descriptor()) });
Ok(uffd)
}
}
diff --git a/third_party/vmm_vhost/Android.bp b/third_party/vmm_vhost/Android.bp
index b767bc492..13f98717f 100644
--- a/third_party/vmm_vhost/Android.bp
+++ b/third_party/vmm_vhost/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/tools/chromeos/merge_bot b/tools/chromeos/merge_bot
index 1e4a9ae0b..08268a54c 100755
--- a/tools/chromeos/merge_bot
+++ b/tools/chromeos/merge_bot
@@ -212,6 +212,11 @@ def create_merge_commits(
if not commits:
print("Nothing to merge.")
return (0, False)
+ else:
+ commit_authors = git_log(f"HEAD..{revision}", "--pretty=%an").lines()
+ if all(map(lambda x: x == "recipe-roller", commit_authors)):
+ print("All commits are from recipe roller, don't merge yet")
+ return (0, False)
# Create a merge commit for each batch
batches = list(batched(commits, max_size)) if max_size > 0 else [commits]
diff --git a/tools/impl/cros_container/Dockerfile b/tools/impl/cros_container/Dockerfile
index 9bf4de6a4..4ba7d10c9 100644
--- a/tools/impl/cros_container/Dockerfile
+++ b/tools/impl/cros_container/Dockerfile
@@ -38,4 +38,4 @@ RUN repo sync -j 8 --current-branch
RUN --security=insecure cros_sdk --create && rm /home/crosvmdev/chromiumos/.cache/sdks/*
RUN --security=insecure cros_sdk setup_board --board=${BOARD}
-RUN --security=insecure cros_sdk emerge-${BOARD} --update --deep -j$(nproc) crosvm
+RUN --security=insecure cros_sdk emerge-${BOARD} --update --deep -j$(nproc) chromeos-base/crosvm
diff --git a/usb_sys/Android.bp b/usb_sys/Android.bp
index d5d60f8ec..f78cbf725 100644
--- a/usb_sys/Android.bp
+++ b/usb_sys/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/usb_util/Android.bp b/usb_util/Android.bp
index c3979105a..dcd10ce9d 100644
--- a/usb_util/Android.bp
+++ b/usb_util/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vendor/generic/anti_tamper/Android.bp b/vendor/generic/anti_tamper/Android.bp
index 9065f3a59..b13eaa112 100644
--- a/vendor/generic/anti_tamper/Android.bp
+++ b/vendor/generic/anti_tamper/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vendor/generic/broker_ipc/Android.bp b/vendor/generic/broker_ipc/Android.bp
index 13fce8054..bec0cebbc 100644
--- a/vendor/generic/broker_ipc/Android.bp
+++ b/vendor/generic/broker_ipc/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vendor/generic/crash_report/Android.bp b/vendor/generic/crash_report/Android.bp
index 00a636f91..23becbc8d 100644
--- a/vendor/generic/crash_report/Android.bp
+++ b/vendor/generic/crash_report/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vendor/generic/crypto/Android.bp b/vendor/generic/crypto/Android.bp
index 05b69120a..15db0799a 100644
--- a/vendor/generic/crypto/Android.bp
+++ b/vendor/generic/crypto/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vendor/generic/metrics/Android.bp b/vendor/generic/metrics/Android.bp
index e208771ed..42a670288 100644
--- a/vendor/generic/metrics/Android.bp
+++ b/vendor/generic/metrics/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
@@ -24,8 +25,6 @@ rust_library {
rustlibs: [
"libanyhow",
"libbase_rust",
- "libcfg_if",
- "libprotobuf",
- "libserde",
+ "libmetrics_events",
],
}
diff --git a/vendor/generic/metrics/Cargo.toml b/vendor/generic/metrics/Cargo.toml
index b7dd68dc9..b5f57f8b5 100644
--- a/vendor/generic/metrics/Cargo.toml
+++ b/vendor/generic/metrics/Cargo.toml
@@ -12,12 +12,4 @@ collect = []
[dependencies]
anyhow = "*"
base = { path = "../../../base" }
-cfg-if = "*"
-protobuf = "3.2"
-serde = { version = "1", features = ["derive"] }
-
-[target.'cfg(windows)'.dependencies]
-win_util = { path = "../../../win_util" }
-
-[build-dependencies]
-proto_build_tools = { path = "../../../proto_build_tools" }
+metrics_events = { path = "../../../metrics_events" }
diff --git a/vendor/generic/metrics/build.rs b/vendor/generic/metrics/build.rs
deleted file mode 100644
index bf1fe6179..000000000
--- a/vendor/generic/metrics/build.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// 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::path::PathBuf;
-
-fn main() {
- let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
-
- #[cfg(debug_assertions)]
- println!(
- "cargo:rustc-link-search={}\\..\\..\\..\\libs\\debug\\",
- manifest_dir
- );
- #[cfg(not(debug_assertions))]
- println!(
- "cargo:rustc-link-search={}\\..\\..\\..\\libs\\release\\",
- manifest_dir
- );
-
- build_protos(&PathBuf::from(manifest_dir));
-}
-
-fn build_protos(manifest_dir: &PathBuf) {
- let mut event_details_path = manifest_dir.to_owned();
- event_details_path.extend(["protos", "event_details.proto"]);
-
- let mut out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR env does not exist."));
- // ANDROID: b/259142784 - we remove metrics_out subdir b/c cargo2android
- // out_dir.push("metrics_protos");
- proto_build_tools::build_protos(&out_dir, &[event_details_path]);
-}
diff --git a/vendor/generic/metrics/protos/event_details.proto b/vendor/generic/metrics/protos/event_details.proto
deleted file mode 100644
index 60e46f4e9..000000000
--- a/vendor/generic/metrics/protos/event_details.proto
+++ /dev/null
@@ -1,105 +0,0 @@
-// Provides data structures for additional details for metrics events.
-syntax = "proto2";
-
-message RecordDetails {
- reserved 1 to 11, 14 to 18;
- // Additional details about an unexpected exit of a child process within
- // the emulator.
- optional EmulatorChildProcessExitDetails emulator_child_process_exit_details =
- 12;
- // Additional details about wave formats from the Window's host system.
- optional WaveFormatDetails wave_format_details = 13;
- optional EmulatorDllDetails emulator_dll_details = 19;
-}
-
-message WaveFormatDetails {
- // Format requested by WASAPI `GetMixFormat` system call.
- optional WaveFormat requested = 1;
- // Originally the requested wave format that's modified by the emulator. Only
- // populated if the emulator decides the requested wave format should not be
- // used.
- optional WaveFormat modified = 2;
- // Format that is valid and closest matching to the modified format, if the
- // modified was rejected. Should only be populated if modified is also
- // non-null and was rejected by WASAPI `IsFormatSupported` system call.
- optional WaveFormat closest_matched = 3;
-}
-
-// Defines the format of waveformat audio data. This information is used by
-// WASAPI to determine how to process the audio playback data coming from the
-// emulator.
-//
-// The fields in the structure come from WAVEFORMATEXTENSIBLE of win32 api.
-// https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatextensible
-message WaveFormat {
- // Ex. 65534 (Maps to WAVE_FORMAT_EXTENSIBLE)
- optional int32 format_tag = 1;
- // Number of channels.
- optional int32 channels = 2;
- // Sample rate in Hz. Ex: 48000
- optional int32 samples_per_sec = 3;
- // Required average data-transfer rate for the format tag. Usually this will
- // be samples_per_sec * block_align, since the format tag is usually
- // WAVE_FORMAT_IEEE_FLOAT or it's extensible and SubFormat is
- // KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.
- optional int32 avg_bytes_per_sec = 4;
- // Minimum atomic unit of data based on the format_tag. Usually this will
- // just be bits_per_samples * channels.
- optional int32 block_align = 5;
- // Bits used per sample. Must be a multiple of 8.
- optional int32 bits_per_sample = 6;
- // Size in bytes of extra information appended to WAVEFORMATEX struct.
- optional int32 size_bytes = 7;
-
- // The next fields are part of the WAVEFORMATEXTENSIBLE struct. They will only
- // be non-null if format_tag is WAVE_FORMAT_EXTENSIBLE.
-
- // Bit depth. Can be any value. Ex. bits_per_sample is 24,
- // but samples is 20. Note: This value is a union, so it could mean something
- // slightly different, but most likely won't. Refer to doc for more info.
- optional int32 samples = 8;
- // Bitmask mapping channels in stream to speaker positions.
- // Ex. 3 ( SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT )
- optional int64 channel_mask = 9;
- // Similar to format_tag, but for WAVEFORMATEXTENSIBLE structs.
- optional WaveFormatSubFormat sub_format = 10;
-
- // Subformat GUID mapping:
- // https://github.com/retep998/winapi-rs/blob/2f76bdea3a79817ccfab496fbd1786d5a697387b/src/shared/ksmedia.rs
- enum WaveFormatSubFormat {
- KSDATAFORMAT_SUBTYPE_INVALID = 0;
- KSDATAFORMAT_SUBTYPE_ANALOG = 1;
- KSDATAFORMAT_SUBTYPE_PCM = 2;
- KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = 3;
- KSDATAFORMAT_SUBTYPE_DRM = 4;
- KSDATAFORMAT_SUBTYPE_ALAW = 5;
- KSDATAFORMAT_SUBTYPE_MULAW = 6;
- KSDATAFORMAT_SUBTYPE_ADPCM = 7;
- KSDATAFORMAT_SUBTYPE_MPEG = 8;
- }
-}
-
-enum EmulatorProcessType {
- PROCESS_TYPE_UNKNOWN = 0;
- PROCESS_TYPE_MAIN = 1;
- PROCESS_TYPE_BLOCK = 2;
- PROCESS_TYPE_METRICS = 3;
- PROCESS_TYPE_NET = 4;
- PROCESS_TYPE_SLIRP = 5;
- PROCESS_TYPE_GPU = 6;
- PROCESS_TYPE_SOUND = 7;
- PROCESS_TYPE_BROKER = 8;
- PROCESS_TYPE_SPU = 9;
-}
-
-message EmulatorChildProcessExitDetails {
- // The Windows exit code of the child process
- optional uint32 exit_code = 1;
- // The process identifier, as defined by the ProcessType enum in the
- // emulator code.
- optional EmulatorProcessType process_type = 2;
-}
-
-message EmulatorDllDetails {
- optional string dll_base_name = 1;
-}
diff --git a/vendor/generic/metrics/src/client.rs b/vendor/generic/metrics/src/client.rs
index 380cca580..f7e691ac9 100644
--- a/vendor/generic/metrics/src/client.rs
+++ b/vendor/generic/metrics/src/client.rs
@@ -2,17 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-use base::Tube;
+use base::RawDescriptor;
+use base::SendTube;
+use metrics_events::MetricEventType;
+use metrics_events::RecordDetails;
-use crate::protos::event_details::RecordDetails;
-use crate::MetricEventType;
use crate::MetricsClientDestructor;
/// This interface exists to be used and re-implemented by downstream forks. Updates shouldn't be
/// done without ensuring they won't cause breakages in dependent codebases.
-pub fn initialize(_: Tube) {}
+pub fn initialize(_: SendTube) {}
#[cfg(test)]
-pub fn force_initialize(_: Tube) {}
+pub fn force_initialize(_: SendTube) {}
+
+pub fn push_descriptors(_: &mut Vec<RawDescriptor>) {}
+
pub fn get_destructor() -> MetricsClientDestructor {
MetricsClientDestructor::new(|| {})
}
diff --git a/vendor/generic/metrics/src/event_types.rs b/vendor/generic/metrics/src/event_types.rs
deleted file mode 100644
index 64ee5edd5..000000000
--- a/vendor/generic/metrics/src/event_types.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::convert::From;
-use std::convert::TryFrom;
-
-use anyhow::Error;
-use serde::Deserialize;
-use serde::Serialize;
-
-// TODO(mikehoyle): Create a way to generate these directly from the
-// proto for a single source-of-truth.
-#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
-pub enum MetricEventType {
- CpuUsage,
- MemoryUsage,
- Fps,
- JankyFps,
- NetworkTxRate,
- NetworkRxRate,
- Interrupts,
- FrameTime,
- EmulatorGraphicsFreeze,
- EmulatorGraphicsUnfreeze,
- EmulatorGfxstreamVkAbortReason,
- ChildProcessExit,
- ReadIo,
- WriteIo,
- AudioFormatRequestOk,
- AudioFormatModifiedOk,
- AudioFormatFailed,
- TscCoresOutOfSync,
- NetworkTxRateSummarized,
- NetworkRxRateSummarized,
- DllLoaded,
- GraphicsHangRenderThread,
- GraphicsHangSyncThread,
- AudioNoopStreamForced,
- AudioPlaybackError,
- Other(i64),
-}
-
-impl From<MetricEventType> for i64 {
- fn from(event_code: MetricEventType) -> Self {
- match event_code {
- MetricEventType::CpuUsage => 10001,
- MetricEventType::MemoryUsage => 10002,
- MetricEventType::Fps => 10003,
- MetricEventType::JankyFps => 10004,
- MetricEventType::NetworkTxRate => 10005,
- MetricEventType::NetworkRxRate => 10006,
- MetricEventType::Interrupts => 10007,
- MetricEventType::FrameTime => 10008,
- MetricEventType::EmulatorGraphicsFreeze => 10009,
- MetricEventType::EmulatorGraphicsUnfreeze => 10010,
- MetricEventType::EmulatorGfxstreamVkAbortReason => 10011,
- MetricEventType::ChildProcessExit => 10012,
- MetricEventType::ReadIo => 10013,
- MetricEventType::WriteIo => 10014,
- MetricEventType::AudioFormatRequestOk => 10015,
- MetricEventType::AudioFormatModifiedOk => 10016,
- MetricEventType::AudioFormatFailed => 10017,
- MetricEventType::TscCoresOutOfSync => 10018,
- MetricEventType::NetworkTxRateSummarized => 10019,
- MetricEventType::NetworkRxRateSummarized => 10020,
- MetricEventType::DllLoaded => 10021,
- MetricEventType::GraphicsHangRenderThread => 10024,
- MetricEventType::GraphicsHangSyncThread => 10026,
- MetricEventType::AudioNoopStreamForced => 10038,
- MetricEventType::AudioPlaybackError => 10039,
- MetricEventType::Other(code) => code,
- }
- }
-}
-
-impl TryFrom<i64> for MetricEventType {
- type Error = Error;
-
- fn try_from(event_code: i64) -> Result<Self, Self::Error> {
- match event_code {
- 10001 => Ok(MetricEventType::CpuUsage),
- 10002 => Ok(MetricEventType::MemoryUsage),
- 10003 => Ok(MetricEventType::Fps),
- 10004 => Ok(MetricEventType::JankyFps),
- 10005 => Ok(MetricEventType::NetworkTxRate),
- 10006 => Ok(MetricEventType::NetworkRxRate),
- 10007 => Ok(MetricEventType::Interrupts),
- 10008 => Ok(MetricEventType::FrameTime),
- 10009 => Ok(MetricEventType::EmulatorGraphicsFreeze),
- 10010 => Ok(MetricEventType::EmulatorGraphicsUnfreeze),
- 10011 => Ok(MetricEventType::EmulatorGfxstreamVkAbortReason),
- 10012 => Ok(MetricEventType::ChildProcessExit),
- 10013 => Ok(MetricEventType::ReadIo),
- 10014 => Ok(MetricEventType::WriteIo),
- 10015 => Ok(MetricEventType::AudioFormatRequestOk),
- 10016 => Ok(MetricEventType::AudioFormatModifiedOk),
- 10017 => Ok(MetricEventType::AudioFormatFailed),
- 10018 => Ok(MetricEventType::TscCoresOutOfSync),
- 10019 => Ok(MetricEventType::NetworkTxRateSummarized),
- 10020 => Ok(MetricEventType::NetworkRxRateSummarized),
- 10021 => Ok(MetricEventType::DllLoaded),
- 10024 => Ok(MetricEventType::GraphicsHangRenderThread),
- 10026 => Ok(MetricEventType::GraphicsHangSyncThread),
- 10038 => Ok(MetricEventType::AudioNoopStreamForced),
- 10039 => Ok(MetricEventType::AudioPlaybackError),
- _ => Ok(MetricEventType::Other(event_code)),
- }
- }
-}
diff --git a/vendor/generic/metrics/src/lib.rs b/vendor/generic/metrics/src/lib.rs
index 45c30d3c0..da5c81217 100644
--- a/vendor/generic/metrics/src/lib.rs
+++ b/vendor/generic/metrics/src/lib.rs
@@ -6,16 +6,9 @@
//! to log metrics.
mod client;
-mod event_types;
-mod metrics_requests;
mod periodic_logger;
mod request_handler;
-mod sys;
-pub mod protos {
- // ANDROID: b/259142784 - we remove metrics_out subdir b/c cargo2android
- include!(concat!(env!("OUT_DIR"), "/generated.rs"));
-}
mod metrics_cleanup;
use std::time::Duration;
@@ -32,12 +25,11 @@ pub use client::log_high_frequency_descriptor_event;
pub use client::log_histogram_metric;
pub use client::log_metric;
pub use client::merge_session_invariants;
+pub use client::push_descriptors;
pub use client::set_auth_token;
pub use client::set_graphics_api;
pub use client::set_package_name;
-pub use event_types::MetricEventType;
pub use metrics_cleanup::MetricsClientDestructor;
-pub use metrics_requests::MetricsRequest;
pub use periodic_logger::PeriodicLogger;
pub use request_handler::MetricsRequestHandler;
diff --git a/vendor/generic/metrics/src/metrics_requests.rs b/vendor/generic/metrics/src/metrics_requests.rs
deleted file mode 100644
index 33b77f22e..000000000
--- a/vendor/generic/metrics/src/metrics_requests.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-//! Structs used to transport log requests between client processes and the logging controller
-
-use serde::Deserialize;
-use serde::Serialize;
-
-use crate::MetricEventType;
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct LogMetric {
- pub event_code: MetricEventType,
- pub value: i64,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct LogDescriptor {
- pub event_code: MetricEventType,
- pub descriptor: i64,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct LogHighFrequencyDescriptorMetric {
- pub event_code: MetricEventType,
- pub descriptor: i64,
- pub step: i64,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct EventWithSerializedDetails {
- pub event_code: MetricEventType,
- pub serialized_details: Box<[u8]>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub enum MetricsRequest {
- LogDescriptor(LogDescriptor),
- LogEvent(MetricEventType),
- LogMetric(LogMetric),
- LogHistogram(LogMetric),
- SetAuthToken(String),
- SetGraphicsApi(String),
- SetPackageName(String),
- MergeSessionInvariants(Vec<u8>),
- LogHighFrequencyDescriptorMetric(LogHighFrequencyDescriptorMetric),
- LogEventWithSerializedDetails(EventWithSerializedDetails),
-}
diff --git a/vendor/generic/metrics/src/out/event_details.rs b/vendor/generic/metrics/src/out/event_details.rs
deleted file mode 100644
index a44b05c90..000000000
--- a/vendor/generic/metrics/src/out/event_details.rs
+++ /dev/null
@@ -1,1408 +0,0 @@
-// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.21.12
-// @generated
-
-// https://github.com/rust-lang/rust-clippy/issues/702
-#![allow(unknown_lints)]
-#![allow(clippy::all)]
-
-#![allow(unused_attributes)]
-#![cfg_attr(rustfmt, rustfmt::skip)]
-
-#![allow(box_pointers)]
-#![allow(dead_code)]
-#![allow(missing_docs)]
-#![allow(non_camel_case_types)]
-#![allow(non_snake_case)]
-#![allow(non_upper_case_globals)]
-#![allow(trivial_casts)]
-#![allow(unused_results)]
-#![allow(unused_mut)]
-
-//! Generated file from `event_details.proto`
-
-/// Generated files are compatible only with the same version
-/// of protobuf runtime.
-const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
-
-#[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:RecordDetails)
-pub struct RecordDetails {
- // message fields
- // @@protoc_insertion_point(field:RecordDetails.emulator_child_process_exit_details)
- pub emulator_child_process_exit_details: ::protobuf::MessageField<EmulatorChildProcessExitDetails>,
- // @@protoc_insertion_point(field:RecordDetails.wave_format_details)
- pub wave_format_details: ::protobuf::MessageField<WaveFormatDetails>,
- // @@protoc_insertion_point(field:RecordDetails.emulator_dll_details)
- pub emulator_dll_details: ::protobuf::MessageField<EmulatorDllDetails>,
- // special fields
- // @@protoc_insertion_point(special_field:RecordDetails.special_fields)
- pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a RecordDetails {
- fn default() -> &'a RecordDetails {
- <RecordDetails as ::protobuf::Message>::default_instance()
- }
-}
-
-impl RecordDetails {
- pub fn new() -> RecordDetails {
- ::std::default::Default::default()
- }
-
- fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
- let mut fields = ::std::vec::Vec::with_capacity(3);
- let mut oneofs = ::std::vec::Vec::with_capacity(0);
- fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, EmulatorChildProcessExitDetails>(
- "emulator_child_process_exit_details",
- |m: &RecordDetails| { &m.emulator_child_process_exit_details },
- |m: &mut RecordDetails| { &mut m.emulator_child_process_exit_details },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, WaveFormatDetails>(
- "wave_format_details",
- |m: &RecordDetails| { &m.wave_format_details },
- |m: &mut RecordDetails| { &mut m.wave_format_details },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, EmulatorDllDetails>(
- "emulator_dll_details",
- |m: &RecordDetails| { &m.emulator_dll_details },
- |m: &mut RecordDetails| { &mut m.emulator_dll_details },
- ));
- ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RecordDetails>(
- "RecordDetails",
- fields,
- oneofs,
- )
- }
-}
-
-impl ::protobuf::Message for RecordDetails {
- const NAME: &'static str = "RecordDetails";
-
- fn is_initialized(&self) -> bool {
- true
- }
-
- fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
- while let Some(tag) = is.read_raw_tag_or_eof()? {
- match tag {
- 98 => {
- ::protobuf::rt::read_singular_message_into_field(is, &mut self.emulator_child_process_exit_details)?;
- },
- 106 => {
- ::protobuf::rt::read_singular_message_into_field(is, &mut self.wave_format_details)?;
- },
- 154 => {
- ::protobuf::rt::read_singular_message_into_field(is, &mut self.emulator_dll_details)?;
- },
- tag => {
- ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
- },
- };
- }
- ::std::result::Result::Ok(())
- }
-
- // Compute sizes of nested messages
- #[allow(unused_variables)]
- fn compute_size(&self) -> u64 {
- let mut my_size = 0;
- if let Some(v) = self.emulator_child_process_exit_details.as_ref() {
- let len = v.compute_size();
- my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
- }
- if let Some(v) = self.wave_format_details.as_ref() {
- let len = v.compute_size();
- my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
- }
- if let Some(v) = self.emulator_dll_details.as_ref() {
- let len = v.compute_size();
- my_size += 2 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
- }
- my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
- self.special_fields.cached_size().set(my_size as u32);
- my_size
- }
-
- fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
- if let Some(v) = self.emulator_child_process_exit_details.as_ref() {
- ::protobuf::rt::write_message_field_with_cached_size(12, v, os)?;
- }
- if let Some(v) = self.wave_format_details.as_ref() {
- ::protobuf::rt::write_message_field_with_cached_size(13, v, os)?;
- }
- if let Some(v) = self.emulator_dll_details.as_ref() {
- ::protobuf::rt::write_message_field_with_cached_size(19, v, os)?;
- }
- os.write_unknown_fields(self.special_fields.unknown_fields())?;
- ::std::result::Result::Ok(())
- }
-
- fn special_fields(&self) -> &::protobuf::SpecialFields {
- &self.special_fields
- }
-
- fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
- &mut self.special_fields
- }
-
- fn new() -> RecordDetails {
- RecordDetails::new()
- }
-
- fn clear(&mut self) {
- self.emulator_child_process_exit_details.clear();
- self.wave_format_details.clear();
- self.emulator_dll_details.clear();
- self.special_fields.clear();
- }
-
- fn default_instance() -> &'static RecordDetails {
- static instance: RecordDetails = RecordDetails {
- emulator_child_process_exit_details: ::protobuf::MessageField::none(),
- wave_format_details: ::protobuf::MessageField::none(),
- emulator_dll_details: ::protobuf::MessageField::none(),
- special_fields: ::protobuf::SpecialFields::new(),
- };
- &instance
- }
-}
-
-impl ::protobuf::MessageFull for RecordDetails {
- fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
- static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
- descriptor.get(|| file_descriptor().message_by_package_relative_name("RecordDetails").unwrap()).clone()
- }
-}
-
-impl ::std::fmt::Display for RecordDetails {
- fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- ::protobuf::text_format::fmt(self, f)
- }
-}
-
-impl ::protobuf::reflect::ProtobufValue for RecordDetails {
- type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
-}
-
-#[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:WaveFormatDetails)
-pub struct WaveFormatDetails {
- // message fields
- // @@protoc_insertion_point(field:WaveFormatDetails.requested)
- pub requested: ::protobuf::MessageField<WaveFormat>,
- // @@protoc_insertion_point(field:WaveFormatDetails.modified)
- pub modified: ::protobuf::MessageField<WaveFormat>,
- // @@protoc_insertion_point(field:WaveFormatDetails.closest_matched)
- pub closest_matched: ::protobuf::MessageField<WaveFormat>,
- // special fields
- // @@protoc_insertion_point(special_field:WaveFormatDetails.special_fields)
- pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a WaveFormatDetails {
- fn default() -> &'a WaveFormatDetails {
- <WaveFormatDetails as ::protobuf::Message>::default_instance()
- }
-}
-
-impl WaveFormatDetails {
- pub fn new() -> WaveFormatDetails {
- ::std::default::Default::default()
- }
-
- fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
- let mut fields = ::std::vec::Vec::with_capacity(3);
- let mut oneofs = ::std::vec::Vec::with_capacity(0);
- fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, WaveFormat>(
- "requested",
- |m: &WaveFormatDetails| { &m.requested },
- |m: &mut WaveFormatDetails| { &mut m.requested },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, WaveFormat>(
- "modified",
- |m: &WaveFormatDetails| { &m.modified },
- |m: &mut WaveFormatDetails| { &mut m.modified },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, WaveFormat>(
- "closest_matched",
- |m: &WaveFormatDetails| { &m.closest_matched },
- |m: &mut WaveFormatDetails| { &mut m.closest_matched },
- ));
- ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<WaveFormatDetails>(
- "WaveFormatDetails",
- fields,
- oneofs,
- )
- }
-}
-
-impl ::protobuf::Message for WaveFormatDetails {
- const NAME: &'static str = "WaveFormatDetails";
-
- fn is_initialized(&self) -> bool {
- true
- }
-
- fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
- while let Some(tag) = is.read_raw_tag_or_eof()? {
- match tag {
- 10 => {
- ::protobuf::rt::read_singular_message_into_field(is, &mut self.requested)?;
- },
- 18 => {
- ::protobuf::rt::read_singular_message_into_field(is, &mut self.modified)?;
- },
- 26 => {
- ::protobuf::rt::read_singular_message_into_field(is, &mut self.closest_matched)?;
- },
- tag => {
- ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
- },
- };
- }
- ::std::result::Result::Ok(())
- }
-
- // Compute sizes of nested messages
- #[allow(unused_variables)]
- fn compute_size(&self) -> u64 {
- let mut my_size = 0;
- if let Some(v) = self.requested.as_ref() {
- let len = v.compute_size();
- my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
- }
- if let Some(v) = self.modified.as_ref() {
- let len = v.compute_size();
- my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
- }
- if let Some(v) = self.closest_matched.as_ref() {
- let len = v.compute_size();
- my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
- }
- my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
- self.special_fields.cached_size().set(my_size as u32);
- my_size
- }
-
- fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
- if let Some(v) = self.requested.as_ref() {
- ::protobuf::rt::write_message_field_with_cached_size(1, v, os)?;
- }
- if let Some(v) = self.modified.as_ref() {
- ::protobuf::rt::write_message_field_with_cached_size(2, v, os)?;
- }
- if let Some(v) = self.closest_matched.as_ref() {
- ::protobuf::rt::write_message_field_with_cached_size(3, v, os)?;
- }
- os.write_unknown_fields(self.special_fields.unknown_fields())?;
- ::std::result::Result::Ok(())
- }
-
- fn special_fields(&self) -> &::protobuf::SpecialFields {
- &self.special_fields
- }
-
- fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
- &mut self.special_fields
- }
-
- fn new() -> WaveFormatDetails {
- WaveFormatDetails::new()
- }
-
- fn clear(&mut self) {
- self.requested.clear();
- self.modified.clear();
- self.closest_matched.clear();
- self.special_fields.clear();
- }
-
- fn default_instance() -> &'static WaveFormatDetails {
- static instance: WaveFormatDetails = WaveFormatDetails {
- requested: ::protobuf::MessageField::none(),
- modified: ::protobuf::MessageField::none(),
- closest_matched: ::protobuf::MessageField::none(),
- special_fields: ::protobuf::SpecialFields::new(),
- };
- &instance
- }
-}
-
-impl ::protobuf::MessageFull for WaveFormatDetails {
- fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
- static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
- descriptor.get(|| file_descriptor().message_by_package_relative_name("WaveFormatDetails").unwrap()).clone()
- }
-}
-
-impl ::std::fmt::Display for WaveFormatDetails {
- fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- ::protobuf::text_format::fmt(self, f)
- }
-}
-
-impl ::protobuf::reflect::ProtobufValue for WaveFormatDetails {
- type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
-}
-
-#[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:WaveFormat)
-pub struct WaveFormat {
- // message fields
- // @@protoc_insertion_point(field:WaveFormat.format_tag)
- pub format_tag: ::std::option::Option<i32>,
- // @@protoc_insertion_point(field:WaveFormat.channels)
- pub channels: ::std::option::Option<i32>,
- // @@protoc_insertion_point(field:WaveFormat.samples_per_sec)
- pub samples_per_sec: ::std::option::Option<i32>,
- // @@protoc_insertion_point(field:WaveFormat.avg_bytes_per_sec)
- pub avg_bytes_per_sec: ::std::option::Option<i32>,
- // @@protoc_insertion_point(field:WaveFormat.block_align)
- pub block_align: ::std::option::Option<i32>,
- // @@protoc_insertion_point(field:WaveFormat.bits_per_sample)
- pub bits_per_sample: ::std::option::Option<i32>,
- // @@protoc_insertion_point(field:WaveFormat.size_bytes)
- pub size_bytes: ::std::option::Option<i32>,
- // @@protoc_insertion_point(field:WaveFormat.samples)
- pub samples: ::std::option::Option<i32>,
- // @@protoc_insertion_point(field:WaveFormat.channel_mask)
- pub channel_mask: ::std::option::Option<i64>,
- // @@protoc_insertion_point(field:WaveFormat.sub_format)
- pub sub_format: ::std::option::Option<::protobuf::EnumOrUnknown<wave_format::WaveFormatSubFormat>>,
- // special fields
- // @@protoc_insertion_point(special_field:WaveFormat.special_fields)
- pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a WaveFormat {
- fn default() -> &'a WaveFormat {
- <WaveFormat as ::protobuf::Message>::default_instance()
- }
-}
-
-impl WaveFormat {
- pub fn new() -> WaveFormat {
- ::std::default::Default::default()
- }
-
- // optional int32 format_tag = 1;
-
- pub fn format_tag(&self) -> i32 {
- self.format_tag.unwrap_or(0)
- }
-
- pub fn clear_format_tag(&mut self) {
- self.format_tag = ::std::option::Option::None;
- }
-
- pub fn has_format_tag(&self) -> bool {
- self.format_tag.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_format_tag(&mut self, v: i32) {
- self.format_tag = ::std::option::Option::Some(v);
- }
-
- // optional int32 channels = 2;
-
- pub fn channels(&self) -> i32 {
- self.channels.unwrap_or(0)
- }
-
- pub fn clear_channels(&mut self) {
- self.channels = ::std::option::Option::None;
- }
-
- pub fn has_channels(&self) -> bool {
- self.channels.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_channels(&mut self, v: i32) {
- self.channels = ::std::option::Option::Some(v);
- }
-
- // optional int32 samples_per_sec = 3;
-
- pub fn samples_per_sec(&self) -> i32 {
- self.samples_per_sec.unwrap_or(0)
- }
-
- pub fn clear_samples_per_sec(&mut self) {
- self.samples_per_sec = ::std::option::Option::None;
- }
-
- pub fn has_samples_per_sec(&self) -> bool {
- self.samples_per_sec.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_samples_per_sec(&mut self, v: i32) {
- self.samples_per_sec = ::std::option::Option::Some(v);
- }
-
- // optional int32 avg_bytes_per_sec = 4;
-
- pub fn avg_bytes_per_sec(&self) -> i32 {
- self.avg_bytes_per_sec.unwrap_or(0)
- }
-
- pub fn clear_avg_bytes_per_sec(&mut self) {
- self.avg_bytes_per_sec = ::std::option::Option::None;
- }
-
- pub fn has_avg_bytes_per_sec(&self) -> bool {
- self.avg_bytes_per_sec.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_avg_bytes_per_sec(&mut self, v: i32) {
- self.avg_bytes_per_sec = ::std::option::Option::Some(v);
- }
-
- // optional int32 block_align = 5;
-
- pub fn block_align(&self) -> i32 {
- self.block_align.unwrap_or(0)
- }
-
- pub fn clear_block_align(&mut self) {
- self.block_align = ::std::option::Option::None;
- }
-
- pub fn has_block_align(&self) -> bool {
- self.block_align.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_block_align(&mut self, v: i32) {
- self.block_align = ::std::option::Option::Some(v);
- }
-
- // optional int32 bits_per_sample = 6;
-
- pub fn bits_per_sample(&self) -> i32 {
- self.bits_per_sample.unwrap_or(0)
- }
-
- pub fn clear_bits_per_sample(&mut self) {
- self.bits_per_sample = ::std::option::Option::None;
- }
-
- pub fn has_bits_per_sample(&self) -> bool {
- self.bits_per_sample.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_bits_per_sample(&mut self, v: i32) {
- self.bits_per_sample = ::std::option::Option::Some(v);
- }
-
- // optional int32 size_bytes = 7;
-
- pub fn size_bytes(&self) -> i32 {
- self.size_bytes.unwrap_or(0)
- }
-
- pub fn clear_size_bytes(&mut self) {
- self.size_bytes = ::std::option::Option::None;
- }
-
- pub fn has_size_bytes(&self) -> bool {
- self.size_bytes.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_size_bytes(&mut self, v: i32) {
- self.size_bytes = ::std::option::Option::Some(v);
- }
-
- // optional int32 samples = 8;
-
- pub fn samples(&self) -> i32 {
- self.samples.unwrap_or(0)
- }
-
- pub fn clear_samples(&mut self) {
- self.samples = ::std::option::Option::None;
- }
-
- pub fn has_samples(&self) -> bool {
- self.samples.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_samples(&mut self, v: i32) {
- self.samples = ::std::option::Option::Some(v);
- }
-
- // optional int64 channel_mask = 9;
-
- pub fn channel_mask(&self) -> i64 {
- self.channel_mask.unwrap_or(0)
- }
-
- pub fn clear_channel_mask(&mut self) {
- self.channel_mask = ::std::option::Option::None;
- }
-
- pub fn has_channel_mask(&self) -> bool {
- self.channel_mask.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_channel_mask(&mut self, v: i64) {
- self.channel_mask = ::std::option::Option::Some(v);
- }
-
- // optional .WaveFormat.WaveFormatSubFormat sub_format = 10;
-
- pub fn sub_format(&self) -> wave_format::WaveFormatSubFormat {
- match self.sub_format {
- Some(e) => e.enum_value_or(wave_format::WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID),
- None => wave_format::WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID,
- }
- }
-
- pub fn clear_sub_format(&mut self) {
- self.sub_format = ::std::option::Option::None;
- }
-
- pub fn has_sub_format(&self) -> bool {
- self.sub_format.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_sub_format(&mut self, v: wave_format::WaveFormatSubFormat) {
- self.sub_format = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v));
- }
-
- fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
- let mut fields = ::std::vec::Vec::with_capacity(10);
- let mut oneofs = ::std::vec::Vec::with_capacity(0);
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "format_tag",
- |m: &WaveFormat| { &m.format_tag },
- |m: &mut WaveFormat| { &mut m.format_tag },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "channels",
- |m: &WaveFormat| { &m.channels },
- |m: &mut WaveFormat| { &mut m.channels },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "samples_per_sec",
- |m: &WaveFormat| { &m.samples_per_sec },
- |m: &mut WaveFormat| { &mut m.samples_per_sec },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "avg_bytes_per_sec",
- |m: &WaveFormat| { &m.avg_bytes_per_sec },
- |m: &mut WaveFormat| { &mut m.avg_bytes_per_sec },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "block_align",
- |m: &WaveFormat| { &m.block_align },
- |m: &mut WaveFormat| { &mut m.block_align },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "bits_per_sample",
- |m: &WaveFormat| { &m.bits_per_sample },
- |m: &mut WaveFormat| { &mut m.bits_per_sample },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "size_bytes",
- |m: &WaveFormat| { &m.size_bytes },
- |m: &mut WaveFormat| { &mut m.size_bytes },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "samples",
- |m: &WaveFormat| { &m.samples },
- |m: &mut WaveFormat| { &mut m.samples },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "channel_mask",
- |m: &WaveFormat| { &m.channel_mask },
- |m: &mut WaveFormat| { &mut m.channel_mask },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "sub_format",
- |m: &WaveFormat| { &m.sub_format },
- |m: &mut WaveFormat| { &mut m.sub_format },
- ));
- ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<WaveFormat>(
- "WaveFormat",
- fields,
- oneofs,
- )
- }
-}
-
-impl ::protobuf::Message for WaveFormat {
- const NAME: &'static str = "WaveFormat";
-
- fn is_initialized(&self) -> bool {
- true
- }
-
- fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
- while let Some(tag) = is.read_raw_tag_or_eof()? {
- match tag {
- 8 => {
- self.format_tag = ::std::option::Option::Some(is.read_int32()?);
- },
- 16 => {
- self.channels = ::std::option::Option::Some(is.read_int32()?);
- },
- 24 => {
- self.samples_per_sec = ::std::option::Option::Some(is.read_int32()?);
- },
- 32 => {
- self.avg_bytes_per_sec = ::std::option::Option::Some(is.read_int32()?);
- },
- 40 => {
- self.block_align = ::std::option::Option::Some(is.read_int32()?);
- },
- 48 => {
- self.bits_per_sample = ::std::option::Option::Some(is.read_int32()?);
- },
- 56 => {
- self.size_bytes = ::std::option::Option::Some(is.read_int32()?);
- },
- 64 => {
- self.samples = ::std::option::Option::Some(is.read_int32()?);
- },
- 72 => {
- self.channel_mask = ::std::option::Option::Some(is.read_int64()?);
- },
- 80 => {
- self.sub_format = ::std::option::Option::Some(is.read_enum_or_unknown()?);
- },
- tag => {
- ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
- },
- };
- }
- ::std::result::Result::Ok(())
- }
-
- // Compute sizes of nested messages
- #[allow(unused_variables)]
- fn compute_size(&self) -> u64 {
- let mut my_size = 0;
- if let Some(v) = self.format_tag {
- my_size += ::protobuf::rt::int32_size(1, v);
- }
- if let Some(v) = self.channels {
- my_size += ::protobuf::rt::int32_size(2, v);
- }
- if let Some(v) = self.samples_per_sec {
- my_size += ::protobuf::rt::int32_size(3, v);
- }
- if let Some(v) = self.avg_bytes_per_sec {
- my_size += ::protobuf::rt::int32_size(4, v);
- }
- if let Some(v) = self.block_align {
- my_size += ::protobuf::rt::int32_size(5, v);
- }
- if let Some(v) = self.bits_per_sample {
- my_size += ::protobuf::rt::int32_size(6, v);
- }
- if let Some(v) = self.size_bytes {
- my_size += ::protobuf::rt::int32_size(7, v);
- }
- if let Some(v) = self.samples {
- my_size += ::protobuf::rt::int32_size(8, v);
- }
- if let Some(v) = self.channel_mask {
- my_size += ::protobuf::rt::int64_size(9, v);
- }
- if let Some(v) = self.sub_format {
- my_size += ::protobuf::rt::int32_size(10, v.value());
- }
- my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
- self.special_fields.cached_size().set(my_size as u32);
- my_size
- }
-
- fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
- if let Some(v) = self.format_tag {
- os.write_int32(1, v)?;
- }
- if let Some(v) = self.channels {
- os.write_int32(2, v)?;
- }
- if let Some(v) = self.samples_per_sec {
- os.write_int32(3, v)?;
- }
- if let Some(v) = self.avg_bytes_per_sec {
- os.write_int32(4, v)?;
- }
- if let Some(v) = self.block_align {
- os.write_int32(5, v)?;
- }
- if let Some(v) = self.bits_per_sample {
- os.write_int32(6, v)?;
- }
- if let Some(v) = self.size_bytes {
- os.write_int32(7, v)?;
- }
- if let Some(v) = self.samples {
- os.write_int32(8, v)?;
- }
- if let Some(v) = self.channel_mask {
- os.write_int64(9, v)?;
- }
- if let Some(v) = self.sub_format {
- os.write_enum(10, ::protobuf::EnumOrUnknown::value(&v))?;
- }
- os.write_unknown_fields(self.special_fields.unknown_fields())?;
- ::std::result::Result::Ok(())
- }
-
- fn special_fields(&self) -> &::protobuf::SpecialFields {
- &self.special_fields
- }
-
- fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
- &mut self.special_fields
- }
-
- fn new() -> WaveFormat {
- WaveFormat::new()
- }
-
- fn clear(&mut self) {
- self.format_tag = ::std::option::Option::None;
- self.channels = ::std::option::Option::None;
- self.samples_per_sec = ::std::option::Option::None;
- self.avg_bytes_per_sec = ::std::option::Option::None;
- self.block_align = ::std::option::Option::None;
- self.bits_per_sample = ::std::option::Option::None;
- self.size_bytes = ::std::option::Option::None;
- self.samples = ::std::option::Option::None;
- self.channel_mask = ::std::option::Option::None;
- self.sub_format = ::std::option::Option::None;
- self.special_fields.clear();
- }
-
- fn default_instance() -> &'static WaveFormat {
- static instance: WaveFormat = WaveFormat {
- format_tag: ::std::option::Option::None,
- channels: ::std::option::Option::None,
- samples_per_sec: ::std::option::Option::None,
- avg_bytes_per_sec: ::std::option::Option::None,
- block_align: ::std::option::Option::None,
- bits_per_sample: ::std::option::Option::None,
- size_bytes: ::std::option::Option::None,
- samples: ::std::option::Option::None,
- channel_mask: ::std::option::Option::None,
- sub_format: ::std::option::Option::None,
- special_fields: ::protobuf::SpecialFields::new(),
- };
- &instance
- }
-}
-
-impl ::protobuf::MessageFull for WaveFormat {
- fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
- static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
- descriptor.get(|| file_descriptor().message_by_package_relative_name("WaveFormat").unwrap()).clone()
- }
-}
-
-impl ::std::fmt::Display for WaveFormat {
- fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- ::protobuf::text_format::fmt(self, f)
- }
-}
-
-impl ::protobuf::reflect::ProtobufValue for WaveFormat {
- type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
-}
-
-/// Nested message and enums of message `WaveFormat`
-pub mod wave_format {
- #[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
- // @@protoc_insertion_point(enum:WaveFormat.WaveFormatSubFormat)
- pub enum WaveFormatSubFormat {
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_INVALID)
- KSDATAFORMAT_SUBTYPE_INVALID = 0,
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_ANALOG)
- KSDATAFORMAT_SUBTYPE_ANALOG = 1,
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_PCM)
- KSDATAFORMAT_SUBTYPE_PCM = 2,
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
- KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = 3,
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_DRM)
- KSDATAFORMAT_SUBTYPE_DRM = 4,
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_ALAW)
- KSDATAFORMAT_SUBTYPE_ALAW = 5,
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_MULAW)
- KSDATAFORMAT_SUBTYPE_MULAW = 6,
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_ADPCM)
- KSDATAFORMAT_SUBTYPE_ADPCM = 7,
- // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_MPEG)
- KSDATAFORMAT_SUBTYPE_MPEG = 8,
- }
-
- impl ::protobuf::Enum for WaveFormatSubFormat {
- const NAME: &'static str = "WaveFormatSubFormat";
-
- fn value(&self) -> i32 {
- *self as i32
- }
-
- fn from_i32(value: i32) -> ::std::option::Option<WaveFormatSubFormat> {
- match value {
- 0 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID),
- 1 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ANALOG),
- 2 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_PCM),
- 3 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT),
- 4 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_DRM),
- 5 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ALAW),
- 6 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_MULAW),
- 7 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ADPCM),
- 8 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_MPEG),
- _ => ::std::option::Option::None
- }
- }
-
- const VALUES: &'static [WaveFormatSubFormat] = &[
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID,
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ANALOG,
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_PCM,
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_DRM,
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ALAW,
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_MULAW,
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ADPCM,
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_MPEG,
- ];
- }
-
- impl ::protobuf::EnumFull for WaveFormatSubFormat {
- fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor {
- static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new();
- descriptor.get(|| super::file_descriptor().enum_by_package_relative_name("WaveFormat.WaveFormatSubFormat").unwrap()).clone()
- }
-
- fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor {
- let index = *self as usize;
- Self::enum_descriptor().value_by_index(index)
- }
- }
-
- impl ::std::default::Default for WaveFormatSubFormat {
- fn default() -> Self {
- WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID
- }
- }
-
- impl WaveFormatSubFormat {
- pub(in super) fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData {
- ::protobuf::reflect::GeneratedEnumDescriptorData::new::<WaveFormatSubFormat>("WaveFormat.WaveFormatSubFormat")
- }
- }
-}
-
-#[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:EmulatorChildProcessExitDetails)
-pub struct EmulatorChildProcessExitDetails {
- // message fields
- // @@protoc_insertion_point(field:EmulatorChildProcessExitDetails.exit_code)
- pub exit_code: ::std::option::Option<u32>,
- // @@protoc_insertion_point(field:EmulatorChildProcessExitDetails.process_type)
- pub process_type: ::std::option::Option<::protobuf::EnumOrUnknown<EmulatorProcessType>>,
- // special fields
- // @@protoc_insertion_point(special_field:EmulatorChildProcessExitDetails.special_fields)
- pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a EmulatorChildProcessExitDetails {
- fn default() -> &'a EmulatorChildProcessExitDetails {
- <EmulatorChildProcessExitDetails as ::protobuf::Message>::default_instance()
- }
-}
-
-impl EmulatorChildProcessExitDetails {
- pub fn new() -> EmulatorChildProcessExitDetails {
- ::std::default::Default::default()
- }
-
- // optional uint32 exit_code = 1;
-
- pub fn exit_code(&self) -> u32 {
- self.exit_code.unwrap_or(0)
- }
-
- pub fn clear_exit_code(&mut self) {
- self.exit_code = ::std::option::Option::None;
- }
-
- pub fn has_exit_code(&self) -> bool {
- self.exit_code.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_exit_code(&mut self, v: u32) {
- self.exit_code = ::std::option::Option::Some(v);
- }
-
- // optional .EmulatorProcessType process_type = 2;
-
- pub fn process_type(&self) -> EmulatorProcessType {
- match self.process_type {
- Some(e) => e.enum_value_or(EmulatorProcessType::PROCESS_TYPE_UNKNOWN),
- None => EmulatorProcessType::PROCESS_TYPE_UNKNOWN,
- }
- }
-
- pub fn clear_process_type(&mut self) {
- self.process_type = ::std::option::Option::None;
- }
-
- pub fn has_process_type(&self) -> bool {
- self.process_type.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_process_type(&mut self, v: EmulatorProcessType) {
- self.process_type = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v));
- }
-
- fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
- let mut fields = ::std::vec::Vec::with_capacity(2);
- let mut oneofs = ::std::vec::Vec::with_capacity(0);
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "exit_code",
- |m: &EmulatorChildProcessExitDetails| { &m.exit_code },
- |m: &mut EmulatorChildProcessExitDetails| { &mut m.exit_code },
- ));
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "process_type",
- |m: &EmulatorChildProcessExitDetails| { &m.process_type },
- |m: &mut EmulatorChildProcessExitDetails| { &mut m.process_type },
- ));
- ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<EmulatorChildProcessExitDetails>(
- "EmulatorChildProcessExitDetails",
- fields,
- oneofs,
- )
- }
-}
-
-impl ::protobuf::Message for EmulatorChildProcessExitDetails {
- const NAME: &'static str = "EmulatorChildProcessExitDetails";
-
- fn is_initialized(&self) -> bool {
- true
- }
-
- fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
- while let Some(tag) = is.read_raw_tag_or_eof()? {
- match tag {
- 8 => {
- self.exit_code = ::std::option::Option::Some(is.read_uint32()?);
- },
- 16 => {
- self.process_type = ::std::option::Option::Some(is.read_enum_or_unknown()?);
- },
- tag => {
- ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
- },
- };
- }
- ::std::result::Result::Ok(())
- }
-
- // Compute sizes of nested messages
- #[allow(unused_variables)]
- fn compute_size(&self) -> u64 {
- let mut my_size = 0;
- if let Some(v) = self.exit_code {
- my_size += ::protobuf::rt::uint32_size(1, v);
- }
- if let Some(v) = self.process_type {
- my_size += ::protobuf::rt::int32_size(2, v.value());
- }
- my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
- self.special_fields.cached_size().set(my_size as u32);
- my_size
- }
-
- fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
- if let Some(v) = self.exit_code {
- os.write_uint32(1, v)?;
- }
- if let Some(v) = self.process_type {
- os.write_enum(2, ::protobuf::EnumOrUnknown::value(&v))?;
- }
- os.write_unknown_fields(self.special_fields.unknown_fields())?;
- ::std::result::Result::Ok(())
- }
-
- fn special_fields(&self) -> &::protobuf::SpecialFields {
- &self.special_fields
- }
-
- fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
- &mut self.special_fields
- }
-
- fn new() -> EmulatorChildProcessExitDetails {
- EmulatorChildProcessExitDetails::new()
- }
-
- fn clear(&mut self) {
- self.exit_code = ::std::option::Option::None;
- self.process_type = ::std::option::Option::None;
- self.special_fields.clear();
- }
-
- fn default_instance() -> &'static EmulatorChildProcessExitDetails {
- static instance: EmulatorChildProcessExitDetails = EmulatorChildProcessExitDetails {
- exit_code: ::std::option::Option::None,
- process_type: ::std::option::Option::None,
- special_fields: ::protobuf::SpecialFields::new(),
- };
- &instance
- }
-}
-
-impl ::protobuf::MessageFull for EmulatorChildProcessExitDetails {
- fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
- static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
- descriptor.get(|| file_descriptor().message_by_package_relative_name("EmulatorChildProcessExitDetails").unwrap()).clone()
- }
-}
-
-impl ::std::fmt::Display for EmulatorChildProcessExitDetails {
- fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- ::protobuf::text_format::fmt(self, f)
- }
-}
-
-impl ::protobuf::reflect::ProtobufValue for EmulatorChildProcessExitDetails {
- type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
-}
-
-#[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:EmulatorDllDetails)
-pub struct EmulatorDllDetails {
- // message fields
- // @@protoc_insertion_point(field:EmulatorDllDetails.dll_base_name)
- pub dll_base_name: ::std::option::Option<::std::string::String>,
- // special fields
- // @@protoc_insertion_point(special_field:EmulatorDllDetails.special_fields)
- pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a EmulatorDllDetails {
- fn default() -> &'a EmulatorDllDetails {
- <EmulatorDllDetails as ::protobuf::Message>::default_instance()
- }
-}
-
-impl EmulatorDllDetails {
- pub fn new() -> EmulatorDllDetails {
- ::std::default::Default::default()
- }
-
- // optional string dll_base_name = 1;
-
- pub fn dll_base_name(&self) -> &str {
- match self.dll_base_name.as_ref() {
- Some(v) => v,
- None => "",
- }
- }
-
- pub fn clear_dll_base_name(&mut self) {
- self.dll_base_name = ::std::option::Option::None;
- }
-
- pub fn has_dll_base_name(&self) -> bool {
- self.dll_base_name.is_some()
- }
-
- // Param is passed by value, moved
- pub fn set_dll_base_name(&mut self, v: ::std::string::String) {
- self.dll_base_name = ::std::option::Option::Some(v);
- }
-
- // Mutable pointer to the field.
- // If field is not initialized, it is initialized with default value first.
- pub fn mut_dll_base_name(&mut self) -> &mut ::std::string::String {
- if self.dll_base_name.is_none() {
- self.dll_base_name = ::std::option::Option::Some(::std::string::String::new());
- }
- self.dll_base_name.as_mut().unwrap()
- }
-
- // Take field
- pub fn take_dll_base_name(&mut self) -> ::std::string::String {
- self.dll_base_name.take().unwrap_or_else(|| ::std::string::String::new())
- }
-
- fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
- let mut fields = ::std::vec::Vec::with_capacity(1);
- let mut oneofs = ::std::vec::Vec::with_capacity(0);
- fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
- "dll_base_name",
- |m: &EmulatorDllDetails| { &m.dll_base_name },
- |m: &mut EmulatorDllDetails| { &mut m.dll_base_name },
- ));
- ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<EmulatorDllDetails>(
- "EmulatorDllDetails",
- fields,
- oneofs,
- )
- }
-}
-
-impl ::protobuf::Message for EmulatorDllDetails {
- const NAME: &'static str = "EmulatorDllDetails";
-
- fn is_initialized(&self) -> bool {
- true
- }
-
- fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
- while let Some(tag) = is.read_raw_tag_or_eof()? {
- match tag {
- 10 => {
- self.dll_base_name = ::std::option::Option::Some(is.read_string()?);
- },
- tag => {
- ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
- },
- };
- }
- ::std::result::Result::Ok(())
- }
-
- // Compute sizes of nested messages
- #[allow(unused_variables)]
- fn compute_size(&self) -> u64 {
- let mut my_size = 0;
- if let Some(v) = self.dll_base_name.as_ref() {
- my_size += ::protobuf::rt::string_size(1, &v);
- }
- my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
- self.special_fields.cached_size().set(my_size as u32);
- my_size
- }
-
- fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
- if let Some(v) = self.dll_base_name.as_ref() {
- os.write_string(1, v)?;
- }
- os.write_unknown_fields(self.special_fields.unknown_fields())?;
- ::std::result::Result::Ok(())
- }
-
- fn special_fields(&self) -> &::protobuf::SpecialFields {
- &self.special_fields
- }
-
- fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
- &mut self.special_fields
- }
-
- fn new() -> EmulatorDllDetails {
- EmulatorDllDetails::new()
- }
-
- fn clear(&mut self) {
- self.dll_base_name = ::std::option::Option::None;
- self.special_fields.clear();
- }
-
- fn default_instance() -> &'static EmulatorDllDetails {
- static instance: EmulatorDllDetails = EmulatorDllDetails {
- dll_base_name: ::std::option::Option::None,
- special_fields: ::protobuf::SpecialFields::new(),
- };
- &instance
- }
-}
-
-impl ::protobuf::MessageFull for EmulatorDllDetails {
- fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
- static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
- descriptor.get(|| file_descriptor().message_by_package_relative_name("EmulatorDllDetails").unwrap()).clone()
- }
-}
-
-impl ::std::fmt::Display for EmulatorDllDetails {
- fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- ::protobuf::text_format::fmt(self, f)
- }
-}
-
-impl ::protobuf::reflect::ProtobufValue for EmulatorDllDetails {
- type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
-}
-
-#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
-// @@protoc_insertion_point(enum:EmulatorProcessType)
-pub enum EmulatorProcessType {
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_UNKNOWN)
- PROCESS_TYPE_UNKNOWN = 0,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_MAIN)
- PROCESS_TYPE_MAIN = 1,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_BLOCK)
- PROCESS_TYPE_BLOCK = 2,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_METRICS)
- PROCESS_TYPE_METRICS = 3,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_NET)
- PROCESS_TYPE_NET = 4,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_SLIRP)
- PROCESS_TYPE_SLIRP = 5,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_GPU)
- PROCESS_TYPE_GPU = 6,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_SOUND)
- PROCESS_TYPE_SOUND = 7,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_BROKER)
- PROCESS_TYPE_BROKER = 8,
- // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_SPU)
- PROCESS_TYPE_SPU = 9,
-}
-
-impl ::protobuf::Enum for EmulatorProcessType {
- const NAME: &'static str = "EmulatorProcessType";
-
- fn value(&self) -> i32 {
- *self as i32
- }
-
- fn from_i32(value: i32) -> ::std::option::Option<EmulatorProcessType> {
- match value {
- 0 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_UNKNOWN),
- 1 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_MAIN),
- 2 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_BLOCK),
- 3 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_METRICS),
- 4 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_NET),
- 5 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_SLIRP),
- 6 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_GPU),
- 7 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_SOUND),
- 8 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_BROKER),
- 9 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_SPU),
- _ => ::std::option::Option::None
- }
- }
-
- const VALUES: &'static [EmulatorProcessType] = &[
- EmulatorProcessType::PROCESS_TYPE_UNKNOWN,
- EmulatorProcessType::PROCESS_TYPE_MAIN,
- EmulatorProcessType::PROCESS_TYPE_BLOCK,
- EmulatorProcessType::PROCESS_TYPE_METRICS,
- EmulatorProcessType::PROCESS_TYPE_NET,
- EmulatorProcessType::PROCESS_TYPE_SLIRP,
- EmulatorProcessType::PROCESS_TYPE_GPU,
- EmulatorProcessType::PROCESS_TYPE_SOUND,
- EmulatorProcessType::PROCESS_TYPE_BROKER,
- EmulatorProcessType::PROCESS_TYPE_SPU,
- ];
-}
-
-impl ::protobuf::EnumFull for EmulatorProcessType {
- fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor {
- static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new();
- descriptor.get(|| file_descriptor().enum_by_package_relative_name("EmulatorProcessType").unwrap()).clone()
- }
-
- fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor {
- let index = *self as usize;
- Self::enum_descriptor().value_by_index(index)
- }
-}
-
-impl ::std::default::Default for EmulatorProcessType {
- fn default() -> Self {
- EmulatorProcessType::PROCESS_TYPE_UNKNOWN
- }
-}
-
-impl EmulatorProcessType {
- fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData {
- ::protobuf::reflect::GeneratedEnumDescriptorData::new::<EmulatorProcessType>("EmulatorProcessType")
- }
-}
-
-static file_descriptor_proto_data: &'static [u8] = b"\
- \n\x13event_details.proto\"\x96\x02\n\rRecordDetails\x12n\n#emulator_chi\
- ld_process_exit_details\x18\x0c\x20\x01(\x0b2\x20.EmulatorChildProcessEx\
- itDetailsR\x1femulatorChildProcessExitDetails\x12B\n\x13wave_format_deta\
- ils\x18\r\x20\x01(\x0b2\x12.WaveFormatDetailsR\x11waveFormatDetails\x12E\
- \n\x14emulator_dll_details\x18\x13\x20\x01(\x0b2\x13.EmulatorDllDetailsR\
- \x12emulatorDllDetailsJ\x04\x08\x01\x10\x0cJ\x04\x08\x0e\x10\x13\"\x9d\
- \x01\n\x11WaveFormatDetails\x12)\n\trequested\x18\x01\x20\x01(\x0b2\x0b.\
- WaveFormatR\trequested\x12'\n\x08modified\x18\x02\x20\x01(\x0b2\x0b.Wave\
- FormatR\x08modified\x124\n\x0fclosest_matched\x18\x03\x20\x01(\x0b2\x0b.\
- WaveFormatR\x0eclosestMatched\"\xb9\x05\n\nWaveFormat\x12\x1d\n\nformat_\
- tag\x18\x01\x20\x01(\x05R\tformatTag\x12\x1a\n\x08channels\x18\x02\x20\
- \x01(\x05R\x08channels\x12&\n\x0fsamples_per_sec\x18\x03\x20\x01(\x05R\r\
- samplesPerSec\x12)\n\x11avg_bytes_per_sec\x18\x04\x20\x01(\x05R\x0eavgBy\
- tesPerSec\x12\x1f\n\x0bblock_align\x18\x05\x20\x01(\x05R\nblockAlign\x12\
- &\n\x0fbits_per_sample\x18\x06\x20\x01(\x05R\rbitsPerSample\x12\x1d\n\ns\
- ize_bytes\x18\x07\x20\x01(\x05R\tsizeBytes\x12\x18\n\x07samples\x18\x08\
- \x20\x01(\x05R\x07samples\x12!\n\x0cchannel_mask\x18\t\x20\x01(\x03R\x0b\
- channelMask\x12>\n\nsub_format\x18\n\x20\x01(\x0e2\x1f.WaveFormat.WaveFo\
- rmatSubFormatR\tsubFormat\"\xb7\x02\n\x13WaveFormatSubFormat\x12\x20\n\
- \x1cKSDATAFORMAT_SUBTYPE_INVALID\x10\0\x12\x1f\n\x1bKSDATAFORMAT_SUBTYPE\
- _ANALOG\x10\x01\x12\x1c\n\x18KSDATAFORMAT_SUBTYPE_PCM\x10\x02\x12#\n\x1f\
- KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\x10\x03\x12\x1c\n\x18KSDATAFORMAT_SUBTYP\
- E_DRM\x10\x04\x12\x1d\n\x19KSDATAFORMAT_SUBTYPE_ALAW\x10\x05\x12\x1e\n\
- \x1aKSDATAFORMAT_SUBTYPE_MULAW\x10\x06\x12\x1e\n\x1aKSDATAFORMAT_SUBTYPE\
- _ADPCM\x10\x07\x12\x1d\n\x19KSDATAFORMAT_SUBTYPE_MPEG\x10\x08\"w\n\x1fEm\
- ulatorChildProcessExitDetails\x12\x1b\n\texit_code\x18\x01\x20\x01(\rR\
- \x08exitCode\x127\n\x0cprocess_type\x18\x02\x20\x01(\x0e2\x14.EmulatorPr\
- ocessTypeR\x0bprocessType\"8\n\x12EmulatorDllDetails\x12\"\n\rdll_base_n\
- ame\x18\x01\x20\x01(\tR\x0bdllBaseName*\x83\x02\n\x13EmulatorProcessType\
- \x12\x18\n\x14PROCESS_TYPE_UNKNOWN\x10\0\x12\x15\n\x11PROCESS_TYPE_MAIN\
- \x10\x01\x12\x16\n\x12PROCESS_TYPE_BLOCK\x10\x02\x12\x18\n\x14PROCESS_TY\
- PE_METRICS\x10\x03\x12\x14\n\x10PROCESS_TYPE_NET\x10\x04\x12\x16\n\x12PR\
- OCESS_TYPE_SLIRP\x10\x05\x12\x14\n\x10PROCESS_TYPE_GPU\x10\x06\x12\x16\n\
- \x12PROCESS_TYPE_SOUND\x10\x07\x12\x17\n\x13PROCESS_TYPE_BROKER\x10\x08\
- \x12\x14\n\x10PROCESS_TYPE_SPU\x10\t\
-";
-
-/// `FileDescriptorProto` object which was a source for this generated file
-fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
- static file_descriptor_proto_lazy: ::protobuf::rt::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::Lazy::new();
- file_descriptor_proto_lazy.get(|| {
- ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
- })
-}
-
-/// `FileDescriptor` object which allows dynamic access to files
-pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
- static generated_file_descriptor_lazy: ::protobuf::rt::Lazy<::protobuf::reflect::GeneratedFileDescriptor> = ::protobuf::rt::Lazy::new();
- static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new();
- file_descriptor.get(|| {
- let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {
- let mut deps = ::std::vec::Vec::with_capacity(0);
- let mut messages = ::std::vec::Vec::with_capacity(5);
- messages.push(RecordDetails::generated_message_descriptor_data());
- messages.push(WaveFormatDetails::generated_message_descriptor_data());
- messages.push(WaveFormat::generated_message_descriptor_data());
- messages.push(EmulatorChildProcessExitDetails::generated_message_descriptor_data());
- messages.push(EmulatorDllDetails::generated_message_descriptor_data());
- let mut enums = ::std::vec::Vec::with_capacity(2);
- enums.push(EmulatorProcessType::generated_enum_descriptor_data());
- enums.push(wave_format::WaveFormatSubFormat::generated_enum_descriptor_data());
- ::protobuf::reflect::GeneratedFileDescriptor::new_generated(
- file_descriptor_proto(),
- deps,
- messages,
- enums,
- )
- });
- ::protobuf::reflect::FileDescriptor::new_generated_2(generated_file_descriptor)
- })
-}
diff --git a/vendor/generic/metrics/src/out/generated.rs b/vendor/generic/metrics/src/out/generated.rs
deleted file mode 100644
index c7dc617d2..000000000
--- a/vendor/generic/metrics/src/out/generated.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-#[path = "./event_details.rs"]
-pub mod event_details;
diff --git a/vendor/generic/metrics/src/out/mod.rs b/vendor/generic/metrics/src/out/mod.rs
deleted file mode 100644
index 2db7abd20..000000000
--- a/vendor/generic/metrics/src/out/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-// @generated
-
-pub mod event_details;
diff --git a/vendor/generic/metrics/src/periodic_logger.rs b/vendor/generic/metrics/src/periodic_logger.rs
index 409307856..b6469f648 100644
--- a/vendor/generic/metrics/src/periodic_logger.rs
+++ b/vendor/generic/metrics/src/periodic_logger.rs
@@ -5,7 +5,7 @@
use std::result::Result;
use std::time::Duration;
-use crate::MetricEventType;
+use metrics_events::MetricEventType;
/// A logging struct meant for use in tracking and periodically
/// logging a single metric. The metric is aggregated over the
diff --git a/vendor/generic/metrics/src/request_handler.rs b/vendor/generic/metrics/src/request_handler.rs
index 7ed300d22..7520c700a 100644
--- a/vendor/generic/metrics/src/request_handler.rs
+++ b/vendor/generic/metrics/src/request_handler.rs
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-use crate::metrics_requests::MetricsRequest;
+use base::RecvTube;
#[derive(Default)]
pub struct MetricsRequestHandler;
@@ -10,6 +10,8 @@ impl MetricsRequestHandler {
pub fn new() -> Self {
MetricsRequestHandler
}
- pub fn handle_request(&self, _req: MetricsRequest) {}
+ pub fn handle_tube_readable(&self, _tube: &RecvTube) {
+ unreachable!();
+ }
pub fn shutdown(&self) {}
}
diff --git a/vendor/generic/metrics/src/sys/windows.rs b/vendor/generic/metrics/src/sys/windows.rs
deleted file mode 100644
index 0aedb5c51..000000000
--- a/vendor/generic/metrics/src/sys/windows.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use win_util::ProcessType;
-
-use crate::protos::event_details::EmulatorProcessType;
-
-impl From<ProcessType> for EmulatorProcessType {
- fn from(process_type: ProcessType) -> Self {
- match process_type {
- ProcessType::Block => EmulatorProcessType::PROCESS_TYPE_BLOCK,
- ProcessType::Main => EmulatorProcessType::PROCESS_TYPE_MAIN,
- ProcessType::Metrics => EmulatorProcessType::PROCESS_TYPE_METRICS,
- ProcessType::Net => EmulatorProcessType::PROCESS_TYPE_NET,
- ProcessType::Slirp => EmulatorProcessType::PROCESS_TYPE_SLIRP,
- ProcessType::Gpu => EmulatorProcessType::PROCESS_TYPE_GPU,
- ProcessType::Snd => EmulatorProcessType::PROCESS_TYPE_SOUND,
- ProcessType::Broker => EmulatorProcessType::PROCESS_TYPE_BROKER,
- ProcessType::Spu => EmulatorProcessType::PROCESS_TYPE_SPU,
- ProcessType::UnknownType => panic!("Unknown process type found"),
- }
- }
-}
diff --git a/vendor/generic/metrics_events/Android.bp b/vendor/generic/metrics_events/Android.bp
new file mode 100644
index 000000000..90ea97aca
--- /dev/null
+++ b/vendor/generic/metrics_events/Android.bp
@@ -0,0 +1,20 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
+// Content before the first "rust_*" or "genrule" module is preserved.
+
+package {
+ default_applicable_licenses: ["external_crosvm_license"],
+}
+
+rust_library {
+ name: "libmetrics_events_generic",
+ defaults: ["crosvm_inner_defaults"],
+ host_supported: true,
+ crate_name: "metrics_events_generic",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.1.0",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: ["libserde"],
+}
diff --git a/vendor/generic/metrics_events/Cargo.toml b/vendor/generic/metrics_events/Cargo.toml
new file mode 100644
index 000000000..19d3a5ba5
--- /dev/null
+++ b/vendor/generic/metrics_events/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "metrics_events_generic"
+version = "0.1.0"
+authors = ["The ChromiumOS Authors"]
+edition = "2021"
+
+[dependencies]
+serde = { version = "1", features = ["derive"] }
diff --git a/vendor/generic/metrics_events/src/lib.rs b/vendor/generic/metrics_events/src/lib.rs
new file mode 100644
index 000000000..85a7075e5
--- /dev/null
+++ b/vendor/generic/metrics_events/src/lib.rs
@@ -0,0 +1,18 @@
+// Copyright 2024 The ChromiumOS Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use serde::Deserialize;
+use serde::Serialize;
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum MetricEventType {
+ // No events should ever be added to this enum - all events defined in
+ // upstream CrosVM should be added to the metrics_event package. Downstream
+ // projects can replace the generic metrics_event package if they need
+ // downstream only events.
+}
+
+pub struct RecordDetails {
+ // Similar to above, this is for downstream projects.
+}
diff --git a/vendor/generic/vm_control/Android.bp b/vendor/generic/vm_control/Android.bp
index 56b56b96b..1371389cd 100644
--- a/vendor/generic/vm_control/Android.bp
+++ b/vendor/generic/vm_control/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vfio_sys/Android.bp b/vfio_sys/Android.bp
index 2f2ddde0d..a8024daa0 100644
--- a/vfio_sys/Android.bp
+++ b/vfio_sys/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vhost/Android.bp b/vhost/Android.bp
index 1522e3ec3..86146a2fe 100644
--- a/vhost/Android.bp
+++ b/vhost/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/virtio_sys/Android.bp b/virtio_sys/Android.bp
index 92f33a5e1..da9f5cecb 100644
--- a/virtio_sys/Android.bp
+++ b/virtio_sys/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vm_control/Android.bp b/vm_control/Android.bp
index 032a4c921..ff9274ca5 100644
--- a/vm_control/Android.bp
+++ b/vm_control/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/vm_memory/Android.bp b/vm_memory/Android.bp
index 461e85a2f..5ab3db8e9 100644
--- a/vm_memory/Android.bp
+++ b/vm_memory/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/win_audio/src/win_audio_impl/mod.rs b/win_audio/src/win_audio_impl/mod.rs
index 7b8704773..1840e5160 100644
--- a/win_audio/src/win_audio_impl/mod.rs
+++ b/win_audio/src/win_audio_impl/mod.rs
@@ -1801,7 +1801,7 @@ mod tests {
use std::thread;
use cros_async::Executor;
- use metrics::MetricEventType;
+ use metrics::sys::WaveFormatDetails;
use winapi::shared::ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
use winapi::shared::mmreg::WAVEFORMATEX;
use winapi::shared::mmreg::WAVEFORMATEXTENSIBLE;
@@ -2038,8 +2038,8 @@ mod tests {
assert!(check_format(
&audio_client,
&format,
- WaveFormatDetailsProto::new(),
- MetricEventType::AudioFormatRequestOk,
+ WaveFormatDetails::default(),
+ AudioFormatEventType::RequestOk,
)
.is_ok());
@@ -2067,8 +2067,8 @@ mod tests {
assert!(check_format(
&audio_client,
&format,
- WaveFormatDetailsProto::new(),
- MetricEventType::AudioFormatRequestOk,
+ WaveFormatDetails::default(),
+ AudioFormatEventType::RequestOk,
)
.is_ok());
@@ -2097,8 +2097,8 @@ mod tests {
assert!(check_format(
&audio_client,
&format,
- WaveFormatDetailsProto::new(),
- MetricEventType::AudioFormatRequestOk,
+ WaveFormatDetails::default(),
+ AudioFormatEventType::RequestOk,
)
.is_err());
}
diff --git a/win_audio/src/win_audio_impl/wave_format.rs b/win_audio/src/win_audio_impl/wave_format.rs
index 9fd0eaf68..0e6ed6948 100644
--- a/win_audio/src/win_audio_impl/wave_format.rs
+++ b/win_audio/src/win_audio_impl/wave_format.rs
@@ -11,10 +11,9 @@ use base::error;
use base::info;
use base::warn;
use base::Error;
-use metrics::protos::event_details::wave_format::WaveFormatSubFormat;
-use metrics::protos::event_details::RecordDetails;
-use metrics::protos::event_details::WaveFormat;
-use metrics::protos::event_details::WaveFormatDetails;
+use metrics::sys::WaveFormat as WaveFormatMetric;
+use metrics::sys::WaveFormatDetails as WaveFormatDetailsMetric;
+use metrics::sys::WaveFormatSubFormat as WaveFormatSubFormatMetric;
use metrics::MetricEventType;
use winapi::shared::guiddef::IsEqualGUID;
use winapi::shared::guiddef::GUID;
@@ -46,10 +45,6 @@ use crate::WinAudioError;
use crate::MONO_CHANNEL_COUNT;
use crate::STEREO_CHANNEL_COUNT;
-pub type WaveFormatDetailsProto = WaveFormatDetails;
-pub type WaveFormatProto = WaveFormat;
-pub type SubFormatProto = WaveFormatSubFormat;
-
/// Wrapper around `WAVEFORMATEX` and `WAVEFORMATEXTENSIBLE` to hide some of the unsafe calls
/// that could be made.
pub enum WaveAudioFormat {
@@ -59,6 +54,12 @@ pub enum WaveAudioFormat {
WaveFormatExtensible(WAVEFORMATEXTENSIBLE),
}
+pub(crate) enum AudioFormatEventType {
+ RequestOk,
+ ModifiedOk,
+ Failed,
+}
+
impl WaveAudioFormat {
/// Wraps a WAVEFORMATEX pointer to make it's use more safe.
///
@@ -389,59 +390,51 @@ impl PartialEq for WaveAudioFormat {
}
}
-impl From<&WaveAudioFormat> for WaveFormatProto {
- fn from(format: &WaveAudioFormat) -> WaveFormatProto {
- let mut wave_format_proto = WaveFormatProto::new();
-
+impl From<&WaveAudioFormat> for WaveFormatMetric {
+ fn from(format: &WaveAudioFormat) -> WaveFormatMetric {
match format {
- WaveAudioFormat::WaveFormat(wave_format) => {
- wave_format_proto.set_format_tag(wave_format.wFormatTag.into());
- wave_format_proto.set_channels(wave_format.nChannels.into());
- wave_format_proto.set_samples_per_sec(
- wave_format
- .nSamplesPerSec
- .try_into()
- .expect("Failed to cast nSamplesPerSec to i32"),
- );
- wave_format_proto.set_avg_bytes_per_sec(
- wave_format
- .nAvgBytesPerSec
- .try_into()
- .expect("Failed to cast nAvgBytesPerSec"),
- );
- wave_format_proto.set_block_align(wave_format.nBlockAlign.into());
- wave_format_proto.set_bits_per_sample(wave_format.wBitsPerSample.into());
- wave_format_proto.set_size_bytes(wave_format.cbSize.into());
- }
+ WaveAudioFormat::WaveFormat(wave_format) => WaveFormatMetric {
+ format_tag: wave_format.wFormatTag.into(),
+ channels: wave_format.nChannels.into(),
+ samples_per_sec: wave_format
+ .nSamplesPerSec
+ .try_into()
+ .expect("Failed to cast nSamplesPerSec to i32"),
+ avg_bytes_per_sec: wave_format
+ .nAvgBytesPerSec
+ .try_into()
+ .expect("Failed to cast nAvgBytesPerSec"),
+ block_align: wave_format.nBlockAlign.into(),
+ bits_per_sample: wave_format.wBitsPerSample.into(),
+ size_bytes: wave_format.cbSize.into(),
+ samples: None,
+ channel_mask: None,
+ sub_format: None,
+ },
WaveAudioFormat::WaveFormatExtensible(wave_format_extensible) => {
- wave_format_proto.set_format_tag(wave_format_extensible.Format.wFormatTag.into());
- wave_format_proto.set_channels(wave_format_extensible.Format.nChannels.into());
- wave_format_proto.set_samples_per_sec(
- wave_format_extensible
+ let sub_format = wave_format_extensible.SubFormat;
+ WaveFormatMetric {
+ format_tag: wave_format_extensible.Format.wFormatTag.into(),
+ channels: wave_format_extensible.Format.nChannels.into(),
+ samples_per_sec: wave_format_extensible
.Format
.nSamplesPerSec
.try_into()
.expect("Failed to cast nSamplesPerSec to i32"),
- );
- wave_format_proto.set_avg_bytes_per_sec(
- wave_format_extensible
+ avg_bytes_per_sec: wave_format_extensible
.Format
.nAvgBytesPerSec
.try_into()
.expect("Failed to cast nAvgBytesPerSec"),
- );
- wave_format_proto.set_block_align(wave_format_extensible.Format.nBlockAlign.into());
- wave_format_proto
- .set_bits_per_sample(wave_format_extensible.Format.wBitsPerSample.into());
- wave_format_proto.set_size_bytes(wave_format_extensible.Format.cbSize.into());
- wave_format_proto.set_samples(wave_format_extensible.Samples.into());
- wave_format_proto.set_channel_mask(wave_format_extensible.dwChannelMask.into());
- let sub_format = wave_format_extensible.SubFormat;
- wave_format_proto.set_sub_format(GuidWrapper(&sub_format).into());
+ block_align: wave_format_extensible.Format.nBlockAlign.into(),
+ bits_per_sample: wave_format_extensible.Format.wBitsPerSample.into(),
+ size_bytes: wave_format_extensible.Format.cbSize.into(),
+ samples: Some(wave_format_extensible.Samples.into()),
+ channel_mask: Some(wave_format_extensible.dwChannelMask.into()),
+ sub_format: Some(GuidWrapper(&sub_format).into()),
+ }
}
}
-
- wave_format_proto
}
}
@@ -465,18 +458,18 @@ pub(crate) fn get_valid_mix_format(
WaveAudioFormat::new(format_ptr)
};
- let mut wave_format_details = WaveFormatDetailsProto::new();
- let mut event_code = MetricEventType::AudioFormatRequestOk;
- wave_format_details.requested = Some(WaveFormatProto::from(&format)).into();
+ let mut wave_format_details = WaveFormatDetailsMetric::default();
+ let mut event_code = AudioFormatEventType::RequestOk;
+ wave_format_details.requested = Some(WaveFormatMetric::from(&format));
info!("Printing mix format from `GetMixFormat`:\n{:?}", format);
const BIT_DEPTH: usize = 32;
format.modify_mix_format(BIT_DEPTH, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
- let modified_wave_format = Some(WaveFormatProto::from(&format)).into();
+ let modified_wave_format = Some(WaveFormatMetric::from(&format));
if modified_wave_format != wave_format_details.requested {
wave_format_details.modified = modified_wave_format;
- event_code = MetricEventType::AudioFormatModifiedOk;
+ event_code = AudioFormatEventType::ModifiedOk;
}
info!("Audio Engine Mix Format Used: \n{:?}", format);
@@ -491,8 +484,8 @@ pub(crate) fn get_valid_mix_format(
pub(crate) fn check_format(
audio_client: &IAudioClient,
format: &WaveAudioFormat,
- mut wave_format_details: WaveFormatDetailsProto,
- event_code: MetricEventType,
+ mut wave_format_details: WaveFormatDetailsMetric,
+ event_code: AudioFormatEventType,
) -> Result<(), WinAudioError> {
let mut closest_match_format: *mut WAVEFORMATEX = std::ptr::null_mut();
// SAFETY: All values passed into `IsFormatSupport` is owned by us and we will
@@ -511,8 +504,7 @@ pub(crate) fn check_format(
// SAFETY: If the `hr` value is `S_FALSE`, then `IsFormatSupported` must've
// given us a closest match.
let closest_match_enum = unsafe { WaveAudioFormat::new(closest_match_format) };
- wave_format_details.closest_matched =
- Some(WaveFormatProto::from(&closest_match_enum)).into();
+ wave_format_details.closest_matched = Some(WaveFormatMetric::from(&closest_match_enum));
error!(
"Current audio format not supported, the closest format is:\n{:?}",
@@ -526,7 +518,7 @@ pub(crate) fn check_format(
let last_error = Error::last();
// TODO:(b/253509368): Only upload for audio rendering, since these metrics can't
// differentiate between rendering and capture.
- upload_metrics(wave_format_details, MetricEventType::AudioFormatFailed);
+ upload_metrics(wave_format_details, AudioFormatEventType::Failed);
Err(WinAudioError::WindowsError(hr, last_error))
} else {
@@ -536,38 +528,38 @@ pub(crate) fn check_format(
}
}
-fn upload_metrics(
- wave_format_details: WaveFormatDetailsProto,
- metrics_event_code: MetricEventType,
-) {
- let mut details = RecordDetails::new();
- details.wave_format_details = Some(wave_format_details).into();
- metrics::log_event_with_details(metrics_event_code, &details);
+fn upload_metrics(details: WaveFormatDetailsMetric, event_type: AudioFormatEventType) {
+ let event = match event_type {
+ AudioFormatEventType::RequestOk => MetricEventType::AudioFormatRequestOk(details),
+ AudioFormatEventType::ModifiedOk => MetricEventType::AudioFormatModifiedOk(details),
+ AudioFormatEventType::Failed => MetricEventType::AudioFormatFailed(details),
+ };
+ metrics::log_event(event);
}
struct GuidWrapper<'a>(&'a GUID);
-impl<'a> From<GuidWrapper<'a>> for SubFormatProto {
- fn from(guid: GuidWrapper) -> SubFormatProto {
+impl<'a> From<GuidWrapper<'a>> for WaveFormatSubFormatMetric {
+ fn from(guid: GuidWrapper) -> WaveFormatSubFormatMetric {
let guid = guid.0;
if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_ANALOG) {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_ANALOG
+ WaveFormatSubFormatMetric::Analog
} else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_PCM) {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_PCM
+ WaveFormatSubFormatMetric::Pcm
} else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
+ WaveFormatSubFormatMetric::IeeeFloat
} else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_DRM) {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_DRM
+ WaveFormatSubFormatMetric::Drm
} else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_ALAW) {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_ALAW
+ WaveFormatSubFormatMetric::ALaw
} else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_MULAW) {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_MULAW
+ WaveFormatSubFormatMetric::MuLaw
} else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_ADPCM) {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_ADPCM
+ WaveFormatSubFormatMetric::Adpcm
} else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_MPEG) {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_MPEG
+ WaveFormatSubFormatMetric::Mpeg
} else {
- SubFormatProto::KSDATAFORMAT_SUBTYPE_INVALID
+ WaveFormatSubFormatMetric::Invalid
}
}
}
@@ -1150,18 +1142,22 @@ mod tests {
unsafe { WaveAudioFormat::new((&wave_format) as *const _ as *mut WAVEFORMATEX) };
// Testing the `into`.
- let wave_format_proto = WaveFormatProto::from(&wave_audio_format);
-
- let mut expected = WaveFormatProto::new();
- expected.set_format_tag(WAVE_FORMAT_PCM.into());
- expected.set_channels(2);
- expected.set_samples_per_sec(48000);
- expected.set_avg_bytes_per_sec(192000);
- expected.set_block_align(4);
- expected.set_bits_per_sample(16);
- expected.set_size_bytes(0);
-
- assert_eq!(wave_format_proto, expected);
+ let wave_format_metric = WaveFormatMetric::from(&wave_audio_format);
+
+ let expected = WaveFormatMetric {
+ format_tag: WAVE_FORMAT_PCM.into(),
+ channels: 2,
+ samples_per_sec: 48000,
+ avg_bytes_per_sec: 192000,
+ block_align: 4,
+ bits_per_sample: 16,
+ size_bytes: 0,
+ samples: None,
+ channel_mask: None,
+ sub_format: None,
+ };
+
+ assert_eq!(wave_format_metric, expected);
}
#[test]
@@ -1188,20 +1184,21 @@ mod tests {
};
// Testing the `into`.
- let wave_format_proto = WaveFormatProto::from(&wave_audio_format);
-
- let mut expected = WaveFormatProto::new();
- expected.set_format_tag(WAVE_FORMAT_EXTENSIBLE.into());
- expected.set_channels(2);
- expected.set_samples_per_sec(48000);
- expected.set_avg_bytes_per_sec(8 * 48000);
- expected.set_block_align(8);
- expected.set_bits_per_sample(32);
- expected.set_size_bytes(22);
- expected.set_samples(32);
- expected.set_channel_mask((SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) as i64);
- expected.set_sub_format(GuidWrapper(&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT).into());
-
- assert_eq!(wave_format_proto, expected);
+ let wave_format_metric = WaveFormatMetric::from(&wave_audio_format);
+
+ let expected = WaveFormatMetric {
+ format_tag: WAVE_FORMAT_EXTENSIBLE.into(),
+ channels: 2,
+ samples_per_sec: 48000,
+ avg_bytes_per_sec: 8 * 48000,
+ block_align: 8,
+ bits_per_sample: 32,
+ size_bytes: 22,
+ samples: Some(32),
+ channel_mask: Some((SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) as i64),
+ sub_format: Some(WaveFormatSubFormatMetric::IeeeFloat),
+ };
+
+ assert_eq!(wave_format_metric, expected);
}
}
diff --git a/x86_64/Android.bp b/x86_64/Android.bp
index 266c6031e..4c882f24e 100644
--- a/x86_64/Android.bp
+++ b/x86_64/Android.bp
@@ -1,5 +1,6 @@
// This file is generated by cargo_embargo.
-// Do not modify this file as most changes will be overridden on upgrade.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
// Content before the first "rust_*" or "genrule" module is preserved.
package {
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index c0356b42a..6202e2400 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -1004,8 +1004,8 @@ impl arch::LinuxArch for X8664arch {
let pci_start = read_pci_mmio_before_32bit().start;
let mut vcpu_init = vec![VcpuInitX86_64::default(); vcpu_count];
+ let mut msrs = BTreeMap::new();
- let mut msrs;
match components.vm_image {
VmImage::Bios(ref mut bios) => {
// Allow a bios to hardcode CMDLINE_OFFSET and read the kernel command line from it.
@@ -1016,7 +1016,7 @@ impl arch::LinuxArch for X8664arch {
)
.map_err(Error::LoadCmdline)?;
Self::load_bios(&mem, bios)?;
- msrs = regs::default_msrs();
+ regs::set_default_msrs(&mut msrs);
// The default values for `Regs` and `Sregs` already set up the reset vector.
}
VmImage::Kernel(ref mut kernel_image) => {
@@ -1039,8 +1039,8 @@ impl arch::LinuxArch for X8664arch {
vcpu_init[0].regs.rsp = BOOT_STACK_POINTER;
vcpu_init[0].regs.rsi = ZERO_PAGE_OFFSET;
- msrs = regs::long_mode_msrs();
- msrs.append(&mut regs::mtrr_msrs(&vm, pci_start));
+ regs::set_long_mode_msrs(&mut msrs);
+ regs::set_mtrr_msrs(&mut msrs, &vm, pci_start);
// Set up long mode and enable paging.
regs::configure_segments_and_sregs(&mem, &mut vcpu_init[0].sregs)
@@ -1111,23 +1111,25 @@ impl arch::LinuxArch for X8664arch {
let vcpu_supported_var_mtrrs = regs::vcpu_supported_variable_mtrrs(vcpu);
let num_var_mtrrs = regs::count_variable_mtrrs(&vcpu_init.msrs);
- let msrs = if num_var_mtrrs > vcpu_supported_var_mtrrs {
+ let skip_mtrr_msrs = if num_var_mtrrs > vcpu_supported_var_mtrrs {
warn!(
"Too many variable MTRR entries ({} required, {} supported),
please check pci_start addr, guest with pass through device may be very slow",
num_var_mtrrs, vcpu_supported_var_mtrrs,
);
// Filter out the MTRR entries from the MSR list.
- vcpu_init
- .msrs
- .into_iter()
- .filter(|&msr| !regs::is_mtrr_msr(msr.id))
- .collect()
+ true
} else {
- vcpu_init.msrs
+ false
};
- vcpu.set_msrs(&msrs).map_err(Error::SetupMsrs)?;
+ for (msr_index, value) in vcpu_init.msrs.into_iter() {
+ if skip_mtrr_msrs && regs::is_mtrr_msr(msr_index) {
+ continue;
+ }
+
+ vcpu.set_msr(msr_index, value).map_err(Error::SetupMsrs)?;
+ }
interrupts::set_lint(vcpu_id, irq_chip).map_err(Error::SetLint)?;
diff --git a/x86_64/src/regs.rs b/x86_64/src/regs.rs
index 2dc48b88c..cbdd189ec 100644
--- a/x86_64/src/regs.rs
+++ b/x86_64/src/regs.rs
@@ -2,11 +2,11 @@
// 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::mem;
use std::result;
use base::warn;
-use hypervisor::Register;
use hypervisor::Sregs;
use hypervisor::VcpuX86_64;
use hypervisor::Vm;
@@ -98,15 +98,12 @@ fn get_mtrr_pairs(base: u64, len: u64) -> Vec<(u64, u64)> {
/// Returns the number of variable MTRR entries supported by `vcpu`.
pub fn vcpu_supported_variable_mtrrs(vcpu: &dyn VcpuX86_64) -> usize {
// Get VAR MTRR num from MSR_MTRRcap
- let mut msrs = vec![Register {
- id: crate::msr_index::MSR_MTRRcap,
- ..Default::default()
- }];
- if vcpu.get_msrs(&mut msrs).is_err() {
- warn!("get msrs fail, guest with pass through device may be very slow");
- 0
- } else {
- (msrs[0].value & VAR_MTRR_NUM_MASK) as usize
+ match vcpu.get_msr(crate::msr_index::MSR_MTRRcap) {
+ Ok(value) => (value & VAR_MTRR_NUM_MASK) as usize,
+ Err(_e) => {
+ warn!("failed to get MSR_MTRRcap, guests with passthrough devices may be very slow");
+ 0
+ }
}
}
@@ -119,105 +116,64 @@ pub fn is_mtrr_msr(id: u32) -> bool {
}
/// Returns the count of variable MTRR entries specified by the list of `msrs`.
-pub fn count_variable_mtrrs(msrs: &[Register]) -> usize {
+pub fn count_variable_mtrrs(msrs: &BTreeMap<u32, u64>) -> usize {
// Each variable MTRR takes up two MSRs (base + mask), so divide by 2. This will also count the
// MTRRdefType entry, but that is only one extra and the division truncates, so it won't affect
// the final count.
- msrs.iter().filter(|msr| is_mtrr_msr(msr.id)).count() / 2
+ msrs.keys().filter(|&msr| is_mtrr_msr(*msr)).count() / 2
}
/// Returns a set of MSRs containing the MTRR configuration.
-pub fn mtrr_msrs(vm: &dyn Vm, pci_start: u64) -> Vec<Register> {
+pub fn set_mtrr_msrs(msrs: &mut BTreeMap<u32, u64>, vm: &dyn Vm, pci_start: u64) {
// Set pci_start .. 4G as UC
// all others are set to default WB
let pci_len = (1 << 32) - pci_start;
let vecs = get_mtrr_pairs(pci_start, pci_len);
- let mut entries = Vec::new();
-
let phys_mask: u64 = (1 << vm.get_guest_phys_addr_bits()) - 1;
for (idx, (base, len)) in vecs.iter().enumerate() {
let reg_idx = idx as u32 * 2;
- entries.push(Register {
- id: MTRR_PHYS_BASE_MSR + reg_idx,
- value: base | MTRR_MEMTYPE_UC as u64,
- });
+ msrs.insert(MTRR_PHYS_BASE_MSR + reg_idx, base | MTRR_MEMTYPE_UC as u64);
let mask: u64 = len.wrapping_neg() & phys_mask | MTRR_VAR_VALID;
- entries.push(Register {
- id: MTRR_PHYS_MASK_MSR + reg_idx,
- value: mask,
- });
+ msrs.insert(MTRR_PHYS_MASK_MSR + reg_idx, mask);
}
// Disable fixed MTRRs and enable variable MTRRs, set default type as WB
- entries.push(Register {
- id: crate::msr_index::MSR_MTRRdefType,
- value: MTRR_ENABLE | MTRR_MEMTYPE_WB as u64,
- });
- entries
+ msrs.insert(
+ crate::msr_index::MSR_MTRRdefType,
+ MTRR_ENABLE | MTRR_MEMTYPE_WB as u64,
+ );
}
/// Returns the default value of MSRs at reset.
///
/// Currently only sets IA32_TSC to 0.
-pub fn default_msrs() -> Vec<Register> {
- vec![
- Register {
- id: crate::msr_index::MSR_IA32_TSC,
- value: 0x0,
- },
- Register {
- id: crate::msr_index::MSR_IA32_MISC_ENABLE,
- value: crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64,
- },
- ]
+pub fn set_default_msrs(msrs: &mut BTreeMap<u32, u64>) {
+ msrs.insert(crate::msr_index::MSR_IA32_TSC, 0x0);
+ msrs.insert(
+ crate::msr_index::MSR_IA32_MISC_ENABLE,
+ crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64,
+ );
}
/// Configure Model specific registers for long (64-bit) mode.
-pub fn long_mode_msrs() -> Vec<Register> {
- vec![
- Register {
- id: crate::msr_index::MSR_IA32_SYSENTER_CS,
- value: 0x0,
- },
- Register {
- id: crate::msr_index::MSR_IA32_SYSENTER_ESP,
- value: 0x0,
- },
- Register {
- id: crate::msr_index::MSR_IA32_SYSENTER_EIP,
- value: 0x0,
- },
- // x86_64 specific msrs, we only run on x86_64 not x86
- Register {
- id: crate::msr_index::MSR_STAR,
- value: 0x0,
- },
- Register {
- id: crate::msr_index::MSR_CSTAR,
- value: 0x0,
- },
- Register {
- id: crate::msr_index::MSR_KERNEL_GS_BASE,
- value: 0x0,
- },
- Register {
- id: crate::msr_index::MSR_SYSCALL_MASK,
- value: 0x0,
- },
- Register {
- id: crate::msr_index::MSR_LSTAR,
- value: 0x0,
- },
- // end of x86_64 specific code
- Register {
- id: crate::msr_index::MSR_IA32_TSC,
- value: 0x0,
- },
- Register {
- id: crate::msr_index::MSR_IA32_MISC_ENABLE,
- value: crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64,
- },
- ]
+pub fn set_long_mode_msrs(msrs: &mut BTreeMap<u32, u64>) {
+ msrs.insert(crate::msr_index::MSR_IA32_SYSENTER_CS, 0x0);
+ msrs.insert(crate::msr_index::MSR_IA32_SYSENTER_ESP, 0x0);
+ msrs.insert(crate::msr_index::MSR_IA32_SYSENTER_EIP, 0x0);
+
+ // x86_64 specific msrs, we only run on x86_64 not x86
+ msrs.insert(crate::msr_index::MSR_STAR, 0x0);
+ msrs.insert(crate::msr_index::MSR_CSTAR, 0x0);
+ msrs.insert(crate::msr_index::MSR_KERNEL_GS_BASE, 0x0);
+ msrs.insert(crate::msr_index::MSR_SYSCALL_MASK, 0x0);
+ msrs.insert(crate::msr_index::MSR_LSTAR, 0x0);
+ // end of x86_64 specific code
+
+ msrs.insert(crate::msr_index::MSR_IA32_TSC, 0x0);
+ msrs.insert(
+ crate::msr_index::MSR_IA32_MISC_ENABLE,
+ crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64,
+ );
}
const X86_CR0_PE: u64 = 0x1;
diff --git a/x86_64/tests/integration/main.rs b/x86_64/tests/integration/main.rs
index 4948f5916..9c617b27f 100644
--- a/x86_64/tests/integration/main.rs
+++ b/x86_64/tests/integration/main.rs
@@ -45,8 +45,8 @@ use x86_64::mptable;
use x86_64::read_pci_mmio_before_32bit;
use x86_64::read_pcie_cfg_mmio;
use x86_64::regs::configure_segments_and_sregs;
-use x86_64::regs::long_mode_msrs;
-use x86_64::regs::mtrr_msrs;
+use x86_64::regs::set_long_mode_msrs;
+use x86_64::regs::set_mtrr_msrs;
use x86_64::regs::setup_page_tables;
use x86_64::smbios;
use x86_64::X8664arch;
@@ -281,9 +281,12 @@ where
setup_cpuid(&hyp, &irq_chip, &vcpu, 0, 1, cpu_config).unwrap();
}
- let mut msrs = long_mode_msrs();
- msrs.append(&mut mtrr_msrs(&vm, read_pci_mmio_before_32bit().start));
- vcpu.set_msrs(&msrs).unwrap();
+ let mut msrs = BTreeMap::new();
+ set_long_mode_msrs(&mut msrs);
+ set_mtrr_msrs(&mut msrs, &vm, read_pci_mmio_before_32bit().start);
+ for (msr_index, value) in msrs {
+ vcpu.set_msr(msr_index, value).unwrap();
+ }
let mut vcpu_regs = Regs {
rip: start_addr.offset(),