Skip to content

Commit bcbd1f5

Browse files
cpercivaaljimenezb
andcommitted
pvh/arch-x86_64: Write start_info to guest memory
Fill the hvm_start_info and related structures as specified in the PVH boot protocol. Write the data structures to guest memory at the GPA that will be stored in %rbx when the guest starts. Signed-off-by: Colin Percival <[email protected]> Co-authored-by: Alejandro Jimenez <[email protected]>
1 parent c5e8eb9 commit bcbd1f5

File tree

3 files changed

+233
-7
lines changed

3 files changed

+233
-7
lines changed

src/arch/src/x86_64/layout.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,13 @@ pub const KVM_TSS_ADDRESS: u64 = 0xfffb_d000;
3030
/// Address of the hvm_start_info struct used in PVH boot
3131
pub const PVH_INFO_START: u64 = 0x6000;
3232

33+
/// Starting address of array of modules of hvm_modlist_entry type.
34+
/// Used to enable initrd support using the PVH boot ABI.
35+
pub const MODLIST_START: u64 = 0x6040;
36+
37+
/// Address of memory map table used in PVH boot. Can overlap
38+
/// with the zero page address since they are mutually exclusive.
39+
pub const MEMMAP_START: u64 = 0x7000;
40+
3341
/// The 'zero page', a.k.a linux kernel bootparams.
3442
pub const ZERO_PAGE_START: u64 = 0x7000;

src/arch/src/x86_64/mod.rs

Lines changed: 224 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Copyright © 2020, Oracle and/or its affiliates.
2+
//
13
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
24
// SPDX-License-Identifier: Apache-2.0
35
//
@@ -17,14 +19,20 @@ pub mod msr;
1719
pub mod regs;
1820

1921
use linux_loader::configurator::linux::LinuxBootConfigurator;
22+
use linux_loader::configurator::pvh::PvhBootConfigurator;
2023
use linux_loader::configurator::{BootConfigurator, BootParams};
2124
use linux_loader::loader::bootparam::boot_params;
25+
use linux_loader::loader::elf::start_info::{
26+
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
27+
};
2228
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion};
2329

30+
use super::BootProtocol;
2431
use crate::InitrdConfig;
2532

2633
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
2734
const E820_RAM: u32 = 1;
35+
const MEMMAP_TYPE_RAM: u32 = 1;
2836

2937
/// Errors thrown while configuring x86_64 system.
3038
#[derive(Debug, PartialEq, Eq, derive_more::From)]
@@ -37,6 +45,12 @@ pub enum Error {
3745
ZeroPageSetup,
3846
/// Failed to compute initrd address.
3947
InitrdAddress,
48+
/// Error writing module entry to guest memory.
49+
ModlistSetup,
50+
/// Error writing memory map table to guest memory.
51+
MemmapTableSetup,
52+
/// Error writing hvm_start_info to guest memory.
53+
StartInfoSetup,
4054
}
4155

