Skip to content

Commit f2e1f61

Browse files
committed
Revert "Create a separate BumpAllocator for the EBDA space"
This reverts commit 24e4497. Reason for revert: possibly broke SEV Change-Id: I4f1e8a79a6c0bef618f2a3dff1bad77af6685df5
1 parent 3e8ba87 commit f2e1f61

File tree

5 files changed

+184
-155
lines changed

5 files changed

+184
-155
lines changed

stage0/src/acpi.rs

Lines changed: 164 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -19,134 +19,155 @@
1919
2020
// These data structures (and constants) are derived from
2121
// qemu/hw/acpi/bios-linker-loader.c that defines the interface.
22-
use alloc::{boxed::Box, collections::btree_map::BTreeMap};
2322
use core::{
24-
alloc::{Allocator, Layout},
23+
cell::OnceCell,
2524
ffi::CStr,
2625
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},
3028
};
3129

3230
use sha2::{Digest, Sha256};
33-
use spinning_top::Spinlock;
3431
use strum::FromRepr;
3532
use zerocopy::AsBytes;
3633

3734
use crate::{
3835
acpi_tables::{
3936
DescriptionHeader, MultiprocessorWakeup, ProcessorLocalApic, ProcessorLocalX2Apic, Rsdp,
4037
},
41-
allocator::BumpAllocator,
42-
fw_cfg::FwCfg,
38+
fw_cfg::{DirEntry, FwCfg},
4339
Madt,
4440
};
4541

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();
12947

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.
13050
pub const EBDA_SIZE: usize = 127 * 1024;
131-
type EbdaAllocator = BumpAllocator<EBDA_SIZE>;
13251

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.
13555
#[link_section = ".ebda"]
136-
pub static EBDA_ALLOCATOR: EbdaAllocator = EbdaAllocator::uninit();
56+
static mut EBDA_MEM: MaybeUninit<EbdaMemT> = MaybeUninit::uninit();
13757

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();
14159

60+
// Safety: we include a nul byte at the end of the string, and that is the only
61+
// nul byte.
14262
const TABLE_LOADER_FILE_NAME: &CStr =
14363
unsafe { CStr::from_bytes_with_nul_unchecked(b"etc/table-loader\0") };
14464
const RSDP_FILE_NAME_SUFFIX: &str = "acpi/rsdp";
65+
const ACPI_TABLES_FILE_NAME_SUFFIX: &str = "acpi/tables";
14566

14667
const ROMFILE_LOADER_FILESZ: usize = 56;
14768

14869
type RomfileName = [u8; ROMFILE_LOADER_FILESZ];
14970

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+
150171
#[repr(u32)]
151172
#[derive(Debug, Eq, FromRepr, Ord, PartialEq, PartialOrd)]
152173
enum CommandTag {
@@ -203,13 +224,40 @@ impl Allocate {
203224
acpi_digest: &mut Sha256,
204225
) -> Result<(), &'static str> {
205226
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+
}
206236

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()) };
209239

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+
}
213261
}
214262
}
215263

@@ -248,10 +296,8 @@ impl AddPointer {
248296
}
249297

250298
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())?;
255301

256302
if self.offset as usize + self.size as usize > dest_file.len() {
257303
return Err("Write for COMMAND_ADD_POINTER would overflow destination file");
@@ -264,7 +310,7 @@ impl AddPointer {
264310
pointer.as_bytes_mut()[..self.size as usize].copy_from_slice(
265311
&dest_file[self.offset as usize..(self.offset + self.size as u32) as usize],
266312
);
267-
pointer += src_file_ptr as u64;
313+
pointer += src_file.as_ptr() as u64;
268314
dest_file[self.offset as usize..(self.offset + self.size as u32) as usize]
269315
.copy_from_slice(&pointer.as_bytes()[..self.size as usize]);
270316

@@ -305,8 +351,7 @@ impl AddChecksum {
305351
}
306352

307353
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())?;
310355

311356
if self.start as usize > file.len()
312357
|| (self.start + self.length) as usize > file.len()
@@ -427,8 +472,7 @@ impl AddPciHoles {
427472
}
428473

429474
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())?;
432476

433477
if file.len() < self.pci_start_offset_32 as usize
434478
|| file.len() - 4 < self.pci_start_offset_32 as usize
@@ -618,17 +662,19 @@ pub fn build_acpi_tables<P: crate::Platform>(
618662
command.invoke(fwcfg, acpi_digest)?;
619663
}
620664

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>()?;
622668
log::info!("ACPI tables before finalizing:");
623-
debug_print_acpi_tables(&rsdp)?;
669+
debug_print_acpi_tables(rsdp)?;
624670

625671
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>()?;
628674
log::info!("ACPI tables after finalizing:");
629-
debug_print_acpi_tables(&rsdp)?;
675+
debug_print_acpi_tables(rsdp)?;
630676

631-
Ok(rsdp.into_ref().get_ref())
677+
Ok(rsdp)
632678
}
633679

634680
/// Prints ACPI metadata including RSDP, RSDT and XSDT (if present).

0 commit comments

Comments
 (0)