@@ -10,39 +10,38 @@ use multiboot2_common::{MaybeDynSized, Tag};
1010#[ cfg( feature = "builder" ) ]
1111use { alloc:: boxed:: Box , multiboot2_common:: new_boxed} ;
1212
13- /// TODO this memory reader is unsafe and causes UB, according to Miri.
14- /// We need to replace it.
15- ///
1613/// Helper struct to read bytes from a raw pointer and increase the pointer
1714/// automatically.
18- struct Reader {
19- ptr : * const u8 ,
15+ struct Reader < ' a > {
16+ buffer : & ' a [ u8 ] ,
2017 off : usize ,
2118}
2219
23- impl Reader {
24- const fn new < T > ( ptr : * const T ) -> Self {
25- Self {
26- ptr : ptr as * const u8 ,
27- off : 0 ,
28- }
20+ impl < ' a > Reader < ' a > {
21+ const fn new ( buffer : & ' a [ u8 ] ) -> Self {
22+ Self { buffer, off : 0 }
2923 }
3024
3125 fn read_u8 ( & mut self ) -> u8 {
26+ let val = self
27+ . buffer
28+ . get ( self . off )
29+ . cloned ( )
30+ // This is not a solution I'm proud of, but at least it is safe.
31+ // The whole framebuffer tag code originally is not from me.
32+ // I hope someone from the community wants to improve this overall
33+ // functionality someday.
34+ . expect ( "Embedded framebuffer info should be properly sized and available" ) ;
3235 self . off += 1 ;
33- unsafe { * self . ptr . add ( self . off - 1 ) }
36+ val
3437 }
3538
3639 fn read_u16 ( & mut self ) -> u16 {
3740 self . read_u8 ( ) as u16 | ( self . read_u8 ( ) as u16 ) << 8
3841 }
3942
40- fn read_u32 ( & mut self ) -> u32 {
41- self . read_u16 ( ) as u32 | ( self . read_u16 ( ) as u32 ) << 16
42- }
43-
44- fn current_address ( & self ) -> usize {
45- unsafe { self . ptr . add ( self . off ) as usize }
43+ const fn current_ptr ( & self ) -> * const u8 {
44+ unsafe { self . buffer . as_ptr ( ) . add ( self . off ) }
4645 }
4746}
4847
@@ -71,15 +70,16 @@ pub struct FramebufferTag {
7170 /// Contains number of bits per pixel.
7271 bpp : u8 ,
7372
74- /// The type of framebuffer, one of: `Indexed`, `RGB` or `Text`.
75- type_no : u8 ,
73+ /// The type of framebuffer. See [`FramebufferTypeId`].
74+ // TODO: Strictly speaking this causes UB for invalid values. However, no
75+ // sane bootloader puts something illegal there at the moment. When we
76+ // refactor this (newtype pattern?), we should also streamline other
77+ // parts in the code base accordingly.
78+ framebuffer_type : FramebufferTypeId ,
7679
77- // In the multiboot spec, it has this listed as a u8 _NOT_ a u16.
78- // Reading the GRUB2 source code reveals it is in fact a u16.
79- _reserved : u16 ,
80+ _padding : u16 ,
8081
81- // TODO This situation is currently not properly typed. Someone needs to
82- // look into it.
82+ /// This optional data and its meaning depend on the [`FramebufferTypeId`].
8383 buffer : [ u8 ] ,
8484}
8585
@@ -93,14 +93,16 @@ impl FramebufferTag {
9393 width : u32 ,
9494 height : u32 ,
9595 bpp : u8 ,
96- buffer_type : FramebufferTypeId ,
96+ buffer_type : FramebufferType ,
9797 ) -> Box < Self > {
9898 let header = TagHeader :: new ( Self :: ID , 0 ) ;
9999 let address = address. to_ne_bytes ( ) ;
100100 let pitch = pitch. to_ne_bytes ( ) ;
101101 let width = width. to_ne_bytes ( ) ;
102102 let height = height. to_ne_bytes ( ) ;
103+ let buffer_type_id = buffer_type. id ( ) ;
103104 let padding = [ 0 ; 2 ] ;
105+ let optional_buffer = buffer_type. serialize ( ) ;
104106 new_boxed (
105107 header,
106108 & [
@@ -109,8 +111,9 @@ impl FramebufferTag {
109111 & width,
110112 & height,
111113 & [ bpp] ,
112- & [ buffer_type as u8 ] ,
114+ & [ buffer_type_id as u8 ] ,
113115 & padding,
116+ & optional_buffer,
114117 ] ,
115118 )
116119 }
@@ -149,26 +152,33 @@ impl FramebufferTag {
149152 self . bpp
150153 }
151154
152- /// TODO unsafe. Someone needs to fix this. This causes UB according to Miri.
153- /// Dont forget to reenable all test usages once fixed.
154- ///
155155 /// The type of framebuffer, one of: `Indexed`, `RGB` or `Text`.
156- ///
157- /// # Safety
158- /// This function needs refactoring. This was never safe, since the beginning.
159- pub unsafe fn buffer_type ( & self ) -> Result < FramebufferType , UnknownFramebufferType > {
160- let mut reader = Reader :: new ( self . buffer . as_ptr ( ) ) ;
161- let typ = FramebufferTypeId :: try_from ( self . type_no ) ?;
162- match typ {
156+ pub fn buffer_type ( & self ) -> Result < FramebufferType , UnknownFramebufferType > {
157+ let mut reader = Reader :: new ( & self . buffer ) ;
158+
159+ // TODO: We should use the newtype pattern instead or so to properly
160+ // solve this.
161+ let fb_type_raw = self . framebuffer_type as u8 ;
162+ let fb_type = FramebufferTypeId :: try_from ( fb_type_raw) ?;
163+
164+ match fb_type {
163165 FramebufferTypeId :: Indexed => {
164- let num_colors = reader. read_u32 ( ) ;
165- // TODO static cast looks like UB?
166- let palette = unsafe {
167- slice:: from_raw_parts (
168- reader. current_address ( ) as * const FramebufferColor ,
169- num_colors as usize ,
170- )
171- } as & ' static [ FramebufferColor ] ;
166+ // TODO we can create a struct for this and implement
167+ // DynSizedStruct for it to leverage the already existing
168+ // functionality
169+ let num_colors = reader. read_u16 ( ) ;
170+
171+ let palette = {
172+ // Ensure the slice can be created without causing UB
173+ assert_eq ! ( mem:: size_of:: <FramebufferColor >( ) , 3 ) ;
174+
175+ unsafe {
176+ slice:: from_raw_parts (
177+ reader. current_ptr ( ) . cast :: < FramebufferColor > ( ) ,
178+ num_colors as usize ,
179+ )
180+ }
181+ } ;
172182 Ok ( FramebufferType :: Indexed { palette } )
173183 }
174184 FramebufferTypeId :: RGB => {
@@ -224,8 +234,7 @@ impl Debug for FramebufferTag {
224234 f. debug_struct ( "FramebufferTag" )
225235 . field ( "typ" , & self . header . typ )
226236 . field ( "size" , & self . header . size )
227- // TODO unsafe. Fix in a follow-up commit
228- //.field("buffer_type", &self.buffer_type())
237+ . field ( "buffer_type" , & self . buffer_type ( ) )
229238 . field ( "address" , & self . address )
230239 . field ( "pitch" , & self . pitch )
231240 . field ( "width" , & self . width )
@@ -243,12 +252,12 @@ impl PartialEq for FramebufferTag {
243252 && self . width == { other. width }
244253 && self . height == { other. height }
245254 && self . bpp == { other. bpp }
246- && self . type_no == { other. type_no }
255+ && self . framebuffer_type == { other. framebuffer_type }
247256 && self . buffer == other. buffer
248257 }
249258}
250259
251- /// Helper struct for [`FramebufferType`] .
260+ /// ABI-compatible framebuffer type .
252261#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
253262#[ repr( u8 ) ]
254263#[ allow( clippy:: upper_case_acronyms) ]
@@ -282,7 +291,8 @@ impl From<FramebufferType<'_>> for FramebufferTypeId {
282291 }
283292}
284293
285- /// The type of framebuffer.
294+ /// Structured accessory to the provided framebuffer type that is not ABI
295+ /// compatible.
286296#[ derive( Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
287297pub enum FramebufferType < ' a > {
288298 /// Indexed color.
@@ -309,6 +319,47 @@ pub enum FramebufferType<'a> {
309319 Text ,
310320}
311321
322+ impl < ' a > FramebufferType < ' a > {
323+ #[ must_use]
324+ #[ cfg( feature = "builder" ) ]
325+ const fn id ( & self ) -> FramebufferTypeId {
326+ match self {
327+ FramebufferType :: Indexed { .. } => FramebufferTypeId :: Indexed ,
328+ FramebufferType :: RGB { .. } => FramebufferTypeId :: RGB ,
329+ FramebufferType :: Text => FramebufferTypeId :: Text ,
330+ }
331+ }
332+
333+ #[ must_use]
334+ #[ cfg( feature = "builder" ) ]
335+ fn serialize ( & self ) -> alloc:: vec:: Vec < u8 > {
336+ let mut data = alloc:: vec:: Vec :: new ( ) ;
337+ match self {
338+ FramebufferType :: Indexed { palette } => {
339+ // TODO we can create a struct for this and implement
340+ // DynSizedStruct for it to leverage the already existing
341+ // functionality
342+ let num_colors = palette. len ( ) as u16 ;
343+ data. extend ( & num_colors. to_ne_bytes ( ) ) ;
344+ for color in * palette {
345+ let serialized_color = [ color. red , color. green , color. blue ] ;
346+ data. extend ( & serialized_color) ;
347+ }
348+ }
349+ FramebufferType :: RGB { red, green, blue } => data. extend ( & [
350+ red. position ,
351+ red. size ,
352+ green. position ,
353+ green. size ,
354+ blue. position ,
355+ blue. size ,
356+ ] ) ,
357+ FramebufferType :: Text => { }
358+ }
359+ data
360+ }
361+ }
362+
312363/// An RGB color type field.
313364#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
314365#[ repr( C ) ]
@@ -320,7 +371,9 @@ pub struct FramebufferField {
320371 pub size : u8 ,
321372}
322373
323- /// A framebuffer color descriptor in the palette.
374+ /// A framebuffer color descriptor in the palette. On the ABI level, multiple
375+ /// values are consecutively without padding bytes. The spec is not precise in
376+ /// that regard, but looking at Limine's and GRUB's source code confirm that.
324377#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
325378#[ repr( C ) ] // no align(8) here is correct
326379pub struct FramebufferColor {
@@ -355,7 +408,56 @@ mod tests {
355408 #[ test]
356409 #[ cfg( feature = "builder" ) ]
357410 fn create_new ( ) {
358- let tag = FramebufferTag :: new ( 0x1000 , 1 , 1024 , 1024 , 8 , FramebufferTypeId :: Indexed ) ;
411+ let tag = FramebufferTag :: new ( 0x1000 , 1 , 1024 , 1024 , 8 , FramebufferType :: Text ) ;
412+ // Good test for Miri
413+ dbg ! ( tag) ;
414+
415+ let tag = FramebufferTag :: new (
416+ 0x1000 ,
417+ 1 ,
418+ 1024 ,
419+ 1024 ,
420+ 8 ,
421+ FramebufferType :: Indexed {
422+ palette : & [
423+ FramebufferColor {
424+ red : 255 ,
425+ green : 255 ,
426+ blue : 255 ,
427+ } ,
428+ FramebufferColor {
429+ red : 127 ,
430+ green : 42 ,
431+ blue : 73 ,
432+ } ,
433+ ] ,
434+ } ,
435+ ) ;
436+ // Good test for Miri
437+ dbg ! ( tag) ;
438+
439+ let tag = FramebufferTag :: new (
440+ 0x1000 ,
441+ 1 ,
442+ 1024 ,
443+ 1024 ,
444+ 8 ,
445+ FramebufferType :: RGB {
446+ red : FramebufferField {
447+ position : 0 ,
448+ size : 0 ,
449+ } ,
450+ green : FramebufferField {
451+ position : 10 ,
452+ size : 20 ,
453+ } ,
454+ blue : FramebufferField {
455+ position : 30 ,
456+ size : 40 ,
457+ } ,
458+ } ,
459+ ) ;
460+ // Good test for Miri
359461 dbg ! ( tag) ;
360462 }
361463}
0 commit comments