devices: Add kernel cmdline, kernel, and initramfs to fw_cfg device
The kernel and initramfs are passed to the fw_cfg device as file references. The cmdline is passed directly. Signed-off-by: Alex Orozco <alexorozco@google.com>
This commit is contained in:
parent
777b7ee11e
commit
623fadfa9d
6 changed files with 203 additions and 6 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -531,6 +531,7 @@ dependencies = [
|
|||
"event_monitor",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"linux-loader",
|
||||
"log",
|
||||
"num_enum",
|
||||
"pci",
|
||||
|
|
@ -1143,8 +1144,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "870c3814345f050991f99869417779f6062542bcf4ed81db7a1b926ad1306638"
|
||||
source = "git+https://github.com/rust-vmm/linux-loader?branch=main#d5f39c09d59c8f50d5313b78ce4de511b12d1848"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -107,7 +107,8 @@ members = [
|
|||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||
kvm-bindings = "0.12.0"
|
||||
kvm-ioctls = "0.22.0"
|
||||
linux-loader = "0.13.0"
|
||||
# TODO: update to 0.13.1+
|
||||
linux-loader = { git = "https://github.com/rust-vmm/linux-loader", branch = "main" }
|
||||
mshv-bindings = "0.5.2"
|
||||
mshv-ioctls = "0.5.2"
|
||||
seccompiler = "0.5.0"
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ byteorder = { workspace = true }
|
|||
event_monitor = { path = "../event_monitor" }
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = { workspace = true }
|
||||
linux-loader = { workspace = true, features = [
|
||||
"bzimage",
|
||||
"elf",
|
||||
"pe",
|
||||
], optional = true }
|
||||
log = { workspace = true }
|
||||
num_enum = "0.7.2"
|
||||
pci = { path = "../pci" }
|
||||
|
|
@ -38,6 +43,6 @@ arch = { path = "../arch" }
|
|||
|
||||
[features]
|
||||
default = []
|
||||
fw_cfg = ["zerocopy"]
|
||||
fw_cfg = ["linux-loader", "zerocopy"]
|
||||
kvm = ["arch/kvm"]
|
||||
pvmemcontrol = []
|
||||
|
|
|
|||
|
|
@ -14,12 +14,17 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::Result,
|
||||
mem::size_of_val,
|
||||
mem::{size_of, size_of_val},
|
||||
os::unix::fs::FileExt,
|
||||
sync::{Arc, Barrier},
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use linux_loader::bootparam::boot_params;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use linux_loader::loader::pe::arm64_image_header as boot_params;
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::ByteValued;
|
||||
use vmm_sys_util::sock_ctrl_msg::IntoIovec;
|
||||
use zerocopy::{FromBytes, IntoBytes};
|
||||
|
||||
|
|
@ -50,6 +55,14 @@ pub const PORT_FW_CFG_WIDTH: u64 = 0x10;
|
|||
|
||||
const FW_CFG_SIGNATURE: u16 = 0x00;
|
||||
const FW_CFG_ID: u16 = 0x01;
|
||||
const FW_CFG_KERNEL_SIZE: u16 = 0x08;
|
||||
const FW_CFG_INITRD_SIZE: u16 = 0x0b;
|
||||
const FW_CFG_KERNEL_DATA: u16 = 0x11;
|
||||
const FW_CFG_INITRD_DATA: u16 = 0x12;
|
||||
const FW_CFG_CMDLINE_SIZE: u16 = 0x14;
|
||||
const FW_CFG_CMDLINE_DATA: u16 = 0x15;
|
||||
const FW_CFG_SETUP_SIZE: u16 = 0x17;
|
||||
const FW_CFG_SETUP_DATA: u16 = 0x18;
|
||||
const FW_CFG_FILE_DIR: u16 = 0x19;
|
||||
const FW_CFG_KNOWN_ITEMS: usize = 0x20;
|
||||
|
||||
|
|
@ -183,6 +196,46 @@ impl FwCfg {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_kernel_data(&mut self, file: &File) -> Result<()> {
|
||||
let mut buffer = vec![0u8; size_of::<boot_params>()];
|
||||
file.read_exact_at(&mut buffer, 0)?;
|
||||
let bp = boot_params::from_mut_slice(&mut buffer).unwrap();
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
// must set to 4 for backwards compatibility
|
||||
// https://docs.kernel.org/arch/x86/boot.html#the-real-mode-kernel-header
|
||||
if bp.hdr.setup_sects == 0 {
|
||||
bp.hdr.setup_sects = 4;
|
||||
}
|
||||
// wildcard boot loader type
|
||||
bp.hdr.type_of_loader = 0xff;
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let kernel_start = bp.text_offset;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let kernel_start = (bp.hdr.setup_sects as usize + 1) * 512;
|
||||
self.known_items[FW_CFG_SETUP_SIZE as usize] = FwCfgContent::U32(buffer.len() as u32);
|
||||
self.known_items[FW_CFG_SETUP_DATA as usize] = FwCfgContent::Bytes(buffer);
|
||||
self.known_items[FW_CFG_KERNEL_SIZE as usize] =
|
||||
FwCfgContent::U32(file.metadata()?.len() as u32 - kernel_start as u32);
|
||||
self.known_items[FW_CFG_KERNEL_DATA as usize] =
|
||||
FwCfgContent::File(kernel_start as u64, file.try_clone()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_kernel_cmdline(&mut self, s: std::ffi::CString) {
|
||||
let bytes = s.into_bytes_with_nul();
|
||||
self.known_items[FW_CFG_CMDLINE_SIZE as usize] = FwCfgContent::U32(bytes.len() as u32);
|
||||
self.known_items[FW_CFG_CMDLINE_DATA as usize] = FwCfgContent::Bytes(bytes);
|
||||
}
|
||||
|
||||
pub fn add_initramfs_data(&mut self, file: &File) -> Result<()> {
|
||||
let initramfs_size = file.metadata()?.len();
|
||||
self.known_items[FW_CFG_INITRD_SIZE as usize] = FwCfgContent::U32(initramfs_size as _);
|
||||
self.known_items[FW_CFG_INITRD_DATA as usize] = FwCfgContent::File(0, file.try_clone()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_content(content: &FwCfgContent, offset: u32, data: &mut [u8], size: u32) -> Option<u8> {
|
||||
let start = offset as usize;
|
||||
let end = start + size as usize;
|
||||
|
|
@ -278,6 +331,11 @@ impl BusDevice for FwCfg {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::CString;
|
||||
use std::io::Write;
|
||||
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
|
@ -306,4 +364,51 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_kernel_cmdline() {
|
||||
let mut fw_cfg = FwCfg::new();
|
||||
|
||||
let cmdline = *b"cmdline\0";
|
||||
|
||||
fw_cfg.add_kernel_cmdline(CString::from_vec_with_nul(cmdline.to_vec()).unwrap());
|
||||
|
||||
let mut data = vec![0u8];
|
||||
|
||||
let mut cmdline_iter = cmdline.into_iter();
|
||||
fw_cfg.write(0, SELECTOR_OFFSET, &[FW_CFG_CMDLINE_DATA as u8, 0]);
|
||||
loop {
|
||||
if let Some(char) = cmdline_iter.next() {
|
||||
fw_cfg.read(0, DATA_OFFSET, &mut data);
|
||||
assert_eq!(data[0], char);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_initram_fs() {
|
||||
let mut fw_cfg = FwCfg::new();
|
||||
|
||||
let temp = TempFile::new().unwrap();
|
||||
let mut temp_file = temp.as_file();
|
||||
|
||||
let initram_content = b"this is the initramfs";
|
||||
let written = temp_file.write(initram_content);
|
||||
assert_eq!(written.unwrap(), 21);
|
||||
let _ = fw_cfg.add_initramfs_data(temp_file);
|
||||
|
||||
let mut data = vec![0u8];
|
||||
|
||||
let mut initram_iter = (*initram_content).into_iter();
|
||||
fw_cfg.write(0, SELECTOR_OFFSET, &[FW_CFG_INITRD_DATA as u8, 0]);
|
||||
loop {
|
||||
if let Some(char) = initram_iter.next() {
|
||||
fw_cfg.read(0, DATA_OFFSET, &mut data);
|
||||
assert_eq!(data[0], char);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ epoll = "4.3.3"
|
|||
hypervisor = { path = "../hypervisor", features = ["mshv_emulator"] }
|
||||
libc = "0.2.155"
|
||||
libfuzzer-sys = "0.4.7"
|
||||
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
|
||||
# TODO: update to 0.13.1+
|
||||
linux-loader = { git = "https://github.com/rust-vmm/linux-loader", branch = "main", features = [
|
||||
"bzimage",
|
||||
"elf",
|
||||
"pe",
|
||||
] }
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
mshv-bindings = "0.5.2"
|
||||
net_util = { path = "../net_util" }
|
||||
|
|
|
|||
|
|
@ -339,6 +339,18 @@ pub enum Error {
|
|||
|
||||
#[error("Error locking disk images: Another instance likely holds a lock")]
|
||||
LockingError(#[source] DeviceManagerError),
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Fw Cfg missing kernel")]
|
||||
MissingFwCfgKernelFile(#[source] io::Error),
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Fw Cfg missing initramfs")]
|
||||
MissingFwCfgInitramfs(#[source] io::Error),
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Fw Cfg missing kernel cmdline")]
|
||||
MissingFwCfgCmdline,
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
|
|
@ -784,6 +796,72 @@ impl Vm {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fn populate_fw_cfg(
|
||||
device_manager: &Arc<Mutex<DeviceManager>>,
|
||||
config: &Arc<Mutex<VmConfig>>,
|
||||
) -> Result<()> {
|
||||
let kernel = config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.payload
|
||||
.as_ref()
|
||||
.map(|p| p.kernel.as_ref().map(File::open))
|
||||
.unwrap_or_default()
|
||||
.transpose()
|
||||
.map_err(Error::MissingFwCfgKernelFile)?;
|
||||
if let Some(kernel_file) = kernel {
|
||||
device_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.fw_cfg()
|
||||
.expect("fw_cfg device must be present")
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_kernel_data(&kernel_file)
|
||||
.map_err(Error::MissingFwCfgKernelFile)?
|
||||
}
|
||||
let cmdline = Vm::generate_cmdline(
|
||||
config.lock().unwrap().payload.as_ref().unwrap(),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
device_manager,
|
||||
)
|
||||
.map_err(|_| Error::MissingFwCfgCmdline)?
|
||||
.as_cstring()
|
||||
.map_err(|_| Error::MissingFwCfgCmdline)?;
|
||||
device_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.fw_cfg()
|
||||
.expect("fw_cfg device must be present")
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_kernel_cmdline(cmdline);
|
||||
let initramfs = config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.payload
|
||||
.as_ref()
|
||||
.map(|p| p.initramfs.as_ref().map(File::open))
|
||||
.unwrap_or_default()
|
||||
.transpose()
|
||||
.map_err(Error::MissingFwCfgInitramfs)?;
|
||||
// We measure the initramfs when running Oak Containers in SNP mode (initramfs = Stage1)
|
||||
// o/w use Stage0 to launch cloud disk images
|
||||
if let Some(initramfs_file) = initramfs {
|
||||
device_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.fw_cfg()
|
||||
.expect("fw_cfg device must be present")
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_initramfs_data(&initramfs_file)
|
||||
.map_err(Error::MissingFwCfgInitramfs)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_numa_nodes(
|
||||
configs: Option<Vec<NumaConfig>>,
|
||||
memory_manager: &Arc<Mutex<MemoryManager>>,
|
||||
|
|
@ -2272,6 +2350,9 @@ impl Vm {
|
|||
};
|
||||
current_state.valid_transition(new_state)?;
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
Self::populate_fw_cfg(&self.device_manager, &self.config)?;
|
||||
|
||||
// Do earlier to parallelise with loading kernel
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
cfg_if::cfg_if! {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue