Skip to content

Commit 627bce6

Browse files
Alexandra Iordacheandreeaflorescu
authored andcommitted
configurator: BootParams struct
Added a new struct that encapsulates boot parameters expressed as bytes. * header: section of the parameters that is necessary regardless of boot protocol. For Linux, it's the boot_params struct; for PVH, it's the start_info; for ARM+FDT, it's the device tree blob. * sections: vector of additional boot parameters written at a different address than the header. Unused for Linux & FDT. For PVH, it's the memory map table. * modules: vector of module boot configurations, written at a different address than the header and sections. Unused for Linux & FDT. For PVH, it's (optionally) a vector of 1 modlist_entry containing the initrd configuration, if any. Signed-off-by: Alexandra Iordache <aghecen@amazon.com>
1 parent e5bec42 commit 627bce6

File tree

10 files changed

+292
-140
lines changed

10 files changed

+292
-140
lines changed

coverage_config_aarch64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 75.7,
2+
"coverage_score": 77.2,
33
"exclude_path": "",
44
"crate_features": ""
55
}

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 74.8,
2+
"coverage_score": 77.8,
33
"exclude_path": "",
44
"crate_features": ""
55
}

src/configurator/aarch64/fdt.rs

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
//! Traits and structs for loading the device tree.
66
7-
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory};
7+
use vm_memory::{Bytes, GuestMemory};
88

99
use std::error::Error as StdError;
1010
use std::fmt;
1111

12-
use super::super::{BootConfigurator, Error as BootConfiguratorError, Result};
12+
use crate::configurator::{BootConfigurator, BootParams, Error as BootConfiguratorError, Result};
1313

1414
/// Errors specific to the device tree boot protocol configuration.
1515
#[derive(Debug, PartialEq)]
@@ -51,22 +51,70 @@ impl BootConfigurator for FdtBootConfigurator {
5151
///
5252
/// # Arguments
5353
///
54-
/// * `fdt` - flattened device tree.
55-
/// * `sections` - unused.
54+
/// * `params` - boot parameters containing the FDT.
5655
/// * `guest_memory` - guest's physical memory.
57-
fn write_bootparams<T, S, M>(
58-
fdt: (T, GuestAddress),
59-
_sections: Option<(Vec<S>, GuestAddress)>,
60-
guest_memory: &M,
61-
) -> Result<()>
56+
fn write_bootparams<M>(params: BootParams, guest_memory: &M) -> Result<()>
6257
where
63-
T: ByteValued,
64-
S: ByteValued,
6558
M: GuestMemory,
6659
{
6760
// The VMM has filled an FDT and passed it as a `ByteValued` object.
6861
guest_memory
69-
.write_obj(fdt.0, fdt.1)
62+
.write_slice(params.header.0.as_slice(), params.header.1)
7063
.map_err(|_| Error::WriteFDTToMemory.into())
7164
}
7265
}
66+
67+
#[cfg(test)]
68+
mod tests {
69+
use super::*;
70+
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryMmap};
71+
72+
const FDT_MAX_SIZE: usize = 0x20;
73+
const MEM_SIZE: u64 = 0x100_0000;
74+
75+
fn create_guest_mem() -> GuestMemoryMmap {
76+
GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
77+
}
78+
79+
#[derive(Clone, Copy, Default)]
80+
struct FdtPlaceholder([u8; FDT_MAX_SIZE]);
81+
unsafe impl ByteValued for FdtPlaceholder {}
82+
83+
#[test]
84+
fn test_configure_fdt_boot() {
85+
let fdt = FdtPlaceholder([0u8; FDT_MAX_SIZE]);
86+
let guest_memory = create_guest_mem();
87+
88+
// Error case: FDT doesn't fit in guest memory.
89+
let fdt_addr = guest_memory
90+
.last_addr()
91+
.checked_sub(FDT_MAX_SIZE as u64 - 2)
92+
.unwrap();
93+
assert_eq!(
94+
FdtBootConfigurator::write_bootparams::<GuestMemoryMmap>(
95+
BootParams::new::<FdtPlaceholder>(&fdt, fdt_addr),
96+
&guest_memory,
97+
)
98+
.err(),
99+
Some(Error::WriteFDTToMemory.into())
100+
);
101+
102+
let fdt_addr = guest_memory
103+
.last_addr()
104+
.checked_sub(FDT_MAX_SIZE as u64 - 1)
105+
.unwrap();
106+
assert!(FdtBootConfigurator::write_bootparams::<GuestMemoryMmap>(
107+
BootParams::new::<FdtPlaceholder>(&fdt, fdt_addr),
108+
&guest_memory,
109+
)
110+
.is_ok());
111+
}
112+
113+
#[test]
114+
fn test_error_messages() {
115+
assert_eq!(
116+
format!("{}", Error::WriteFDTToMemory),
117+
"Device Tree Boot Configurator Error: Error writing FDT in guest memory."
118+
)
119+
}
120+
}

