@@ -8,8 +8,40 @@ use crate::{
8
8
EntryAddressHeaderTag , EntryEfi32HeaderTag , EntryEfi64HeaderTag , FramebufferHeaderTag ,
9
9
ModuleAlignHeaderTag , Multiboot2BasicHeader , RelocatableHeaderTag ,
10
10
} ;
11
+ use alloc:: boxed:: Box ;
11
12
use alloc:: vec:: Vec ;
12
13
use core:: mem:: size_of;
14
+ use core:: ops:: Deref ;
15
+
16
+ /// Holds the raw bytes of a boot information built with [`HeaderBuilder`]
17
+ /// on the heap. The bytes returned by [`HeaderBytes::as_bytes`] are
18
+ /// guaranteed to be properly aligned.
19
+ #[ derive( Clone , Debug ) ]
20
+ pub struct HeaderBytes {
21
+ // Offset into the bytes where the header starts. This is necessary to
22
+ // guarantee alignment at the moment.
23
+ offset : usize ,
24
+ bytes : Box < [ u8 ] > ,
25
+ }
26
+
27
+ impl HeaderBytes {
28
+ /// Returns the bytes. They are guaranteed to be correctly aligned.
29
+ pub fn as_bytes ( & self ) -> & [ u8 ] {
30
+ let slice = & self . bytes [ self . offset ..] ;
31
+ // At this point, the alignment is guaranteed. If not, something is
32
+ // broken fundamentally.
33
+ assert_eq ! ( slice. as_ptr( ) . align_offset( 8 ) , 0 ) ;
34
+ slice
35
+ }
36
+ }
37
+
38
+ impl Deref for HeaderBytes {
39
+ type Target = [ u8 ] ;
40
+
41
+ fn deref ( & self ) -> & Self :: Target {
42
+ self . as_bytes ( )
43
+ }
44
+ }
13
45
14
46
/// Builder to construct a valid Multiboot2 header dynamically at runtime.
15
47
/// The tags will appear in the order of their corresponding enumeration,
@@ -61,16 +93,11 @@ impl HeaderBuilder {
61
93
/// easily calculate the size of a Multiboot2 header, where
62
94
/// all the tags are 8-byte aligned.
63
95
const fn size_or_up_aligned ( size : usize ) -> usize {
64
- let remainder = size % 8 ;
65
- if remainder == 0 {
66
- size
67
- } else {
68
- size + 8 - remainder
69
- }
96
+ ( size + 7 ) & !7
70
97
}
71
98
72
- /// Returns the expected length of the Multiboot2 header,
73
- /// when the ` build()` -method gets called.
99
+ /// Returns the expected length of the Multiboot2 header, when the
100
+ /// [`Self:: build] -method gets called.
74
101
pub fn expected_len ( & self ) -> usize {
75
102
let base_len = size_of :: < Multiboot2BasicHeader > ( ) ;
76
103
// size_or_up_aligned not required, because basic header length is 16 and the
@@ -115,8 +142,12 @@ impl HeaderBuilder {
115
142
}
116
143
117
144
/// Adds the bytes of a tag to the final Multiboot2 header byte vector.
118
- /// Align should be true for all tags except the end tag.
119
145
fn build_add_bytes ( dest : & mut Vec < u8 > , source : & [ u8 ] , is_end_tag : bool ) {
146
+ let vec_next_write_ptr = unsafe { dest. as_ptr ( ) . add ( dest. len ( ) ) } ;
147
+ // At this point, the alignment is guaranteed. If not, something is
148
+ // broken fundamentally.
149
+ assert_eq ! ( vec_next_write_ptr. align_offset( 8 ) , 0 ) ;
150
+
120
151
dest. extend ( source) ;
121
152
if !is_end_tag {
122
153
let size = source. len ( ) ;
@@ -128,9 +159,22 @@ impl HeaderBuilder {
128
159
}
129
160
130
161
/// Constructs the bytes for a valid Multiboot2 header with the given properties.
131
- /// The bytes can be casted to a Multiboot2 structure.
132
- pub fn build ( mut self ) -> Vec < u8 > {
133
- let mut data = Vec :: new ( ) ;
162
+ pub fn build ( mut self ) -> HeaderBytes {
163
+ // We allocate more than necessary so that we can ensure an correct
164
+ // alignment within this data.
165
+ let mut data = Vec :: < u8 > :: with_capacity ( self . expected_len ( ) + 7 ) ;
166
+ // Pointer to check that no relocation happened.
167
+ let alloc_ptr = data. as_ptr ( ) ;
168
+
169
+ // As long as there is no nice way in stable Rust to guarantee the
170
+ // alignment of a vector, I add zero bytes at the beginning and the
171
+ // header might not start at the start of the allocation.
172
+ //
173
+ // Unfortunately, it is not possible to reliably test this in a unit
174
+ // test as long as the allocator_api feature is not stable.
175
+ // Due to my manual testing, however, it works.
176
+ let offset = data. as_ptr ( ) . align_offset ( 8 ) ;
177
+ data. extend ( [ 0 ] . repeat ( offset) ) ;
134
178
135
179
Self :: build_add_bytes (
136
180
& mut data,
@@ -176,7 +220,28 @@ impl HeaderBuilder {
176
220
177
221
Self :: build_add_bytes ( & mut data, & EndHeaderTag :: new ( ) . struct_as_bytes ( ) , true ) ;
178
222
179
- data
223
+ assert_eq ! (
224
+ alloc_ptr,
225
+ data. as_ptr( ) ,
226
+ "Vector was reallocated. Alignment of header probably broken!"
227
+ ) ;
228
+ assert_eq ! (
229
+ data[ 0 ..offset] . iter( ) . sum:: <u8 >( ) ,
230
+ 0 ,
231
+ "The offset to alignment area should be zero."
232
+ ) ;
233
+
234
+ let boxed = data. into_boxed_slice ( ) ;
235
+ assert_eq ! (
236
+ boxed. as_ptr( ) ,
237
+ boxed,
238
+ "Memory was reallocated. Alignment of header probably broken!"
239
+ ) ;
240
+
241
+ HeaderBytes {
242
+ offset,
243
+ bytes : boxed,
244
+ }
180
245
}
181
246
182
247
// clippy thinks this can be a const fn but the compiler denies it
0 commit comments