vmm/vmm_config/
boot_source.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::fs::File;
5use std::io;
6
7use serde::{Deserialize, Serialize};
8
9/// Default guest kernel command line:
10/// - `reboot=k` shut down the guest on reboot, instead of well... rebooting;
11/// - `panic=1` on panic, reboot after 1 second;
12/// - `nomodule` disable loadable kernel module support;
13/// - `8250.nr_uarts=0` disable 8250 serial interface;
14/// - `i8042.noaux` do not probe the i8042 controller for an attached mouse (save boot time);
15/// - `i8042.nomux` do not probe i8042 for a multiplexing controller (save boot time);
16/// - `i8042.dumbkbd` do not attempt to control kbd state via the i8042 (save boot time).
17/// - `swiotlb=noforce` disable software bounce buffers (SWIOTLB)
18pub const DEFAULT_KERNEL_CMDLINE: &str = "reboot=k panic=1 nomodule 8250.nr_uarts=0 i8042.noaux \
19                                          i8042.nomux i8042.dumbkbd swiotlb=noforce";
20
21/// Strongly typed data structure used to configure the boot source of the
22/// microvm.
23#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
24#[serde(deny_unknown_fields)]
25pub struct BootSourceConfig {
26    /// Path of the kernel image.
27    pub kernel_image_path: String,
28    /// Path of the initrd, if there is one.
29    pub initrd_path: Option<String>,
30    /// The boot arguments to pass to the kernel. If this field is uninitialized,
31    /// DEFAULT_KERNEL_CMDLINE is used.
32    pub boot_args: Option<String>,
33}
34
35/// Errors associated with actions on `BootSourceConfig`.
36#[derive(Debug, thiserror::Error, displaydoc::Display)]
37pub enum BootSourceConfigError {
38    /// The kernel file cannot be opened: {0}
39    InvalidKernelPath(io::Error),
40    /// The initrd file cannot be opened due to invalid path or invalid permissions. {0}
41    InvalidInitrdPath(io::Error),
42    /// The kernel command line is invalid: {0}
43    InvalidKernelCommandLine(String),
44}
45
46/// Holds the kernel specification (both configuration as well as runtime details).
47#[derive(Debug, Default)]
48pub struct BootSource {
49    /// The boot source configuration.
50    pub config: BootSourceConfig,
51    /// The boot source builder (a boot source allocated and validated).
52    /// It is an option cause a resumed microVM does not need it.
53    pub builder: Option<BootConfig>,
54}
55
56/// Holds the kernel builder (created and validates based on BootSourceConfig).
57#[derive(Debug)]
58pub struct BootConfig {
59    /// The commandline validated against correctness.
60    pub cmdline: linux_loader::cmdline::Cmdline,
61    /// The descriptor to the kernel file.
62    pub kernel_file: File,
63    /// The descriptor to the initrd file, if there is one.
64    pub initrd_file: Option<File>,
65}
66
67impl BootConfig {
68    /// Creates the BootConfig based on a given configuration.
69    pub fn new(cfg: &BootSourceConfig) -> Result<Self, BootSourceConfigError> {
70        use self::BootSourceConfigError::{
71            InvalidInitrdPath, InvalidKernelCommandLine, InvalidKernelPath,
72        };
73
74        // Validate boot source config.
75        let kernel_file = File::open(&cfg.kernel_image_path).map_err(InvalidKernelPath)?;
76        let initrd_file: Option<File> = match &cfg.initrd_path {
77            Some(path) => Some(File::open(path).map_err(InvalidInitrdPath)?),
78            None => None,
79        };
80
81        let cmdline_str = match cfg.boot_args.as_ref() {
82            None => DEFAULT_KERNEL_CMDLINE,
83            Some(str) => str.as_str(),
84        };
85        let cmdline =
86            linux_loader::cmdline::Cmdline::try_from(cmdline_str, crate::arch::CMDLINE_MAX_SIZE)
87                .map_err(|err| InvalidKernelCommandLine(err.to_string()))?;
88
89        Ok(BootConfig {
90            cmdline,
91            kernel_file,
92            initrd_file,
93        })
94    }
95}
96
97#[cfg(test)]
98pub(crate) mod tests {
99    use vmm_sys_util::tempfile::TempFile;
100
101    use super::*;
102    use crate::snapshot::Snapshot;
103
104    #[test]
105    fn test_boot_config() {
106        let kernel_file = TempFile::new().unwrap();
107        let kernel_path = kernel_file.as_path().to_str().unwrap().to_string();
108
109        let boot_src_cfg = BootSourceConfig {
110            boot_args: None,
111            initrd_path: None,
112            kernel_image_path: kernel_path,
113        };
114
115        let boot_cfg = BootConfig::new(&boot_src_cfg).unwrap();
116        assert!(boot_cfg.initrd_file.is_none());
117        assert_eq!(
118            boot_cfg.cmdline.as_cstring().unwrap().as_bytes_with_nul(),
119            [DEFAULT_KERNEL_CMDLINE.as_bytes(), b"\0"].concat()
120        );
121    }
122
123    #[test]
124    fn test_serde() {
125        let boot_src_cfg = BootSourceConfig {
126            boot_args: Some(DEFAULT_KERNEL_CMDLINE.to_string()),
127            initrd_path: Some("/tmp/initrd".to_string()),
128            kernel_image_path: "./vmlinux.bin".to_string(),
129        };
130
131        let mut snapshot_data = vec![0u8; 1000];
132        Snapshot::new(&boot_src_cfg)
133            .save(&mut snapshot_data.as_mut_slice())
134            .unwrap();
135        let restored_boot_cfg = Snapshot::load_without_crc_check(snapshot_data.as_slice())
136            .unwrap()
137            .data;
138        assert_eq!(boot_src_cfg, restored_boot_cfg);
139    }
140}