Skip to content

Commit 3dfe066

Browse files
committed
loader: add support for initrd
Add the optional "initrd_path" property to the "boot-source" the REST API. The initrd is for the moment loaded at a fixed address. Co-developed-by: Tim Deegan <[email protected]> Signed-off-by: Tim Deegan <[email protected]> Co-developed-by: Marco Vedovati <[email protected]> Signed-off-by: Marco Vedovati <[email protected]>
1 parent f4673ca commit 3dfe066

File tree

6 files changed

+109
-11
lines changed

6 files changed

+109
-11
lines changed

api_server/src/request/boot_source.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ mod tests {
3232
fn test_into_parsed_request() {
3333
let body = BootSourceConfig {
3434
kernel_image_path: String::from("/foo/bar"),
35+
initrd_path: None,
3536
boot_args: Some(String::from("foobar")),
3637
};
3738
let same_body = BootSourceConfig {
3839
kernel_image_path: String::from("/foo/bar"),
40+
initrd_path: None,
3941
boot_args: Some(String::from("foobar")),
4042
};
4143
let (sender, receiver) = oneshot::channel();

api_server/swagger/firecracker.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,9 @@ definitions:
395395
kernel_image_path:
396396
type: string
397397
description: Host level path to the kernel image used to boot the guest
398+
initrd_path:
399+
type: string
400+
description: Host level path to the initrd image used to boot the guest
398401
boot_args:
399402
type: string
400403
description: Kernel boot arguments

arch/src/x86_64/mod.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ pub fn configure_system(
9898
guest_mem: &GuestMemory,
9999
cmdline_addr: GuestAddress,
100100
cmdline_size: usize,
101+
initrd_addr: GuestAddress,
102+
initrd_size: usize,
101103
num_cpus: u8,
102104
) -> super::Result<()> {
103105
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
@@ -120,6 +122,8 @@ pub fn configure_system(
120122
params.0.hdr.cmd_line_ptr = cmdline_addr.offset() as u32;
121123
params.0.hdr.cmdline_size = cmdline_size as u32;
122124
params.0.hdr.kernel_alignment = KERNEL_MIN_ALIGNMENT_BYTES;
125+
params.0.hdr.ramdisk_image = initrd_addr.offset() as u32;
126+
params.0.hdr.ramdisk_size = initrd_size as u32;
123127

124128
add_e820_entry(&mut params.0, 0, EBDA_START, E820_RAM)?;
125129

@@ -212,7 +216,7 @@ mod tests {
212216
fn test_system_configuration() {
213217
let no_vcpus = 4;
214218
let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
215-
let config_err = configure_system(&gm, GuestAddress(0), 0, 1);
219+
let config_err = configure_system(&gm, GuestAddress(0), 0, GuestAddress(0), 0, 1);
216220
assert!(config_err.is_err());
217221
match config_err.unwrap_err() {
218222
super::super::Error::X86_64Setup(e) => assert_eq!(
@@ -224,19 +228,19 @@ mod tests {
224228
let mem_size = 128 << 20;
225229
let arch_mem_regions = arch_memory_regions(mem_size);
226230
let gm = GuestMemory::new(&arch_mem_regions).unwrap();
227-
configure_system(&gm, GuestAddress(0), 0, no_vcpus).unwrap();
231+
configure_system(&gm, GuestAddress(0), 0, GuestAddress(0), 0, no_vcpus).unwrap();
228232

229233
// Now assigning some memory that is equal to the start of the 32bit memory hole.
230234
let mem_size = 3328 << 20;
231235
let arch_mem_regions = arch_memory_regions(mem_size);
232236
let gm = GuestMemory::new(&arch_mem_regions).unwrap();
233-
configure_system(&gm, GuestAddress(0), 0, no_vcpus).unwrap();
237+
configure_system(&gm, GuestAddress(0), 0, GuestAddress(0), 0, no_vcpus).unwrap();
234238

235239
// Now assigning some memory that falls after the 32bit memory hole.
236240
let mem_size = 3330 << 20;
237241
let arch_mem_regions = arch_memory_regions(mem_size);
238242
let gm = GuestMemory::new(&arch_mem_regions).unwrap();
239-
configure_system(&gm, GuestAddress(0), 0, no_vcpus).unwrap();
243+
configure_system(&gm, GuestAddress(0), 0, GuestAddress(0), 0, no_vcpus).unwrap();
240244
}
241245

242246
#[test]

kernel/src/loader/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ pub enum Error {
3030
InvalidProgramHeaderSize,
3131
InvalidProgramHeaderOffset,
3232
InvalidProgramHeaderAddress,
33+
ReadInitrd,
3334
ReadKernelDataStruct(&'static str),
3435
ReadKernelImage,
36+
SeekInitrd,
3537
SeekKernelStart,
3638
SeekKernelImage,
3739
SeekProgramHeader,
@@ -49,12 +51,14 @@ impl fmt::Display for Error {
4951
Error::InvalidProgramHeaderSize => "Invalid ELF program header size",
5052
Error::InvalidProgramHeaderOffset => "Invalid ELF program header offset",
5153
Error::InvalidProgramHeaderAddress => "Invalid ELF program header address",
54+
Error::ReadInitrd => "Failed to read initrd image",
5255
Error::ReadKernelDataStruct(ref e) => e,
5356
Error::ReadKernelImage => "Failed to write kernel image to guest memory",
5457
Error::SeekKernelStart => {
5558
"Failed to seek to file offset as pointed by the ELF program header"
5659
}
5760
Error::SeekKernelImage => "Failed to seek to offset of kernel image",
61+
Error::SeekInitrd => "Failed to seek initrd image",
5862
Error::SeekProgramHeader => "Failed to seek to ELF program header",
5963
}
6064
)
@@ -258,6 +262,37 @@ pub fn load_cmdline(
258262
Ok(())
259263
}
260264

265+
/// Loads the initrd from a file into the given memory slice.
266+
///
267+
/// * `guest_mem` - The guest memory region the initrd is written to.
268+
/// * `initrd_image` - Input initrd image.
269+
///
270+
/// Returns the entry address of the initrd and its length as a tuple
271+
pub fn load_initrd<F>(
272+
guest_mem: &GuestMemory,
273+
initrd_image: &mut F,
274+
) -> Result<(GuestAddress, usize)>
275+
where
276+
F: Read + Seek,
277+
{
278+
// This works for bzImage kernels because they load at 1MiB and have 16MiB init space.
279+
// In practice works for many ELF kernels too because they load at 16MiB.
280+
// Ought to have an allocator in guest_mem to find an empty spot.
281+
const INITRD_LOAD_ADDRESS: usize = 0x300_0000; // 48 MiB
282+
283+
let load_bytes = initrd_image
284+
.seek(SeekFrom::End(0))
285+
.map_err(|_| Error::SeekInitrd)? as usize;
286+
initrd_image
287+
.seek(SeekFrom::Start(0))
288+
.map_err(|_| Error::SeekInitrd)?;
289+
guest_mem
290+
.read_to_memory(GuestAddress(INITRD_LOAD_ADDRESS), initrd_image, load_bytes)
291+
.map_err(|_| Error::ReadInitrd)?;
292+
293+
Ok((GuestAddress(INITRD_LOAD_ADDRESS), load_bytes))
294+
}
295+
261296
#[cfg(test)]
262297
mod tests {
263298
use super::super::cmdline::Cmdline;

vmm/src/lib.rs

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ impl Drop for EpollContext {
742742
struct KernelConfig {
743743
cmdline: kernel_cmdline::Cmdline,
744744
kernel_file: File,
745+
initrd_file: Option<File>,
745746
#[cfg(target_arch = "x86_64")]
746747
cmdline_addr: GuestAddress,
747748
}
@@ -1323,10 +1324,10 @@ impl Vmm {
13231324
Ok(entry_addr)
13241325
}
13251326

1326-
fn configure_system(&self) -> std::result::Result<(), StartMicrovmError> {
1327+
fn configure_system(&mut self) -> std::result::Result<(), StartMicrovmError> {
13271328
use StartMicrovmError::*;
13281329

1329-
let kernel_config = self.kernel_config.as_ref().ok_or(MissingKernelConfig)?;
1330+
let kernel_config = self.kernel_config.as_mut().ok_or(MissingKernelConfig)?;
13301331

13311332
let vm_memory = self.vm.get_memory().ok_or(GuestMemory(
13321333
memory_model::GuestMemoryError::MemoryNotInitialized,
@@ -1336,11 +1337,22 @@ impl Vmm {
13361337
// having set the vcpu count.
13371338
let vcpu_count = self.vm_config.vcpu_count.ok_or(VcpusNotConfigured)?;
13381339

1340+
let initrd_file: &mut Option<File> = &mut kernel_config.initrd_file;
1341+
1342+
let (initrd_addr, initrd_size) = if let Some(ref mut initrd_file) = initrd_file {
1343+
kernel_loader::load_initrd(vm_memory, initrd_file)
1344+
.map_err(|e| StartMicrovmError::KernelLoader(e))?
1345+
} else {
1346+
(GuestAddress(0), 0)
1347+
};
1348+
13391349
#[cfg(target_arch = "x86_64")]
13401350
arch::x86_64::configure_system(
13411351
vm_memory,
13421352
kernel_config.cmdline_addr,
13431353
kernel_config.cmdline.len() + 1,
1354+
initrd_addr,
1355+
initrd_size,
13441356
vcpu_count,
13451357
)
13461358
.map_err(ConfigureSystem)?;
@@ -1606,6 +1618,7 @@ impl Vmm {
16061618
fn configure_boot_source(
16071619
&mut self,
16081620
kernel_image_path: String,
1621+
initrd_path: Option<String>,
16091622
kernel_cmdline: Option<String>,
16101623
) -> std::result::Result<VmmData, VmmActionError> {
16111624
use BootSourceConfigError::{
@@ -1621,13 +1634,26 @@ impl Vmm {
16211634
let kernel_file =
16221635
File::open(kernel_image_path).map_err(|_| BootSource(User, InvalidKernelPath))?;
16231636

1637+
let initrd_file = match initrd_path {
1638+
None => None,
1639+
Some(path) => Some({
1640+
File::open(path).map_err(|_| {
1641+
VmmActionError::BootSource(
1642+
ErrorKind::User,
1643+
BootSourceConfigError::InvalidInitrdPath,
1644+
)
1645+
})?
1646+
}),
1647+
};
1648+
16241649
let mut cmdline = kernel_cmdline::Cmdline::new(arch::CMDLINE_MAX_SIZE);
16251650
cmdline
16261651
.insert_str(kernel_cmdline.unwrap_or_else(|| String::from(DEFAULT_KERNEL_CMDLINE)))
16271652
.map_err(|_| BootSource(User, InvalidKernelCommandLine))?;
16281653

16291654
let kernel_config = KernelConfig {
16301655
kernel_file,
1656+
initrd_file,
16311657
cmdline,
16321658
#[cfg(target_arch = "x86_64")]
16331659
cmdline_addr: GuestAddress(arch::x86_64::layout::CMDLINE_START),
@@ -1934,6 +1960,7 @@ impl Vmm {
19341960
Vmm::send_response(
19351961
self.configure_boot_source(
19361962
boot_source_body.kernel_image_path,
1963+
boot_source_body.initrd_path,
19371964
boot_source_body.boot_args,
19381965
),
19391966
sender,
@@ -2142,6 +2169,7 @@ mod tests {
21422169
let kernel_cfg = KernelConfig {
21432170
cmdline,
21442171
kernel_file,
2172+
initrd_file: None,
21452173
#[cfg(target_arch = "x86_64")]
21462174
cmdline_addr: GuestAddress(arch::x86_64::layout::CMDLINE_START),
21472175
};
@@ -2685,6 +2713,7 @@ mod tests {
26852713
cmdline_addr: dummy_addr,
26862714
cmdline: kernel_cmdline::Cmdline::new(10),
26872715
kernel_file: tempfile::tempfile().unwrap(),
2716+
initrd_file: None,
26882717
});
26892718
assert!(vmm.check_health().is_ok());
26902719
}
@@ -2875,27 +2904,43 @@ mod tests {
28752904

28762905
// Test invalid kernel path.
28772906
assert!(vmm
2878-
.configure_boot_source(String::from("dummy-path"), None)
2907+
.configure_boot_source(String::from("dummy-path"), None, None)
28792908
.is_err());
28802909

28812910
// Test valid kernel path and invalid cmdline.
28822911
let kernel_file = NamedTempFile::new().expect("Failed to create temporary kernel file.");
28832912
let kernel_path = String::from(kernel_file.path().to_path_buf().to_str().unwrap());
2913+
let initrd_file = NamedTempFile::new().expect("Failed to create temporary initrd file.");
2914+
let initrd_path = String::from(initrd_file.path().to_path_buf().to_str().unwrap());
28842915
let invalid_cmdline = String::from_utf8(vec![b'X'; arch::CMDLINE_MAX_SIZE + 1]).unwrap();
28852916
assert!(vmm
2886-
.configure_boot_source(kernel_path.clone(), Some(invalid_cmdline))
2917+
.configure_boot_source(kernel_path.clone(), None, Some(invalid_cmdline))
28872918
.is_err());
28882919

28892920
// Test valid configuration.
2890-
assert!(vmm.configure_boot_source(kernel_path.clone(), None).is_ok());
28912921
assert!(vmm
2892-
.configure_boot_source(kernel_path.clone(), Some(String::from("reboot=k")))
2922+
.configure_boot_source(kernel_path.clone(), None, None)
2923+
.is_ok());
2924+
assert!(vmm
2925+
.configure_boot_source(kernel_path.clone(), None, Some(String::from("reboot=k")))
2926+
.is_ok());
2927+
2928+
// Test valid configuration + initrd.
2929+
assert!(vmm
2930+
.configure_boot_source(kernel_path.clone(), Some(initrd_path.clone()), None)
2931+
.is_ok());
2932+
assert!(vmm
2933+
.configure_boot_source(
2934+
kernel_path.clone(),
2935+
Some(initrd_path.clone()),
2936+
Some(String::from("reboot=k"))
2937+
)
28932938
.is_ok());
28942939

28952940
// Test valid configuration after boot (should fail).
28962941
vmm.set_instance_state(InstanceState::Running);
28972942
assert!(vmm
2898-
.configure_boot_source(kernel_path.clone(), None)
2943+
.configure_boot_source(kernel_path.clone(), None, None)
28992944
.is_err());
29002945
}
29012946

vmm/src/vmm_config/boot_source.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use std::fmt::{Display, Formatter, Result};
1010
pub struct BootSourceConfig {
1111
/// Path of the kernel image.
1212
pub kernel_image_path: String,
13+
/// Path of the initrd, if there is one.
14+
pub initrd_path: Option<String>,
1315
/// The boot arguments to pass to the kernel. If this field is uninitialized, the default
1416
/// kernel command line is used: `reboot=k panic=1 pci=off nomodules 8250.nr_uarts=0`.
1517
#[serde(skip_serializing_if = "Option::is_none")]
@@ -21,6 +23,8 @@ pub struct BootSourceConfig {
2123
pub enum BootSourceConfigError {
2224
/// The kernel file cannot be opened.
2325
InvalidKernelPath,
26+
/// The initrd file cannot be opened.
27+
InvalidInitrdPath,
2428
/// The kernel command line is invalid.
2529
InvalidKernelCommandLine,
2630
/// The boot source cannot be update post boot.
@@ -36,6 +40,11 @@ impl Display for BootSourceConfigError {
3640
"The kernel file cannot be opened due to invalid kernel path or \
3741
invalid permissions.",
3842
),
43+
InvalidInitrdPath => write!(
44+
f,
45+
"The initrd file cannot be opened due to invalid path or \
46+
invalid permissions.",
47+
),
3948
InvalidKernelCommandLine => write!(f, "The kernel command line is invalid!"),
4049
UpdateNotAllowedPostBoot => {
4150
write!(f, "The update operation is not allowed after boot.")

0 commit comments

Comments
 (0)