Skip to content

Commit 4caaaa1

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 fac3e99 commit 4caaaa1

File tree

3 files changed

+233
-8
lines changed

3 files changed

+233
-8
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 & 8 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,19 @@ 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

26-
use crate::arch::InitrdConfig;
32+
use crate::arch::{BootProtocol, InitrdConfig};
2733

2834
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
2935
const E820_RAM: u32 = 1;
36+
const MEMMAP_TYPE_RAM: u32 = 1;
3037

3138
/// Errors thrown while configuring x86_64 system.
3239
#[derive(Debug, PartialEq, Eq, derive_more::From)]
@@ -39,6 +46,12 @@ pub enum ConfigurationError {
3946
ZeroPageSetup,
4047
/// Failed to compute initrd address.
4148
InitrdAddress,
49+
/// Error writing module entry to guest memory.
50+
ModlistSetup,
51+
/// Error writing memory map table to guest memory.
52+
MemmapTableSetup,
53+
/// Error writing hvm_start_info to guest memory.
54+
StartInfoSetup,
4255
}
4356

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

119259
let himem_start = GuestAddress(layout::HIMEM_START);
120260

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

126263
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
@@ -225,7 +362,8 @@ mod tests {
225362
false,
226363
)
227364
.unwrap();
228-
let config_err = configure_system(&gm, GuestAddress(0), 0, &None, 1);
365+
let config_err =
366+
configure_system(&gm, GuestAddress(0), 0, &None, 1, BootProtocol::LinuxBoot);
229367
assert!(config_err.is_err());
230368
assert_eq!(
231369
config_err.unwrap_err(),
@@ -237,21 +375,72 @@ mod tests {
237375
let arch_mem_regions = arch_memory_regions(mem_size);
238376
let gm = utils::vm_memory::test_utils::create_anon_guest_memory(&arch_mem_regions, false)
239377
.unwrap();
240-
configure_system(&gm, GuestAddress(0), 0, &None, no_vcpus).unwrap();
378+
configure_system(
379+
&gm,
380+
GuestAddress(0),
381+
0,
382+
&None,
383+
no_vcpus,
384+
BootProtocol::LinuxBoot,
385+
)
386+
.unwrap();
387+
configure_system(
388+
&gm,
389+
GuestAddress(0),
390+
0,
391+
&None,
392+
no_vcpus,
393+
BootProtocol::PvhBoot,
394+
)
395+
.unwrap();
241396

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

249421
// Now assigning some memory that falls after the 32bit memory hole.
250422
let mem_size = 3330 << 20;
251423
let arch_mem_regions = arch_memory_regions(mem_size);
252424
let gm = utils::vm_memory::test_utils::create_anon_guest_memory(&arch_mem_regions, false)
253425
.unwrap();
254-
configure_system(&gm, GuestAddress(0), 0, &None, no_vcpus).unwrap();
426+
configure_system(
427+
&gm,
428+
GuestAddress(0),
429+
0,
430+
&None,
431+
no_vcpus,
432+
BootProtocol::LinuxBoot,
433+
)
434+
.unwrap();
435+
configure_system(
436+
&gm,
437+
GuestAddress(0),
438+
0,
439+
&None,
440+
no_vcpus,
441+
BootProtocol::PvhBoot,
442+
)
443+
.unwrap();
255444
}
256445

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

src/vmm/src/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ pub fn configure_system_for_boot(
845845
cmdline_size,
846846
initrd,
847847
vcpus.len() as u8,
848+
entry_point.protocol,
848849
)
849850
.map_err(ConfigureSystem)?;
850851
}

0 commit comments

Comments
 (0)