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,
+}