mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
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:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -531,6 +531,7 @@ dependencies = [
|
|||||||
"event_monitor",
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
|
"linux-loader",
|
||||||
"log",
|
"log",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"pci",
|
"pci",
|
||||||
@@ -1143,8 +1144,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-loader"
|
name = "linux-loader"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/rust-vmm/linux-loader?branch=main#d5f39c09d59c8f50d5313b78ce4de511b12d1848"
|
||||||
checksum = "870c3814345f050991f99869417779f6062542bcf4ed81db7a1b926ad1306638"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ members = [
|
|||||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||||
kvm-bindings = "0.12.0"
|
kvm-bindings = "0.12.0"
|
||||||
kvm-ioctls = "0.22.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-bindings = "0.5.2"
|
||||||
mshv-ioctls = "0.5.2"
|
mshv-ioctls = "0.5.2"
|
||||||
seccompiler = "0.5.0"
|
seccompiler = "0.5.0"
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ byteorder = { workspace = true }
|
|||||||
event_monitor = { path = "../event_monitor" }
|
event_monitor = { path = "../event_monitor" }
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
|
linux-loader = { workspace = true, features = [
|
||||||
|
"bzimage",
|
||||||
|
"elf",
|
||||||
|
"pe",
|
||||||
|
], optional = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
num_enum = "0.7.2"
|
num_enum = "0.7.2"
|
||||||
pci = { path = "../pci" }
|
pci = { path = "../pci" }
|
||||||
@@ -38,6 +43,6 @@ arch = { path = "../arch" }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
fw_cfg = ["zerocopy"]
|
fw_cfg = ["linux-loader", "zerocopy"]
|
||||||
kvm = ["arch/kvm"]
|
kvm = ["arch/kvm"]
|
||||||
pvmemcontrol = []
|
pvmemcontrol = []
|
||||||
|
|||||||
@@ -14,12 +14,17 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::Result,
|
io::Result,
|
||||||
mem::size_of_val,
|
mem::{size_of, size_of_val},
|
||||||
os::unix::fs::FileExt,
|
os::unix::fs::FileExt,
|
||||||
sync::{Arc, Barrier},
|
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_device::BusDevice;
|
||||||
|
use vm_memory::ByteValued;
|
||||||
use vmm_sys_util::sock_ctrl_msg::IntoIovec;
|
use vmm_sys_util::sock_ctrl_msg::IntoIovec;
|
||||||
use zerocopy::{FromBytes, IntoBytes};
|
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_SIGNATURE: u16 = 0x00;
|
||||||
const FW_CFG_ID: u16 = 0x01;
|
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_FILE_DIR: u16 = 0x19;
|
||||||
const FW_CFG_KNOWN_ITEMS: usize = 0x20;
|
const FW_CFG_KNOWN_ITEMS: usize = 0x20;
|
||||||
|
|
||||||
@@ -183,6 +196,46 @@ impl FwCfg {
|
|||||||
Ok(())
|
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> {
|
fn read_content(content: &FwCfgContent, offset: u32, data: &mut [u8], size: u32) -> Option<u8> {
|
||||||
let start = offset as usize;
|
let start = offset as usize;
|
||||||
let end = start + size as usize;
|
let end = start + size as usize;
|
||||||
@@ -278,6 +331,11 @@ impl BusDevice for FwCfg {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use vmm_sys_util::tempfile::TempFile;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[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"] }
|
hypervisor = { path = "../hypervisor", features = ["mshv_emulator"] }
|
||||||
libc = "0.2.155"
|
libc = "0.2.155"
|
||||||
libfuzzer-sys = "0.4.7"
|
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" }
|
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||||
mshv-bindings = "0.5.2"
|
mshv-bindings = "0.5.2"
|
||||||
net_util = { path = "../net_util" }
|
net_util = { path = "../net_util" }
|
||||||
|
|||||||
@@ -339,6 +339,18 @@ pub enum Error {
|
|||||||
|
|
||||||
#[error("Error locking disk images: Another instance likely holds a lock")]
|
#[error("Error locking disk images: Another instance likely holds a lock")]
|
||||||
LockingError(#[source] DeviceManagerError),
|
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>;
|
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(
|
fn create_numa_nodes(
|
||||||
configs: Option<Vec<NumaConfig>>,
|
configs: Option<Vec<NumaConfig>>,
|
||||||
memory_manager: &Arc<Mutex<MemoryManager>>,
|
memory_manager: &Arc<Mutex<MemoryManager>>,
|
||||||
@@ -2272,6 +2350,9 @@ impl Vm {
|
|||||||
};
|
};
|
||||||
current_state.valid_transition(new_state)?;
|
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
|
// Do earlier to parallelise with loading kernel
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
|
|||||||
Reference in New Issue
Block a user