summary refs log tree commit diff
path: root/protos
diff options
context:
space:
mode:
authorDavid Tolnay <dtolnay@chromium.org>2019-04-05 20:34:50 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-04-12 14:49:57 -0700
commit65928af6c9ebf46abcd4fbd908fc76137e9843cd (patch)
tree5aaacc0e15f10031d09de71dd81c6b5c1e254a81 /protos
parent1aca8b72694fcbefc4f31bf49f41e7491ad29db4 (diff)
downloadcrosvm-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.toml5
-rw-r--r--protos/build.rs98
-rw-r--r--protos/src/lib.rs10
-rw-r--r--protos/src/plugin.proto405
-rw-r--r--protos/src/plugin.rs32
-rw-r--r--protos/tests/trunks.rs2
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;