src/configurator/mod.rs

Lines changed: 129 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,27 +90,137 @@ pub trait BootConfigurator {
9090
///
9191
/// # Arguments
9292
///
93-
/// * `header` - header section of the boot parameters and address where to write it in guest
94-
/// memory. The first element must be a POD struct that implements [`ByteValued`].
95-
/// For the Linux protocol it's the [`boot_params`] struct, and for PVH the
96-
/// [`hvm_start_info`] struct.
97-
/// * `sections` - vector of sections that compose the boot parameters and address where to
98-
/// write them in guest memory. Unused for the Linux protocol. For PVH, it's the
99-
/// memory map table represented as a vector of [`hvm_memmap_table_entry`]. Must
100-
/// be a `Vec` of POD data structs that implement [`ByteValued`].
93+
/// * `params` - struct containing the header section of the boot parameters, additional
94+
/// sections and modules, and their associated addresses in guest memory. These
95+
/// vary with the boot protocol used.
10196
/// * `guest_memory` - guest's physical memory.
97+
fn write_bootparams<M>(params: BootParams, guest_memory: &M) -> Result<()>
98+
where
99+
M: GuestMemory;
100+
}
101+
102+
/// Boot parameters to be written in guest memory.
103+
#[derive(Clone)]
104+
pub struct BootParams {
105+
/// "Header section", always written in guest memory irrespective of boot protocol.
106+
pub header: (Vec<u8>, GuestAddress),
107+
/// Optional sections containing boot configurations (e.g. E820 map).
108+
pub sections: Option<(Vec<u8>, GuestAddress)>,
109+
/// Optional modules specified at boot configuration time.
110+
pub modules: Option<(Vec<u8>, GuestAddress)>,
111+
}
112+
113+
impl BootParams {
114+
/// Creates a new [`BootParams`](struct.BootParams.html) struct with the specified header.
115+
///
116+
/// # Arguments
117+
///
118+
/// * `header` - [`ByteValued`] representation of mandatory boot parameters.
119+
/// * `header_addr` - address in guest memory where `header` will be written.
120+
///
121+
/// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
122+
pub fn new<T: ByteValued>(header: &T, header_addr: GuestAddress) -> Self {
123+
BootParams {
124+
header: (header.as_slice().to_vec(), header_addr),
125+
sections: None,
126+
modules: None,
127+
}
128+
}
129+
130+
/// Adds or overwrites the boot sections and associated memory address.
131+
///
132+
/// Unused on `aarch64` and for the Linux boot protocol.
133+
/// For the PVH boot protocol, the sections specify the memory map table in
134+
/// [`hvm_memmap_table_entry`] structs.
135+
///
136+
/// # Arguments
137+
///
138+
/// * `sections` - vector of [`ByteValued`] boot configurations.
139+
/// * `sections_addr` - address where the sections will be written in guest memory.
102140
///
103-
/// [`boot_params`]: ../loader/bootparam/struct.boot_e820_entry.html
141+
/// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
104142
/// [`hvm_memmap_table_entry`]: ../loader/elf/start_info/struct.hvm_memmap_table_entry.html
105-
/// [`hvm_start_info`]: ../loader/elf/start_info/struct.hvm_start_info.html
143+
pub fn add_sections<T: ByteValued>(&mut self, sections: &[T], sections_addr: GuestAddress) {
144+
self.sections = Some((
145+
sections
146+
.iter()
147+
.flat_map(|section| section.as_slice().to_vec())
148+
.collect(),
149+
sections_addr,
150+
));
151+
}
152+
153+
/// Adds or overwrites the boot modules and associated memory address.
154+
///
155+
/// Unused on `aarch64` and for the Linux boot protocol.
156+
/// For the PVH boot protocol, the modules are specified in [`hvm_modlist_entry`] structs.
157+
///
158+
/// # Arguments
159+
///
160+
/// * `modules` - vector of [`ByteValued`] boot configurations.
161+
/// * `modules_addr` - address where the modules will be written in guest memory.
162+
///
106163
/// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
107-
fn write_bootparams<T, S, M>(
108-
header: (T, GuestAddress),
109-
sections: Option<(Vec<S>, GuestAddress)>,
110-
guest_memory: &M,
111-
) -> Result<()>
112-
where
113-
T: ByteValued,
114-
S: ByteValued,
115-
M: GuestMemory;
164+
/// [`hvm_modlist_entry`]: ../loader/elf/start_info/struct.hvm_modlist_entry.html
165+
pub fn add_modules<T: ByteValued>(&mut self, modules: &[T], modules_addr: GuestAddress) {
166+
self.modules = Some((
167+
modules
168+
.iter()
169+
.flat_map(|module| module.as_slice().to_vec())
170+
.collect(),
171+
modules_addr,
172+
));
173+
}
174+
}
175+
176+
#[cfg(test)]
177+
mod tests {
178+
use super::*;
179+
180+
#[test]
181+
fn test_error_messages() {
182+
#[cfg(target_arch = "x86_64")]
183+
{
184+
// Linux
185+
assert_eq!(
186+
format!("{}", Error::Linux(linux::Error::ZeroPagePastRamEnd)),
187+
"Boot Configurator Error: The zero page extends past the end of guest memory."
188+
);
189+
assert_eq!(
190+
format!("{}", Error::Linux(linux::Error::ZeroPageSetup)),
191+
"Boot Configurator Error: Error writing to the zero page of guest memory."
192+
);
193+
194+
// PVH
195+
assert_eq!(
196+
format!("{}", Error::Pvh(pvh::Error::MemmapTableMissing)),
197+
"Boot Configurator Error: No memory map was passed to the boot configurator."
198+
);
199+
assert_eq!(
200+
format!("{}", Error::Pvh(pvh::Error::MemmapTablePastRamEnd)),
201+
"Boot Configurator Error: \
202+
The memory map table extends past the end of guest memory."
203+
);
204+
assert_eq!(
205+
format!("{}", Error::Pvh(pvh::Error::MemmapTableSetup)),
206+
"Boot Configurator Error: Error writing memory map table to guest memory."
207+
);
208+
assert_eq!(
209+
format!("{}", Error::Pvh(pvh::Error::StartInfoPastRamEnd)),
210+
"Boot Configurator Error: \
211+
The hvm_start_info structure extends past the end of guest memory."
212+
);
213+
assert_eq!(
214+
format!("{}", Error::Pvh(pvh::Error::StartInfoSetup)),
215+
"Boot Configurator Error: Error writing hvm_start_info to guest memory."
216+
);
217+
}
218+
219+
#[cfg(target_arch = "aarch64")]
220+
// FDT
221+
assert_eq!(
222+
format!("{}", Error::Fdt(fdt::Error::WriteFDTToMemory)),
223+
"Boot Configurator Error: Error writing FDT in guest memory."
224+
);
225+
}
116226
}

