mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-05 18:20:34 +00:00
x86_64: allow arbitrary-size BIOS images
A BIOS can be larger or smaller than 1 MB; as long as the image is placed so the reset vector lines up in the correct place at the end of the 32-bit address space, everything should work. BUG=b:179053182 TEST=`crosvm run --bios OVMF.fd` and observe debug messages Change-Id: Ibafd9bb5ee4fd4b0ff2c28c38f022ff3b36dd95d Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2651288 Reviewed-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Zach Reizner <zachr@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
parent
e0ea4e013a
commit
531cbbdbbf
2 changed files with 57 additions and 33 deletions
|
@ -205,11 +205,6 @@ const END_ADDR_BEFORE_32BITS: u64 = FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE;
|
|||
const MMIO_SIZE: u64 = MEM_32BIT_GAP_SIZE - 0x8000000;
|
||||
const KERNEL_64BIT_ENTRY_OFFSET: u64 = 0x200;
|
||||
const ZERO_PAGE_OFFSET: u64 = 0x7000;
|
||||
/// The x86 reset vector for i386+ and x86_64 puts the processor into an "unreal mode" where it
|
||||
/// can access the last 1 MB of the 32-bit address space in 16-bit mode, and starts the instruction
|
||||
/// pointer at the effective physical address 0xFFFFFFF0.
|
||||
const BIOS_LEN: usize = 1 << 20;
|
||||
const BIOS_START: u64 = FIRST_ADDR_PAST_32BITS - (BIOS_LEN as u64);
|
||||
const TSS_ADDR: u64 = 0xfffbd000;
|
||||
|
||||
const KERNEL_START_OFFSET: u64 = 0x200000;
|
||||
|
@ -228,6 +223,13 @@ pub const X86_64_SCI_IRQ: u32 = 5;
|
|||
pub const X86_64_IRQ_BASE: u32 = 9;
|
||||
const ACPI_HI_RSDP_WINDOW_BASE: u64 = 0x000E0000;
|
||||
|
||||
/// The x86 reset vector for i386+ and x86_64 puts the processor into an "unreal mode" where it
|
||||
/// can access the last 1 MB of the 32-bit address space in 16-bit mode, and starts the instruction
|
||||
/// pointer at the effective physical address 0xFFFFFFF0.
|
||||
fn bios_start(bios_size: u64) -> GuestAddress {
|
||||
GuestAddress(FIRST_ADDR_PAST_32BITS - bios_size)
|
||||
}
|
||||
|
||||
fn configure_system(
|
||||
guest_mem: &GuestMemory,
|
||||
_mem_size: u64,
|
||||
|
@ -317,7 +319,7 @@ fn add_e820_entry(params: &mut boot_params, addr: u64, size: u64, mem_type: u32)
|
|||
/// These should be used to configure the GuestMemory structure for the platform.
|
||||
/// For x86_64 all addresses are valid from the start of the kernel except a
|
||||
/// carve out at the end of 32bit address space.
|
||||
fn arch_memory_regions(size: u64, has_bios: bool) -> Vec<(GuestAddress, u64)> {
|
||||
fn arch_memory_regions(size: u64, bios_size: Option<u64>) -> Vec<(GuestAddress, u64)> {
|
||||
let mem_end = GuestAddress(size);
|
||||
let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS);
|
||||
let end_32bit_gap_start = GuestAddress(END_ADDR_BEFORE_32BITS);
|
||||
|
@ -325,13 +327,13 @@ fn arch_memory_regions(size: u64, has_bios: bool) -> Vec<(GuestAddress, u64)> {
|
|||
let mut regions = Vec::new();
|
||||
if mem_end <= end_32bit_gap_start {
|
||||
regions.push((GuestAddress(0), size));
|
||||
if has_bios {
|
||||
regions.push((GuestAddress(BIOS_START), BIOS_LEN as u64));
|
||||
if let Some(bios_size) = bios_size {
|
||||
regions.push((bios_start(bios_size), bios_size));
|
||||
}
|
||||
} else {
|
||||
regions.push((GuestAddress(0), end_32bit_gap_start.offset()));
|
||||
if has_bios {
|
||||
regions.push((GuestAddress(BIOS_START), BIOS_LEN as u64));
|
||||
if let Some(bios_size) = bios_size {
|
||||
regions.push((bios_start(bios_size), bios_size));
|
||||
}
|
||||
regions.push((
|
||||
first_addr_past_32bits,
|
||||
|
@ -370,8 +372,14 @@ impl arch::LinuxArch for X8664arch {
|
|||
E2: StdError + 'static,
|
||||
E3: StdError + 'static,
|
||||
{
|
||||
let has_bios = matches!(components.vm_image, VmImage::Bios(_));
|
||||
let mem = Self::setup_memory(components.memory_size, has_bios)?;
|
||||
let bios_size = match components.vm_image {
|
||||
VmImage::Bios(ref mut bios_file) => {
|
||||
Some(bios_file.metadata().map_err(Error::LoadBios)?.len())
|
||||
}
|
||||
VmImage::Kernel(_) => None,
|
||||
};
|
||||
let has_bios = bios_size.is_some();
|
||||
let mem = Self::setup_memory(components.memory_size, bios_size)?;
|
||||
let mut resources = Self::get_resource_allocator(&mem);
|
||||
|
||||
let vcpu_count = components.vcpu_count;
|
||||
|
@ -797,20 +805,24 @@ impl X8664arch {
|
|||
let bios_image_length = bios_image
|
||||
.seek(io::SeekFrom::End(0))
|
||||
.map_err(Error::LoadBios)?;
|
||||
if bios_image_length != BIOS_LEN as u64 {
|
||||
if bios_image_length >= FIRST_ADDR_PAST_32BITS {
|
||||
return Err(Error::LoadBios(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"bios was {} bytes, expected {}",
|
||||
bios_image_length, BIOS_LEN
|
||||
"bios was {} bytes, expected less than {}",
|
||||
bios_image_length, FIRST_ADDR_PAST_32BITS,
|
||||
),
|
||||
)));
|
||||
}
|
||||
bios_image
|
||||
.seek(io::SeekFrom::Start(0))
|
||||
.map_err(Error::LoadBios)?;
|
||||
mem.read_to_memory(GuestAddress(BIOS_START), bios_image, BIOS_LEN)
|
||||
.map_err(Error::SetupGuestMemory)?;
|
||||
mem.read_to_memory(
|
||||
bios_start(bios_image_length),
|
||||
bios_image,
|
||||
bios_image_length as usize,
|
||||
)
|
||||
.map_err(Error::SetupGuestMemory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -914,8 +926,8 @@ impl X8664arch {
|
|||
/// This creates a GuestMemory object for this VM
|
||||
///
|
||||
/// * `mem_size` - Desired physical memory size in bytes for this VM
|
||||
fn setup_memory(mem_size: u64, has_bios: bool) -> Result<GuestMemory> {
|
||||
let arch_mem_regions = arch_memory_regions(mem_size, has_bios);
|
||||
fn setup_memory(mem_size: u64, bios_size: Option<u64>) -> Result<GuestMemory> {
|
||||
let arch_mem_regions = arch_memory_regions(mem_size, bios_size);
|
||||
let mem = GuestMemory::new(&arch_mem_regions).map_err(Error::SetupGuestMemory)?;
|
||||
Ok(mem)
|
||||
}
|
||||
|
@ -974,7 +986,7 @@ impl X8664arch {
|
|||
|
||||
let mut io_bus = devices::Bus::new();
|
||||
|
||||
let mem_regions = arch_memory_regions(mem_size, false);
|
||||
let mem_regions = arch_memory_regions(mem_size, None);
|
||||
|
||||
let mem_below_4g = mem_regions
|
||||
.iter()
|
||||
|
@ -1151,7 +1163,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn regions_lt_4gb_nobios() {
|
||||
let regions = arch_memory_regions(1u64 << 29, /* has_bios */ false);
|
||||
let regions = arch_memory_regions(1u64 << 29, /* bios_size */ None);
|
||||
assert_eq!(1, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(1u64 << 29, regions[0].1);
|
||||
|
@ -1159,7 +1171,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn regions_gt_4gb_nobios() {
|
||||
let regions = arch_memory_regions((1u64 << 32) + 0x8000, /* has_bios */ false);
|
||||
let regions = arch_memory_regions((1u64 << 32) + 0x8000, /* bios_size */ None);
|
||||
assert_eq!(2, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(GuestAddress(1u64 << 32), regions[1].0);
|
||||
|
@ -1167,28 +1179,36 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn regions_lt_4gb_bios() {
|
||||
let regions = arch_memory_regions(1u64 << 29, /* has_bios */ true);
|
||||
let bios_len = 1 << 20;
|
||||
let regions = arch_memory_regions(1u64 << 29, Some(bios_len));
|
||||
assert_eq!(2, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(1u64 << 29, regions[0].1);
|
||||
assert_eq!(GuestAddress(BIOS_START), regions[1].0);
|
||||
assert_eq!(BIOS_LEN as u64, regions[1].1);
|
||||
assert_eq!(
|
||||
GuestAddress(FIRST_ADDR_PAST_32BITS - bios_len),
|
||||
regions[1].0
|
||||
);
|
||||
assert_eq!(bios_len, regions[1].1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regions_gt_4gb_bios() {
|
||||
let regions = arch_memory_regions((1u64 << 32) + 0x8000, /* has_bios */ true);
|
||||
let bios_len = 1 << 20;
|
||||
let regions = arch_memory_regions((1u64 << 32) + 0x8000, Some(bios_len));
|
||||
assert_eq!(3, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(GuestAddress(BIOS_START), regions[1].0);
|
||||
assert_eq!(BIOS_LEN as u64, regions[1].1);
|
||||
assert_eq!(
|
||||
GuestAddress(FIRST_ADDR_PAST_32BITS - bios_len),
|
||||
regions[1].0
|
||||
);
|
||||
assert_eq!(bios_len, regions[1].1);
|
||||
assert_eq!(GuestAddress(1u64 << 32), regions[2].0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regions_eq_4gb_nobios() {
|
||||
// Test with size = 3328, which is exactly 4 GiB minus the size of the gap (768 MiB).
|
||||
let regions = arch_memory_regions(3328 << 20, /* has_bios */ false);
|
||||
let regions = arch_memory_regions(3328 << 20, /* bios_size */ None);
|
||||
assert_eq!(1, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(3328 << 20, regions[0].1);
|
||||
|
@ -1197,11 +1217,15 @@ mod tests {
|
|||
#[test]
|
||||
fn regions_eq_4gb_bios() {
|
||||
// Test with size = 3328, which is exactly 4 GiB minus the size of the gap (768 MiB).
|
||||
let regions = arch_memory_regions(3328 << 20, /* has_bios */ true);
|
||||
let bios_len = 1 << 20;
|
||||
let regions = arch_memory_regions(3328 << 20, Some(bios_len));
|
||||
assert_eq!(2, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(3328 << 20, regions[0].1);
|
||||
assert_eq!(GuestAddress(BIOS_START), regions[1].0);
|
||||
assert_eq!(BIOS_LEN as u64, regions[1].1);
|
||||
assert_eq!(
|
||||
GuestAddress(FIRST_ADDR_PAST_32BITS - bios_len),
|
||||
regions[1].0
|
||||
);
|
||||
assert_eq!(bios_len, regions[1].1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ where
|
|||
let write_addr = GuestAddress(0x4000);
|
||||
|
||||
// guest mem is 400 pages
|
||||
let guest_mem = X8664arch::setup_memory(memory_size, false).unwrap();
|
||||
let guest_mem = X8664arch::setup_memory(memory_size, None).unwrap();
|
||||
// let guest_mem = GuestMemory::new(&[(GuestAddress(0), memory_size)]).unwrap();
|
||||
let mut resources = X8664arch::get_resource_allocator(&guest_mem);
|
||||
|
||||
|
|
Loading…
Reference in a new issue