Skip to content

Commit

Permalink
First version of hypercall interface V2
Browse files Browse the repository at this point in the history
  • Loading branch information
jounathaen committed Sep 18, 2024
1 parent 2b8b689 commit 5d872a4
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions uhyve-interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 2 additions & 0 deletions uhyve-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
92 changes: 92 additions & 0 deletions uhyve-interface/src/v2/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! # Uhyve Hypervisor Interface V2
//!
//! The Uhyve hypercall interface works as follows:
//!
//! - The guest writes (or reads) to the respective [`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<Hypercall<'_>> 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
}
}
196 changes: 196 additions & 0 deletions uhyve-interface/src/v2/parameters.rs
Original file line number Diff line number Diff line change
@@ -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<GuestPhysAddr, SharedMemOpenError>,
/// 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>,
}

0 comments on commit 5d872a4

Please sign in to comment.