From 5f26fb292debfa2942b3b99ae315ea0c437bdc15 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Tue, 17 Sep 2024 16:40:15 +0200 Subject: [PATCH 1/3] moved present uhyve interface into v1 folder --- src/hypercall.rs | 5 +- src/linux/x86_64/kvm_cpu.rs | 2 +- src/macos/aarch64/vcpu.rs | 2 +- src/macos/x86_64/vcpu.rs | 2 +- src/vm.rs | 2 +- tests/test-kernels/src/bin/serial.rs | 5 +- uhyve-interface/src/lib.rs | 131 +-------------------- uhyve-interface/src/v1/mod.rs | 111 +++++++++++++++++ uhyve-interface/src/{ => v1}/parameters.rs | 24 ++-- 9 files changed, 138 insertions(+), 146 deletions(-) create mode 100644 uhyve-interface/src/v1/mod.rs rename uhyve-interface/src/{ => v1}/parameters.rs (76%) diff --git a/src/hypercall.rs b/src/hypercall.rs index 9e667f9d..e36dafa4 100644 --- a/src/hypercall.rs +++ b/src/hypercall.rs @@ -4,7 +4,10 @@ use std::{ os::unix::ffi::OsStrExt, }; -use uhyve_interface::{parameters::*, GuestPhysAddr, Hypercall, HypercallAddress, MAX_ARGC_ENVC}; +use uhyve_interface::{ + v1::{parameters::*, Hypercall, HypercallAddress, MAX_ARGC_ENVC}, + GuestPhysAddr, +}; use crate::{ consts::BOOT_PML4, diff --git a/src/linux/x86_64/kvm_cpu.rs b/src/linux/x86_64/kvm_cpu.rs index ac31c2fd..6ab4872c 100644 --- a/src/linux/x86_64/kvm_cpu.rs +++ b/src/linux/x86_64/kvm_cpu.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use kvm_bindings::*; use kvm_ioctls::{VcpuExit, VcpuFd, VmFd}; -use uhyve_interface::{GuestPhysAddr, Hypercall}; +use uhyve_interface::{v1::Hypercall, GuestPhysAddr}; use vmm_sys_util::eventfd::EventFd; use x86_64::registers::control::{Cr0Flags, Cr4Flags}; diff --git a/src/macos/aarch64/vcpu.rs b/src/macos/aarch64/vcpu.rs index bdfbb614..73b0b4cc 100644 --- a/src/macos/aarch64/vcpu.rs +++ b/src/macos/aarch64/vcpu.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use log::debug; -use uhyve_interface::{GuestPhysAddr, Hypercall}; +use uhyve_interface::{v1::Hypercall, GuestPhysAddr}; use xhypervisor::{ self, create_vm, map_mem, MemPerm, Register, SystemRegister, VirtualCpuExitReason, }; diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index 7a155942..819b2d54 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -7,7 +7,7 @@ use std::{ use burst::x86::{disassemble_64, InstructionOperation, OperandType}; use log::{debug, trace}; -use uhyve_interface::{GuestPhysAddr, Hypercall}; +use uhyve_interface::{v1::Hypercall, GuestPhysAddr}; use x86_64::{ registers::control::{Cr0Flags, Cr4Flags}, structures::gdt::SegmentSelector, diff --git a/src/vm.rs b/src/vm.rs index f3b9e65b..ac2de51b 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -268,7 +268,7 @@ impl UhyveVm { phys_addr_range: self.mem.guest_address.as_u64() ..self.mem.guest_address.as_u64() + self.mem.memory_size as u64, serial_port_base: self.verbose().then(|| { - SerialPortBase::new((uhyve_interface::HypercallAddress::Uart as u16).into()) + SerialPortBase::new((uhyve_interface::v1::HypercallAddress::Uart as u16).into()) .unwrap() }), device_tree: Some(FDT_ADDR.as_u64().try_into().unwrap()), diff --git a/tests/test-kernels/src/bin/serial.rs b/tests/test-kernels/src/bin/serial.rs index dd1af2af..47a41af8 100644 --- a/tests/test-kernels/src/bin/serial.rs +++ b/tests/test-kernels/src/bin/serial.rs @@ -1,6 +1,9 @@ #[cfg(target_os = "hermit")] use hermit as _; -use uhyve_interface::{parameters::SerialWriteBufferParams, GuestPhysAddr, HypercallAddress}; +use uhyve_interface::{ + v1::{parameters::SerialWriteBufferParams, HypercallAddress}, + GuestPhysAddr, +}; #[cfg(target_arch = "x86_64")] use x86_64::{ instructions::port::Port, diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index d925a491..670fa906 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -1,19 +1,10 @@ //! # Uhyve Hypervisor Interface -//! -//! The Uhyve hypercall interface works as follows: -//! -//! - On `x86_64` you use an out port instruction. The address of the `out`-port corresponds to the -//! hypercall you want to use. You can obtain it from the [`IoPorts`] enum. The data send to -//! that port is the physical memory address (of the VM) of the parameters of that hypercall. -//! - On `aarch64` you write to the respective [`HypercallAddress`]. The 64-bit value written to that location is the guest's physical memory address of the hypercall's parameter. #![cfg_attr(not(feature = "std"), no_std)] -// TODO: Throw this out, once https://github.com/rust-lang/rfcs/issues/2783 or https://github.com/rust-lang/rust/issues/86772 is resolved -use num_enum::TryFromPrimitive; - pub mod elf; -pub mod parameters; +/// Version 1 of the Hypercall Interface +pub mod v1; #[cfg(target_arch = "aarch64")] pub use ::aarch64::paging::PhysAddr as GuestPhysAddr; @@ -26,123 +17,7 @@ pub use ::x86_64::addr::VirtAddr as GuestVirtAddr; #[cfg(not(target_pointer_width = "64"))] compile_error!("Using uhyve-interface on a non-64-bit system is not (yet?) supported"); -use parameters::*; /// The version of the Uhyve interface. Note: This is not the same as the semver of the crate but /// should be increased on every version bump that changes the API. -pub const UHYVE_INTERFACE_VERSION: u32 = 1; - -/// Enum containing all valid port mappings for hypercalls. -/// -/// The discriminants of this enum are the respective ports, so one can get the code by calling -/// e.g., `HypercallPorts::FileWrite as u16`. -#[non_exhaustive] -#[repr(u16)] -#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] -pub enum HypercallAddress { - /// Port address = `0x400` - FileWrite = 0x400, - /// Port address = `0x440` - FileOpen = 0x440, - /// Port address = `0x480` - FileClose = 0x480, - /// Port address = `0x500` - FileRead = 0x500, - /// Port address = `0x540` - Exit = 0x540, - /// Port address = `0x580` - FileLseek = 0x580, - #[deprecated = "was never really in use"] - /// Port address = `0x640` - Netwrite = 0x640, - #[deprecated = "was never really in use"] - /// Port address = `0x680` - Netread = 0x680, - #[deprecated = "was never really in use"] - /// Port address = `0x700` - Netstat = 0x700, - /// Port address = `0x740` - Cmdsize = 0x740, - /// Port address = `0x780` - Cmdval = 0x780, - /// Port address = `0x800` - Uart = 0x800, - /// Port address = `0x840` - FileUnlink = 0x840, - /// Port address = `0x880` - SerialBufferWrite = 0x880, -} -// TODO: Remove this in the next major version -impl From> for HypercallAddress { - fn from(value: Hypercall) -> Self { - match value { - Hypercall::Cmdsize(_) => Self::Cmdsize, - Hypercall::Cmdval(_) => Self::Cmdval, - Hypercall::Exit(_) => Self::Exit, - Hypercall::FileClose(_) => Self::FileClose, - Hypercall::FileLseek(_) => Self::FileLseek, - Hypercall::FileOpen(_) => Self::FileOpen, - Hypercall::FileRead(_) => Self::FileRead, - Hypercall::FileWrite(_) => Self::FileWrite, - Hypercall::FileUnlink(_) => Self::FileUnlink, - Hypercall::SerialWriteByte(_) => Self::Uart, - Hypercall::SerialWriteBuffer(_) => Self::SerialBufferWrite, - } - } -} -impl From<&Hypercall<'_>> for HypercallAddress { - fn from(value: &Hypercall) -> Self { - match value { - Hypercall::Cmdsize(_) => Self::Cmdsize, - Hypercall::Cmdval(_) => Self::Cmdval, - Hypercall::Exit(_) => Self::Exit, - Hypercall::FileClose(_) => Self::FileClose, - Hypercall::FileLseek(_) => Self::FileLseek, - Hypercall::FileOpen(_) => Self::FileOpen, - Hypercall::FileRead(_) => Self::FileRead, - Hypercall::FileWrite(_) => Self::FileWrite, - Hypercall::FileUnlink(_) => Self::FileUnlink, - Hypercall::SerialWriteByte(_) => Self::Uart, - Hypercall::SerialWriteBuffer(_) => Self::SerialBufferWrite, - } - } -} - -/// Hypervisor calls available in Uhyve with their respective parameters. See the [module level documentation](crate) on how to invoke them. -#[non_exhaustive] -#[derive(Debug)] -pub enum Hypercall<'a> { - /// Get the size of the argument and environment strings. Used to allocate memory for - /// [`Hypercall::Cmdval`]. - Cmdsize(&'a mut CmdsizeParams), - /// Copy the argument and environment strings into the VM. Usually preceeeded by - /// [`Hypercall::Cmdsize`] so that the guest can allocate the memory for this call. - Cmdval(&'a CmdvalParams), - /// Exit the VM and return a status. - Exit(&'a ExitParams), - FileClose(&'a mut CloseParams), - FileLseek(&'a mut LseekParams), - FileOpen(&'a mut OpenParams), - FileRead(&'a mut ReadParams), - FileWrite(&'a WriteParams), - FileUnlink(&'a mut UnlinkParams), - /// Write a char to the terminal. - SerialWriteByte(u8), - /// Write a buffer to the terminal. - SerialWriteBuffer(&'a SerialWriteBufferParams), -} -impl<'a> Hypercall<'a> { - // TODO: Remove this in the next major version - /// Get a hypercall's port address. - pub fn port(self) -> u16 { - HypercallAddress::from(self) as u16 - } -} - -// Networkports (not used at the moment) -// TODO: Remove this -pub const UHYVE_PORT_NETWRITE: u16 = 0x640; - -// FIXME: Do not use a fix number of arguments -/// The maximum number of items in an argument of environment vector. -pub const MAX_ARGC_ENVC: usize = 128; +pub const UHYVE_INTERFACE_VERSION: u32 = 2; diff --git a/uhyve-interface/src/v1/mod.rs b/uhyve-interface/src/v1/mod.rs new file mode 100644 index 00000000..b3015c20 --- /dev/null +++ b/uhyve-interface/src/v1/mod.rs @@ -0,0 +1,111 @@ +//! # Uhyve Hypervisor Interface V1 +//! +//! The Uhyve hypercall interface works as follows: +//! +//! - On `x86_64` you use an out port instruction. The address of the `out`-port corresponds to the +//! hypercall you want to use. You can obtain it from the [`HypercallAddress`](v1::HypercallAddress) enum. The data send to +//! that port is the physical memory address (of the VM) of the parameters of that hypercall. +//! - On `aarch64` you write to the respective [`HypercallAddress`](v1::HypercallAddress). The 64-bit value written to that location is the guest's physical memory address of the hypercall's parameter. + +// TODO: Throw this out, once https://github.com/rust-lang/rfcs/issues/2783 or https://github.com/rust-lang/rust/issues/86772 is resolved + +use num_enum::TryFromPrimitive; + +pub mod parameters; +use crate::v1::parameters::*; + +/// Enum containing all valid port mappings for hypercalls. +/// +/// The discriminants of this enum are the respective ports, so one can get the code by calling +/// e.g., `HypercallPorts::FileWrite as u16`. +#[non_exhaustive] +#[repr(u16)] +#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] +pub enum HypercallAddress { + /// Port address = `0x400` + FileWrite = 0x400, + /// Port address = `0x440` + FileOpen = 0x440, + /// Port address = `0x480` + FileClose = 0x480, + /// Port address = `0x500` + FileRead = 0x500, + /// Port address = `0x540` + Exit = 0x540, + /// Port address = `0x580` + FileLseek = 0x580, + #[deprecated = "was never really in use"] + /// Port address = `0x640` + Netwrite = 0x640, + #[deprecated = "was never really in use"] + /// Port address = `0x680` + Netread = 0x680, + #[deprecated = "was never really in use"] + /// Port address = `0x700` + Netstat = 0x700, + /// Port address = `0x740` + Cmdsize = 0x740, + /// Port address = `0x780` + Cmdval = 0x780, + /// Port address = `0x800` + Uart = 0x800, + /// Port address = `0x840` + FileUnlink = 0x840, + /// Port address = `0x880` + SerialBufferWrite = 0x880, +} +impl From> for HypercallAddress { + fn from(value: Hypercall) -> Self { + match value { + Hypercall::Cmdsize(_) => Self::Cmdsize, + Hypercall::Cmdval(_) => Self::Cmdval, + Hypercall::Exit(_) => Self::Exit, + Hypercall::FileClose(_) => Self::FileClose, + Hypercall::FileLseek(_) => Self::FileLseek, + Hypercall::FileOpen(_) => Self::FileOpen, + Hypercall::FileRead(_) => Self::FileRead, + Hypercall::FileWrite(_) => Self::FileWrite, + Hypercall::FileUnlink(_) => Self::FileUnlink, + Hypercall::SerialWriteByte(_) => Self::Uart, + Hypercall::SerialWriteBuffer(_) => Self::SerialBufferWrite, + } + } +} + +/// Hypervisor calls available in Uhyve with their respective parameters. See the [module level documentation](crate) on how to invoke them. +#[non_exhaustive] +#[derive(Debug)] +pub enum Hypercall<'a> { + /// Get the size of the argument and environment strings. Used to allocate memory for + /// [`Hypercall::Cmdval`]. + Cmdsize(&'a mut CmdsizeParams), + /// Copy the argument and environment strings into the VM. Usually preceeeded by + /// [`Hypercall::Cmdsize`] so that the guest can allocate the memory for this call. + Cmdval(&'a CmdvalParams), + /// Exit the VM and return a status. + Exit(&'a ExitParams), + FileClose(&'a mut CloseParams), + FileLseek(&'a mut LseekParams), + FileOpen(&'a mut OpenParams), + FileRead(&'a mut ReadParams), + FileWrite(&'a WriteParams), + FileUnlink(&'a mut UnlinkParams), + /// Write a char to the terminal. + SerialWriteByte(u8), + /// Write a buffer to the terminal. + SerialWriteBuffer(&'a SerialWriteBufferParams), +} +impl<'a> Hypercall<'a> { + /// Get a hypercall's port address. + pub fn port(self) -> u16 { + HypercallAddress::from(self) as u16 + } +} + +// Networkports (not used at the moment) +// TODO: Remove this +pub const UHYVE_PORT_NETWRITE: u16 = 0x640; + +// FIXME: Do not use a fix number of arguments +/// The maximum number of items in an argument of environment vector. +pub const MAX_ARGC_ENVC: usize = 128; diff --git a/uhyve-interface/src/parameters.rs b/uhyve-interface/src/v1/parameters.rs similarity index 76% rename from uhyve-interface/src/parameters.rs rename to uhyve-interface/src/v1/parameters.rs index e255ae08..4b7691f7 100644 --- a/uhyve-interface/src/parameters.rs +++ b/uhyve-interface/src/v1/parameters.rs @@ -1,8 +1,8 @@ //! Parameters for hypercalls. -use crate::{GuestPhysAddr, GuestVirtAddr, MAX_ARGC_ENVC}; +use crate::{v1::MAX_ARGC_ENVC, GuestPhysAddr, GuestVirtAddr}; -/// Parameters for a [`Cmdsize`](crate::Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. +/// Parameters for a [`Cmdsize`](crate::v1::Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct CmdsizeParams { @@ -21,7 +21,7 @@ impl CmdsizeParams { /// - `path` is usually the path and name of the application. E.g., "/home/hermit/app" /// - `args` is a list of strings that form the parameters. (E.g., `["-v", "myarg"]`) /// - /// Note that this hypercall only transfers the sizes. It usually has to be followed up with the [`Cmdval` Hypercall](crate::Hypercall::Cmdval). + /// Note that this hypercall only transfers the sizes. It usually has to be followed up with the [`Cmdval` Hypercall](crate::v1::Hypercall::Cmdval). pub fn update(&mut self, path: &std::path::Path, args: &[String]) { self.argc = 0; @@ -47,7 +47,7 @@ impl CmdsizeParams { } } -/// Parameters for a [`Cmdval`](crate::Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. +/// Parameters for a [`Cmdval`](crate::v1::Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct CmdvalParams { @@ -57,7 +57,7 @@ pub struct CmdvalParams { pub envp: GuestPhysAddr, } -/// Parameters for a [`Exit`](crate::Hypercall::Exit) hypercall. +/// Parameters for a [`Exit`](crate::v1::Hypercall::Exit) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct ExitParams { @@ -65,7 +65,7 @@ pub struct ExitParams { pub arg: i32, } -/// Parameters for a [`FileUnlink`](crate::Hypercall::FileUnlink) hypercall. +/// Parameters for a [`FileUnlink`](crate::v1::Hypercall::FileUnlink) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct UnlinkParams { @@ -75,7 +75,7 @@ pub struct UnlinkParams { pub ret: i32, } -/// Parameters for a [`FileWrite`](crate::Hypercall::FileWrite) hypercall. +/// Parameters for a [`FileWrite`](crate::v1::Hypercall::FileWrite) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct WriteParams { @@ -87,7 +87,7 @@ pub struct WriteParams { pub len: usize, } -/// Parameters for a [`FileRead`](crate::Hypercall::FileRead) hypercall. +/// Parameters for a [`FileRead`](crate::v1::Hypercall::FileRead) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct ReadParams { @@ -101,7 +101,7 @@ pub struct ReadParams { pub ret: isize, } -/// Parameters for a [`FileClose`](crate::Hypercall::FileClose) hypercall. +/// Parameters for a [`FileClose`](crate::v1::Hypercall::FileClose) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct CloseParams { @@ -111,7 +111,7 @@ pub struct CloseParams { pub ret: i32, } -/// Parameters for a [`FileOpen`](crate::Hypercall::FileOpen) hypercall. +/// Parameters for a [`FileOpen`](crate::v1::Hypercall::FileOpen) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct OpenParams { @@ -125,7 +125,7 @@ pub struct OpenParams { pub ret: i32, } -/// Parameters for a [`FileLseek`](crate::Hypercall::FileLseek) hypercall +/// Parameters for a [`FileLseek`](crate::v1::Hypercall::FileLseek) hypercall #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct LseekParams { @@ -137,7 +137,7 @@ pub struct LseekParams { pub whence: i32, } -/// Parameters for a [`SerialWriteBuffer`](crate::Hypercall::SerialWriteBuffer) hypercall. +/// Parameters for a [`SerialWriteBuffer`](crate::v1::Hypercall::SerialWriteBuffer) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SerialWriteBufferParams { From 0e58c7d70eaa82718baf26cef9faa42f61f17fb3 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 18 Sep 2024 18:48:42 +0200 Subject: [PATCH 2/3] First version of hypercall interface V2 --- Cargo.lock | 3 +- Cargo.toml | 2 +- uhyve-interface/Cargo.toml | 3 +- uhyve-interface/src/lib.rs | 2 + uhyve-interface/src/v2/mod.rs | 92 +++++++++++++ uhyve-interface/src/v2/parameters.rs | 196 +++++++++++++++++++++++++++ 6 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 uhyve-interface/src/v2/mod.rs create mode 100644 uhyve-interface/src/v2/parameters.rs diff --git a/Cargo.lock b/Cargo.lock index a4af7e61..826a4e41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1495,9 +1495,10 @@ dependencies = [ [[package]] name = "uhyve-interface" -version = "0.1.2" +version = "0.2.0" dependencies = [ "aarch64", + "bitflags 2.6.0", "log", "num_enum", "x86_64", diff --git a/Cargo.toml b/Cargo.toml index 9c365c87..7b9c3365 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ mac_address = "1.1" thiserror = "2.0" time = "0.3" tun-tap = { version = "0.1.3", default-features = false } -uhyve-interface = { version = "0.1.1", path = "uhyve-interface", features = ["std"] } +uhyve-interface = { version = "0.2.0", path = "uhyve-interface", features = ["std"] } virtio-bindings = { version = "0.2", features = ["virtio-v4_14_0"] } rftrace = { version = "0.1", optional = true } rftrace-frontend = { version = "0.1", optional = true } diff --git a/uhyve-interface/Cargo.toml b/uhyve-interface/Cargo.toml index 03c2cdc7..06a49623 100644 --- a/uhyve-interface/Cargo.toml +++ b/uhyve-interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uhyve-interface" -version = "0.1.2" +version = "0.2.0" edition = "2021" authors = [ "Jonathan Klimt ", @@ -15,6 +15,7 @@ categories = ["os"] [dependencies] num_enum = { version = "0.7", default-features = false } log = { version = "0.4", optional = true } +bitflags = "2.6.0" [features] std = ["dep:log"] diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 670fa906..0100f78b 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -5,6 +5,8 @@ pub mod elf; /// Version 1 of the Hypercall Interface pub mod v1; +/// Version 2 of the Hypercall Interface +pub mod v2; #[cfg(target_arch = "aarch64")] pub use ::aarch64::paging::PhysAddr as GuestPhysAddr; diff --git a/uhyve-interface/src/v2/mod.rs b/uhyve-interface/src/v2/mod.rs new file mode 100644 index 00000000..0438b8ca --- /dev/null +++ b/uhyve-interface/src/v2/mod.rs @@ -0,0 +1,92 @@ +//! # Uhyve Hypervisor Interface V2 +//! +//! The Uhyve hypercall interface works as follows: +//! +//! - The guest writes (or reads) to the respective [`HypercallAddress`](v2::HypercallAddress). The 64-bit value written to that location is the guest's physical memory address of the hypercall's parameter. +//! - The hypervisor handles the hypercall. Depending on the Hypercall, the hypervisor might change the parameters struct in the guest's memory. + +// TODO: Throw this out, once https://github.com/rust-lang/rfcs/issues/2783 or https://github.com/rust-lang/rust/issues/86772 is resolved +use num_enum::TryFromPrimitive; + +pub mod parameters; +use crate::v2::parameters::*; + +/// Enum containing all valid MMIO addresses for hypercalls. +/// +/// The discriminants of this enum are the respective addresses, so one can get the code by calling +/// e.g., `HypercallAddress::Exit as u64`. +#[non_exhaustive] +#[repr(u64)] +#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] +pub enum HypercallAddress { + Exit = 0x1010, + SerialWriteByte = 0x1020, + SerialWriteBuffer = 0x1030, + SerialReadByte = 0x1040, + SerialReadBuffer = 0x1050, + GetTime = 0x1060, + Sleep = 0x1070, + FileWrite = 0x1100, + FileOpen = 0x1110, + FileClose = 0x1120, + FileRead = 0x1130, + FileLseek = 0x1140, + FileUnlink = 0x1150, + SharedMemOpen = 0x1200, + SharedMemClose = 0x1210, +} +impl From> for HypercallAddress { + fn from(value: Hypercall) -> Self { + match value { + Hypercall::Exit(_) => Self::Exit, + Hypercall::FileClose(_) => Self::FileClose, + Hypercall::FileLseek(_) => Self::FileLseek, + Hypercall::FileOpen(_) => Self::FileOpen, + Hypercall::FileRead(_) => Self::FileRead, + Hypercall::FileUnlink(_) => Self::FileUnlink, + Hypercall::FileWrite(_) => Self::FileWrite, + Hypercall::GetTime(_) => Self::GetTime, + Hypercall::SerialReadBuffer(_) => Self::SerialReadBuffer, + Hypercall::SerialReadByte => Self::SerialReadByte, + Hypercall::SerialWriteBuffer(_) => Self::SerialWriteBuffer, + Hypercall::SerialWriteByte(_) => Self::SerialWriteByte, + Hypercall::Sleep(_) => Self::Sleep, + Hypercall::SharedMemOpen(_) => Self::SharedMemOpen, + Hypercall::SharedMemClose(_) => Self::SharedMemClose, + } + } +} + +/// Hypervisor calls available in Uhyve with their respective parameters. See the [module level documentation](crate) on how to invoke them. +#[non_exhaustive] +#[derive(Debug)] +pub enum Hypercall<'a> { + /// Exit the VM and return a status code. + Exit(i32), + FileClose(&'a mut CloseParams), + FileLseek(&'a mut LseekParams), + FileOpen(&'a mut OpenParams), + FileRead(&'a mut ReadPrams), + FileWrite(&'a WriteParams), + FileUnlink(&'a mut UnlinkParams), + /// Write a char to the terminal. + SerialWriteByte(u8), + /// Write a buffer to the terminal + SerialWriteBuffer(&'a SerialWriteBufferParams), + /// Read a single byte from the terminal + SerialReadByte, + /// Read a buffer from the terminal + SerialReadBuffer(&'a SerialReadBufferParams), + SharedMemOpen(&'a SharedMemOpenParams), + SharedMemClose(&'a SharedMemCloseParams), + /// Get system time + GetTime(&'a TimeParams), + /// Suspend the vm for (at least) the specified duration. + Sleep(&'a SleepParams), +} +impl<'a> Hypercall<'a> { + /// Get a hypercall's port address. + pub fn port(self) -> u16 { + HypercallAddress::from(self) as u16 + } +} diff --git a/uhyve-interface/src/v2/parameters.rs b/uhyve-interface/src/v2/parameters.rs new file mode 100644 index 00000000..f736b107 --- /dev/null +++ b/uhyve-interface/src/v2/parameters.rs @@ -0,0 +1,196 @@ +//! Parameters for [Hypercalls](crate::v2::Hypercall). + +use core::num::NonZeroU16; + +use bitflags::bitflags; + +use crate::{GuestPhysAddr, GuestVirtAddr}; + +/// Parameters for a [`Exit`](crate::v2::Hypercall::Exit) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct ExitParams { + /// The return code of the guest. + pub arg: i32, +} + +/// Parameters for a [`FileUnlink`](crate::v2::Hypercall::FileUnlink) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct UnlinkParams { + /// Address of the file that should be unlinked. + pub name: GuestPhysAddr, + /// On success, `0` is returned. On error, `-1` is returned. + pub ret: i32, +} + +/// Parameters for a [`FileWrite`](crate::v2::Hypercall::FileWrite) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct WriteParams { + /// File descriptor of the file. + pub fd: i32, + /// Buffer to be written into the file. + pub buf: GuestVirtAddr, + /// Number of bytes in the buffer to be written. + pub len: usize, +} + +/// Parameters for a [`FileRead`](crate::v2::Hypercall::FileRead) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct ReadPrams { + /// File descriptor of the file. + pub fd: i32, + /// Buffer to read the file into. + pub buf: GuestVirtAddr, + /// Number of bytes to read into the buffer. + pub len: usize, + /// Number of bytes read on success. `-1` on failure. + pub ret: isize, +} + +/// Parameters for a [`FileClose`](crate::v2::Hypercall::FileClose) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct CloseParams { + /// File descriptor of the file. + pub fd: i32, + /// Zero on success, `-1` on failure. + pub ret: i32, +} + +/// Parameters for a [`FileOpen`](crate::v2::Hypercall::FileOpen) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct OpenParams { + /// Pathname of the file to be opened. + pub name: GuestPhysAddr, + /// Posix file access mode flags. + pub flags: i32, + /// Access permissions upon opening/creating a file. + pub mode: i32, + /// File descriptor upon successful opening or `-1` upon failure. + pub ret: i32, +} + +/// Parameters for a [`FileLseek`](crate::v2::Hypercall::FileLseek) hypercall +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct LseekParams { + /// File descriptor of the file. + pub fd: i32, + /// Offset in the file. + pub offset: isize, + /// `whence` value of the lseek call. + pub whence: i32, +} + +/// Parameters for a [`SerialWriteBuffer`](crate::v2::Hypercall::SerialWriteBuffer) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SerialWriteBufferParams { + /// Address of the buffer to be printed. + pub buf: GuestPhysAddr, + /// Length of the buffer. + pub len: usize, +} + +/// Parameters for a [`SerialReadBuffer`](crate::v2::Hypercall::SerialReadBuffer) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SerialReadBufferParams { + /// Address to write to. + pub buf: GuestPhysAddr, + /// length of `buf`. + pub maxlen: usize, + /// Amount of bytes acutally written. + pub len: usize, +} + +/// Parameters for a [`GetTime`](crate::v2::Hypercall::GetTime) hypercall. This follows the semantics of POSIX's `struct timeval` +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct TimeParams { + /// Seconds since the Unix Epoch. + pub seconds: u64, + /// Microseconds since the Unix Epoch (in addition to the `seconds`). + pub u_seconds: u64, +} + +/// Parameters for a [`Sleep`](crate::v2::Hypercall::Sleep) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SleepParams { + /// Desired seconds duration (seconds, milliseconds). + pub sleep_duration: (u16, u16), + /// Actual sleep duration. (Appoximately: does not include vm entry/exit duration). Might not be supported. + pub actual_sleep_duration: Option<(NonZeroU16, NonZeroU16)>, +} + +#[repr(u8)] +#[derive(Debug, Copy, Clone)] +pub enum SharedMemOpenError { + /// The shared memory due to invalid parameters. + InvalidParams, + /// New shared memory creation was requested, but the shared memory already exists. + AlreadyExisting, + /// There limit of shared memories is exceeded. + TooManySharedMems, + /// Unspecified error + Unspecified, +} + +#[derive(Debug, Copy, Clone)] +pub struct SharedMemFlags(u8); +bitflags! { + impl SharedMemFlags: u8 { + /// The shared memory should be created if not present. + const CREATE = 0b0000_0001; + /// Return an error if the shared memory exists. + const CREATE_EXCLUSIVE = 0b0000_0010; + /// Map the shared memory in read-only mode. + const READ_ONLY = 0b0000_0100; + /// Experimental = Create a shared memory, that can only be written by the current VM. + const CREATE_EXCLUSIVE_WRITE = 0b0000_1000; + } +} + +/// Parameters for a [`SharedMemOpen`](crate::v2::Hypercall::SharedMemOpen) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SharedMemOpenParams { + /// Address of the mapped shared memory in the guest. Is set by the host. + pub buf: Result, + /// length of `buf`. + pub len: usize, + /// Address of the shared memory identifier utf8 string. + pub identifier: GuestPhysAddr, + /// length of `identifier` in bytes. + pub identifier_len: usize, + /// Flags for opening the shared memory. + pub flags: SharedMemFlags, +} + +#[repr(u8)] +#[derive(Debug, Copy, Clone)] +pub enum SharedMemCloseError { + /// The identifier is not valid. + InvalidIdentifier, + /// The shared memory does not exist. + NotExisting, + /// Unspecified error. + Unspecified, +} + +/// Parameters for a [`SharedMemClose`](crate::v2::Hypercall::SharedMemOpen) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SharedMemCloseParams { + /// Address of the shared memory identifier utf8 string. + pub identifier: GuestPhysAddr, + /// length of `identifier` in bytes. + pub identifier_len: usize, + /// Flags for Closeing the shared memory. + pub result: Result<(), SharedMemCloseError>, +} From 588a92da6c8d9982701e17f5f973d3944ffa8d11 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Thu, 19 Sep 2024 10:17:55 +0200 Subject: [PATCH 3/3] Added README for uhyve-interface --- uhyve-interface/README.md | 7 +++++++ uhyve-interface/src/lib.rs | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 uhyve-interface/README.md diff --git a/uhyve-interface/README.md b/uhyve-interface/README.md new file mode 100644 index 00000000..3d2ebebf --- /dev/null +++ b/uhyve-interface/README.md @@ -0,0 +1,7 @@ + + +# Uhyve Hypercall Interface +[![crates.io](https://img.shields.io/crates/v/uhyve.svg)](https://crates.io/crates/uhyve) + +This crate specifies the interface between the [Hermit Unikernel](https://github.com/hermit-os/kernel) and the hypervisor [Uhyve](https://github.com/hermit-os/uhyve). +It includes the definition of the hypercalls and hypercall parameters and is intended to be used in both projects to ensure a coherent and well defined interface. diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 0100f78b..e03445dd 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -1,4 +1,13 @@ -//! # Uhyve Hypervisor Interface +#![doc( + html_favicon_url = "https://media.githubusercontent.com/media/hermit-os/uhyve/main/img/uhyve_128.png" +)] +#![doc( + html_logo_url = "https://media.githubusercontent.com/media/hermit-os/uhyve/main/img/uhyve_512.png" +)] +//! # Uhyve Hypercall Interface +//! +//! This crate specifies the interface between the [Hermit Unikernel](https://github.com/hermit-os/kernel) and the hypervisor [Uhyve](https://github.com/hermit-os/uhyve). +//! It includes the definition of the hypercalls and hypercall parameters and is intended to be used in both projects to ensure a coherent and well defined interface. #![cfg_attr(not(feature = "std"), no_std)]