src/configurator/x86_64/linux.rs

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@
1212
//! Traits and structs for configuring and loading boot parameters on `x86_64` using the Linux
1313
//! boot protocol.
1414
15-
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory};
15+
use vm_memory::{Bytes, GuestMemory};
1616

17-
use super::super::{BootConfigurator, Error as BootConfiguratorError, Result};
18-
use crate::loader::bootparam::boot_params;
17+
use crate::configurator::{BootConfigurator, BootParams, Error as BootConfiguratorError, Result};
1918

2019
use std::error::Error as StdError;
2120
use std::fmt;
22-
use std::mem;
2321

2422
/// Boot configurator for the Linux boot protocol.
2523
pub struct LinuxBootConfigurator {}
@@ -64,28 +62,22 @@ impl BootConfigurator for LinuxBootConfigurator {
6462
///
6563
/// # Arguments
6664
///
67-
/// * `header` - boot parameters encapsulated in a [`boot_params`] struct.
68-
/// * `sections` - unused.
65+
/// * `params` - boot parameters. The header contains a [`boot_params`] struct. The `sections`
66+
/// and `modules` are unused.
6967
/// * `guest_memory` - guest's physical memory.
7068
///
71-
/// [`boot_params`]: ../loader/bootparam/struct.boot_e820_entry.html
72-
fn write_bootparams<T, S, M>(
73-
header: (T, GuestAddress),
74-
_sections: Option<(Vec<S>, GuestAddress)>,
75-
guest_memory: &M,
76-
) -> Result<()>
69+
/// [`boot_params`]: ../loader/bootparam/struct.boot_params.html
70+
fn write_bootparams<M>(params: BootParams, guest_memory: &M) -> Result<()>
7771
where
78-
T: ByteValued,
79-
S: ByteValued,
8072
M: GuestMemory,
8173
{
8274
// The VMM has filled a `boot_params` struct and its e820 map.
8375
// This will be written in guest memory at the zero page.
8476
guest_memory
85-
.checked_offset(header.1, mem::size_of::<boot_params>())
77+
.checked_offset(params.header.1, params.header.0.len())
8678
.ok_or(Error::ZeroPagePastRamEnd)?;
8779
guest_memory
88-
.write_obj(header.0, header.1)
80+
.write_slice(params.header.0.as_slice(), params.header.1)
8981
.map_err(|_| Error::ZeroPageSetup)?;
9082

9183
Ok(())
@@ -95,14 +87,15 @@ impl BootConfigurator for LinuxBootConfigurator {
9587
#[cfg(test)]
9688
mod tests {
9789
use super::*;
90+
use crate::loader::bootparam::boot_params;
9891
use std::mem;
9992
use vm_memory::{Address, GuestAddress, GuestMemoryMmap};
10093

10194
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
10295
const KERNEL_HDR_MAGIC: u32 = 0x53726448;
10396
const KERNEL_LOADER_OTHER: u8 = 0xff;
10497
const KERNEL_MIN_ALIGNMENT_BYTES: u32 = 0x1000000;
105-
const MEM_SIZE: u64 = 0x1000000;
98+
const MEM_SIZE: u64 = 0x100_0000;
10699

107100
fn create_guest_mem() -> GuestMemoryMmap {
108101
GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
@@ -130,24 +123,34 @@ mod tests {
130123
let bad_zeropg_addr = GuestAddress(
131124
guest_memory.last_addr().raw_value() - mem::size_of::<boot_params>() as u64 + 1,
132125
);
126+
let mut bootparams = BootParams::new::<boot_params>(&params, bad_zeropg_addr);
133127
assert_eq!(
134-
LinuxBootConfigurator::write_bootparams::<boot_params, boot_params, GuestMemoryMmap>(
135-
(params, bad_zeropg_addr),
136-
None,
128+
LinuxBootConfigurator::write_bootparams::<GuestMemoryMmap>(
129+
bootparams.clone(),
137130
&guest_memory,
138131
)
139132
.err(),
140133
Some(Error::ZeroPagePastRamEnd.into()),
141134
);
142135

143136
// Success case.
144-
assert!(
145-
LinuxBootConfigurator::write_bootparams::<boot_params, boot_params, GuestMemoryMmap>(
146-
(params, zero_page_addr),
147-
None,
148-
&guest_memory
149-
)
150-
.is_ok()
137+
bootparams.header.1 = zero_page_addr;
138+
assert!(LinuxBootConfigurator::write_bootparams::<GuestMemoryMmap>(
139+
bootparams,
140+
&guest_memory,
141+
)
142+
.is_ok());
143+
}
144+
145+
#[test]
146+
fn test_error_messages() {
147+
assert_eq!(
148+
format!("{}", Error::ZeroPagePastRamEnd),
149+
"Linux Boot Configurator Error: The zero page extends past the end of guest memory."
150+
);
151+
assert_eq!(
152+
format!("{}", Error::ZeroPageSetup),
153+
"Linux Boot Configurator Error: Error writing to the zero page of guest memory."
151154
);
152155
}
153156
}

0 commit comments

Comments
 (0)