4256
// Where BIOS/VGA magic would live on a real PC.
@@ -97,12 +111,139 @@ pub fn initrd_load_addr(guest_mem: &GuestMemoryMmap, initrd_size: usize) -> supe
97111
/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
98112
/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
99113
/// * `num_cpus` - Number of virtual CPUs the guest will have.
114+
/// * `boot_prot` - Boot protocol that will be used to boot the guest.
100115
pub fn configure_system(
101116
guest_mem: &GuestMemoryMmap,
102117
cmdline_addr: GuestAddress,
103118
cmdline_size: usize,
104119
initrd: &Option<InitrdConfig>,
105120
num_cpus: u8,
121+
boot_prot: BootProtocol,
122+
) -> super::Result<()> {
123+
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
124+
mptable::setup_mptable(guest_mem, num_cpus).map_err(Error::MpTableSetup)?;
125+
126+
match boot_prot {
127+
BootProtocol::PvhBoot => {
128+
configure_pvh(guest_mem, cmdline_addr, initrd)?;
129+
}
130+
BootProtocol::LinuxBoot => {
131+
configure_64bit_boot(guest_mem, cmdline_addr, cmdline_size, initrd)?;
132+
}
133+
}
134+
135+
Ok(())
136+
}
137+
138+
fn configure_pvh(
139+
guest_mem: &GuestMemoryMmap,
140+
cmdline_addr: GuestAddress,
141+
initrd: &Option<InitrdConfig>,
142+
) -> super::Result<()> {
143+
const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336e_c578;
144+
let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS);
145+
let end_32bit_gap_start = GuestAddress(MMIO_MEM_START);
146+
let himem_start = GuestAddress(layout::HIMEM_START);
147+
148+
// Vector to hold modules (currently either empty or holding initrd).
149+
let mut modules: Vec<hvm_modlist_entry> = Vec::new();
150+
if let Some(initrd_config) = initrd {
151+
// The initrd has been written to guest memory already, here we just need to
152+
// create the module structure that describes it.
153+
modules.push(hvm_modlist_entry {
154+
paddr: initrd_config.address.raw_value(),
155+
size: initrd_config.size as u64,
156+
..Default::default()
157+
});
158+
}
159+
160+
// Vector to hold the memory maps which needs to be written to guest memory
161+
// at MEMMAP_START after all of the mappings are recorded.
162+
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
163+
164+
// Create the memory map entries.
165+
add_memmap_entry(&mut memmap, 0, EBDA_START, MEMMAP_TYPE_RAM)?;
166+
let last_addr = guest_mem.last_addr();
167+
if last_addr < end_32bit_gap_start {
168+
add_memmap_entry(
169+
&mut memmap,
170+
himem_start.raw_value() as u64,
171+
last_addr.unchecked_offset_from(himem_start) as u64 + 1,
172+
MEMMAP_TYPE_RAM,
173+
)?;
174+
} else {
175+
add_memmap_entry(
176+
&mut memmap,
177+
himem_start.raw_value(),
178+
end_32bit_gap_start.unchecked_offset_from(himem_start),
179+
MEMMAP_TYPE_RAM,
180+
)?;
181+
182+
if last_addr > first_addr_past_32bits {
183+
add_memmap_entry(
184+
&mut memmap,
185+
first_addr_past_32bits.raw_value(),
186+
last_addr.unchecked_offset_from(first_addr_past_32bits) + 1,
187+
MEMMAP_TYPE_RAM,
188+
)?;
189+
}
190+
}
191+
192+
// Construct the hvm_start_info structure and serialize it into
193+
// boot_params. This will be stored at PVH_INFO_START address, and %rbx
194+
// will be initialized to contain PVH_INFO_START prior to starting the
195+
// guest, as required by the PVH ABI.
196+
let mut start_info = hvm_start_info {
197+
magic: XEN_HVM_START_MAGIC_VALUE,
198+
version: 1,
199+
cmdline_paddr: cmdline_addr.raw_value(),
200+
memmap_paddr: layout::MEMMAP_START,
201+
memmap_entries: memmap.len() as u32,
202+
nr_modules: modules.len() as u32,
203+
..Default::default()
204+
};
205+
if !modules.is_empty() {
206+
start_info.modlist_paddr = layout::MODLIST_START;
207+
}
208+
let mut boot_params =
209+
BootParams::new::<hvm_start_info>(&start_info, GuestAddress(layout::PVH_INFO_START));
210+
211+
// Copy the vector with the memmap table to the MEMMAP_START address
212+
// which is already saved in the memmap_paddr field of hvm_start_info struct.
213+
boot_params.set_sections::<hvm_memmap_table_entry>(&memmap, GuestAddress(layout::MEMMAP_START));
214+
215+
// Copy the vector with the modules list to the MODLIST_START address.
216+
// Note that we only set the modlist_paddr address if there is a nonzero
217+
// number of modules, but serializing an empty list is harmless.
218+
boot_params.set_modules::<hvm_modlist_entry>(&modules, GuestAddress(layout::MODLIST_START));
219+
220+
// Write the hvm_start_info struct to guest memory.
221+
PvhBootConfigurator::write_bootparams(&boot_params, guest_mem)
222+
.map_err(|_| Error::StartInfoSetup)
223+
}
224+
225+
fn add_memmap_entry(
226+
memmap: &mut Vec<hvm_memmap_table_entry>,
227+
addr: u64,
228+
size: u64,
229+
mem_type: u32,
230+
) -> super::Result<()> {
231+
// Add the table entry to the vector
232+
memmap.push(hvm_memmap_table_entry {
233+
addr,
234+
size,
235+
type_: mem_type,
236+
reserved: 0,
237+
});
238+
239+
Ok(())
240+
}
241+
242+
fn configure_64bit_boot(
243+
guest_mem: &GuestMemoryMmap,
244+
cmdline_addr: GuestAddress,
245+
cmdline_size: usize,
246+
initrd: &Option<InitrdConfig>,
106247
) -> super::Result<()> {
107248
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
108249
const KERNEL_HDR_MAGIC: u32 = 0x5372_6448;
@@ -113,9 +254,6 @@ pub fn configure_system(
113254

114255
let himem_start = GuestAddress(layout::HIMEM_START);
115256

116-
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
117-
mptable::setup_mptable(guest_mem, num_cpus)?;
118-
119257
let mut params = boot_params::default();
120258

121259
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
@@ -218,7 +356,8 @@ mod tests {
218356
let gm =
219357
vm_memory::test_utils::create_anon_guest_memory(&[(GuestAddress(0), 0x10000)], false)
220358
.unwrap();
221-
let config_err = configure_system(&gm, GuestAddress(0), 0, &None, 1);
359+
let config_err =
360+
configure_system(&gm, GuestAddress(0), 0, &None, 1, BootProtocol::LinuxBoot);
222361
assert!(config_err.is_err());
223362
assert_eq!(
224363
config_err.unwrap_err(),
@@ -229,19 +368,70 @@ mod tests {
229368
let mem_size = 128 << 20;
230369
let arch_mem_regions = arch_memory_regions(mem_size);
231370
let gm = vm_memory::test_utils::create_anon_guest_memory(&arch_mem_regions, false).unwrap();
232-
configure_system(&gm, GuestAddress(0), 0, &None, no_vcpus).unwrap();
371+
configure_system(
372+
&gm,
373+
GuestAddress(0),
374+
0,
375+
&None,
376+
no_vcpus,
377+
BootProtocol::LinuxBoot,
378+
)
379+
.unwrap();
380+
configure_system(
381+
&gm,
382+
GuestAddress(0),
383+
0,
384+
&None,
385+
no_vcpus,
386+
BootProtocol::PvhBoot,
387+
)
388+
.unwrap();
233389

234390
// Now assigning some memory that is equal to the start of the 32bit memory hole.
235391
let mem_size = 3328 << 20;
236392
let arch_mem_regions = arch_memory_regions(mem_size);
237393
let gm = vm_memory::test_utils::create_anon_guest_memory(&arch_mem_regions, false).unwrap();
238-
configure_system(&gm, GuestAddress(0), 0, &None, no_vcpus).unwrap();
394+
configure_system(
395+
&gm,
396+
GuestAddress(0),
397+
0,
398+
&None,
399+
no_vcpus,
400+
BootProtocol::LinuxBoot,
401+
)
402+
.unwrap();
403+
configure_system(
404+
&gm,
405+
GuestAddress(0),
406+
0,
407+
&None,
408+
no_vcpus,
409+
BootProtocol::PvhBoot,
410+
)
411+
.unwrap();
239412

240413
// Now assigning some memory that falls after the 32bit memory hole.
241414
let mem_size = 3330 << 20;
242415
let arch_mem_regions = arch_memory_regions(mem_size);
243416
let gm = vm_memory::test_utils::create_anon_guest_memory(&arch_mem_regions, false).unwrap();
244-
configure_system(&gm, GuestAddress(0), 0, &None, no_vcpus).unwrap();
417+
configure_system(
418+
&gm,
419+
GuestAddress(0),
420+
0,
421+
&None,
422+
no_vcpus,
423+
BootProtocol::LinuxBoot,
424+
)
425+
.unwrap();
426+
configure_system(
427+
&gm,
428+
GuestAddress(0),
429+
0,
430+
&None,
431+
no_vcpus,
432+
BootProtocol::PvhBoot,
433+
)
434+
.unwrap();
245435
}
246436

247437
#[test]
@@ -283,4 +473,31 @@ mod tests {
283473
)
284474
.is_err());
285475
}
476+
477+
#[test]
478+
fn test_add_memmap_entry() {
479+
const MEMMAP_TYPE_RESERVED: u32 = 2;
480+
481+
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
482+
483+
let expected_memmap = vec![
484+
hvm_memmap_table_entry {
485+
addr: 0x0,
486+
size: 0x1000,
487+
type_: MEMMAP_TYPE_RAM,
488+
..Default::default()
489+
},
490+
hvm_memmap_table_entry {
491+
addr: 0x10000,
492+
size: 0xa000,
493+
type_: MEMMAP_TYPE_RESERVED,
494+
..Default::default()
495+
},
496+
];
497+
498+
add_memmap_entry(&mut memmap, 0, 0x1000, MEMMAP_TYPE_RAM).unwrap();
499+
add_memmap_entry(&mut memmap, 0x10000, 0xa000, MEMMAP_TYPE_RESERVED).unwrap();
500+
501+
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
502+
}
286503
}

src/vmm/src/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,7 @@ pub fn configure_system_for_boot(
886886
cmdline_size,
887887
initrd,
888888
vcpus.len() as u8,
889+
entry_point.protocol,
889890
)
890891
.map_err(ConfigureSystem)?;
891892
}

0 commit comments

Comments
 (0)