Skip to main content

hermit_entry/
lib.rs

1//! # Hermit's loading and entry API.
2//!
3//! This crate parses and loads Hermit applications ([`elf`]).
4//!
5//! Additionally, this crate unifies Hermit's entry API ([`Entry`]) for all loaders and the kernel.
6
7#![no_std]
8#![cfg_attr(docsrs, feature(doc_cfg))]
9#![warn(missing_docs)]
10
11#[cfg(feature = "loader")]
12extern crate alloc;
13
14pub mod boot_info;
15
16#[cfg(feature = "loader")]
17pub mod config;
18
19#[cfg(feature = "loader")]
20pub mod elf;
21
22#[cfg(feature = "kernel")]
23mod note;
24
25use core::error::Error;
26use core::fmt;
27use core::str::FromStr;
28
29#[doc(hidden)]
30pub use const_parse::parse_u128 as _parse_u128;
31#[cfg(feature = "kernel")]
32#[doc(hidden)]
33pub use note::{_AbiTag, _Note};
34
35/// GZIP magic number.
36///
37/// For details, see [10.17487/RFC1952](https://doi.org/10.17487/RFC1952).
38#[cfg(feature = "loader")]
39const GZIPMAG: &[u8; 3] = &[0x1f, 0x8b, 0x08];
40
41/// Possible input formats for a Hermit loader.
42#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
43#[cfg(feature = "loader")]
44pub enum Format {
45    /// An ELF file, probably a Hermit kernel.
46    Elf,
47    /// A gzipped tar file, probably containing a config + ELF kernel image, and associated files.
48    Gzip,
49}
50
51/// Attempts to detect the format of an input file (using magic bytes), whether it is an ELF kernel or an image.
52#[cfg(feature = "loader")]
53pub fn detect_format(data: &[u8]) -> Option<Format> {
54    if data.len() < 4 {
55        None
56    } else if data.starts_with(goblin::elf64::header::ELFMAG) {
57        Some(Format::Elf)
58    } else if data.starts_with(GZIPMAG) {
59        Some(Format::Gzip)
60    } else {
61        None
62    }
63}
64
65/// Kernel entry point.
66///
67/// This is the signature of the entry point of the kernel.
68///
69/// `cpu_id` is the number of the CPU core with the boot processor being number 0.
70///
71/// The stack pointer has to be valid for the boot processor only.
72#[cfg(not(target_arch = "riscv64"))]
73pub type Entry =
74    unsafe extern "C" fn(raw_boot_info: &'static boot_info::RawBootInfo, cpu_id: u32) -> !;
75
76/// Kernel entry point.
77///
78/// This is the signature of the entry point of the kernel.
79///
80/// `hart_id` is the number of the hardware thread.
81///
82/// The stack pointer has to be valid for the boot processor only.
83#[cfg(target_arch = "riscv64")]
84pub type Entry =
85    unsafe extern "C" fn(hart_id: usize, raw_boot_info: &'static boot_info::RawBootInfo) -> !;
86
87/// Note type for specifying the hermit entry version.
88///
89/// The note name for this is `HERMIT`.
90///
91/// The `desc` field will be 1 word, which specifies the hermit entry version.
92#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
93const NT_HERMIT_ENTRY_VERSION: u32 = 0x5a00;
94
95/// The current hermit entry version.
96#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
97const HERMIT_ENTRY_VERSION: u8 = 4;
98
99/// Note type for specifying the Uhyve interface version in an elf header.
100#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
101const NT_UHYVE_INTERFACE_VERSION: u32 = 0x5b00;
102
103/// Offsets and values used to interpret the boot params ("zeropage") setup by firecracker
104/// For the full list of values see
105/// <https://github.com/torvalds/linux/blob/b6839ef26e549de68c10359d45163b0cfb031183/arch/x86/include/uapi/asm/bootparam.h#L151-L198>
106#[expect(missing_docs)]
107#[deprecated]
108pub mod fc {
109    pub const LINUX_KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
110    pub const LINUX_KERNEL_HRD_MAGIC: u32 = 0x53726448;
111    pub const LINUX_SETUP_HEADER_OFFSET: usize = 0x1f1;
112    pub const BOOT_FLAG_OFFSET: usize = 13;
113    pub const HDR_MAGIC_OFFSET: usize = 17;
114    pub const E820_ENTRIES_OFFSET: usize = 0x1e8;
115    pub const E820_TABLE_OFFSET: usize = 0x2d0;
116    pub const RAMDISK_IMAGE_OFFSET: usize = 39;
117    pub const RAMDISK_SIZE_OFFSET: usize = 43;
118    pub const CMD_LINE_PTR_OFFSET: usize = 55;
119    pub const CMD_LINE_SIZE_OFFSET: usize = 71;
120}
121
122#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
123const NT_GNU_ABI_TAG: u32 = 1;
124#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
125const ELF_NOTE_OS_HERMIT: u32 = 6;
126
127/// A Hermit version.
128#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)]
129pub struct HermitVersion {
130    /// The major version of Hermit.
131    pub major: u32,
132
133    /// The minor version of Hermit.
134    pub minor: u32,
135
136    /// The patch version of Hermit.
137    pub patch: u32,
138}
139
140impl fmt::Display for HermitVersion {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        let Self {
143            major,
144            minor,
145            patch,
146        } = self;
147        write!(f, "{major}.{minor}.{patch}")
148    }
149}
150
151impl FromStr for HermitVersion {
152    type Err = ParseHermitVersionError;
153
154    fn from_str(s: &str) -> Result<Self, Self::Err> {
155        let (major, rest) = s.split_once('.').ok_or(ParseHermitVersionError)?;
156        let (minor, patch) = rest.split_once('.').ok_or(ParseHermitVersionError)?;
157
158        let major = major.parse().map_err(|_| ParseHermitVersionError)?;
159        let minor = minor.parse().map_err(|_| ParseHermitVersionError)?;
160        let patch = patch.parse().map_err(|_| ParseHermitVersionError)?;
161
162        Ok(Self {
163            major,
164            minor,
165            patch,
166        })
167    }
168}
169
170/// An error which can be returned when parsing a [`HermitVersion`].
171#[derive(Debug, Clone, PartialEq, Eq)]
172#[non_exhaustive]
173pub struct ParseHermitVersionError;
174
175impl fmt::Display for ParseHermitVersionError {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        f.write_str("provided string could not be parsed as Hermit version")
178    }
179}
180
181impl Error for ParseHermitVersionError {}
182
183/// A Uhyve interface version.
184#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)]
185pub struct UhyveIfVersion(pub u32);
186
187impl fmt::Display for UhyveIfVersion {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        self.0.fmt(f)
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn cmp_hermit_version() {
199        let small = HermitVersion {
200            major: 0,
201            minor: 1,
202            patch: 2,
203        };
204        let big = HermitVersion {
205            major: 2,
206            minor: 1,
207            patch: 0,
208        };
209
210        assert!(small < big);
211        assert!(small == small);
212        assert!(big == big);
213        assert!(big > small);
214    }
215
216    #[test]
217    fn parse_hermit_version() {
218        let version = HermitVersion::from_str("0.1.2").unwrap();
219        assert_eq!(
220            version,
221            HermitVersion {
222                major: 0,
223                minor: 1,
224                patch: 2,
225            }
226        );
227
228        let version = HermitVersion::from_str("2.1.0").unwrap();
229        assert_eq!(
230            version,
231            HermitVersion {
232                major: 2,
233                minor: 1,
234                patch: 0,
235            }
236        );
237    }
238}