Skip to content

Commit 6e87d70

Browse files
cpercivaaljimenezb
authored 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 20b50ce commit 6e87d70

File tree

3 files changed

+221
-9
lines changed

3 files changed

+221
-9
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,6 +30,14 @@ 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;
3543

src/vmm/src/arch/x86_64/mod.rs

Lines changed: 212 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
//
@@ -22,10 +24,15 @@ pub mod regs;
2224
pub mod gen;
2325

2426
use linux_loader::configurator::linux::LinuxBootConfigurator;
27+
use linux_loader::configurator::pvh::PvhBootConfigurator;
2528
use linux_loader::configurator::{BootConfigurator, BootParams};
2629
use linux_loader::loader::bootparam::boot_params;
2730

28-
use crate::arch::InitrdConfig;
31+
use linux_loader::loader::elf::start_info::{
32+
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
33+
};
34+
35+
use crate::arch::{BootProtocol, InitrdConfig};
2936
use crate::device_manager::resources::ResourceAllocator;
3037
use crate::utils::u64_to_usize;
3138
use crate::vstate::memory::{
@@ -35,8 +42,10 @@ use crate::vstate::memory::{
3542
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
3643
// Usable normal RAM
3744
const E820_RAM: u32 = 1;
45+
3846
// Reserved area that should be avoided during memory allocations
3947
const E820_RESERVED: u32 = 2;
48+
const MEMMAP_TYPE_RAM: u32 = 1;
4049

4150
/// Errors thrown while configuring x86_64 system.
4251
#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
@@ -49,6 +58,12 @@ pub enum ConfigurationError {
4958
ZeroPageSetup,
5059
/// Failed to compute initrd address.
5160
InitrdAddress,
61+
/// Error writing module entry to guest memory.
62+
ModlistSetup,
63+
/// Error writing memory map table to guest memory.
64+
MemmapTableSetup,
65+
/// Error writing hvm_start_info to guest memory.
66+
StartInfoSetup,
5267
}
5368

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

128276
let himem_start = GuestAddress(layout::HIMEM_START);
129277

130-
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
131-
mptable::setup_mptable(guest_mem, resource_allocator, num_cpus)?;
278+
let mut params = boot_params::default();
132279

133280
// Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
134-
let mut params = boot_params {
135-
acpi_rsdp_addr: layout::RSDP_ADDR,
136-
..Default::default()
137-
};
138-
281+
params.acpi_rsdp_addr = layout::RSDP_ADDR;
139282
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
140283
params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
141284
params.hdr.header = KERNEL_HDR_MAGIC;
@@ -246,7 +389,7 @@ mod tests {
246389
let gm = single_region_mem(0x10000);
247390
let mut resource_allocator = ResourceAllocator::new().unwrap();
248391
let config_err =
249-
configure_system(&gm, &mut resource_allocator, GuestAddress(0), 0, &None, 1);
392+
configure_system(&gm, &mut resource_allocator, GuestAddress(0), 0, &None, 1, BootProtocol::LinuxBoot);
250393
assert_eq!(
251394
config_err.unwrap_err(),
252395
super::ConfigurationError::MpTableSetup(mptable::MptableError::NotEnoughMemory)
@@ -263,6 +406,17 @@ mod tests {
263406
0,
264407
&None,
265408
no_vcpus,
409+
BootProtocol::LinuxBoot,
410+
)
411+
.unwrap();
412+
configure_system(
413+
&gm,
414+
&mut resource_allocator,
415+
GuestAddress(0),
416+
0,
417+
&None,
418+
no_vcpus,
419+
BootProtocol::PvhBoot,
266420
)
267421
.unwrap();
268422

@@ -277,6 +431,17 @@ mod tests {
277431
0,
278432
&None,
279433
no_vcpus,
434+
BootProtocol::LinuxBoot,
435+
)
436+
.unwrap();
437+
configure_system(
438+
&gm,
439+
&mut resource_allocator,
440+
GuestAddress(0),
441+
0,
442+
&None,
443+
no_vcpus,
444+
BootProtocol::PvhBoot,
280445
)
281446
.unwrap();
282447

@@ -291,6 +456,17 @@ mod tests {
291456
0,
292457
&None,
293458
no_vcpus,
459+
BootProtocol::LinuxBoot,
460+
)
461+
.unwrap();
462+
configure_system(
463+
&gm,
464+
&mut resource_allocator,
465+
GuestAddress(0),
466+
0,
467+
&None,
468+
no_vcpus,
469+
BootProtocol::PvhBoot,
294470
)
295471
.unwrap();
296472
}
@@ -334,4 +510,31 @@ mod tests {
334510
)
335511
.is_err());
336512
}
513+
514+
#[test]
515+
fn test_add_memmap_entry() {
516+
const MEMMAP_TYPE_RESERVED: u32 = 2;
517+
518+
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
519+
520+
let expected_memmap = vec![
521+
hvm_memmap_table_entry {
522+
addr: 0x0,
523+
size: 0x1000,
524+
type_: MEMMAP_TYPE_RAM,
525+
..Default::default()
526+
},
527+
hvm_memmap_table_entry {
528+
addr: 0x10000,
529+
size: 0xa000,
530+
type_: MEMMAP_TYPE_RESERVED,
531+
..Default::default()
532+
},
533+
];
534+
535+
add_memmap_entry(&mut memmap, 0, 0x1000, MEMMAP_TYPE_RAM).unwrap();
536+
add_memmap_entry(&mut memmap, 0x10000, 0xa000, MEMMAP_TYPE_RESERVED).unwrap();
537+
538+
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
539+
}
337540
}

src/vmm/src/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,7 @@ pub fn configure_system_for_boot(
855855
cmdline_size,
856856
initrd,
857857
vcpu_config.vcpu_count,
858+
entry_point.protocol,
858859
)
859860
.map_err(ConfigureSystem)?;
860861

0 commit comments

Comments
 (0)