Skip to content

Commit 571627b

Browse files
PVALIDATE and initialize the legacy SMBIOS range (#4982)
* PVALIDATE and initialize the legacy SMBIOS range Linux commit 0f4a1e80989a ("x86/sev: Skip ROM range scans and validation for SEV-SNP guests") removes Linux's attempt to PVALIDATE the legacy ROM regions, including the legacy SMBIOS range [0xf0000, 0x100000). However, legacy code may still attempt to scan this range when using non-EFI firmware such as Stage0. To avoid a crash during guest boot, PVALIDATE the range in Stage0. To avoid legacy code reading garbage from this region, initialize the PVALIDATEd memory to 0. * FIXUP: address reviewer feedback on legacy smbios range readability * FIXUP: fix typo on line 590 (should be equals, not NE) * FIXUP: provide context in panic message per reviewer feedback * FIXUP: fix typo in panic message (SMBOIS -> SMBIOS)
1 parent f5898b0 commit 571627b

3 files changed

Lines changed: 48 additions & 3 deletions

File tree

stage0/src/sev.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,39 @@ pub fn validate_memory(e820_table: &[BootE820Entry], encrypted: u64) {
580580
}
581581
}
582582

583+
// Locate the legacy SMBIOS range [0xF_0000, 0x10_0000) in the E820 table.
584+
// Unwrap() will panic if entry not found with expected start, size, and type.
585+
let legacy_smbios_range_entry = e820_table
586+
.iter()
587+
.find(|entry| {
588+
entry.addr() == 0xF_0000
589+
&& entry.size() == 0x1_0000
590+
&& entry.entry_type() == Some(E820EntryType::RESERVED)
591+
})
592+
.expect("couldn't find legacy SMBIOS memory range");
593+
594+
// Pvalidate the legacy SMBIOS range since legacy code may scan this range for
595+
// the SMBIOS entry point table, even if the range is marked as reserved.
596+
let range = PhysFrame::<Size4KiB>::range(
597+
PhysFrame::from_start_address(PhysAddr::new(legacy_smbios_range_entry.addr() as u64))
598+
.unwrap(),
599+
PhysFrame::from_start_address(PhysAddr::new(
600+
(legacy_smbios_range_entry.addr() + legacy_smbios_range_entry.size()) as u64,
601+
))
602+
.unwrap(),
603+
);
604+
range.pvalidate(&mut validation_pt, encrypted).expect("failed to validate SMBIOS memory");
605+
606+
// Safety: the E820 table indicates that this is the correct memory segment.
607+
let legacy_smbios_range_bytes = unsafe {
608+
core::slice::from_raw_parts_mut::<u8>(
609+
legacy_smbios_range_entry.addr() as *mut u8,
610+
legacy_smbios_range_entry.size(),
611+
)
612+
};
613+
// Zeroize the legacy SMBIOS range bytes to avoid legacy code reading garbage.
614+
legacy_smbios_range_bytes.zeroize();
615+
583616
page_tables.pd_0[1].set_unused();
584617
page_tables.pdpt[1].set_unused();
585618
tlb::flush_all();

stage0/src/zero_page.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,13 @@ impl ZeroPage {
149149
self.validate_e820_table();
150150

151151
// Carve out a chunk of memory for the ACPI area in the range
152-
// [0x80000-0xA0000). We also remove the region [0xA0000,0x100000)
152+
// [0x80000-0xA0000). We also remove the region [0xA0000,0xF0000)
153153
// since historically this contained hardware-related regions such as
154-
// the VGA bios rom.
154+
// the VGA bios rom. Finally, reserve [0xF0000,0x100000) as Stage0
155+
// initializes this memory to handle legacy scans of the SMBIOS range.
155156
self.insert_e820_entry(BootE820Entry::new(0x8_0000, 0x2_0000, E820EntryType::ACPI));
156-
self.ensure_e820_gap(0xA_0000, 0x6_0000);
157+
self.ensure_e820_gap(0xA_0000, 0x5_0000);
158+
self.insert_e820_entry(BootE820Entry::new(0xF_0000, 0x1_0000, E820EntryType::RESERVED));
157159

158160
for entry in self.inner.e820_table() {
159161
log::debug!(

stage0_bin/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ Stage0 maps the first 1 GiB of physical memory using identity mapping before
123123
handing control over to the kernel. The stage0 binary itself is mapped into
124124
memory just below the end of 4 GiB address space, like any other BIOS ROM.
125125

126+
We explicitly reserve the SMBIOS entry table memory range (0xF0000-0xFFFFF) to
127+
indicate that Stage0 initializes this range.
128+
126129
```text
127130
| ... |
128131
0x1_0000_0000 +------------------------------------------------+ 4GiB
@@ -144,6 +147,8 @@ memory just below the end of 4 GiB address space, like any other BIOS ROM.
144147
0x20_0000 +------------------------------------------------+ 2MiB
145148
| |
146149
0x10_0000 +------------------------------------------------+ 1MiB
150+
| Reserved SMBIOS entry point table memory |
151+
0xF_0000 +------------------------------------------------+ 960KiB
147152
| BIOS Hole |
148153
0xA_0000 +------------------------------------------------+ 640KiB
149154
| Extended BIOS Data Area (ACPI Tables) |
@@ -161,6 +166,9 @@ The Linux kernel and the Oak restricted kernel both assume that the firmware
161166
will validate all (or at least most) of the guest-physical memory before jumping
162167
into the kernel (by calling the PVALIDATE instruction).
163168

169+
Because legacy code may scan the SMBIOS region (even if marked as reserved in
170+
the E820 table), we pvalidate this region to prevent crashes.
171+
164172
We don't have to PVALIDATE the Stage 0 ROM image range, since that memory was
165173
already set in the appropriate validated state by the Secure Processor before
166174
launching the guest VM.
@@ -172,6 +180,8 @@ launching the guest VM.
172180
0xFFFE_0000 +------------------------------------------------+ 4GiB - 128MiB
173181
| PVALIDATEd |
174182
0x10_0000 +------------------------------------------------+ 1MiB
183+
| PVALIDATEd SMBIOS entry point table memory |
184+
0xF_0000 +------------------------------------------------+ 960KiB
175185
| BIOS Hole |
176186
0xA_0000 +------------------------------------------------+ 640KiB
177187
| PVALIDATEd by bootstrap assembly code |

0 commit comments

Comments
 (0)