diff options
author | David Tolnay <dtolnay@chromium.org> | 2019-04-05 20:34:50 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-04-12 14:49:57 -0700 |
commit | 65928af6c9ebf46abcd4fbd908fc76137e9843cd (patch) | |
tree | 5aaacc0e15f10031d09de71dd81c6b5c1e254a81 /protos | |
parent | 1aca8b72694fcbefc4f31bf49f41e7491ad29db4 (diff) | |
download | crosvm-65928af6c9ebf46abcd4fbd908fc76137e9843cd.tar crosvm-65928af6c9ebf46abcd4fbd908fc76137e9843cd.tar.gz crosvm-65928af6c9ebf46abcd4fbd908fc76137e9843cd.tar.bz2 crosvm-65928af6c9ebf46abcd4fbd908fc76137e9843cd.tar.lz crosvm-65928af6c9ebf46abcd4fbd908fc76137e9843cd.tar.xz crosvm-65928af6c9ebf46abcd4fbd908fc76137e9843cd.tar.zst crosvm-65928af6c9ebf46abcd4fbd908fc76137e9843cd.zip |
protos: Merge plugin_proto crate under protos::plugin
This de-duplicates the two separate build.rs files dealing with proto compilation. The trunks interface.proto will be exposed under protos::trunks and the plugin proto will be exposed under protos::plugin. BUG=none TEST=cargo check TEST=cargo check --features tpm TEST=cargo check --features plugin TEST=cargo check --features tpm,plugin TEST=FEATURES=test emerge-nami crosvm TEST=FEATURES=test USE=crosvm-tpm emerge-nami crosvm TEST=FEATURES=test USE=crosvm-plugin emerge-nami crosvm TEST=FEATURES=test USE='crosvm-tpm crosvm-plugin' emerge-nami crosvm TEST=local kokoro CQ-DEPEND=CL:1553971 Change-Id: I203b654a38e9d671a508156ae06dfb6f70047c4f Reviewed-on: https://chromium-review.googlesource.com/1556417 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: David Tolnay <dtolnay@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'protos')
-rw-r--r-- | protos/Cargo.toml | 5 | ||||
-rw-r--r-- | protos/build.rs | 98 | ||||
-rw-r--r-- | protos/src/lib.rs | 10 | ||||
-rw-r--r-- | protos/src/plugin.proto | 405 | ||||
-rw-r--r-- | protos/src/plugin.rs | 32 | ||||
-rw-r--r-- | protos/tests/trunks.rs | 2 |
6 files changed, 516 insertions, 36 deletions
diff --git a/protos/Cargo.toml b/protos/Cargo.toml index 1bc2c49..9fd0e5a 100644 --- a/protos/Cargo.toml +++ b/protos/Cargo.toml @@ -4,7 +4,12 @@ version = "0.1.0" authors = ["The Chromium OS Authors"] edition = "2018" +[features] +plugin = ["kvm_sys"] +trunks = [] + [dependencies] +kvm_sys = { path = "../kvm_sys", optional = true } protobuf = "2.3" [build-dependencies] diff --git a/protos/build.rs b/protos/build.rs index f4f5d97..fa839ca 100644 --- a/protos/build.rs +++ b/protos/build.rs @@ -4,15 +4,15 @@ use std::env; use std::error::Error; -use std::fs::File; +use std::fs::{self, File}; use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; type Result<T> = std::result::Result<T, Box<dyn Error>>; -struct Proto { - // Where to find protos during builds within cros_sdk. Relative to $SYSROOT - // environment variable set by emerge builds. +struct ExternalProto { + // Where to find protos during builds within cros_sdk. Relative to + // $SYSROOT environment variable set by emerge builds. dir_relative_to_sysroot: &'static str, // Where to find protos during "cargo build" in a local developer @@ -22,57 +22,85 @@ struct Proto { // *.proto file expected to exist in both of the above directories. proto_file_name: &'static str, - // Code generated by proto compiler will be exported under - // protos::$module_name. - name_for_module: &'static str, + // Code generated by proto compiler will be placed under + // protos::generated::$module_name. + module: &'static str, } -static PROTOS: &[Proto] = &[Proto { - dir_relative_to_sysroot: "usr/include/chromeos/dbus/trunks", - dir_relative_to_us: "../../../platform2/trunks", - proto_file_name: "interface.proto", - name_for_module: "trunks", -}]; - -fn paths_to_strs(paths: &[PathBuf]) -> Vec<&str> { - paths - .iter() - .map(|p| p.as_os_str().to_str().unwrap()) - .collect() +// Rustfmt bug: https://github.com/rust-lang/rustfmt/issues/3498 +#[rustfmt::skip] +static EXTERNAL_PROTOS: &[ExternalProto] = &[ + #[cfg(feature = "trunks")] + ExternalProto { + dir_relative_to_sysroot: "usr/include/chromeos/dbus/trunks", + dir_relative_to_us: "../../../platform2/trunks", + proto_file_name: "interface.proto", + module: "trunks", + }, +]; + +struct LocalProto { + // Corresponding to the input file src/$module.proto. + module: &'static str, } +#[rustfmt::skip] +static LOCAL_PROTOS: &[LocalProto] = &[ + #[cfg(feature = "plugin")] + LocalProto { module: "plugin" }, +]; + fn main() -> Result<()> { let out_dir = env::var("OUT_DIR")?; let sysroot = env::var_os("SYSROOT"); // Write out a Rust module that imports the modules generated by protoc. - let protos_rs = PathBuf::from(&out_dir).join("protos.rs"); - let mut out = File::create(protos_rs)?; - - let mut input_files = Vec::new(); - let mut include_dirs = Vec::new(); + let generated = PathBuf::from(&out_dir).join("generated.rs"); + let out = File::create(generated)?; - for proto in PROTOS { + // Compile external protos. + for proto in EXTERNAL_PROTOS { let dir = match &sysroot { Some(dir) => PathBuf::from(dir).join(proto.dir_relative_to_sysroot), None => PathBuf::from(proto.dir_relative_to_us), }; + let input_path = dir.join(proto.proto_file_name); + protoc(proto.module, input_path, &out)?; + } - input_files.push(dir.join(proto.proto_file_name)); - include_dirs.push(dir); - - let generated_module = proto.proto_file_name.replace(".proto", ""); - writeln!(out, "#[path = \"{}/{}.rs\"]", out_dir, generated_module)?; - writeln!(out, "pub mod {};", proto.name_for_module)?; + // Compile protos from the local src directory. + for proto in LOCAL_PROTOS { + let input_path = format!("src/{}.proto", proto.module); + protoc(proto.module, input_path, &out)?; } - // Invoke proto compiler. + Ok(()) +} + +// Compile a single proto file located at $input_path, placing the generated +// code at $OUT_DIR/$module and emitting the right `pub mod $module` into the +// output file. +fn protoc<P: AsRef<Path>>(module: &str, input_path: P, mut out: &File) -> Result<()> { + let input_path = input_path.as_ref(); + let input_dir = input_path.parent().unwrap(); + + // Place output in a subdirectory so that different protos with the same + // common filename (like interface.proto) do not conflict. + let out_dir = format!("{}/{}", env::var("OUT_DIR")?, module); + fs::create_dir_all(&out_dir)?; + + // Invoke protobuf compiler. protoc_rust::run(protoc_rust::Args { out_dir: &out_dir, - input: &paths_to_strs(&input_files), - includes: &paths_to_strs(&include_dirs), + includes: &[input_dir.as_os_str().to_str().unwrap()], + input: &[input_path.as_os_str().to_str().unwrap()], ..Default::default() })?; + // Write out a `mod` that refers to the generated module. + let file_stem = input_path.file_stem().unwrap().to_str().unwrap(); + writeln!(out, "#[path = \"{}/{}.rs\"]", out_dir, file_stem)?; + writeln!(out, "pub mod {};", module)?; + Ok(()) } diff --git a/protos/src/lib.rs b/protos/src/lib.rs index 5381935..3fdbdc0 100644 --- a/protos/src/lib.rs +++ b/protos/src/lib.rs @@ -2,4 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -include!(concat!(env!("OUT_DIR"), "/protos.rs")); +mod generated { + include!(concat!(env!("OUT_DIR"), "/generated.rs")); +} + +#[cfg(feature = "plugin")] +pub mod plugin; + +#[cfg(feature = "trunks")] +pub use generated::trunks; diff --git a/protos/src/plugin.proto b/protos/src/plugin.proto new file mode 100644 index 0000000..5b6eec2 --- /dev/null +++ b/protos/src/plugin.proto @@ -0,0 +1,405 @@ +syntax = "proto3"; + +// The protocol defined here is actually two sub-protocols, one protocol for control of the main +// process (MainRequest/MainResponse), and one for control of each VCPU thread +// (VcpuRequest/VcpuResponse). Each protocol works the same: the client creates a protobuf of either +// a MainRequest or VcpuRequest, sends it encoded over the main socket or one of the vcpu sockets, +// reads the the MainResponse or VcpuResponse over the same socket and decodes it as a protobuf. For +// specific information on the purpose of each request, see the C API in crosvm.h. Most requests +// here map 1:1 to a function in that API. Only the intricacies unique to the wire protocol are +// commented on here. + +enum AddressSpace { + IOPORT = 0; + MMIO = 1; + } + +message CpuidEntry { + uint32 function = 1; + bool has_index = 3; + uint32 index = 4; + uint32 eax = 5; + uint32 ebx = 6; + uint32 ecx = 7; + uint32 edx = 8; +} + +// A request made to the crosvm main process that affects the global aspects of the VM. +message MainRequest { + // Every message under the Create namespace will instantiate an object with the given ID. The + // type of object is determined by the oneof constructor field. + message Create { + message IoEvent { + AddressSpace space = 1; + uint64 address = 2; + uint32 length = 3; + uint64 datamatch = 4; + } + + message Memory { + uint64 offset = 1; + uint64 start = 2; + uint64 length = 3; + bool read_only = 4; + // Must be true for the MemoryDirtyLog method to work on this object. + bool dirty_log = 5; + } + + message IrqEvent { + uint32 irq_id = 1; + bool resample = 2; + } + + uint32 id = 1; + oneof constructor { + IoEvent io_event = 2; + // This message also requires a memfd sent over the socket. + Memory memory = 3; + IrqEvent irq_event = 4; + } + } + + // No matter what the type an object is, it can be destroyed using this common method. + message Destroy { + uint32 id = 1; + } + + message NewConnection {} + + message GetShutdownEventfd {} + + message CheckExtension { + uint32 extension = 1; + } + + message CpuidRequest { + } + + message MsrListRequest { + } + + message GetNetConfig {} + + message ReserveRange { + AddressSpace space = 1; + uint64 start = 2; + uint64 length = 3; + } + + message SetIrq { + uint32 irq_id = 1; + bool active = 2; + } + + message SetIrqRouting { + message Route { + message Irqchip { + uint32 irqchip = 1; + uint32 pin = 2; + } + + message Msi { + uint64 address = 1; + uint32 data = 2; + } + + uint32 irq_id = 1; + oneof route { + Irqchip irqchip = 2; + Msi msi = 3; + } + } + + repeated Route routes = 1; + } + + // Each type refers to certain piece of VM state (such as PIT state). + // The structure of the data corresponds to the kvm structure. + enum StateSet { + // struct kvm_pic_state + PIC0 = 0; + // struct kvm_pic_state + PIC1 = 1; + // struct kvm_ioapic_state + IOAPIC = 2; + // struct kvm_pit_state2 + PIT = 3; + // struct kvm_clock_data + CLOCK = 4; + } + + message GetState { + StateSet set = 1; + } + + message SetState { + StateSet set = 1; + // The in memory representation of certain state, depending on the value + // of the StateSet. + bytes state = 2; + } + + message SetIdentityMapAddr { + uint32 address = 1; + } + + message PauseVcpus { + uint64 cpu_mask = 1; + uint64 user = 2; + } + + message GetVcpus {} + message Start {} + + message MemoryDirtyLog { + uint32 id = 1; + } + + // The type of the message is determined by which of these oneof fields is present in the + // protobuf. + oneof message { + // Common method for instantiating a new object of any type. + Create create = 1; + // Common method for destroying an object of any type. + Destroy destroy = 2; + NewConnection new_connection = 3; + GetShutdownEventfd get_shutdown_eventfd = 4; + CheckExtension check_extension = 5; + CpuidRequest get_supported_cpuid = 6; + CpuidRequest get_emulated_cpuid = 7; + MsrListRequest get_msr_index_list = 8; + GetNetConfig get_net_config = 9; + ReserveRange reserve_range = 10; + SetIrq set_irq = 11; + SetIrqRouting set_irq_routing = 12; + GetState get_state = 13; + SetState set_state = 14; + SetIdentityMapAddr set_identity_map_addr = 15; + PauseVcpus pause_vcpus = 16; + GetVcpus get_vcpus = 17; + Start start = 18; + // Method for a Memory type object for retrieving the dirty bitmap. Only valid if the memory + // object was created with dirty_log set. + MemoryDirtyLog dirty_log = 101; + } +} + +message MainResponse { + // Depending on the object that was created, an fd might also come from the socket. + message Create {} + message Destroy {} + // NewMessage receives a socket fd along with the data from reading this socket. + // The returned socket can be used totally independently of the original socket, and can perform + // requests and responses independent of the other sockets. + message NewConnection {} + message GetShutdownEventfd {} + message CheckExtension { + bool has_extension = 1; + } + message CpuidResponse { + repeated CpuidEntry entries = 1; + } + message MsrListResponse { + repeated uint32 indices = 1; + } + + // GetNetConfig messages also return a file descriptor for the tap device. + message GetNetConfig { + bytes host_mac_address = 1; + fixed32 host_ipv4_address = 2; + fixed32 netmask = 3; + } + + message ReserveRange {} + message SetIrq {} + message SetIrqRouting {} + message GetState { + // The in memory representation of a state, depending on what StateSet was + // requested in GetState. + bytes state = 1; + } + message SetState {} + message SetIdentityMapAddr {} + message PauseVcpus {} + // This message should also receive a socket fd per VCPU along with the data from reading this + // socket. The VcpuRequest/VcpuResponse protocol is run over each of the returned fds. + message GetVcpus {} + message Start {} + message MemoryDirtyLog { + bytes bitmap = 1; + } + + // This is zero on success, and a negative integer on failure. + sint32 errno = 1; + // The field present here is always the same as the one present in the corresponding + // MainRequest. + oneof message { + Create create = 2; + Destroy destroy = 3; + NewConnection new_connection = 4; + GetShutdownEventfd get_shutdown_eventfd = 5; + CheckExtension check_extension = 6; + CpuidResponse get_supported_cpuid = 7; + CpuidResponse get_emulated_cpuid = 8; + MsrListResponse get_msr_index_list = 9; + GetNetConfig get_net_config = 10; + ReserveRange reserve_range = 11; + SetIrq set_irq = 12; + SetIrqRouting set_irq_routing = 13; + GetState get_state = 14; + SetState set_state = 15; + SetIdentityMapAddr set_identity_map_addr = 16; + PauseVcpus pause_vcpus = 17; + GetVcpus get_vcpus = 18; + Start start = 19; + MemoryDirtyLog dirty_log = 101; + } +} + +// A request made for a specific VCPU. These requests are sent over the sockets returned from the +// GetVcpu MainRequest. +message VcpuRequest { + // This message will block until a non-empty response can be sent. The first response will + // always be an Init wait reason. + message Wait { + } + + message Resume { + // The data is only necessary for non-write (read) I/O accesses. In all other cases, data is + // ignored. + bytes data = 1; + } + + // Each type refers to certain piece of VCPU state (a set registers, or something else). + // The structure of the data corresponds to the kvm structure. + enum StateSet { + // struct kvm_regs + REGS = 0; + // struct kvm_sregs + SREGS = 1; + // struct kvm_fpu + FPU = 2; + // struct kvm_debugregs + DEBUGREGS = 3; + // struct kvm_lapic_state + LAPIC = 4; + // struct kvm_mp_state + MP = 5; + // struct kvm_xcrs + XCREGS = 6; + // struct kvm_vcpu_events + EVENTS = 7; + } + + message GetState { + StateSet set = 1; + } + + message SetState { + StateSet set = 1; + // The in memory representation of a struct kvm_regs, struct kvm_sregs, + // struct kvm_fpu, struct kvm_debugregs, struct kvm_lapic_state, + // struct kvm_mp_state, struct kvm_xcrs or struct kvm_vcpu_events + // depending on the value of the StateSet. + bytes state = 2; + } + + message GetMsrs { + // The entry data will be returned in the same order as this in the + // VcpuResponse::GetMsrs::entry_data array. + repeated uint32 entry_indices = 1; + } + + message MsrEntry { + uint32 index = 1; + uint64 data = 2; + } + + message SetMsrs { + repeated MsrEntry entries = 1; + } + + message SetCpuid { + repeated CpuidEntry entries = 1; + } + + // The type of the message is determined by which of these oneof fields is present in the + // protobuf. + oneof message { + Wait wait = 1; + Resume resume = 2; + GetState get_state = 3; + SetState set_state = 4; + GetMsrs get_msrs = 5; + SetMsrs set_msrs = 6; + SetCpuid set_cpuid = 7; + } +} + + +message VcpuResponse { + // Depending on the reason a VCPU has exited, the Wait request will unblock and return a field + // in the oneof exit. This is called the "wait reason." + message Wait { + // This request will always be the first wait reason returend by the first wait request. + message Init { + } + + // This type of wait reason is only generated if the access occurred on this VCPU on an + // address previously reserved by a ReserveRange main request. + message Io { + AddressSpace space = 1; + uint64 address = 2; + bool is_write = 3; + bytes data = 4; + } + + // This type of wait reason is only generated after a PuaseVcpus request on this VCPU. + message User { + uint64 user = 1; + } + + oneof exit { + Init init = 1; + Io io = 2; + User user = 3; + } + } + + message Resume {} + + message GetState { + // The in memory representation of a struct kvm_regs, struct kvm_sregs, + // struct kvm_fpu, struct kvm_debugregs, struct kvm_lapic_state, + // struct kvm_mp_state, struct kvm_xcrs or struct kvm_vcpu_events + // depending on the value of the StateSet. + bytes state = 1; + } + + message SetState { + } + + message GetMsrs { + // The order of the entry_data values is the same order as the array of indices given in the + // corresponding request. + repeated uint64 entry_data = 1; + } + + message SetMsrs {} + + message SetCpuid {} + + // This is zero on success, and a negative integer on failure. + sint32 errno = 1; + // The field present here is always the same as the one present in the corresponding + // VcpuRequest. + oneof message { + Wait wait = 2; + Resume resume = 3; + GetState get_state = 4; + SetState set_state = 5; + GetMsrs get_msrs = 6; + SetMsrs set_msrs = 7; + SetCpuid set_cpuid = 8; + } +} diff --git a/protos/src/plugin.rs b/protos/src/plugin.rs new file mode 100644 index 0000000..3e58fe3 --- /dev/null +++ b/protos/src/plugin.rs @@ -0,0 +1,32 @@ +pub use crate::generated::plugin::*; + +/// Converts protobuf representation of CpuId data into KVM format. +pub fn cpuid_proto_to_kvm(entry: &CpuidEntry) -> kvm_sys::kvm_cpuid_entry2 { + // Safe: C structures are expected to be zero-initialized. + let mut e: kvm_sys::kvm_cpuid_entry2 = unsafe { std::mem::zeroed() }; + e.function = entry.function; + if entry.has_index { + e.flags = kvm_sys::KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + } + e.index = entry.index; + e.eax = entry.eax; + e.ebx = entry.ebx; + e.ecx = entry.ecx; + e.edx = entry.edx; + + e +} + +/// Converts KVM representation of CpuId data into protobuf format. +pub fn cpuid_kvm_to_proto(entry: &kvm_sys::kvm_cpuid_entry2) -> CpuidEntry { + let mut e = CpuidEntry::new(); + e.function = entry.function; + e.has_index = entry.flags & kvm_sys::KVM_CPUID_FLAG_SIGNIFCANT_INDEX != 0; + e.index = entry.index; + e.eax = entry.eax; + e.ebx = entry.ebx; + e.ecx = entry.ecx; + e.edx = entry.edx; + + e +} diff --git a/protos/tests/trunks.rs b/protos/tests/trunks.rs index 574c110..39723ae 100644 --- a/protos/tests/trunks.rs +++ b/protos/tests/trunks.rs @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#![cfg(feature = "trunks")] + mod common; use crate::common::test_round_trip; |