Skip to content

Commit 2493073

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 3797009 commit 2493073

File tree

3 files changed

+233
-7
lines changed

3 files changed

+233
-7
lines changed

src/vmm/src/arch/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/vmm/src/arch/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
//
@@ -19,14 +21,20 @@ pub mod msr;
1921
pub mod regs;
2022

2123
use linux_loader::configurator::linux::LinuxBootConfigurator;
24+
use linux_loader::configurator::pvh::PvhBootConfigurator;
2225
use linux_loader::configurator::{BootConfigurator, BootParams};
2326
use linux_loader::loader::bootparam::boot_params;
27+
use linux_loader::loader::elf::start_info::{
28+
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
29+
};
2430
use utils::vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion};
2531

32+
use super::BootProtocol;
2633
use crate::arch::InitrdConfig;
2734

2835
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
2936
const E820_RAM: u32 = 1;
37+
const MEMMAP_TYPE_RAM: u32 = 1;
3038

3139
/// Errors thrown while configuring x86_64 system.
3240
#[derive(Debug, PartialEq, Eq, derive_more::From)]
@@ -39,6 +47,12 @@ pub enum ConfigurationError {
3947
ZeroPageSetup,
4048
/// Failed to compute initrd address.
4149
InitrdAddress,
50+
/// Error writing module entry to guest memory.
51+
ModlistSetup,
52+
/// Error writing memory map table to guest memory.
53+
MemmapTableSetup,
54+
/// Error writing hvm_start_info to guest memory.
55+
StartInfoSetup,
4256
}
4357

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

119260
let himem_start = GuestAddress(layout::HIMEM_START);
120261

121-
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
122-
mptable::setup_mptable(guest_mem, num_cpus)?;
123-
124262
let mut params = boot_params::default();
125263

126264
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
@@ -225,7 +363,8 @@ mod tests {
225363
false,
226364
)
227365
.unwrap();
228-
let config_err = configure_system(&gm, GuestAddress(0), 0, &None, 1);
366+
let config_err =
367+
configure_system(&gm, GuestAddress(0), 0, &None, 1, BootProtocol::LinuxBoot);
229368
assert!(config_err.is_err());
230369
assert_eq!(
231370
config_err.unwrap_err(),
@@ -237,21 +376,72 @@ mod tests {
237376
let arch_mem_regions = arch_memory_regions(mem_size);
238377
let gm = utils::vm_memory::test_utils::create_anon_guest_memory(&arch_mem_regions, false)
239378
.unwrap();
240-
configure_system(&gm, GuestAddress(0), 0, &None, no_vcpus).unwrap();
379+
configure_system(
380+
&gm,
381+
GuestAddress(0),
382+
0,
383+
&None,
384+
no_vcpus,
385+
BootProtocol::LinuxBoot,
386+
)
387+
.unwrap();
388+
configure_system(
389+
&gm,
390+
GuestAddress(0),
391+
0,
392+
&None,
393+
no_vcpus,
394+
BootProtocol::PvhBoot,
395+
)
396+
.unwrap();
241397

242398
// Now assigning some memory that is equal to the start of the 32bit memory hole.
243399
let mem_size = 3328 << 20;
244400
let arch_mem_regions = arch_memory_regions(mem_size);
245401
let gm = utils::vm_memory::test_utils::create_anon_guest_memory(&arch_mem_regions, false)
246402
.unwrap();
247-
configure_system(&gm, GuestAddress(0), 0, &None, no_vcpus).unwrap();
403+
configure_system(
404+
&gm,
405+
GuestAddress(0),
406+
0,
407+
&None,
408+
no_vcpus,
409+
BootProtocol::LinuxBoot,
410+
)
411+
.unwrap();
412+
configure_system(
413+
&gm,
414+
GuestAddress(0),
415+
0,
416+
&None,
417+
no_vcpus,
418+
BootProtocol::PvhBoot,
419+
)
420+
.unwrap();
248421

249422
// Now assigning some memory that falls after the 32bit memory hole.
250423
let mem_size = 3330 << 20;
251424
let arch_mem_regions = arch_memory_regions(mem_size);
252425
let gm = utils::vm_memory::test_utils::create_anon_guest_memory(&arch_mem_regions, false)
253426
.unwrap();
254-
configure_system(&gm, GuestAddress(0), 0, &None, no_vcpus).unwrap();
427+
configure_system(
428+
&gm,
429+
GuestAddress(0),
430+
0,
431+
&None,
432+
no_vcpus,
433+
BootProtocol::LinuxBoot,
434+
)
435+
.unwrap();
436+
configure_system(
437+
&gm,
438+
GuestAddress(0),
439+
0,
440+
&None,
441+
no_vcpus,
442+
BootProtocol::PvhBoot,
443+
)
444+
.unwrap();
255445
}
256446

257447
#[test]
@@ -293,4 +483,31 @@ mod tests {
293483
)
294484
.is_err());
295485
}
486+
487+
#[test]
488+
fn test_add_memmap_entry() {
489+
const MEMMAP_TYPE_RESERVED: u32 = 2;
490+
491+
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
492+
493+
let expected_memmap = vec![
494+
hvm_memmap_table_entry {
495+
addr: 0x0,
496+
size: 0x1000,
497+
type_: MEMMAP_TYPE_RAM,
498+
..Default::default()
499+
},
500+
hvm_memmap_table_entry {
501+
addr: 0x10000,
502+
size: 0xa000,
503+
type_: MEMMAP_TYPE_RESERVED,
504+
..Default::default()
505+
},
506+
];
507+
508+
add_memmap_entry(&mut memmap, 0, 0x1000, MEMMAP_TYPE_RAM).unwrap();
509+
add_memmap_entry(&mut memmap, 0x10000, 0xa000, MEMMAP_TYPE_RESERVED).unwrap();
510+
511+
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
512+
}
296513
}

src/vmm/src/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,7 @@ pub fn configure_system_for_boot(
833833
cmdline_size,
834834
initrd,
835835
vcpus.len() as u8,
836+
entry_point.protocol,
836837
)
837838
.map_err(ConfigureSystem)?;
838839
}

0 commit comments

Comments
 (0)