1#![no_std]
8#![cfg_attr(docsrs, feature(doc_cfg))]
9#![warn(missing_docs)]
10
11pub mod boot_info;
12
13#[cfg(feature = "loader")]
14pub mod elf;
15
16#[cfg(feature = "kernel")]
17mod note;
18
19use core::error::Error;
20use core::fmt;
21use core::str::FromStr;
22
23#[doc(hidden)]
24pub use const_parse::parse_u128 as _parse_u128;
25#[cfg(feature = "kernel")]
26#[doc(hidden)]
27pub use note::{_AbiTag, _Note};
28
29#[cfg(feature = "loader")]
33const GZIPMAG: &[u8; 3] = &[0x1f, 0x8b, 0x08];
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
37#[cfg(feature = "loader")]
38pub enum Format {
39 Elf,
41 Gzip,
43}
44
45#[cfg(feature = "loader")]
47pub fn detect_format(data: &[u8]) -> Option<Format> {
48 if data.len() < 4 {
49 None
50 } else if data.starts_with(goblin::elf64::header::ELFMAG) {
51 Some(Format::Elf)
52 } else if data.starts_with(GZIPMAG) {
53 Some(Format::Gzip)
54 } else {
55 None
56 }
57}
58
59#[cfg(not(target_arch = "riscv64"))]
67pub type Entry =
68 unsafe extern "C" fn(raw_boot_info: &'static boot_info::RawBootInfo, cpu_id: u32) -> !;
69
70#[cfg(target_arch = "riscv64")]
78pub type Entry =
79 unsafe extern "C" fn(hart_id: usize, raw_boot_info: &'static boot_info::RawBootInfo) -> !;
80
81#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
87const NT_HERMIT_ENTRY_VERSION: u32 = 0x5a00;
88
89#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
91const HERMIT_ENTRY_VERSION: u8 = 4;
92
93#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
95const NT_UHYVE_INTERFACE_VERSION: u32 = 0x5b00;
96
97#[expect(missing_docs)]
101pub mod fc {
102 pub const LINUX_KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
103 pub const LINUX_KERNEL_HRD_MAGIC: u32 = 0x53726448;
104 pub const LINUX_SETUP_HEADER_OFFSET: usize = 0x1f1;
105 pub const BOOT_FLAG_OFFSET: usize = 13;
106 pub const HDR_MAGIC_OFFSET: usize = 17;
107 pub const E820_ENTRIES_OFFSET: usize = 0x1e8;
108 pub const E820_TABLE_OFFSET: usize = 0x2d0;
109 pub const RAMDISK_IMAGE_OFFSET: usize = 39;
110 pub const RAMDISK_SIZE_OFFSET: usize = 43;
111 pub const CMD_LINE_PTR_OFFSET: usize = 55;
112 pub const CMD_LINE_SIZE_OFFSET: usize = 71;
113}
114
115#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
116const NT_GNU_ABI_TAG: u32 = 1;
117#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
118const ELF_NOTE_OS_HERMIT: u32 = 6;
119
120#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)]
122pub struct HermitVersion {
123 pub major: u32,
125
126 pub minor: u32,
128
129 pub patch: u32,
131}
132
133impl fmt::Display for HermitVersion {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 let Self {
136 major,
137 minor,
138 patch,
139 } = self;
140 write!(f, "{major}.{minor}.{patch}")
141 }
142}
143
144impl FromStr for HermitVersion {
145 type Err = ParseHermitVersionError;
146
147 fn from_str(s: &str) -> Result<Self, Self::Err> {
148 let (major, rest) = s.split_once('.').ok_or(ParseHermitVersionError)?;
149 let (minor, patch) = rest.split_once('.').ok_or(ParseHermitVersionError)?;
150
151 let major = major.parse().map_err(|_| ParseHermitVersionError)?;
152 let minor = minor.parse().map_err(|_| ParseHermitVersionError)?;
153 let patch = patch.parse().map_err(|_| ParseHermitVersionError)?;
154
155 Ok(Self {
156 major,
157 minor,
158 patch,
159 })
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq)]
165#[non_exhaustive]
166pub struct ParseHermitVersionError;
167
168impl fmt::Display for ParseHermitVersionError {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 f.write_str("provided string could not be parsed as Hermit version")
171 }
172}
173
174impl Error for ParseHermitVersionError {}
175
176#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)]
178pub struct UhyveIfVersion(pub u32);
179
180impl fmt::Display for UhyveIfVersion {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 self.0.fmt(f)
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn cmp_hermit_version() {
192 let small = HermitVersion {
193 major: 0,
194 minor: 1,
195 patch: 2,
196 };
197 let big = HermitVersion {
198 major: 2,
199 minor: 1,
200 patch: 0,
201 };
202
203 assert!(small < big);
204 assert!(small == small);
205 assert!(big == big);
206 assert!(big > small);
207 }
208
209 #[test]
210 fn parse_hermit_version() {
211 let version = HermitVersion::from_str("0.1.2").unwrap();
212 assert_eq!(
213 version,
214 HermitVersion {
215 major: 0,
216 minor: 1,
217 patch: 2,
218 }
219 );
220
221 let version = HermitVersion::from_str("2.1.0").unwrap();
222 assert_eq!(
223 version,
224 HermitVersion {
225 major: 2,
226 minor: 1,
227 patch: 0,
228 }
229 );
230 }
231}