19
19
20
20
// These data structures (and constants) are derived from
21
21
// qemu/hw/acpi/bios-linker-loader.c that defines the interface.
22
- use alloc:: { boxed:: Box , collections:: btree_map:: BTreeMap } ;
23
22
use core:: {
24
- alloc :: { Allocator , Layout } ,
23
+ cell :: OnceCell ,
25
24
ffi:: CStr ,
26
25
fmt:: { Debug , Formatter , Result as FmtResult } ,
27
- mem:: size_of_val,
28
- ops:: Deref ,
29
- pin:: Pin ,
26
+ mem:: { size_of, size_of_val, zeroed, MaybeUninit } ,
27
+ ops:: { Deref , Range } ,
30
28
} ;
31
29
32
30
use sha2:: { Digest , Sha256 } ;
33
- use spinning_top:: Spinlock ;
34
31
use strum:: FromRepr ;
35
32
use zerocopy:: AsBytes ;
36
33
37
34
use crate :: {
38
35
acpi_tables:: {
39
36
DescriptionHeader , MultiprocessorWakeup , ProcessorLocalApic , ProcessorLocalX2Apic , Rsdp ,
40
37
} ,
41
- allocator:: BumpAllocator ,
42
- fw_cfg:: FwCfg ,
38
+ fw_cfg:: { DirEntry , FwCfg } ,
43
39
Madt ,
44
40
} ;
45
41
46
- pub struct Ebda < ' a , A : Allocator > {
47
- alloc : & ' a A ,
48
- files : BTreeMap < RomfileName , Box < [ u8 ] , & ' a A > > ,
49
- }
50
-
51
- impl < ' a : ' static , A : Allocator > Ebda < ' a , A > {
52
- const fn new ( alloc : & ' a A ) -> Self {
53
- Ebda { alloc, files : BTreeMap :: new ( ) }
54
- }
55
-
56
- /// Creates a new file in memory provided by the EBDA allocator.
57
- ///
58
- /// Returns an error if a file with such a name already exists or memory
59
- /// could not be allocated.
60
- pub fn new_file (
61
- & mut self ,
62
- name : & RomfileName ,
63
- size : usize ,
64
- align : usize ,
65
- ) -> Result < & mut [ u8 ] , & ' static str > {
66
- if self . files . contains_key ( name) {
67
- return Err ( "duplicate file in table_loader" ) ;
68
- }
69
-
70
- // Unfortunately the memory allocation is fairly convoluted because two
71
- // requirements:
72
- // 1. We need to guaratnee alignment to `align`, but `u8` doesn't care about
73
- // alignment. Therefore we can't just use `Box::new_zeroed_slice_in`.
74
- // 2. We need to allocate memory from `EBDA_ALLOCATOR`.
75
- // Note that boxing the value is mostly for good Rust hygiene, as
76
- // the map itself that will own the files will be 'static, and
77
- // `BumpAllocator` doesn't support deallocation anyway. But it makes testing
78
- // easier.
79
-
80
- // Step 1: allocate a pile of raw memory.
81
- let mem = self
82
- . alloc
83
- . allocate_zeroed (
84
- Layout :: from_size_align ( size, align)
85
- . map_err ( |_| "invalid alignment in file_loader" ) ?,
86
- )
87
- . map_err ( |_| "memory allocation failure in file_loader" ) ?;
88
-
89
- // Step 2: wrap it in a `Box` so that we wouldn't leak memory.
90
- // Ideally we'd call `Box::from_non_null_in`, but our current version of Rust
91
- // doesn't have that.
92
- // Safety: we only call the method once (so no double-free can occur); we've
93
- // allocated the memory from the specified allocator; and any layout (and
94
- // content) is valid for [u8].
95
- let mem = unsafe { Box :: from_raw_in ( mem. as_ptr ( ) , self . alloc ) } ;
96
-
97
- self . files . insert ( * name, mem) ;
98
- self . get_file_mut ( name)
99
- }
100
-
101
- pub fn get_file ( & self , name : & RomfileName ) -> Result < & [ u8 ] , & ' static str > {
102
- self . files . get ( name) . map ( |v| & * * v) . ok_or ( "file not found in table_loader" )
103
- }
104
-
105
- pub fn get_file_mut ( & mut self , name : & RomfileName ) -> Result < & mut [ u8 ] , & ' static str > {
106
- self . files . get_mut ( name) . map ( |x| x. as_mut ( ) ) . ok_or ( "file not found in table_loader" )
107
- }
108
-
109
- pub fn get_rsdp_mut ( & mut self ) -> Result < Pin < & ' a mut Rsdp > , & ' static str > {
110
- for ( name, value) in self . files . iter_mut ( ) {
111
- if CStr :: from_bytes_until_nul ( name)
112
- . map_err ( |_| "invalid table_loader file name" ) ?
113
- . to_bytes ( )
114
- . ends_with ( RSDP_FILE_NAME_SUFFIX . as_bytes ( ) )
115
- {
116
- let mem = value. as_mut_ptr ( ) ;
117
-
118
- // Safety: we will validate the RSDP before returning the value.
119
- let rsdp: & mut Rsdp = unsafe { & mut * ( mem as * mut Rsdp ) } ;
120
- rsdp. validate ( ) ?;
121
- // We need to ensure the `Rsdp` doesn't move in memory, so it gets `Pin`-ned.
122
- return Ok ( Pin :: new ( rsdp) ) ;
123
- }
124
- }
125
-
126
- Err ( "RSDP not found in loaded ACPI tables" )
127
- }
128
- }
42
+ /// Root System Descriptor Pointer.
43
+ /// Not really a pointer but a structure (see https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#root-system-description-pointer-rsdp).#
44
+ /// It's located in the first 1KiB of the EBDA.
45
+ #[ link_section = ".ebda.rsdp" ]
46
+ static mut RSDP : MaybeUninit < Rsdp > = MaybeUninit :: uninit ( ) ;
129
47
48
+ /// EBDA (Extended Bios Data Area) size. Its size is 128KiB but we
49
+ /// reserve the first 1KiB for the RSDP which we treat separately.
130
50
pub const EBDA_SIZE : usize = 127 * 1024 ;
131
- type EbdaAllocator = BumpAllocator < EBDA_SIZE > ;
132
51
133
- /// EBDA memory: 128K of memory just below 0xA_0000 (enforced in the linker
134
- /// script).
52
+ type EbdaMemT = [ u8 ; EBDA_SIZE ] ;
53
+
54
+ /// EBDA (Extended Bios Data Area) raw memory.
135
55
#[ link_section = ".ebda" ]
136
- pub static EBDA_ALLOCATOR : EbdaAllocator = EbdaAllocator :: uninit ( ) ;
56
+ static mut EBDA_MEM : MaybeUninit < EbdaMemT > = MaybeUninit :: uninit ( ) ;
137
57
138
- /// Wrapper around the EBDA allocator that manages file_loader files in the EBDA
139
- /// memory.
140
- pub static EBDA : Spinlock < Ebda < EbdaAllocator > > = Spinlock :: new ( Ebda :: new ( & EBDA_ALLOCATOR ) ) ;
58
+ static mut EBDA_INSTANCE : OnceCell < Ebda > = OnceCell :: new ( ) ;
141
59
60
+ // Safety: we include a nul byte at the end of the string, and that is the only
61
+ // nul byte.
142
62
const TABLE_LOADER_FILE_NAME : & CStr =
143
63
unsafe { CStr :: from_bytes_with_nul_unchecked ( b"etc/table-loader\0 " ) } ;
144
64
const RSDP_FILE_NAME_SUFFIX : & str = "acpi/rsdp" ;
65
+ const ACPI_TABLES_FILE_NAME_SUFFIX : & str = "acpi/tables" ;
145
66
146
67
const ROMFILE_LOADER_FILESZ : usize = 56 ;
147
68
148
69
type RomfileName = [ u8 ; ROMFILE_LOADER_FILESZ ] ;
149
70
71
+ fn get_file ( name : & CStr ) -> Result < & ' static mut [ u8 ] , & ' static str > {
72
+ // TODO: b/380246359 - When Ebda wraps around Rsdp too, remove these
73
+ // direct accesses to EBDA_MEM to guarantee Ebda::len stays correct.
74
+
75
+ // Safety: we do not have concurrent threads so accessing the static is safe,
76
+ // and even if Allocate has not been called yet, all values are valid for an
77
+ // [u8].
78
+ let name = name. to_str ( ) . map_err ( |_| "invalid file name" ) ?;
79
+ if name. ends_with ( RSDP_FILE_NAME_SUFFIX ) {
80
+ Ok ( unsafe { RSDP . assume_init_mut ( ) . as_bytes_mut ( ) } )
81
+ } else if name. ends_with ( ACPI_TABLES_FILE_NAME_SUFFIX ) {
82
+ Ok ( unsafe { EBDA_MEM . assume_init_mut ( ) } )
83
+ } else {
84
+ Err ( "Unsupported file in table-loader" )
85
+ }
86
+ }
87
+
88
+ /// A wrapper around EBDA memory with helpers to manage it.
89
+ // TODO: b/380246359 - let Ebda wrap around both RSDP and the rest of EBDA.
90
+ #[ derive( Debug ) ]
91
+ pub struct Ebda {
92
+ ebda_buf : & ' static mut [ u8 ] ,
93
+ /// Count of bytes actually used within ebda_buf.
94
+ len_bytes : usize ,
95
+ }
96
+
97
+ impl Ebda {
98
+ pub fn instance ( ) -> & ' static mut Ebda {
99
+ // Safety: EBDA_INSTANCE accessed for write only from a single thread (BSP).
100
+ // Read-only access from APs (assembly code).
101
+ unsafe {
102
+ match EBDA_INSTANCE . get_mut ( ) {
103
+ Some ( ebda) => ebda,
104
+ None => {
105
+ // Safety: EBDA_MEM accessed for write only from single thread BSP. This block
106
+ // executes exactly once. Always a valid &[u8].
107
+ let ebda = Self { ebda_buf : EBDA_MEM . assume_init_mut ( ) , len_bytes : 0 } ;
108
+ EBDA_INSTANCE . set ( ebda) . unwrap ( ) ;
109
+ EBDA_INSTANCE . get_mut ( ) . unwrap ( )
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ /// Clears EBDA buffer then reads a file from fwcfg into it.
116
+ pub fn read_fwcfg < P : crate :: Platform > (
117
+ & mut self ,
118
+ fwcfg : & mut FwCfg < P > ,
119
+ file : DirEntry ,
120
+ ) -> Result < ( ) , & ' static str > {
121
+ self . clear ( ) ;
122
+ let bytes_read: usize = fwcfg. read_file ( & file, self . ebda_buf ) ?;
123
+ self . len_bytes = bytes_read;
124
+ Ok ( ( ) )
125
+ }
126
+
127
+ /// Allocates byte_count of memory on the EBDA and returns a slice to it.
128
+ pub fn allocate ( & mut self , byte_count : usize ) -> Result < & mut [ u8 ] , & ' static str > {
129
+ let dest_base_ix = self . len_bytes ;
130
+ let dest_top_ix = dest_base_ix + byte_count;
131
+
132
+ self . len_bytes += byte_count;
133
+ Ok ( self . ebda_buf [ dest_base_ix..dest_top_ix] . as_mut ( ) )
134
+ }
135
+
136
+ pub fn check_alignment ( & self , required_alignment : u32 ) -> Result < ( ) , & ' static str > {
137
+ let start = self . ebda_buf . as_ptr_range ( ) . start as u64 ;
138
+ if start % required_alignment as u64 != 0 {
139
+ log:: error!(
140
+ "ACPI tables address: {:#018x}, required alignment: {}" ,
141
+ start,
142
+ required_alignment
143
+ ) ;
144
+ return Err ( "ACPI tables address not aligned properly" ) ;
145
+ }
146
+ Ok ( ( ) )
147
+ }
148
+
149
+ pub fn get_buf ( & self ) -> & [ u8 ] {
150
+ self . ebda_buf
151
+ }
152
+
153
+ /// Returns true if addr is within EBDA address range.
154
+ /// Convenience method for validations.
155
+ pub fn contains_addr ( & self , addr : usize ) -> bool {
156
+ self . ebda_buf . as_ptr_range ( ) . contains ( & ( addr as * const u8 ) )
157
+ }
158
+
159
+ pub fn contains_addr_range ( & self , range : & Range < usize > ) -> bool {
160
+ self . contains_addr ( range. start ) && self . contains_addr ( range. end )
161
+ }
162
+
163
+ fn clear ( & mut self ) {
164
+ for elem in self . ebda_buf . iter_mut ( ) {
165
+ * elem = 0 ;
166
+ }
167
+ self . len_bytes = 0 ;
168
+ }
169
+ }
170
+
150
171
#[ repr( u32 ) ]
151
172
#[ derive( Debug , Eq , FromRepr , Ord , PartialEq , PartialOrd ) ]
152
173
enum CommandTag {
@@ -203,13 +224,40 @@ impl Allocate {
203
224
acpi_digest : & mut Sha256 ,
204
225
) -> Result < ( ) , & ' static str > {
205
226
let file = fwcfg. find ( self . file ( ) ) . unwrap ( ) ;
227
+ let name = self . file ( ) . to_str ( ) . map_err ( |_| "invalid file name" ) ?;
228
+
229
+ if name. ends_with ( RSDP_FILE_NAME_SUFFIX ) {
230
+ // ACPI 1.0 RSDP is 20 bytes, ACPI 2.0 RSDP is 36 bytes.
231
+ // We don't really care which version we're dealing with, as long as the data
232
+ // structure is one of the two.
233
+ if file. size ( ) > size_of :: < Rsdp > ( ) || ( file. size ( ) != 20 && file. size ( ) != 36 ) {
234
+ return Err ( "RSDP doesn't match expected size" ) ;
235
+ }
206
236
207
- let mut ebda = EBDA . lock ( ) ;
208
- let buf = ebda . new_file ( & self . file , file . size ( ) , self . align as usize ) ? ;
237
+ // Safety: we do not have concurrent threads so accessing the static is safe.
238
+ let buf = unsafe { RSDP . write ( zeroed ( ) ) } ;
209
239
210
- fwcfg. read_file ( & file, buf) ?;
211
- acpi_digest. update ( buf) ;
212
- Ok ( ( ) )
240
+ // Sanity checks.
241
+ if ( buf as * const _ as u64 ) < 0x80000 || ( buf as * const _ as u64 ) > 0x81000 {
242
+ log:: error!( "RSDP address: {:p}" , buf) ;
243
+ return Err ( "RSDP address is not within the first 1 KiB of EBDA" ) ;
244
+ }
245
+ if ( buf as * const _ as u64 ) % self . align as u64 != 0 {
246
+ return Err ( "RSDP address not aligned properly" ) ;
247
+ }
248
+
249
+ fwcfg. read_file ( & file, buf. as_bytes_mut ( ) ) ?;
250
+ acpi_digest. update ( buf. as_bytes ( ) ) ;
251
+ Ok ( ( ) )
252
+ } else if name. ends_with ( ACPI_TABLES_FILE_NAME_SUFFIX ) {
253
+ let ebda = Ebda :: instance ( ) ;
254
+ ebda. check_alignment ( self . align ) ?;
255
+ ebda. read_fwcfg ( fwcfg, file) ?;
256
+ acpi_digest. update ( ebda. get_buf ( ) ) ;
257
+ Ok ( ( ) )
258
+ } else {
259
+ Err ( "Unsupported file in table-loader" )
260
+ }
213
261
}
214
262
}
215
263
@@ -248,10 +296,8 @@ impl AddPointer {
248
296
}
249
297
250
298
fn invoke ( & self ) -> Result < ( ) , & ' static str > {
251
- let mut ebda = EBDA . lock ( ) ;
252
-
253
- let src_file_ptr = ebda. get_file ( & self . src_file ) ?. as_ptr ( ) ;
254
- let dest_file = ebda. get_file_mut ( & self . dest_file ) ?;
299
+ let dest_file = get_file ( self . dest_file ( ) ) ?;
300
+ let src_file = get_file ( self . src_file ( ) ) ?;
255
301
256
302
if self . offset as usize + self . size as usize > dest_file. len ( ) {
257
303
return Err ( "Write for COMMAND_ADD_POINTER would overflow destination file" ) ;
@@ -264,7 +310,7 @@ impl AddPointer {
264
310
pointer. as_bytes_mut ( ) [ ..self . size as usize ] . copy_from_slice (
265
311
& dest_file[ self . offset as usize ..( self . offset + self . size as u32 ) as usize ] ,
266
312
) ;
267
- pointer += src_file_ptr as u64 ;
313
+ pointer += src_file . as_ptr ( ) as u64 ;
268
314
dest_file[ self . offset as usize ..( self . offset + self . size as u32 ) as usize ]
269
315
. copy_from_slice ( & pointer. as_bytes ( ) [ ..self . size as usize ] ) ;
270
316
@@ -305,8 +351,7 @@ impl AddChecksum {
305
351
}
306
352
307
353
fn invoke ( & self ) -> Result < ( ) , & ' static str > {
308
- let mut ebda = EBDA . lock ( ) ;
309
- let file = ebda. get_file_mut ( & self . file ) ?;
354
+ let file = get_file ( self . file ( ) ) ?;
310
355
311
356
if self . start as usize > file. len ( )
312
357
|| ( self . start + self . length ) as usize > file. len ( )
@@ -427,8 +472,7 @@ impl AddPciHoles {
427
472
}
428
473
429
474
fn invoke ( & self ) -> Result < ( ) , & ' static str > {
430
- let mut ebda = EBDA . lock ( ) ;
431
- let file = ebda. get_file_mut ( & self . file ) ?;
475
+ let file = get_file ( self . file ( ) ) ?;
432
476
433
477
if file. len ( ) < self . pci_start_offset_32 as usize
434
478
|| file. len ( ) - 4 < self . pci_start_offset_32 as usize
@@ -618,17 +662,19 @@ pub fn build_acpi_tables<P: crate::Platform>(
618
662
command. invoke ( fwcfg, acpi_digest) ?;
619
663
}
620
664
621
- let mut rsdp = EBDA . lock ( ) . get_rsdp_mut ( ) ?;
665
+ // Safety: we ensure that the RSDP is valid before returning a reference to it.
666
+ let rsdp = unsafe { RSDP . assume_init_mut ( ) } ;
667
+ rsdp. validate :: < P > ( ) ?;
622
668
log:: info!( "ACPI tables before finalizing:" ) ;
623
- debug_print_acpi_tables ( & rsdp) ?;
669
+ debug_print_acpi_tables ( rsdp) ?;
624
670
625
671
log:: info!( "Finalizing RSDP" ) ;
626
- P :: finalize_acpi_tables ( rsdp. as_mut ( ) . get_mut ( ) ) ?;
627
- rsdp. validate ( ) ?;
672
+ P :: finalize_acpi_tables ( rsdp) ?;
673
+ rsdp. validate :: < P > ( ) ?;
628
674
log:: info!( "ACPI tables after finalizing:" ) ;
629
- debug_print_acpi_tables ( & rsdp) ?;
675
+ debug_print_acpi_tables ( rsdp) ?;
630
676
631
- Ok ( rsdp. into_ref ( ) . get_ref ( ) )
677
+ Ok ( rsdp)
632
678
}
633
679
634
680
/// Prints ACPI metadata including RSDP, RSDT and XSDT (if present).
0 commit comments