diff --git a/Cargo.toml b/Cargo.toml index ca077bb..767f972 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "xen", "xen-bindings", + "xen-hvm", "xen-ioctls", "xen-store", "xen-sys", diff --git a/xen-hvm/Cargo.toml b/xen-hvm/Cargo.toml new file mode 100644 index 0000000..d8bcd04 --- /dev/null +++ b/xen-hvm/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "xen-hvm" +edition = "2021" +license = "Apache-2.0 OR MIT" diff --git a/xen-hvm/src/elfnote.rs b/xen-hvm/src/elfnote.rs new file mode 100644 index 0000000..7d81848 --- /dev/null +++ b/xen-hvm/src/elfnote.rs @@ -0,0 +1,87 @@ +//! Xen ELF notes. +//! +//! This module helps with adding Xen ELF notes to guests kernels. +//! +//! For details, see [xen/include/public/elfnote.h]. +//! +//! [xen/include/public/elfnote.h]: https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=xen/include/public/elfnote.h;h=2fd8f1b770fd34214313a4b811e910958116f0c7;hb=06af9ef22996cecc2024a2e6523cec77a655581e + +use core::mem; + +use crate::start_info::StartInfo; + +/// Creates a `XEN_ELFNOTE_PHYS32_ENTRY`. +/// +///
The entry itself has to be 32-bit code!
+/// +/// # Safety +/// +/// The provided `phys32_entry` needs to conform to the HVM booting +/// requirements. +/// +/// # Example +/// +/// ``` +/// use xen_hvm::start_info::StartInfo; +/// +/// unsafe extern "C" fn phys32_entry(start_info: &'static StartInfo) -> ! { +/// loop {} +/// } +/// +/// xen_hvm::phys32_entry!(phys32_entry); +/// ``` +#[macro_export] +macro_rules! phys32_entry { + ($phys32_entry:expr) => { + #[used] + #[unsafe(link_section = ".note.Xen")] + static XEN_ELFNOTE: $crate::elfnote::ElfnotePhys32Entry = + $crate::elfnote::ElfnotePhys32Entry::phys32_entry($phys32_entry); + }; +} + +#[repr(C, packed(4))] +pub struct Elfnote { + header: Nhdr32, + name: N, + desc: D, +} + +impl Elfnote { + pub const fn new(n_type: u32, name: N, desc: D) -> Self { + Self { + header: Nhdr32 { + n_namesz: mem::size_of::() as u32, + n_descsz: mem::size_of::() as u32, + n_type, + }, + name, + desc, + } + } +} + +pub type Phys32Entry = unsafe extern "C" fn(start_info: &'static StartInfo) -> !; +pub type ElfnotePhys32Entry = Elfnote<[u8; 4], Phys32Entry>; + +impl ElfnotePhys32Entry { + /// Physical entry point into the kernel. + /// + /// 32bit entry point into the kernel. When requested to launch the + /// guest kernel in a HVM container, Xen will use this entry point to + /// launch the guest in 32bit protected mode with paging disabled. + /// Ignored otherwise. + #[doc(alias = "XEN_ELFNOTE_PHYS32_ENTRY")] + const PHYS32_ENTRY: u32 = 18; + + pub const fn phys32_entry(phys32_entry: Phys32Entry) -> Self { + Self::new(Self::PHYS32_ENTRY, *b"Xen\0", phys32_entry) + } +} + +#[repr(C, packed(4))] +struct Nhdr32 { + n_namesz: u32, + n_descsz: u32, + n_type: u32, +} diff --git a/xen-hvm/src/lib.rs b/xen-hvm/src/lib.rs new file mode 100644 index 0000000..dbe9832 --- /dev/null +++ b/xen-hvm/src/lib.rs @@ -0,0 +1,12 @@ +//! Xen x86/HVM definitions. +//! +//! This crate allows guest kernels to be started via HVM. +//! [`phys32_entry!`] allows creating a `XEN_ELFNOTE_PHYS32_ENTRY`. +//! [`start_info`] provides the definitions for the start info that is passed by +//! the hypervisor. + +#![no_std] + +#[doc(hidden)] +pub mod elfnote; +pub mod start_info; diff --git a/xen-hvm/src/start_info.rs b/xen-hvm/src/start_info.rs new file mode 100644 index 0000000..b3e8976 --- /dev/null +++ b/xen-hvm/src/start_info.rs @@ -0,0 +1,191 @@ +//! HVM start info. +//! +//! For details, see [xen/include/public/arch-x86/hvm/start_info.h]. +//! +//! [xen/include/public/arch-x86/hvm/start_info.h]: https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=xen/include/public/arch-x86/hvm/start_info.h;h=e33557c0b4e98c6db3d3521710daa3838586733c;hb=06af9ef22996cecc2024a2e6523cec77a655581e + +/// Start of day structure passed to PVH guests and to HVM guests in %ebx. +/// +/// NOTE: nothing will be loaded at physical address 0, so a 0 value in any +/// of the address fields should be treated as not present. +/// +/// 0 +----------------+ +/// | magic | Contains the magic value XEN_HVM_START_MAGIC_VALUE +/// | | ("xEn3" with the 0x80 bit of the "E" set). +/// 4 +----------------+ +/// | version | Version of this structure. Current version is 1. New +/// | | versions are guaranteed to be backwards-compatible. +/// 8 +----------------+ +/// | flags | SIF_xxx flags. +/// 12 +----------------+ +/// | nr_modules | Number of modules passed to the kernel. +/// 16 +----------------+ +/// | modlist_paddr | Physical address of an array of modules +/// | | (layout of the structure below). +/// 24 +----------------+ +/// | cmdline_paddr | Physical address of the command line, +/// | | a zero-terminated ASCII string. +/// 32 +----------------+ +/// | rsdp_paddr | Physical address of the RSDP ACPI data structure. +/// 40 +----------------+ +/// | memmap_paddr | Physical address of the (optional) memory map. Only +/// | | present in version 1 and newer of the structure. +/// 48 +----------------+ +/// | memmap_entries | Number of entries in the memory map table. Zero +/// | | if there is no memory map being provided. Only +/// | | present in version 1 and newer of the structure. +/// 52 +----------------+ +/// | reserved | Version 1 and newer only. +/// 56 +----------------+ +/// +/// The layout of each entry in the module structure is the following: +/// +/// 0 +----------------+ +/// | paddr | Physical address of the module. +/// 8 +----------------+ +/// | size | Size of the module in bytes. +/// 16 +----------------+ +/// | cmdline_paddr | Physical address of the command line, +/// | | a zero-terminated ASCII string. +/// 24 +----------------+ +/// | reserved | +/// 32 +----------------+ +/// +/// The layout of each entry in the memory map table is as follows: +/// +/// 0 +----------------+ +/// | addr | Base address +/// 8 +----------------+ +/// | size | Size of mapping in bytes +/// 16 +----------------+ +/// | type | Type of mapping as defined between the hypervisor +/// | | and guest. See XEN_HVM_MEMMAP_TYPE_* values below. +/// 20 +----------------| +/// | reserved | +/// 24 +----------------+ +/// +/// The address and sizes are always a 64bit little endian unsigned integer. +/// +/// NB: Xen on x86 will always try to place all the data below the 4GiB +/// boundary. +/// +/// Version numbers of the hvm_start_info structure have evolved like this: +/// +/// Version 0: Initial implementation. +/// +/// Version 1: Added the memmap_paddr/memmap_entries fields (plus 4 bytes of +/// padding) to the end of the hvm_start_info struct. These new +/// fields can be used to pass a memory map to the guest. The +/// memory map is optional and so guests that understand version 1 +/// of the structure must check that memmap_entries is non-zero +/// before trying to read the memory map. +#[doc(alias = "XEN_HVM_START_MAGIC_VALUE")] +pub const START_MAGIC_VALUE: u32 = 0x336ec578; + +/// The values used in the type field of the memory map table entries are +/// defined below and match the Address Range Types as defined in the "System +/// Address Map Interfaces" section of the ACPI Specification. Please refer to +/// section 15 in version 6.2 of the ACPI spec: http://uefi.org/specifications +#[doc(alias = "XEN_HVM_MEMMAP_TYPE")] +#[derive(Debug)] +#[repr(u32)] +pub enum MemmapType { + #[doc(alias = "XEN_HVM_MEMMAP_TYPE_RAM")] + Ram = 1, + + #[doc(alias = "XEN_HVM_MEMMAP_TYPE_RESERVED")] + Reserved = 2, + + #[doc(alias = "XEN_HVM_MEMMAP_TYPE_ACPI")] + Acpi = 3, + + #[doc(alias = "XEN_HVM_MEMMAP_TYPE_NVS")] + Nvs = 4, + + #[doc(alias = "XEN_HVM_MEMMAP_TYPE_UNUSABLE")] + Unusable = 5, + + #[doc(alias = "XEN_HVM_MEMMAP_TYPE_DISABLED")] + Disabled = 6, + + #[doc(alias = "XEN_HVM_MEMMAP_TYPE_PMEM")] + Pmem = 7, +} + +/// C representation of the x86/HVM start info layout. +/// +/// The canonical definition of this layout is above, this is just a way to +/// represent the layout described there using C types. +#[doc(alias = "hvm_start_info")] +#[derive(Debug)] +#[repr(C)] +pub struct StartInfo { + /// Contains the magic value 0x336ec578 "xEn3" with the 0x80 bit of the "E" + /// set). + /// + /// See [`START_MAGIC_VALUE`] for a definition of the magic value. + magic: u32, + + /// Version of this structure. + version: u32, + + /// SIF_xxx flags. + flags: u32, + + /// Number of modules passed to the kernel. + nr_modules: u32, + + /// Physical address of an array of hvm_modlist_entry. + modlist_paddr: u64, + + /// Physical address of the command line. + cmdline_paddr: u64, + + /// Physical address of the RSDP ACPI data structure. + rsdp_paddr: u64, + + // All following fields only present in version 1 and newer + /// Physical address of an array of hvm_memmap_table_entry. + memmap_paddr: u64, + + /// Number of entries in the memmap table. + /// + /// Value will be zero if there is no memory map being provided. + memmap_entries: u32, + + /// Must be zero. + reserved: u32, +} + +#[doc(alias = "hvm_modlist_entry")] +#[derive(Debug)] +#[repr(C)] +pub struct ModlistEntry { + /// Physical address of the module. + paddr: u64, + + /// Size of the module in bytes. + size: u64, + + /// Physical address of the command line. + cmdline_paddr: u64, + reserved: u64, +} + +#[doc(alias = "hvm_memmap_table_entry")] +#[derive(Debug)] +#[repr(C)] +pub struct MemmapTableEntry { + /// Base address of the memory region + addr: u64, + + /// Size of the memory region in bytes + size: u64, + + /// Mapping type + #[doc(alias = "type")] + ty: u32, + + /// Must be zero for Version 1. + reserved: u32, +}