Skip to content

Commit 4bfde81

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]>
1 parent 87e0cdd commit 4bfde81

File tree

3 files changed

+229
-7
lines changed

3 files changed

+229
-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,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: 220 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
//
@@ -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,148 @@ 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(
248+
memmap: &mut Vec<hvm_memmap_table_entry>,
249+
addr: u64,
250+
size: u64,
251+
mem_type: u32,
252+
) -> Result<(), ConfigurationError> {
253+
// Add the table entry to the vector
254+
memmap.push(hvm_memmap_table_entry {
255+
addr,
256+
size,
257+
type_: mem_type,
258+
reserved: 0,
259+
});
260+
261+
Ok(())
262+
}
263+
264+
fn configure_64bit_boot(
265+
guest_mem: &GuestMemoryMmap,
266+
cmdline_addr: GuestAddress,
267+
cmdline_size: usize,
268+
initrd: &Option<InitrdConfig>,
120269
) -> Result<(), ConfigurationError> {
121270
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
122271
const KERNEL_HDR_MAGIC: u32 = 0x5372_6448;
@@ -127,15 +276,12 @@ pub fn configure_system(
127276

128277
let himem_start = GuestAddress(layout::HIMEM_START);
129278

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-
133-
// Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
134279
let mut params = boot_params {
135280
acpi_rsdp_addr: layout::RSDP_ADDR,
136281
..Default::default()
137282
};
138283

284+
// Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
139285
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
140286
params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
141287
params.hdr.header = KERNEL_HDR_MAGIC;
@@ -245,8 +391,15 @@ mod tests {
245391
let no_vcpus = 4;
246392
let gm = single_region_mem(0x10000);
247393
let mut resource_allocator = ResourceAllocator::new().unwrap();
248-
let config_err =
249-
configure_system(&gm, &mut resource_allocator, GuestAddress(0), 0, &None, 1);
394+
let config_err = configure_system(
395+
&gm,
396+
&mut resource_allocator,
397+
GuestAddress(0),
398+
0,
399+
&None,
400+
1,
401+
BootProtocol::LinuxBoot,
402+
);
250403
assert_eq!(
251404
config_err.unwrap_err(),
252405
super::ConfigurationError::MpTableSetup(mptable::MptableError::NotEnoughMemory)
@@ -263,6 +416,17 @@ mod tests {
263416
0,
264417
&None,
265418
no_vcpus,
419+
BootProtocol::LinuxBoot,
420+
)
421+
.unwrap();
422+
configure_system(
423+
&gm,
424+
&mut resource_allocator,
425+
GuestAddress(0),
426+
0,
427+
&None,
428+
no_vcpus,
429+
BootProtocol::PvhBoot,
266430
)
267431
.unwrap();
268432

@@ -277,6 +441,17 @@ mod tests {
277441
0,
278442
&None,
279443
no_vcpus,
444+
BootProtocol::LinuxBoot,
445+
)
446+
.unwrap();
447+
configure_system(
448+
&gm,
449+
&mut resource_allocator,
450+
GuestAddress(0),
451+
0,
452+
&None,
453+
no_vcpus,
454+
BootProtocol::PvhBoot,
280455
)
281456
.unwrap();
282457

@@ -291,6 +466,17 @@ mod tests {
291466
0,
292467
&None,
293468
no_vcpus,
469+
BootProtocol::LinuxBoot,
470+
)
471+
.unwrap();
472+
configure_system(
473+
&gm,
474+
&mut resource_allocator,
475+
GuestAddress(0),
476+
0,
477+
&None,
478+
no_vcpus,
479+
BootProtocol::PvhBoot,
294480
)
295481
.unwrap();
296482
}
@@ -334,4 +520,31 @@ mod tests {
334520
)
335521
.is_err());
336522
}
523+
524+
#[test]
525+
fn test_add_memmap_entry() {
526+
const MEMMAP_TYPE_RESERVED: u32 = 2;
527+
528+
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
529+
530+
let expected_memmap = vec![
531+
hvm_memmap_table_entry {
532+
addr: 0x0,
533+
size: 0x1000,
534+
type_: MEMMAP_TYPE_RAM,
535+
..Default::default()
536+
},
537+
hvm_memmap_table_entry {
538+
addr: 0x10000,
539+
size: 0xa000,
540+
type_: MEMMAP_TYPE_RESERVED,
541+
..Default::default()
542+
},
543+
];
544+
545+
add_memmap_entry(&mut memmap, 0, 0x1000, MEMMAP_TYPE_RAM).unwrap();
546+
add_memmap_entry(&mut memmap, 0x10000, 0xa000, MEMMAP_TYPE_RESERVED).unwrap();
547+
548+
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
549+
}
337550
}

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)