@@ -6,6 +6,11 @@ use crate::{
6
6
} ;
7
7
use core:: fmt:: { Debug , Formatter } ;
8
8
use core:: mem:: size_of;
9
+ use core:: slice;
10
+ use core:: { mem, ptr} ;
11
+ use multiboot2:: { TagHeader , TagTrait } ;
12
+ use multiboot2_common:: iter:: TagBytesIter ;
13
+ use multiboot2_common:: { BytesRef , DynSizedStructure , Header , ALIGNMENT } ;
9
14
10
15
/// Magic value for a [`Multiboot2Header`], as defined by the spec.
11
16
pub const MAGIC : u32 = 0xe85250d6 ;
@@ -18,7 +23,7 @@ pub const MAGIC: u32 = 0xe85250d6;
18
23
/// please look at `HeaderBuilder` (requires the `builder` feature).
19
24
#[ derive( Debug ) ]
20
25
#[ repr( transparent) ]
21
- pub struct Multiboot2Header < ' a > ( & ' a Multiboot2BasicHeader ) ;
26
+ pub struct Multiboot2Header < ' a > ( & ' a DynSizedStructure < Multiboot2BasicHeader > ) ;
22
27
23
28
impl < ' a > Multiboot2Header < ' a > {
24
29
/// Public constructor for this type with various validations.
@@ -35,21 +40,30 @@ impl<'a> Multiboot2Header<'a> {
35
40
/// Multiboot2 header pointer.
36
41
pub unsafe fn load ( ptr : * const Multiboot2BasicHeader ) -> Result < Self , LoadError > {
37
42
// null or not aligned
38
- if ptr. is_null ( ) || ptr. align_offset ( 8 ) != 0 {
43
+ if ptr. is_null ( ) || ptr. align_offset ( ALIGNMENT ) != 0 {
39
44
return Err ( LoadError :: InvalidAddress ) ;
40
45
}
41
46
42
- let reference = & * ptr;
47
+ let header = & * ptr;
43
48
44
- if reference . header_magic ( ) != MAGIC {
49
+ if header . header_magic ( ) != MAGIC {
45
50
return Err ( LoadError :: MagicNotFound ) ;
46
51
}
47
52
48
- if !reference . verify_checksum ( ) {
53
+ if !header . verify_checksum ( ) {
49
54
return Err ( LoadError :: ChecksumMismatch ) ;
50
55
}
51
56
52
- Ok ( Self ( reference) )
57
+ let slice = slice:: from_raw_parts ( ptr. cast :: < u8 > ( ) , header. length as usize ) ;
58
+ let bytes = BytesRef :: < Multiboot2BasicHeader > :: try_from ( slice) . map_err ( |e| {
59
+ log:: error!( "Load error: {e:#?}" ) ;
60
+ LoadError :: InvalidAddress
61
+ } ) ?;
62
+ let inner = DynSizedStructure :: ref_from ( bytes) . map_err ( |e| {
63
+ log:: error!( "Load error: {e:#?}" ) ;
64
+ LoadError :: TooSmall
65
+ } ) ?;
66
+ Ok ( Self ( inner) )
53
67
}
54
68
55
69
/// Find the header in a given slice.
@@ -60,7 +74,7 @@ impl<'a> Multiboot2Header<'a> {
60
74
/// or because it is truncated), it returns a [`LoadError`].
61
75
/// If there is no header, it returns `None`.
62
76
pub fn find_header ( buffer : & [ u8 ] ) -> Result < Option < ( & [ u8 ] , u32 ) > , LoadError > {
63
- if buffer. as_ptr ( ) . align_offset ( 4 ) != 0 {
77
+ if buffer. as_ptr ( ) . align_offset ( ALIGNMENT ) != 0 {
64
78
return Err ( LoadError :: InvalidAddress ) ;
65
79
}
66
80
@@ -102,35 +116,36 @@ impl<'a> Multiboot2Header<'a> {
102
116
) ) )
103
117
}
104
118
119
+ /// Returns a [`Multiboot2HeaderTagIter`].
120
+ #[ must_use]
121
+ pub fn iter ( & self ) -> Multiboot2HeaderTagIter {
122
+ Multiboot2HeaderTagIter :: new ( self . 0 . payload ( ) )
123
+ }
124
+
105
125
/// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
106
126
#[ must_use]
107
127
pub const fn verify_checksum ( & self ) -> bool {
108
- self . 0 . verify_checksum ( )
128
+ self . 0 . header ( ) . verify_checksum ( )
109
129
}
110
130
/// Wrapper around [`Multiboot2BasicHeader::header_magic`].
111
131
#[ must_use]
112
132
pub const fn header_magic ( & self ) -> u32 {
113
- self . 0 . header_magic ( )
133
+ self . 0 . header ( ) . header_magic ( )
114
134
}
115
135
/// Wrapper around [`Multiboot2BasicHeader::arch`].
116
136
#[ must_use]
117
137
pub const fn arch ( & self ) -> HeaderTagISA {
118
- self . 0 . arch ( )
138
+ self . 0 . header ( ) . arch ( )
119
139
}
120
140
/// Wrapper around [`Multiboot2BasicHeader::length`].
121
141
#[ must_use]
122
142
pub const fn length ( & self ) -> u32 {
123
- self . 0 . length ( )
143
+ self . 0 . header ( ) . length ( )
124
144
}
125
145
/// Wrapper around [`Multiboot2BasicHeader::checksum`].
126
146
#[ must_use]
127
147
pub const fn checksum ( & self ) -> u32 {
128
- self . 0 . checksum ( )
129
- }
130
- /// Wrapper around [`Multiboot2BasicHeader::tag_iter`].
131
- #[ must_use]
132
- pub fn iter ( & self ) -> Multiboot2HeaderTagIter {
133
- self . 0 . tag_iter ( )
148
+ self . 0 . header ( ) . checksum ( )
134
149
}
135
150
/// Wrapper around [`Multiboot2BasicHeader::calc_checksum`].
136
151
#[ must_use]
@@ -204,7 +219,7 @@ impl<'a> Multiboot2Header<'a> {
204
219
205
220
fn get_tag ( & self , typ : HeaderTagType ) -> Option < & HeaderTagHeader > {
206
221
self . iter ( )
207
- . map ( |tag| unsafe { tag . as_ref ( ) } . unwrap ( ) )
222
+ . map ( |tag| )
208
223
. find ( |tag| tag. typ ( ) == typ)
209
224
}
210
225
}
@@ -229,7 +244,7 @@ impl core::error::Error for LoadError {}
229
244
/// The "basic" Multiboot2 header. This means only the properties, that are known during
230
245
/// compile time. All other information are derived during runtime from the size property.
231
246
#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
232
- #[ repr( C ) ]
247
+ #[ repr( C , align ( 8 ) ) ]
233
248
pub struct Multiboot2BasicHeader {
234
249
/// Must be the value of [`MAGIC`].
235
250
header_magic : u32 ,
@@ -290,28 +305,11 @@ impl Multiboot2BasicHeader {
290
305
pub const fn checksum ( & self ) -> u32 {
291
306
self . checksum
292
307
}
308
+ }
293
309
294
- /// Returns a [`Multiboot2HeaderTagIter`].
295
- ///
296
- /// # Panics
297
- /// See doc of [`Multiboot2HeaderTagIter`].
298
- #[ must_use]
299
- pub fn tag_iter ( & self ) -> Multiboot2HeaderTagIter {
300
- let base_hdr_size = size_of :: < Self > ( ) ;
301
- if base_hdr_size == self . length as usize {
302
- panic ! ( "No end tag!" ) ;
303
- }
304
- let tag_base_addr = self as * const Self ;
305
- // cast to u8 so that the offset in bytes works correctly
306
- let tag_base_addr = tag_base_addr as * const u8 ;
307
- // tag_base_addr should now point behind the "static" members
308
- let tag_base_addr = unsafe { tag_base_addr. add ( base_hdr_size) } ;
309
- // align pointer to 8 byte according to spec
310
- let tag_base_addr = unsafe { tag_base_addr. add ( tag_base_addr. align_offset ( 8 ) ) } ;
311
- // cast back
312
- let tag_base_addr = tag_base_addr as * const HeaderTagHeader ;
313
- let tags_len = self . length as usize - base_hdr_size;
314
- Multiboot2HeaderTagIter :: new ( tag_base_addr, tags_len as u32 )
310
+ impl Header for Multiboot2BasicHeader {
311
+ fn payload_len ( & self ) -> usize {
312
+ self . length as usize - size_of :: < Self > ( )
315
313
}
316
314
}
317
315
@@ -322,100 +320,85 @@ impl Debug for Multiboot2BasicHeader {
322
320
. field ( "arch" , & { self . arch } )
323
321
. field ( "length" , & { self . length } )
324
322
. field ( "checksum" , & { self . checksum } )
325
- . field ( "tags" , & self . tag_iter ( ) )
323
+ . field ( "tags" , & self . iter ( ) )
326
324
. finish ( )
327
325
}
328
326
}
329
327
330
- /// Iterator over all tags of a Multiboot2 header. The number of items is derived
331
- /// by the size/length of the header.
332
- ///
333
- /// # Panics
334
- /// Panics if the `length`-attribute doesn't match the number of found tags, there are
335
- /// more tags found than technically possible, or if there is more than one end tag.
336
- /// All of these errors come from bigger, underlying problems. Therefore, they are
337
- /// considered as "abort/panic" and not as recoverable errors.
338
- #[ derive( Clone ) ]
339
- pub struct Multiboot2HeaderTagIter {
340
- /// 8-byte aligned base address
341
- base : * const HeaderTagHeader ,
342
- /// Offset in bytes from the base address.
343
- /// Always <= than size.
344
- n : u32 ,
345
- /// Size / final value of [`Self::n`].
346
- size : u32 ,
347
- /// Counts the number of found tags. If more tags are found
348
- /// than technically possible, for example because the length property
349
- /// was invalid and there are hundreds of "End"-tags, we can use
350
- /// this and enforce a hard iteration limit.
351
- tag_count : u32 ,
352
- /// Marks if the end-tag was found. Together with `tag_count`, this
353
- /// further helps to improve safety when invalid length properties are given.
354
- end_tag_found : bool ,
328
+ /// A generic tag serving as base to cast to specific tags. This is a DST
329
+ /// version of [`Multiboot2BasicHeader`] that solves various type and memory safety
330
+ /// problems by having a type that owns the whole memory of a tag.
331
+ #[ derive( PartialEq , Eq , ptr_meta:: Pointee ) ]
332
+ #[ repr( transparent) ]
333
+ pub struct GenericTag ( DynSizedStructure < Multiboot2BasicHeader > ) ;
334
+
335
+ impl GenericTag {
336
+ /// Returns a reference to [`GenericTag`] to interpret the provided memory
337
+ /// as [`GenericTag`].
338
+ ///
339
+ /// # Panics
340
+ // pub fn ref_from<'a>(bytes: &'a [u8]) -> Result<&'a Self, MemoryError> {
341
+ // TODO return result
342
+ pub fn ref_from < ' a > ( bytes : & ' a [ u8 ] ) -> & ' a Self {
343
+ let bytes = BytesRef :: < TagHeader > :: try_from ( bytes) . unwrap ( ) ;
344
+ let inner = DynSizedStructure :: ref_from ( bytes) . unwrap ( ) ;
345
+ unsafe { mem:: transmute :: < _ , & ' a Self > ( inner) }
346
+ }
347
+
348
+ /// Returns the underlying [`TagHeader`].
349
+ pub const fn header ( & self ) -> & Multiboot2BasicHeader {
350
+ self . 0 . header ( )
351
+ }
352
+
353
+ #[ cfg( all( test, feature = "builder" ) ) ]
354
+ pub const fn payload ( & self ) -> & [ u8 ] {
355
+ self . 0 . payload ( )
356
+ }
357
+
358
+ /// Casts the generic tag to a specific implementation.
359
+ /// TODO use tag trait.
360
+ pub fn cast < T : Sized > ( & self ) -> & T {
361
+ let base_ptr = ptr:: addr_of!( * self ) . cast :: < T > ( ) ;
362
+ let tag = unsafe { & * base_ptr } ;
363
+ tag
364
+ }
355
365
}
356
366
357
- impl Multiboot2HeaderTagIter {
358
- fn new ( base : * const HeaderTagHeader , size : u32 ) -> Self {
359
- // transform to byte pointer => offset works properly
360
- let base = base as * const u8 ;
361
- let base = unsafe { base. add ( base. align_offset ( 8 ) ) } ;
362
- let base = base as * const HeaderTagHeader ;
363
- Self {
364
- base,
365
- n : 0 ,
366
- size,
367
- tag_count : 0 ,
368
- end_tag_found : false ,
369
- }
367
+ impl Debug for GenericTag {
368
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> core:: fmt:: Result {
369
+ f. debug_struct ( "GenericTag" )
370
+ . field ( "header" , self . 0 . header ( ) )
371
+ . field ( "payload" , & "<bytes>" )
372
+ . finish ( )
370
373
}
371
374
}
372
375
373
- impl Iterator for Multiboot2HeaderTagIter {
374
- type Item = * const HeaderTagHeader ;
376
+ /// Iterates the tags of the header from the first tag to the end tag. The en
377
+ /// tag is included.
378
+ #[ derive( Clone ) ]
379
+ pub struct Multiboot2HeaderTagIter < ' a > ( TagBytesIter < ' a , TagHeader > ) ;
375
380
376
- fn next ( & mut self ) -> Option < Self :: Item > {
377
- // no more bytes left to check; length reached
378
- if self . n >= self . size {
379
- return None ;
380
- }
381
+ impl < ' a > Multiboot2HeaderTagIter < ' a > {
382
+ /// Creates a new iterator.
383
+ pub fn new ( mem : & ' a [ u8 ] ) -> Self {
384
+ Self ( TagBytesIter :: new ( mem) )
385
+ }
386
+ }
381
387
382
- // transform to byte ptr => offset works correctly
383
- let ptr = self . base as * const u8 ;
384
- let ptr = unsafe { ptr. add ( self . n as usize ) } ;
385
- let ptr = ptr as * const HeaderTagHeader ;
386
- assert_eq ! ( ptr as usize % 8 , 0 , "must be 8-byte aligned" ) ;
387
- let tag = unsafe { & * ptr } ;
388
- assert ! (
389
- tag. size( ) <= 500 ,
390
- "no real mb2 header should be bigger than 500bytes - probably wrong memory?! is: {}" ,
391
- { tag. size( ) }
392
- ) ;
393
- assert ! (
394
- tag. size( ) >= 8 ,
395
- "no real mb2 header tag is smaller than 8 bytes - probably wrong memory?! is: {}" ,
396
- { tag. size( ) }
397
- ) ;
398
- assert ! (
399
- !self . end_tag_found,
400
- "There is more than one end tag! Maybe the `length` property is invalid?"
401
- ) ;
402
- self . n += tag. size ( ) ;
403
- // 8-byte alignment of pointer address
404
- self . n += self . n % 8 ;
405
- self . tag_count += 1 ;
406
- if tag. typ ( ) == HeaderTagType :: End {
407
- self . end_tag_found = true ;
408
- }
409
- assert ! ( self . tag_count < HeaderTagType :: count( ) , "Invalid Multiboot2 header tags! There are more tags than technically possible! Maybe the `length` property is invalid?" ) ;
410
- Some ( ptr)
388
+ impl < ' a > Iterator for Multiboot2HeaderTagIter < ' a > {
389
+ type Item = & ' a GenericTag ;
390
+
391
+ fn next ( & mut self ) -> Option < Self :: Item > {
392
+ let bytes = self . 0 . next ( ) ?;
393
+ Some ( GenericTag :: ref_from ( bytes. as_ref ( ) ) )
411
394
}
412
395
}
413
396
414
- impl Debug for Multiboot2HeaderTagIter {
397
+ /* impl Debug for Multiboot2HeaderTagIter {
415
398
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
416
399
let mut debug = f.debug_list();
417
400
self.clone().for_each(|t| unsafe {
418
- let typ = ( * t) . typ ( ) ;
401
+ let typ = (*t).header(). typ();
419
402
if typ == HeaderTagType::End {
420
403
let entry = t as *const EndHeaderTag;
421
404
let entry = &*(entry);
@@ -466,7 +449,7 @@ impl Debug for Multiboot2HeaderTagIter {
466
449
});
467
450
debug.finish()
468
451
}
469
- }
452
+ }*/
470
453
471
454
#[ cfg( test) ]
472
455
mod tests {
0 commit comments