11use crate :: {
22 AddressHeaderTag , ConsoleHeaderTag , EfiBootServiceHeaderTag , EndHeaderTag ,
33 EntryAddressHeaderTag , EntryEfi32HeaderTag , EntryEfi64HeaderTag , FramebufferHeaderTag ,
4- HeaderTag , HeaderTagISA , HeaderTagType , InformationRequestHeaderTag , RelocatableHeaderTag ,
4+ HeaderTag , HeaderTagISA , HeaderTagType , InformationRequestHeaderTag , ModuleAlignHeaderTag ,
5+ RelocatableHeaderTag ,
56} ;
7+ use core:: convert:: TryInto ;
68use core:: fmt:: { Debug , Formatter } ;
79use core:: mem:: size_of;
810
@@ -21,12 +23,10 @@ pub struct Multiboot2Header<'a> {
2123}
2224
2325impl < ' a > Multiboot2Header < ' a > {
24- /// Public constructor for this type with various validations. It panics if the address is invalid.
25- /// It panics rather than returning a result, because if this fails, it is
26- /// a fatal, unrecoverable error anyways and a bug in your code.
26+ /// Public constructor for this type with various validations.
2727 ///
28- /// # Panics
29- /// Panics if one of the following conditions is true :
28+ /// If the header is invalid, it returns a [`LoadError`].
29+ /// This may be because :
3030 /// - `addr` is a null-pointer
3131 /// - `addr` isn't 8-byte aligned
3232 /// - the magic value of the header is not present
@@ -35,28 +35,69 @@ impl<'a> Multiboot2Header<'a> {
3535 /// # Safety
3636 /// This function may produce undefined behaviour, if the provided `addr` is not a valid
3737 /// Multiboot2 header pointer.
38- pub unsafe fn from_addr ( addr : usize ) -> Self {
39- assert_ne ! ( 0 , addr, "`addr` is null pointer" ) ;
40- assert_eq ! (
41- addr % 8 ,
42- 0 ,
43- "`addr` must be 8-byte aligned, see Multiboot2 spec"
44- ) ;
38+ // This function can be `const` on newer Rust versions.
39+ #[ allow( clippy:: missing_const_for_fn) ]
40+ pub unsafe fn from_addr ( addr : usize ) -> Result < Self , LoadError > {
41+ if addr == 0 || addr % 8 != 0 {
42+ return Err ( LoadError :: InvalidAddress ) ;
43+ }
4544 let ptr = addr as * const Multiboot2BasicHeader ;
4645 let reference = & * ptr;
47- assert_eq ! (
48- reference. header_magic( ) ,
49- MULTIBOOT2_HEADER_MAGIC ,
50- "The Multiboot2 header must contain the MULTIBOOT2_HEADER_MAGIC={:x}" ,
51- MULTIBOOT2_HEADER_MAGIC
52- ) ;
53- assert ! (
54- reference. verify_checksum( ) ,
55- "checksum invalid! Is {:x}, expected {:x}" ,
56- reference. checksum( ) ,
57- Self :: calc_checksum( reference. header_magic, reference. arch, reference. length)
58- ) ;
59- Self { inner : reference }
46+ if reference. header_magic ( ) != MULTIBOOT2_HEADER_MAGIC {
47+ return Err ( LoadError :: MagicNotFound ) ;
48+ }
49+ if !reference. verify_checksum ( ) {
50+ return Err ( LoadError :: ChecksumMismatch ) ;
51+ }
52+ Ok ( Self { inner : reference } )
53+ }
54+
55+ /// Find the header in a given slice.
56+ ///
57+ /// If it succeeds, it returns a tuple consisting of the subslice containing
58+ /// just the header and the index of the header in the given slice.
59+ /// If it fails (either because the header is not properply 64-bit aligned
60+ /// or because it is truncated), it returns a [`LoadError`].
61+ /// If there is no header, it returns `None`.
62+ pub fn find_header ( buffer : & [ u8 ] ) -> Result < Option < ( & [ u8 ] , u32 ) > , LoadError > {
63+ // the magic is 32 bit aligned and inside the first 8192 bytes
64+ assert ! ( buffer. len( ) >= 8192 ) ;
65+ let mut windows = buffer[ 0 ..8192 ] . windows ( 4 ) ;
66+ let magic_index = match windows. position ( |vals| {
67+ u32:: from_le_bytes ( vals. try_into ( ) . unwrap ( ) ) // yes, there's 4 bytes here
68+ == MULTIBOOT2_HEADER_MAGIC
69+ } ) {
70+ Some ( idx) => {
71+ if idx % 8 == 0 {
72+ idx
73+ } else {
74+ return Err ( LoadError :: InvalidAddress ) ;
75+ }
76+ }
77+ None => return Ok ( None ) ,
78+ } ;
79+ // skip over rest of magic
80+ windows. next ( ) ;
81+ windows. next ( ) ;
82+ windows. next ( ) ;
83+ // arch
84+ windows. next ( ) ;
85+ windows. next ( ) ;
86+ windows. next ( ) ;
87+ windows. next ( ) ;
88+ let header_length: usize = u32:: from_le_bytes (
89+ windows
90+ . next ( )
91+ . ok_or ( LoadError :: TooSmall ) ?
92+ . try_into ( )
93+ . unwrap ( ) , // 4 bytes are a u32
94+ )
95+ . try_into ( )
96+ . unwrap ( ) ;
97+ Ok ( Some ( (
98+ & buffer[ magic_index..magic_index + header_length] ,
99+ magic_index as u32 ,
100+ ) ) )
60101 }
61102
62103 /// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
@@ -87,6 +128,66 @@ impl<'a> Multiboot2Header<'a> {
87128 pub const fn calc_checksum ( magic : u32 , arch : HeaderTagISA , length : u32 ) -> u32 {
88129 Multiboot2BasicHeader :: calc_checksum ( magic, arch, length)
89130 }
131+
132+ /// Search for the address header tag.
133+ pub fn address_tag ( & self ) -> Option < & AddressHeaderTag > {
134+ self . get_tag ( HeaderTagType :: Address )
135+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const AddressHeaderTag ) } )
136+ }
137+
138+ /// Search for the entry address header tag.
139+ pub fn entry_address_tag ( & self ) -> Option < & EntryAddressHeaderTag > {
140+ self . get_tag ( HeaderTagType :: EntryAddress )
141+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const EntryAddressHeaderTag ) } )
142+ }
143+
144+ /// Search for the EFI32 entry address header tag.
145+ pub fn entry_address_efi32_tag ( & self ) -> Option < & EntryEfi32HeaderTag > {
146+ self . get_tag ( HeaderTagType :: EntryAddressEFI32 )
147+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const EntryEfi32HeaderTag ) } )
148+ }
149+
150+ /// Search for the EFI64 entry address header tag.
151+ pub fn entry_address_efi64_tag ( & self ) -> Option < & EntryEfi64HeaderTag > {
152+ self . get_tag ( HeaderTagType :: EntryAddressEFI64 )
153+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const EntryEfi64HeaderTag ) } )
154+ }
155+
156+ /// Search for the console flags header tag.
157+ pub fn console_flags_tag ( & self ) -> Option < & ConsoleHeaderTag > {
158+ self . get_tag ( HeaderTagType :: ConsoleFlags )
159+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const ConsoleHeaderTag ) } )
160+ }
161+
162+ /// Search for the framebuffer header tag.
163+ pub fn framebuffer_tag ( & self ) -> Option < & FramebufferHeaderTag > {
164+ self . get_tag ( HeaderTagType :: Framebuffer )
165+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const FramebufferHeaderTag ) } )
166+ }
167+
168+ /// Search for the module align header tag.
169+ pub fn module_align_tag ( & self ) -> Option < & ModuleAlignHeaderTag > {
170+ self . get_tag ( HeaderTagType :: ModuleAlign )
171+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const ModuleAlignHeaderTag ) } )
172+ }
173+
174+ /// Search for the EFI Boot Services header tag.
175+ pub fn efi_boot_services_tag ( & self ) -> Option < & EfiBootServiceHeaderTag > {
176+ self . get_tag ( HeaderTagType :: EfiBS )
177+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const EfiBootServiceHeaderTag ) } )
178+ }
179+
180+ /// Search for the EFI32 entry address header tag.
181+ pub fn relocatable_tag ( & self ) -> Option < & RelocatableHeaderTag > {
182+ self . get_tag ( HeaderTagType :: Relocatable )
183+ . map ( |tag| unsafe { & * ( tag as * const HeaderTag as * const RelocatableHeaderTag ) } )
184+ }
185+
186+ fn get_tag ( & self , typ : HeaderTagType ) -> Option < & HeaderTag > {
187+ self . iter ( )
188+ . map ( |tag| unsafe { tag. as_ref ( ) } . unwrap ( ) )
189+ . find ( |tag| tag. typ ( ) == typ)
190+ }
90191}
91192
92193impl < ' a > Debug for Multiboot2Header < ' a > {
@@ -97,6 +198,20 @@ impl<'a> Debug for Multiboot2Header<'a> {
97198 }
98199}
99200
201+ /// Errors that can occur when parsing a header from a slice.
202+ /// See [`Multiboot2Header::find_header`].
203+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
204+ pub enum LoadError {
205+ /// The checksum does not match the data.
206+ ChecksumMismatch ,
207+ /// The header is not properly 64-bit aligned (or a null pointer).
208+ InvalidAddress ,
209+ /// The header does not contain the correct magic number.
210+ MagicNotFound ,
211+ /// The header is truncated.
212+ TooSmall ,
213+ }
214+
100215/// **Use this only if you know what you do. You probably want to use
101216/// [`Multiboot2Header`] instead.**
102217///
@@ -313,6 +428,10 @@ impl Debug for Multiboot2HeaderTagIter {
313428 let entry = t as * const EntryEfi64HeaderTag ;
314429 let entry = & * ( entry) ;
315430 debug. entry ( entry) ;
431+ } else if typ == HeaderTagType :: ModuleAlign {
432+ let entry = t as * const ModuleAlignHeaderTag ;
433+ let entry = & * ( entry) ;
434+ debug. entry ( entry) ;
316435 } else if typ == HeaderTagType :: Relocatable {
317436 let entry = t as * const RelocatableHeaderTag ;
318437 let entry = & * ( entry) ;
0 commit comments