Skip to content

Commit f62d046

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 <[email protected]> Co-authored-by: Alejandro Jimenez <[email protected]> Signed-off-by: Patrick Roy <[email protected]>
1 parent 87e0cdd commit f62d046

File tree

3 files changed

+221
-6
lines changed

3 files changed

+221
-6
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 & 6 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,14 @@ 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;
30+
use linux_loader::loader::elf::start_info::{
31+
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
32+
};
2733

28-
use crate::arch::InitrdConfig;
34+
use crate::arch::{BootProtocol, InitrdConfig};
2935
use crate::device_manager::resources::ResourceAllocator;
3036
use crate::utils::u64_to_usize;
3137
use crate::vstate::memory::{
@@ -35,8 +41,10 @@ use crate::vstate::memory::{
3541
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
3642
// Usable normal RAM
3743
const E820_RAM: u32 = 1;
44+
3845
// Reserved area that should be avoided during memory allocations
3946
const E820_RESERVED: u32 = 2;
47+
const MEMMAP_TYPE_RAM: u32 = 1;
4048

4149
/// Errors thrown while configuring x86_64 system.
4250
#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
@@ -49,6 +57,12 @@ pub enum ConfigurationError {
4957
ZeroPageSetup,
5058
/// Failed to compute initrd address.
5159
InitrdAddress,
60+
/// Error writing module entry to guest memory.
61+
ModlistSetup,
62+
/// Error writing memory map table to guest memory.
63+
MemmapTableSetup,
64+
/// Error writing hvm_start_info to guest memory.
65+
StartInfoSetup,
5266
}
5367

5468
const FIRST_ADDR_PAST_32BITS: u64 = 1 << 32;
@@ -110,13 +124,141 @@ pub fn initrd_load_addr(
110124
/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
111125
/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
112126
/// * `num_cpus` - Number of virtual CPUs the guest will have.
127+
/// * `boot_prot` - Boot protocol that will be used to boot the guest.
113128
pub fn configure_system(
114129
guest_mem: &GuestMemoryMmap,
115130
resource_allocator: &mut ResourceAllocator,
116131
cmdline_addr: GuestAddress,
117132
cmdline_size: usize,
118133
initrd: &Option<InitrdConfig>,
119134
num_cpus: u8,
135+
boot_prot: BootProtocol,
136+
) -> Result<(), ConfigurationError> {
137+
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
138+
mptable::setup_mptable(guest_mem, resource_allocator, num_cpus)
139+
.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+
#[allow(clippy::cast_possible_truncation)] // the vec lenghts are single digit integers
218+
let mut start_info = hvm_start_info {
219+
magic: XEN_HVM_START_MAGIC_VALUE,
220+
version: 1,
221+
cmdline_paddr: cmdline_addr.raw_value(),
222+
memmap_paddr: layout::MEMMAP_START,
223+
memmap_entries: memmap.len() as u32,
224+
nr_modules: modules.len() as u32,
225+
..Default::default()
226+
};
227+
if !modules.is_empty() {
228+
start_info.modlist_paddr = layout::MODLIST_START;
229+
}
230+
let mut boot_params =
231+
BootParams::new::<hvm_start_info>(&start_info, GuestAddress(layout::PVH_INFO_START));
232+
233+
// Copy the vector with the memmap table to the MEMMAP_START address
234+
// which is already saved in the memmap_paddr field of hvm_start_info struct.
235+
boot_params.set_sections::<hvm_memmap_table_entry>(&memmap, GuestAddress(layout::MEMMAP_START));
236+
237+
// Copy the vector with the modules list to the MODLIST_START address.
238+
// Note that we only set the modlist_paddr address if there is a nonzero
239+
// number of modules, but serializing an empty list is harmless.
240+
boot_params.set_modules::<hvm_modlist_entry>(&modules, GuestAddress(layout::MODLIST_START));
241+
242+
// Write the hvm_start_info struct to guest memory.
243+
PvhBootConfigurator::write_bootparams(&boot_params, guest_mem)
244+
.map_err(|_| ConfigurationError::StartInfoSetup)
245+
}
246+
247+
fn add_memmap_entry(memmap: &mut Vec<hvm_memmap_table_entry>, addr: u64, size: u64, mem_type: u32) {
248+
// Add the table entry to the vector
249+
memmap.push(hvm_memmap_table_entry {
250+
addr,
251+
size,
252+
type_: mem_type,
253+
reserved: 0,
254+
});
255+
}
256+
257+
fn configure_64bit_boot(
258+
guest_mem: &GuestMemoryMmap,
259+
cmdline_addr: GuestAddress,
260+
cmdline_size: usize,
261+
initrd: &Option<InitrdConfig>,
120262
) -> Result<(), ConfigurationError> {
121263
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
122264
const KERNEL_HDR_MAGIC: u32 = 0x5372_6448;
@@ -127,9 +269,6 @@ pub fn configure_system(
127269

128270
let himem_start = GuestAddress(layout::HIMEM_START);
129271

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)?;
132-
133272
// Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
134273
let mut params = boot_params {
135274
acpi_rsdp_addr: layout::RSDP_ADDR,
@@ -245,8 +384,15 @@ mod tests {
245384
let no_vcpus = 4;
246385
let gm = single_region_mem(0x10000);
247386
let mut resource_allocator = ResourceAllocator::new().unwrap();
248-
let config_err =
249-
configure_system(&gm, &mut resource_allocator, GuestAddress(0), 0, &None, 1);
387+
let config_err = configure_system(
388+
&gm,
389+
&mut resource_allocator,
390+
GuestAddress(0),
391+
0,
392+
&None,
393+
1,
394+
BootProtocol::LinuxBoot,
395+
);
250396
assert_eq!(
251397
config_err.unwrap_err(),
252398
super::ConfigurationError::MpTableSetup(mptable::MptableError::NotEnoughMemory)
@@ -263,6 +409,17 @@ mod tests {
263409
0,
264410
&None,
265411
no_vcpus,
412+
BootProtocol::LinuxBoot,
413+
)
414+
.unwrap();
415+
configure_system(
416+
&gm,
417+
&mut resource_allocator,
418+
GuestAddress(0),
419+
0,
420+
&None,
421+
no_vcpus,
422+
BootProtocol::PvhBoot,
266423
)
267424
.unwrap();
268425

@@ -277,6 +434,17 @@ mod tests {
277434
0,
278435
&None,
279436
no_vcpus,
437+
BootProtocol::LinuxBoot,
438+
)
439+
.unwrap();
440+
configure_system(
441+
&gm,
442+
&mut resource_allocator,
443+
GuestAddress(0),
444+
0,
445+
&None,
446+
no_vcpus,
447+
BootProtocol::PvhBoot,
280448
)
281449
.unwrap();
282450

@@ -291,6 +459,17 @@ mod tests {
291459
0,
292460
&None,
293461
no_vcpus,
462+
BootProtocol::LinuxBoot,
463+
)
464+
.unwrap();
465+
configure_system(
466+
&gm,
467+
&mut resource_allocator,
468+
GuestAddress(0),
469+
0,
470+
&None,
471+
no_vcpus,
472+
BootProtocol::PvhBoot,
294473
)
295474
.unwrap();
296475
}
@@ -334,4 +513,31 @@ mod tests {
334513
)
335514
.is_err());
336515
}
516+
517+
#[test]
518+
fn test_add_memmap_entry() {
519+
const MEMMAP_TYPE_RESERVED: u32 = 2;
520+
521+
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
522+
523+
let expected_memmap = vec![
524+
hvm_memmap_table_entry {
525+
addr: 0x0,
526+
size: 0x1000,
527+
type_: MEMMAP_TYPE_RAM,
528+
..Default::default()
529+
},
530+
hvm_memmap_table_entry {
531+
addr: 0x10000,
532+
size: 0xa000,
533+
type_: MEMMAP_TYPE_RESERVED,
534+
..Default::default()
535+
},
536+
];
537+
538+
add_memmap_entry(&mut memmap, 0, 0x1000, MEMMAP_TYPE_RAM);
539+
add_memmap_entry(&mut memmap, 0x10000, 0xa000, MEMMAP_TYPE_RESERVED);
540+
541+
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
542+
}
337543
}

src/vmm/src/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ pub fn configure_system_for_boot(
827827
cmdline_size,
828828
initrd,
829829
vcpu_config.vcpu_count,
830+
entry_point.protocol,
830831
)
831832
.map_err(ConfigureSystem)?;
832833

0 commit comments

Comments
 (0)