header.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. use crate::{
  2. AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag,
  3. EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag,
  4. HeaderTag, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag, ModuleAlignHeaderTag,
  5. RelocatableHeaderTag,
  6. };
  7. use core::convert::TryInto;
  8. use core::fmt::{Debug, Formatter};
  9. use core::mem::size_of;
  10. /// Magic value for a [`Multiboot2Header`], as defined by the spec.
  11. pub const MAGIC: u32 = 0xe85250d6;
  12. /// Wrapper type around a pointer to the Multiboot2 header.
  13. /// The Multiboot2 header is the [`Multiboot2BasicHeader`] followed
  14. /// by all tags (see [`crate::tags::HeaderTagType`]).
  15. /// Use this if you get a pointer to the header and just want
  16. /// to parse it. If you want to construct the type by yourself,
  17. /// please look at `HeaderBuilder` (requires the `builder` feature).
  18. #[derive(Debug)]
  19. #[repr(transparent)]
  20. pub struct Multiboot2Header<'a>(&'a Multiboot2BasicHeader);
  21. impl<'a> Multiboot2Header<'a> {
  22. /// Public constructor for this type with various validations.
  23. ///
  24. /// If the header is invalid, it returns a [`LoadError`].
  25. /// This may be because:
  26. /// - `addr` is a null-pointer
  27. /// - `addr` isn't 8-byte aligned
  28. /// - the magic value of the header is not present
  29. /// - the checksum field is invalid
  30. ///
  31. /// # Safety
  32. /// This function may produce undefined behaviour, if the provided `addr` is not a valid
  33. /// Multiboot2 header pointer.
  34. pub unsafe fn load(ptr: *const Multiboot2BasicHeader) -> Result<Self, LoadError> {
  35. // null or not aligned
  36. if ptr.is_null() || ptr.align_offset(8) != 0 {
  37. return Err(LoadError::InvalidAddress);
  38. }
  39. let reference = &*ptr;
  40. if reference.header_magic() != MAGIC {
  41. return Err(LoadError::MagicNotFound);
  42. }
  43. if !reference.verify_checksum() {
  44. return Err(LoadError::ChecksumMismatch);
  45. }
  46. Ok(Self(reference))
  47. }
  48. /// Find the header in a given slice.
  49. ///
  50. /// If it succeeds, it returns a tuple consisting of the subslice containing
  51. /// just the header and the index of the header in the given slice.
  52. /// If it fails (either because the header is not properly 64-bit aligned
  53. /// or because it is truncated), it returns a [`LoadError`].
  54. /// If there is no header, it returns `None`.
  55. pub fn find_header(buffer: &[u8]) -> Result<Option<(&[u8], u32)>, LoadError> {
  56. if buffer.as_ptr().align_offset(4) != 0 {
  57. return Err(LoadError::InvalidAddress);
  58. }
  59. let mut windows = buffer[0..8192].windows(4);
  60. let magic_index = match windows.position(|vals| {
  61. u32::from_le_bytes(vals.try_into().unwrap()) // yes, there's 4 bytes here
  62. == MAGIC
  63. }) {
  64. Some(idx) => {
  65. if idx % 8 == 0 {
  66. idx
  67. } else {
  68. return Err(LoadError::InvalidAddress);
  69. }
  70. }
  71. None => return Ok(None),
  72. };
  73. // skip over rest of magic
  74. windows.next();
  75. windows.next();
  76. windows.next();
  77. // arch
  78. windows.next();
  79. windows.next();
  80. windows.next();
  81. windows.next();
  82. let header_length: usize = u32::from_le_bytes(
  83. windows
  84. .next()
  85. .ok_or(LoadError::TooSmall)?
  86. .try_into()
  87. .unwrap(), // 4 bytes are a u32
  88. )
  89. .try_into()
  90. .unwrap();
  91. Ok(Some((
  92. &buffer[magic_index..magic_index + header_length],
  93. magic_index as u32,
  94. )))
  95. }
  96. /// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
  97. pub const fn verify_checksum(&self) -> bool {
  98. self.0.verify_checksum()
  99. }
  100. /// Wrapper around [`Multiboot2BasicHeader::header_magic`].
  101. pub const fn header_magic(&self) -> u32 {
  102. self.0.header_magic()
  103. }
  104. /// Wrapper around [`Multiboot2BasicHeader::arch`].
  105. pub const fn arch(&self) -> HeaderTagISA {
  106. self.0.arch()
  107. }
  108. /// Wrapper around [`Multiboot2BasicHeader::length`].
  109. pub const fn length(&self) -> u32 {
  110. self.0.length()
  111. }
  112. /// Wrapper around [`Multiboot2BasicHeader::checksum`].
  113. pub const fn checksum(&self) -> u32 {
  114. self.0.checksum()
  115. }
  116. /// Wrapper around [`Multiboot2BasicHeader::tag_iter`].
  117. pub fn iter(&self) -> Multiboot2HeaderTagIter {
  118. self.0.tag_iter()
  119. }
  120. /// Wrapper around [`Multiboot2BasicHeader::calc_checksum`].
  121. pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
  122. Multiboot2BasicHeader::calc_checksum(magic, arch, length)
  123. }
  124. /// Search for the address header tag.
  125. pub fn address_tag(&self) -> Option<&AddressHeaderTag> {
  126. self.get_tag(HeaderTagType::Address)
  127. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const AddressHeaderTag) })
  128. }
  129. /// Search for the entry address header tag.
  130. pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> {
  131. self.get_tag(HeaderTagType::EntryAddress)
  132. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryAddressHeaderTag) })
  133. }
  134. /// Search for the EFI32 entry address header tag.
  135. pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> {
  136. self.get_tag(HeaderTagType::EntryAddressEFI32)
  137. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryEfi32HeaderTag) })
  138. }
  139. /// Search for the EFI64 entry address header tag.
  140. pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> {
  141. self.get_tag(HeaderTagType::EntryAddressEFI64)
  142. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryEfi64HeaderTag) })
  143. }
  144. /// Search for the console flags header tag.
  145. pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> {
  146. self.get_tag(HeaderTagType::ConsoleFlags)
  147. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const ConsoleHeaderTag) })
  148. }
  149. /// Search for the framebuffer header tag.
  150. pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> {
  151. self.get_tag(HeaderTagType::Framebuffer)
  152. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const FramebufferHeaderTag) })
  153. }
  154. /// Search for the module align header tag.
  155. pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> {
  156. self.get_tag(HeaderTagType::ModuleAlign)
  157. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const ModuleAlignHeaderTag) })
  158. }
  159. /// Search for the EFI Boot Services header tag.
  160. pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> {
  161. self.get_tag(HeaderTagType::EfiBS)
  162. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EfiBootServiceHeaderTag) })
  163. }
  164. /// Search for the EFI32 entry address header tag.
  165. pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> {
  166. self.get_tag(HeaderTagType::Relocatable)
  167. .map(|tag| unsafe { &*(tag as *const HeaderTag as *const RelocatableHeaderTag) })
  168. }
  169. fn get_tag(&self, typ: HeaderTagType) -> Option<&HeaderTag> {
  170. self.iter()
  171. .map(|tag| unsafe { tag.as_ref() }.unwrap())
  172. .find(|tag| tag.typ() == typ)
  173. }
  174. }
  175. /// Errors that can occur when parsing a header from a slice.
  176. /// See [`Multiboot2Header::find_header`].
  177. #[derive(Copy, Clone, Debug, derive_more::Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
  178. pub enum LoadError {
  179. /// The checksum does not match the data.
  180. ChecksumMismatch,
  181. /// The header is not properly 64-bit aligned (or a null pointer).
  182. InvalidAddress,
  183. /// The header does not contain the correct magic number.
  184. MagicNotFound,
  185. /// The header is truncated.
  186. TooSmall,
  187. }
  188. #[cfg(feature = "unstable")]
  189. impl core::error::Error for LoadError {}
  190. /// The "basic" Multiboot2 header. This means only the properties, that are known during
  191. /// compile time. All other information are derived during runtime from the size property.
  192. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
  193. #[repr(C)]
  194. pub struct Multiboot2BasicHeader {
  195. /// Must be the value of [`MAGIC`].
  196. header_magic: u32,
  197. arch: HeaderTagISA,
  198. length: u32,
  199. checksum: u32,
  200. // Followed by dynamic amount of dynamically sized header tags.
  201. // At minimum, the end tag.
  202. }
  203. impl Multiboot2BasicHeader {
  204. #[cfg(feature = "builder")]
  205. /// Constructor for the basic header.
  206. pub(crate) const fn new(arch: HeaderTagISA, length: u32) -> Self {
  207. let magic = MAGIC;
  208. let checksum = Self::calc_checksum(magic, arch, length);
  209. Multiboot2BasicHeader {
  210. header_magic: magic,
  211. arch,
  212. length,
  213. checksum,
  214. }
  215. }
  216. /// Verifies that a Multiboot2 header is valid.
  217. pub const fn verify_checksum(&self) -> bool {
  218. let check = Self::calc_checksum(self.header_magic, self.arch, self.length);
  219. check == self.checksum
  220. }
  221. /// Calculates the checksum as described in the spec.
  222. pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
  223. (0x100000000 - magic as u64 - arch as u64 - length as u64) as u32
  224. }
  225. pub const fn header_magic(&self) -> u32 {
  226. self.header_magic
  227. }
  228. pub const fn arch(&self) -> HeaderTagISA {
  229. self.arch
  230. }
  231. pub const fn length(&self) -> u32 {
  232. self.length
  233. }
  234. pub const fn checksum(&self) -> u32 {
  235. self.checksum
  236. }
  237. /// Returns a [`Multiboot2HeaderTagIter`].
  238. ///
  239. /// # Panics
  240. /// See doc of [`Multiboot2HeaderTagIter`].
  241. pub fn tag_iter(&self) -> Multiboot2HeaderTagIter {
  242. let base_hdr_size = size_of::<Multiboot2BasicHeader>();
  243. if base_hdr_size == self.length as usize {
  244. panic!("No end tag!");
  245. }
  246. let tag_base_addr = self as *const Multiboot2BasicHeader;
  247. // cast to u8 so that the offset in bytes works correctly
  248. let tag_base_addr = tag_base_addr as *const u8;
  249. // tag_base_addr should now point behind the "static" members
  250. let tag_base_addr = unsafe { tag_base_addr.add(base_hdr_size) };
  251. // align pointer to 8 byte according to spec
  252. let tag_base_addr = unsafe { tag_base_addr.add(tag_base_addr.align_offset(8)) };
  253. // cast back
  254. let tag_base_addr = tag_base_addr as *const HeaderTag;
  255. let tags_len = self.length as usize - base_hdr_size;
  256. Multiboot2HeaderTagIter::new(tag_base_addr, tags_len as u32)
  257. }
  258. }
  259. impl Debug for Multiboot2BasicHeader {
  260. fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
  261. f.debug_struct("Multiboot2Header")
  262. .field("header_magic", &{ self.header_magic })
  263. .field("arch", &{ self.arch })
  264. .field("length", &{ self.length })
  265. .field("checksum", &{ self.checksum })
  266. .field("tags", &self.tag_iter())
  267. .finish()
  268. }
  269. }
  270. /// Iterator over all tags of a Multiboot2 header. The number of items is derived
  271. /// by the size/length of the header.
  272. ///
  273. /// # Panics
  274. /// Panics if the `length`-attribute doesn't match the number of found tags, there are
  275. /// more tags found than technically possible, or if there is more than one end tag.
  276. /// All of these errors come from bigger, underlying problems. Therefore, they are
  277. /// considered as "abort/panic" and not as recoverable errors.
  278. #[derive(Clone)]
  279. pub struct Multiboot2HeaderTagIter {
  280. /// 8-byte aligned base address
  281. base: *const HeaderTag,
  282. /// Offset in bytes from the base address.
  283. /// Always <= than size.
  284. n: u32,
  285. /// Size / final value of [`Self::n`].
  286. size: u32,
  287. /// Counts the number of found tags. If more tags are found
  288. /// than technically possible, for example because the length property
  289. /// was invalid and there are hundreds of "End"-tags, we can use
  290. /// this and enforce a hard iteration limit.
  291. tag_count: u32,
  292. /// Marks if the end-tag was found. Together with `tag_count`, this
  293. /// further helps to improve safety when invalid length properties are given.
  294. end_tag_found: bool,
  295. }
  296. impl Multiboot2HeaderTagIter {
  297. fn new(base: *const HeaderTag, size: u32) -> Self {
  298. // transform to byte pointer => offset works properly
  299. let base = base as *const u8;
  300. let base = unsafe { base.add(base.align_offset(8)) };
  301. let base = base as *const HeaderTag;
  302. Self {
  303. base,
  304. n: 0,
  305. size,
  306. tag_count: 0,
  307. end_tag_found: false,
  308. }
  309. }
  310. }
  311. impl Iterator for Multiboot2HeaderTagIter {
  312. type Item = *const HeaderTag;
  313. fn next(&mut self) -> Option<Self::Item> {
  314. // no more bytes left to check; length reached
  315. if self.n >= self.size {
  316. return None;
  317. }
  318. // transform to byte ptr => offset works correctly
  319. let ptr = self.base as *const u8;
  320. let ptr = unsafe { ptr.add(self.n as usize) };
  321. let ptr = ptr as *const HeaderTag;
  322. assert_eq!(ptr as usize % 8, 0, "must be 8-byte aligned");
  323. let tag = unsafe { &*ptr };
  324. assert!(
  325. tag.size() <= 500,
  326. "no real mb2 header should be bigger than 500bytes - probably wrong memory?! is: {}",
  327. { tag.size() }
  328. );
  329. assert!(
  330. tag.size() >= 8,
  331. "no real mb2 header tag is smaller than 8 bytes - probably wrong memory?! is: {}",
  332. { tag.size() }
  333. );
  334. assert!(
  335. !self.end_tag_found,
  336. "There is more than one end tag! Maybe the `length` property is invalid?"
  337. );
  338. self.n += tag.size();
  339. // 8-byte alignment of pointer address
  340. self.n += self.n % 8;
  341. self.tag_count += 1;
  342. if tag.typ() == HeaderTagType::End {
  343. self.end_tag_found = true;
  344. }
  345. assert!(self.tag_count < HeaderTagType::count(), "Invalid Multiboot2 header tags! There are more tags than technically possible! Maybe the `length` property is invalid?");
  346. Some(ptr)
  347. }
  348. }
  349. impl Debug for Multiboot2HeaderTagIter {
  350. fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
  351. let mut debug = f.debug_list();
  352. self.clone().for_each(|t| unsafe {
  353. let typ = (*t).typ();
  354. if typ == HeaderTagType::End {
  355. let entry = t as *const EndHeaderTag;
  356. let entry = &*(entry);
  357. debug.entry(entry);
  358. } else if typ == HeaderTagType::InformationRequest {
  359. let entry = t as *const InformationRequestHeaderTag<0>;
  360. let entry = &*(entry);
  361. debug.entry(entry);
  362. } else if typ == HeaderTagType::Address {
  363. let entry = t as *const AddressHeaderTag;
  364. let entry = &*(entry);
  365. debug.entry(entry);
  366. } else if typ == HeaderTagType::EntryAddress {
  367. let entry = t as *const EntryAddressHeaderTag;
  368. let entry = &*(entry);
  369. debug.entry(entry);
  370. } else if typ == HeaderTagType::ConsoleFlags {
  371. let entry = t as *const ConsoleHeaderTag;
  372. let entry = &*(entry);
  373. debug.entry(entry);
  374. } else if typ == HeaderTagType::Framebuffer {
  375. let entry = t as *const FramebufferHeaderTag;
  376. let entry = &*(entry);
  377. debug.entry(entry);
  378. } else if typ == HeaderTagType::EfiBS {
  379. let entry = t as *const EfiBootServiceHeaderTag;
  380. let entry = &*(entry);
  381. debug.entry(entry);
  382. } else if typ == HeaderTagType::EntryAddressEFI32 {
  383. let entry = t as *const EntryEfi32HeaderTag;
  384. let entry = &*(entry);
  385. debug.entry(entry);
  386. } else if typ == HeaderTagType::EntryAddressEFI64 {
  387. let entry = t as *const EntryEfi64HeaderTag;
  388. let entry = &*(entry);
  389. debug.entry(entry);
  390. } else if typ == HeaderTagType::ModuleAlign {
  391. let entry = t as *const ModuleAlignHeaderTag;
  392. let entry = &*(entry);
  393. debug.entry(entry);
  394. } else if typ == HeaderTagType::Relocatable {
  395. let entry = t as *const RelocatableHeaderTag;
  396. let entry = &*(entry);
  397. debug.entry(entry);
  398. } else {
  399. panic!("unknown tag ({:?})!", typ);
  400. }
  401. });
  402. debug.finish()
  403. }
  404. }
  405. #[cfg(test)]
  406. mod tests {
  407. use crate::Multiboot2BasicHeader;
  408. #[test]
  409. fn test_assert_size() {
  410. assert_eq!(core::mem::size_of::<Multiboot2BasicHeader>(), 4 + 4 + 4 + 4);
  411. }
  412. }