1
1
use crate :: {
2
2
AddressHeaderTag , ConsoleHeaderTag , EfiBootServiceHeaderTag , EndHeaderTag ,
3
3
EntryAddressHeaderTag , EntryEfi32HeaderTag , EntryEfi64HeaderTag , FramebufferHeaderTag ,
4
- HeaderTag , HeaderTagISA , HeaderTagType , InformationRequestHeaderTag , RelocatableHeaderTag ,
4
+ HeaderTag , HeaderTagISA , HeaderTagType , InformationRequestHeaderTag , ModuleAlignHeaderTag ,
5
+ RelocatableHeaderTag ,
5
6
} ;
7
+ use core:: convert:: TryInto ;
6
8
use core:: fmt:: { Debug , Formatter } ;
7
9
use core:: mem:: size_of;
8
10
@@ -21,12 +23,10 @@ pub struct Multiboot2Header<'a> {
21
23
}
22
24
23
25
impl < ' 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.
27
27
///
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 :
30
30
/// - `addr` is a null-pointer
31
31
/// - `addr` isn't 8-byte aligned
32
32
/// - the magic value of the header is not present
@@ -35,28 +35,69 @@ impl<'a> Multiboot2Header<'a> {
35
35
/// # Safety
36
36
/// This function may produce undefined behaviour, if the provided `addr` is not a valid
37
37
/// 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
+ }
45
44
let ptr = addr as * const Multiboot2BasicHeader ;
46
45
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
+ ) ) )
60
101
}
61
102
62
103
/// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
@@ -87,6 +128,66 @@ impl<'a> Multiboot2Header<'a> {
87
128
pub const fn calc_checksum ( magic : u32 , arch : HeaderTagISA , length : u32 ) -> u32 {
88
129
Multiboot2BasicHeader :: calc_checksum ( magic, arch, length)
89
130
}
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
+ }
90
191
}
91
192
92
193
impl < ' a > Debug for Multiboot2Header < ' a > {
@@ -97,6 +198,20 @@ impl<'a> Debug for Multiboot2Header<'a> {
97
198
}
98
199
}
99
200
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
+
100
215
/// **Use this only if you know what you do. You probably want to use
101
216
/// [`Multiboot2Header`] instead.**
102
217
///
@@ -313,6 +428,10 @@ impl Debug for Multiboot2HeaderTagIter {
313
428
let entry = t as * const EntryEfi64HeaderTag ;
314
429
let entry = & * ( entry) ;
315
430
debug. entry ( entry) ;
431
+ } else if typ == HeaderTagType :: ModuleAlign {
432
+ let entry = t as * const ModuleAlignHeaderTag ;
433
+ let entry = & * ( entry) ;
434
+ debug. entry ( entry) ;
316
435
} else if typ == HeaderTagType :: Relocatable {
317
436
let entry = t as * const RelocatableHeaderTag ;
318
437
let entry = & * ( entry) ;
0 commit comments