Skip to content

Commit 6e572c1

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 <cperciva@freebsd.org> Co-authored-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
1 parent 4e49212 commit 6e572c1

File tree

3 files changed

+255
-9
lines changed

3 files changed

+255
-9
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: 246 additions & 9 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,15 +19,41 @@ 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;
22-
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion};
23-
25+
use linux_loader::loader::elf::start_info::{
26+
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
27+
};
28+
use vm_memory::{
29+
Address, ByteValued, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion,
30+
};
31+
32+
use super::BootProtocol;
2433
use crate::InitrdConfig;
2534

2635
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
2736
const E820_RAM: u32 = 1;
2837

38+
// Workaround for the Rust orphan rules that guarantee trait coherence by wrapping the foreign type
39+
// in a tuple structure. Same approach is used by boot_params and BootParamsWrapper.
40+
#[derive(Copy, Clone, Default)]
41+
struct StartInfoWrapper(hvm_start_info);
42+
43+
#[derive(Copy, Clone, Default)]
44+
struct MemmapTableEntryWrapper(hvm_memmap_table_entry);
45+
46+
#[derive(Copy, Clone, Default)]
47+
struct ModlistEntryWrapper(hvm_modlist_entry);
48+
49+
// It is safe to initialize the following structures. They are wrappers over the structures
50+
// defined by the start_info module, all of which are formed by fields of integer values.
51+
unsafe impl ByteValued for StartInfoWrapper {}
52+
unsafe impl ByteValued for MemmapTableEntryWrapper {}
53+
unsafe impl ByteValued for ModlistEntryWrapper {}
54+
55+
const MEMMAP_TYPE_RAM: u32 = 1;
56+
2957
/// Errors thrown while configuring x86_64 system.
3058
#[derive(Debug, PartialEq, derive_more::From)]
3159
pub enum Error {
@@ -37,6 +65,12 @@ pub enum Error {
3765
ZeroPageSetup,
3866
/// Failed to compute initrd address.
3967
InitrdAddress,
68+
/// Error writing module entry to guest memory.
69+
ModlistSetup,
70+
/// Error writing memory map table to guest memory.
71+
MemmapTableSetup,
72+
/// Error writing hvm_start_info to guest memory.
73+
StartInfoSetup,
4074
}
4175

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

114275
let himem_start = GuestAddress(layout::HIMEM_START);
115276

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-
119277
let mut params = boot_params::default();
120278

121279
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
@@ -218,7 +376,8 @@ mod tests {
218376
let gm =
219377
vm_memory::test_utils::create_anon_guest_memory(&[(GuestAddress(0), 0x10000)], false)
220378
.unwrap();
221-
let config_err = configure_system(&gm, GuestAddress(0), 0, &None, 1);
379+
let config_err =
380+
configure_system(&gm, GuestAddress(0), 0, &None, 1, BootProtocol::LinuxBoot);
222381
assert!(config_err.is_err());
223382
assert_eq!(
224383
config_err.unwrap_err(),
@@ -229,19 +388,70 @@ mod tests {
229388
let mem_size = 128 << 20;
230389
let arch_mem_regions = arch_memory_regions(mem_size);
231390
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();
391+
configure_system(
392+
&gm,
393+
GuestAddress(0),
394+
0,
395+
&None,
396+
no_vcpus,
397+
BootProtocol::LinuxBoot,
398+
)
399+
.unwrap();
400+
configure_system(
401+
&gm,
402+
GuestAddress(0),
403+
0,
404+
&None,
405+
no_vcpus,
406+
BootProtocol::PvhBoot,
407+
)
408+
.unwrap();
233409

234410
// Now assigning some memory that is equal to the start of the 32bit memory hole.
235411
let mem_size = 3328 << 20;
236412
let arch_mem_regions = arch_memory_regions(mem_size);
237413
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();
414+
configure_system(
415+
&gm,
416+
GuestAddress(0),
417+
0,
418+
&None,
419+
no_vcpus,
420+
BootProtocol::LinuxBoot,
421+
)
422+
.unwrap();
423+
configure_system(
424+
&gm,
425+
GuestAddress(0),
426+
0,
427+
&None,
428+
no_vcpus,
429+
BootProtocol::PvhBoot,
430+
)
431+
.unwrap();
239432

240433
// Now assigning some memory that falls after the 32bit memory hole.
241434
let mem_size = 3330 << 20;
242435
let arch_mem_regions = arch_memory_regions(mem_size);
243436
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();
437+
configure_system(
438+
&gm,
439+
GuestAddress(0),
440+
0,
441+
&None,
442+
no_vcpus,
443+
BootProtocol::LinuxBoot,
444+
)
445+
.unwrap();
446+
configure_system(
447+
&gm,
448+
GuestAddress(0),
449+
0,
450+
&None,
451+
no_vcpus,
452+
BootProtocol::PvhBoot,
453+
)
454+
.unwrap();
245455
}
246456

247457
#[test]
@@ -283,4 +493,31 @@ mod tests {
283493
)
284494
.is_err());
285495
}
496+
497+
#[test]
498+
fn test_add_memmap_entry() {
499+
const MEMMAP_TYPE_RESERVED: u32 = 2;
500+
501+
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
502+
503+
let expected_memmap = vec![
504+
hvm_memmap_table_entry {
505+
addr: 0x0,
506+
size: 0x1000,
507+
type_: MEMMAP_TYPE_RAM,
508+
..Default::default()
509+
},
510+
hvm_memmap_table_entry {
511+
addr: 0x10000,
512+
size: 0xa000,
513+
type_: MEMMAP_TYPE_RESERVED,
514+
..Default::default()
515+
},
516+
];
517+
518+
add_memmap_entry(&mut memmap, 0, 0x1000, MEMMAP_TYPE_RAM).unwrap();
519+
add_memmap_entry(&mut memmap, 0x10000, 0xa000, MEMMAP_TYPE_RESERVED).unwrap();
520+
521+
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
522+
}
286523
}

src/vmm/src/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,7 @@ pub fn configure_system_for_boot(
895895
boot_cmdline.as_str().len() + 1,
896896
initrd,
897897
vcpus.len() as u8,
898+
entry_point.protocol,
898899
)
899900
.map_err(ConfigureSystem)?;
900901
}

0 commit comments

Comments
 (0)