@@ -10,39 +10,38 @@ use multiboot2_common::{MaybeDynSized, Tag};
10
10
#[ cfg( feature = "builder" ) ]
11
11
use { alloc:: boxed:: Box , multiboot2_common:: new_boxed} ;
12
12
13
- /// TODO this memory reader is unsafe and causes UB, according to Miri.
14
- /// We need to replace it.
15
- ///
16
13
/// Helper struct to read bytes from a raw pointer and increase the pointer
17
14
/// automatically.
18
- struct Reader {
19
- ptr : * const u8 ,
15
+ struct Reader < ' a > {
16
+ buffer : & ' a [ u8 ] ,
20
17
off : usize ,
21
18
}
22
19
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 }
29
23
}
30
24
31
25
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" ) ;
32
35
self . off += 1 ;
33
- unsafe { * self . ptr . add ( self . off - 1 ) }
36
+ val
34
37
}
35
38
36
39
fn read_u16 ( & mut self ) -> u16 {
37
40
self . read_u8 ( ) as u16 | ( self . read_u8 ( ) as u16 ) << 8
38
41
}
39
42
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 ) }
46
45
}
47
46
}
48
47
@@ -71,15 +70,16 @@ pub struct FramebufferTag {
71
70
/// Contains number of bits per pixel.
72
71
bpp : u8 ,
73
72
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 ,
76
79
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 ,
80
81
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`].
83
83
buffer : [ u8 ] ,
84
84
}
85
85
@@ -93,14 +93,16 @@ impl FramebufferTag {
93
93
width : u32 ,
94
94
height : u32 ,
95
95
bpp : u8 ,
96
- buffer_type : FramebufferTypeId ,
96
+ buffer_type : FramebufferType ,
97
97
) -> Box < Self > {
98
98
let header = TagHeader :: new ( Self :: ID , 0 ) ;
99
99
let address = address. to_ne_bytes ( ) ;
100
100
let pitch = pitch. to_ne_bytes ( ) ;
101
101
let width = width. to_ne_bytes ( ) ;
102
102
let height = height. to_ne_bytes ( ) ;
103
+ let buffer_type_id = buffer_type. id ( ) ;
103
104
let padding = [ 0 ; 2 ] ;
105
+ let optional_buffer = buffer_type. serialize ( ) ;
104
106
new_boxed (
105
107
header,
106
108
& [
@@ -109,8 +111,9 @@ impl FramebufferTag {
109
111
& width,
110
112
& height,
111
113
& [ bpp] ,
112
- & [ buffer_type as u8 ] ,
114
+ & [ buffer_type_id as u8 ] ,
113
115
& padding,
116
+ & optional_buffer,
114
117
] ,
115
118
)
116
119
}
@@ -149,26 +152,33 @@ impl FramebufferTag {
149
152
self . bpp
150
153
}
151
154
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
- ///
155
155
/// 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 {
163
165
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 ! ( 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
+ } ;
172
182
Ok ( FramebufferType :: Indexed { palette } )
173
183
}
174
184
FramebufferTypeId :: RGB => {
@@ -224,8 +234,7 @@ impl Debug for FramebufferTag {
224
234
f. debug_struct ( "FramebufferTag" )
225
235
. field ( "typ" , & self . header . typ )
226
236
. 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 ( ) )
229
238
. field ( "address" , & self . address )
230
239
. field ( "pitch" , & self . pitch )
231
240
. field ( "width" , & self . width )
@@ -243,12 +252,12 @@ impl PartialEq for FramebufferTag {
243
252
&& self . width == { other. width }
244
253
&& self . height == { other. height }
245
254
&& self . bpp == { other. bpp }
246
- && self . type_no == { other. type_no }
255
+ && self . framebuffer_type == { other. framebuffer_type }
247
256
&& self . buffer == other. buffer
248
257
}
249
258
}
250
259
251
- /// Helper struct for [`FramebufferType`] .
260
+ /// ABI-compatible framebuffer type .
252
261
#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
253
262
#[ repr( u8 ) ]
254
263
#[ allow( clippy:: upper_case_acronyms) ]
@@ -282,7 +291,8 @@ impl From<FramebufferType<'_>> for FramebufferTypeId {
282
291
}
283
292
}
284
293
285
- /// The type of framebuffer.
294
+ /// Structured accessory to the provided framebuffer type that is not ABI
295
+ /// compatible.
286
296
#[ derive( Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
287
297
pub enum FramebufferType < ' a > {
288
298
/// Indexed color.
@@ -309,6 +319,46 @@ pub enum FramebufferType<'a> {
309
319
Text ,
310
320
}
311
321
322
+ impl < ' a > FramebufferType < ' a > {
323
+ #[ must_use]
324
+ const fn id ( & self ) -> FramebufferTypeId {
325
+ match self {
326
+ FramebufferType :: Indexed { .. } => FramebufferTypeId :: Indexed ,
327
+ FramebufferType :: RGB { .. } => FramebufferTypeId :: RGB ,
328
+ FramebufferType :: Text => FramebufferTypeId :: Text ,
329
+ }
330
+ }
331
+
332
+ #[ must_use]
333
+ #[ cfg( feature = "builder" ) ]
334
+ fn serialize ( & self ) -> alloc:: vec:: Vec < u8 > {
335
+ let mut data = alloc:: vec:: Vec :: new ( ) ;
336
+ match self {
337
+ FramebufferType :: Indexed { palette } => {
338
+ // TODO we can create a struct for this and implement
339
+ // DynSizedStruct for it to leverage the already existing
340
+ // functionality
341
+ let num_colors = palette. len ( ) as u16 ;
342
+ data. extend ( & num_colors. to_ne_bytes ( ) ) ;
343
+ for color in * palette {
344
+ let serialized_color = [ color. red , color. green , color. blue ] ;
345
+ data. extend ( & serialized_color) ;
346
+ }
347
+ }
348
+ FramebufferType :: RGB { red, green, blue } => data. extend ( & [
349
+ red. position ,
350
+ red. size ,
351
+ green. position ,
352
+ green. size ,
353
+ blue. position ,
354
+ blue. size ,
355
+ ] ) ,
356
+ FramebufferType :: Text => { }
357
+ }
358
+ data
359
+ }
360
+ }
361
+
312
362
/// An RGB color type field.
313
363
#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
314
364
#[ repr( C ) ]
@@ -320,7 +370,9 @@ pub struct FramebufferField {
320
370
pub size : u8 ,
321
371
}
322
372
323
- /// A framebuffer color descriptor in the palette.
373
+ /// A framebuffer color descriptor in the palette. On the ABI level, multiple
374
+ /// values are consecutively without padding bytes. The spec is not precise in
375
+ /// that regard, but looking at Limine's and GRUB's source code confirm that.
324
376
#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
325
377
#[ repr( C ) ] // no align(8) here is correct
326
378
pub struct FramebufferColor {
@@ -355,7 +407,56 @@ mod tests {
355
407
#[ test]
356
408
#[ cfg( feature = "builder" ) ]
357
409
fn create_new ( ) {
358
- let tag = FramebufferTag :: new ( 0x1000 , 1 , 1024 , 1024 , 8 , FramebufferTypeId :: Indexed ) ;
410
+ let tag = FramebufferTag :: new ( 0x1000 , 1 , 1024 , 1024 , 8 , FramebufferType :: Text ) ;
411
+ // Good test for Miri
412
+ dbg ! ( tag) ;
413
+
414
+ let tag = FramebufferTag :: new (
415
+ 0x1000 ,
416
+ 1 ,
417
+ 1024 ,
418
+ 1024 ,
419
+ 8 ,
420
+ FramebufferType :: Indexed {
421
+ palette : & [
422
+ FramebufferColor {
423
+ red : 255 ,
424
+ green : 255 ,
425
+ blue : 255 ,
426
+ } ,
427
+ FramebufferColor {
428
+ red : 127 ,
429
+ green : 42 ,
430
+ blue : 73 ,
431
+ } ,
432
+ ] ,
433
+ } ,
434
+ ) ;
435
+ // Good test for Miri
436
+ dbg ! ( tag) ;
437
+
438
+ let tag = FramebufferTag :: new (
439
+ 0x1000 ,
440
+ 1 ,
441
+ 1024 ,
442
+ 1024 ,
443
+ 8 ,
444
+ FramebufferType :: RGB {
445
+ red : FramebufferField {
446
+ position : 0 ,
447
+ size : 0 ,
448
+ } ,
449
+ green : FramebufferField {
450
+ position : 10 ,
451
+ size : 20 ,
452
+ } ,
453
+ blue : FramebufferField {
454
+ position : 30 ,
455
+ size : 40 ,
456
+ } ,
457
+ } ,
458
+ ) ;
459
+ // Good test for Miri
359
460
dbg ! ( tag) ;
360
461
}
361
462
}
0 commit comments