devices: Add fw_cfg cli options
This allows us to enable/disable the fw_cfg device via the cli We can also now upload files into the guest vm using fw_cfg_items via the cli Signed-off-by: Alex Orozco <alexorozco@google.com>
This commit is contained in:
parent
971f552e09
commit
a70c1b38e7
6 changed files with 429 additions and 75 deletions
|
|
@ -433,6 +433,34 @@ impl FwCfg {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn populate_fw_cfg(
|
||||
&mut self,
|
||||
mem_size: Option<usize>,
|
||||
kernel: Option<File>,
|
||||
initramfs: Option<File>,
|
||||
cmdline: Option<std::ffi::CString>,
|
||||
fw_cfg_item_list: Option<Vec<FwCfgItem>>,
|
||||
) -> Result<()> {
|
||||
if let Some(mem_size) = mem_size {
|
||||
self.add_e820(mem_size)?
|
||||
}
|
||||
if let Some(kernel) = kernel {
|
||||
self.add_kernel_data(&kernel)?;
|
||||
}
|
||||
if let Some(cmdline) = cmdline {
|
||||
self.add_kernel_cmdline(cmdline);
|
||||
}
|
||||
if let Some(initramfs) = initramfs {
|
||||
self.add_initramfs_data(&initramfs)?
|
||||
}
|
||||
if let Some(fw_cfg_item_list) = fw_cfg_item_list {
|
||||
for item in fw_cfg_item_list {
|
||||
self.add_item(item)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_e820(&mut self, mem_size: usize) -> Result<()> {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let mut mem_regions = vec![
|
||||
|
|
|
|||
10
src/main.rs
10
src/main.rs
|
|
@ -27,6 +27,8 @@ use vmm::api::ApiAction;
|
|||
use vmm::config::{RestoreConfig, VmParams};
|
||||
use vmm::landlock::{Landlock, LandlockError};
|
||||
use vmm::vm_config;
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
use vmm::vm_config::FwCfgConfig;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use vmm::vm_config::SgxEpcConfig;
|
||||
use vmm::vm_config::{
|
||||
|
|
@ -269,6 +271,12 @@ fn get_cli_options_sorted(
|
|||
.help(FsConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
Arg::new("fw-cfg-config")
|
||||
.long("fw-cfg-config")
|
||||
.help(FwCfgConfig::SYNTAX)
|
||||
.num_args(1)
|
||||
.group("vm-payload"),
|
||||
#[cfg(feature = "guest_debug")]
|
||||
Arg::new("gdb")
|
||||
.long("gdb")
|
||||
|
|
@ -979,6 +987,8 @@ mod unit_tests {
|
|||
igvm: None,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_data: None,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fw_cfg_config: None,
|
||||
}),
|
||||
rate_limit_groups: None,
|
||||
disks: None,
|
||||
|
|
|
|||
|
|
@ -163,6 +163,10 @@ pub enum Error {
|
|||
/// Missing fields in Landlock rules
|
||||
#[error("Error parsing --landlock-rules: path/access field missing")]
|
||||
ParseLandlockMissingFields,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
/// Failed Parsing FwCfgItem config
|
||||
#[error("Error parsing --fw-cfg-config items")]
|
||||
ParseFwCfgItem(#[source] OptionParserError),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Error)]
|
||||
|
|
@ -318,6 +322,18 @@ pub enum ValidationError {
|
|||
/// Invalid block device serial length
|
||||
#[error("Block device serial length ({0}) exceeds maximum allowed length ({1})")]
|
||||
InvalidSerialLength(usize, usize),
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
/// FwCfg missing kernel
|
||||
#[error("Error --fw-cfg-config: missing --kernel")]
|
||||
FwCfgMissingKernel,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
/// FwCfg missing cmdline
|
||||
#[error("Error --fw-cfg-config: missing --cmdline")]
|
||||
FwCfgMissingCmdline,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
/// FwCfg missing initramfs
|
||||
#[error("Error --fw-cfg-config: missing --initramfs")]
|
||||
FwCfgMissingInitramfs,
|
||||
}
|
||||
|
||||
type ValidationResult<T> = std::result::Result<T, ValidationError>;
|
||||
|
|
@ -373,6 +389,8 @@ pub struct VmParams<'a> {
|
|||
pub host_data: Option<&'a str>,
|
||||
pub landlock_enable: bool,
|
||||
pub landlock_rules: Option<Vec<&'a str>>,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
pub fw_cfg_config: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> VmParams<'a> {
|
||||
|
|
@ -444,7 +462,9 @@ impl<'a> VmParams<'a> {
|
|||
let landlock_rules: Option<Vec<&str>> = args
|
||||
.get_many::<String>("landlock-rules")
|
||||
.map(|x| x.map(|y| y as &str).collect());
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
let fw_cfg_config: Option<&str> =
|
||||
args.get_one::<String>("fw-cfg-config").map(|x| x as &str);
|
||||
VmParams {
|
||||
cpus,
|
||||
memory,
|
||||
|
|
@ -486,6 +506,8 @@ impl<'a> VmParams<'a> {
|
|||
host_data,
|
||||
landlock_enable,
|
||||
landlock_rules,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fw_cfg_config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1603,6 +1625,102 @@ impl FsConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
impl FwCfgConfig {
|
||||
pub const SYNTAX: &'static str = "Boot params to pass to FW CFG device \
|
||||
\"e820=on|off,kernel=on|off,cmdline=on|off,initramfs=on|off,acpi_table=on|off, \
|
||||
items=[name0=<backing_file_path>,file0=<file_path>:name1=<backing_file_path>,file1=<file_path>]\"";
|
||||
pub fn parse(fw_cfg_config: &str) -> Result<Self> {
|
||||
let mut parser = OptionParser::new();
|
||||
parser
|
||||
.add("e820")
|
||||
.add("kernel")
|
||||
.add("cmdline")
|
||||
.add("initramfs")
|
||||
.add("acpi_table")
|
||||
.add("items");
|
||||
parser.parse(fw_cfg_config).map_err(Error::ParseFwCfgItem)?;
|
||||
let e820 = parser
|
||||
.convert::<Toggle>("e820")
|
||||
.map_err(Error::ParseFwCfgItem)?
|
||||
.unwrap_or(Toggle(true))
|
||||
.0;
|
||||
let kernel = parser
|
||||
.convert::<Toggle>("kernel")
|
||||
.map_err(Error::ParseFwCfgItem)?
|
||||
.unwrap_or(Toggle(true))
|
||||
.0;
|
||||
let cmdline = parser
|
||||
.convert::<Toggle>("cmdline")
|
||||
.map_err(Error::ParseFwCfgItem)?
|
||||
.unwrap_or(Toggle(true))
|
||||
.0;
|
||||
let initramfs = parser
|
||||
.convert::<Toggle>("initramfs")
|
||||
.map_err(Error::ParseFwCfgItem)?
|
||||
.unwrap_or(Toggle(true))
|
||||
.0;
|
||||
let acpi_tables = parser
|
||||
.convert::<Toggle>("acpi_table")
|
||||
.map_err(Error::ParseFwCfgItem)?
|
||||
.unwrap_or(Toggle(true))
|
||||
.0;
|
||||
let items = if parser.is_set("items") {
|
||||
Some(
|
||||
parser
|
||||
.convert::<FwCfgItemList>("items")
|
||||
.map_err(Error::ParseFwCfgItem)?
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(FwCfgConfig {
|
||||
e820,
|
||||
kernel,
|
||||
cmdline,
|
||||
initramfs,
|
||||
acpi_tables,
|
||||
items,
|
||||
})
|
||||
}
|
||||
pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> {
|
||||
let payload = vm_config.payload.as_ref().unwrap();
|
||||
if self.kernel && payload.kernel.is_none() {
|
||||
return Err(ValidationError::FwCfgMissingKernel);
|
||||
} else if self.cmdline && payload.cmdline.is_none() {
|
||||
return Err(ValidationError::FwCfgMissingCmdline);
|
||||
} else if self.initramfs && payload.initramfs.is_none() {
|
||||
return Err(ValidationError::FwCfgMissingInitramfs);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
impl FwCfgItem {
|
||||
pub fn parse(fw_cfg: &str) -> Result<Self> {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("name").add("file");
|
||||
parser.parse(fw_cfg).map_err(Error::ParseFwCfgItem)?;
|
||||
|
||||
let name =
|
||||
parser
|
||||
.get("name")
|
||||
.ok_or(Error::ParseFwCfgItem(OptionParserError::InvalidValue(
|
||||
"missing FwCfgItem name".to_string(),
|
||||
)))?;
|
||||
let file = parser
|
||||
.get("file")
|
||||
.map(PathBuf::from)
|
||||
.ok_or(Error::ParseFwCfgItem(OptionParserError::InvalidValue(
|
||||
"missing FwCfgItem file path".to_string(),
|
||||
)))?;
|
||||
Ok(FwCfgItem { name, file })
|
||||
}
|
||||
}
|
||||
|
||||
impl PmemConfig {
|
||||
pub const SYNTAX: &'static str = "Persistent memory parameters \
|
||||
\"file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,\
|
||||
|
|
@ -2661,6 +2779,14 @@ impl VmConfig {
|
|||
disks = Some(disk_config_list);
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
let fw_cfg_config = if let Some(fw_cfg_config_str) = vm_params.fw_cfg_config {
|
||||
let fw_cfg_config = FwCfgConfig::parse(fw_cfg_config_str)?;
|
||||
Some(fw_cfg_config)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut net: Option<Vec<NetConfig>> = None;
|
||||
if let Some(net_list) = &vm_params.net {
|
||||
let mut net_config_list = Vec::new();
|
||||
|
|
@ -2797,6 +2923,8 @@ impl VmConfig {
|
|||
igvm: vm_params.igvm.map(PathBuf::from),
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_data: vm_params.host_data.map(|s| s.to_string()),
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fw_cfg_config,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
@ -3939,6 +4067,8 @@ mod tests {
|
|||
host_data: Some(
|
||||
"243eb7dc1a21129caa91dcbb794922b933baecb5823a377eb431188673288c07".to_string(),
|
||||
),
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fw_cfg_config: None,
|
||||
}),
|
||||
rate_limit_groups: None,
|
||||
disks: None,
|
||||
|
|
@ -4556,6 +4686,8 @@ mod tests {
|
|||
igvm: None,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_data: Some("".to_string()),
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fw_cfg_config: None,
|
||||
});
|
||||
config_with_no_host_data.validate().unwrap_err();
|
||||
|
||||
|
|
@ -4570,6 +4702,8 @@ mod tests {
|
|||
igvm: None,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_data: None,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fw_cfg_config: None,
|
||||
});
|
||||
valid_config_with_no_host_data.validate().unwrap();
|
||||
|
||||
|
|
@ -4586,6 +4720,8 @@ mod tests {
|
|||
host_data: Some(
|
||||
"243eb7dc1a21129caa91dcbb794922b933baecb5823a377eb43118867328".to_string(),
|
||||
),
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fw_cfg_config: None,
|
||||
});
|
||||
config_with_invalid_host_data.validate().unwrap_err();
|
||||
}
|
||||
|
|
@ -4617,4 +4753,49 @@ mod tests {
|
|||
);
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fn test_fw_cfg_config_item_list_parsing() -> Result<()> {
|
||||
// Empty list
|
||||
FwCfgConfig::parse("items=[]").unwrap_err();
|
||||
// Missing closing bracket
|
||||
FwCfgConfig::parse("items=[name=opt/org.test/fw_cfg_test_item,file=/tmp/fw_cfg_test_item")
|
||||
.unwrap_err();
|
||||
// Single Item
|
||||
assert_eq!(
|
||||
FwCfgConfig::parse(
|
||||
"items=[name=opt/org.test/fw_cfg_test_item,file=/tmp/fw_cfg_test_item]"
|
||||
)?,
|
||||
FwCfgConfig {
|
||||
items: Some(FwCfgItemList {
|
||||
item_list: vec![FwCfgItem {
|
||||
name: "opt/org.test/fw_cfg_test_item".to_string(),
|
||||
file: PathBuf::from("/tmp/fw_cfg_test_item"),
|
||||
}]
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
// Multiple Items
|
||||
assert_eq!(
|
||||
FwCfgConfig::parse(
|
||||
"items=[name=opt/org.test/fw_cfg_test_item,file=/tmp/fw_cfg_test_item:name=opt/org.test/fw_cfg_test_item2,file=/tmp/fw_cfg_test_item2]"
|
||||
)?,
|
||||
FwCfgConfig {
|
||||
items: Some(FwCfgItemList {
|
||||
item_list: vec![FwCfgItem {
|
||||
name: "opt/org.test/fw_cfg_test_item".to_string(),
|
||||
file: PathBuf::from("/tmp/fw_cfg_test_item"),
|
||||
},
|
||||
FwCfgItem {
|
||||
name: "opt/org.test/fw_cfg_test_item2".to_string(),
|
||||
file: PathBuf::from("/tmp/fw_cfg_test_item2"),
|
||||
}]
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2391,6 +2391,8 @@ mod unit_tests {
|
|||
igvm: None,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_data: None,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fw_cfg_config: None,
|
||||
}),
|
||||
rate_limit_groups: None,
|
||||
disks: None,
|
||||
|
|
|
|||
206
vmm/src/vm.rs
206
vmm/src/vm.rs
|
|
@ -34,6 +34,8 @@ use arch::PciSpaceInfo;
|
|||
use arch::{get_host_cpu_phys_bits, EntryPoint, NumaNode, NumaNodes};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use devices::interrupt_controller;
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
use devices::legacy::fw_cfg::FwCfgItem;
|
||||
use devices::AcpiNotificationFlags;
|
||||
#[cfg(all(target_arch = "aarch64", feature = "guest_debug"))]
|
||||
use gdbstub_arch::aarch64::reg::AArch64CoreRegs as CoreRegs;
|
||||
|
|
@ -91,6 +93,8 @@ use crate::migration::get_vm_snapshot;
|
|||
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
|
||||
use crate::migration::url_to_file;
|
||||
use crate::migration::{url_to_path, SNAPSHOT_CONFIG_FILE, SNAPSHOT_STATE_FILE};
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
use crate::vm_config::FwCfgConfig;
|
||||
use crate::vm_config::{
|
||||
DeviceConfig, DiskConfig, FsConfig, HotplugMethod, NetConfig, NumaConfig, PayloadConfig,
|
||||
PmemConfig, UserDeviceConfig, VdpaConfig, VmConfig, VsockConfig,
|
||||
|
|
@ -359,6 +363,18 @@ pub enum Error {
|
|||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Error creating acpi tables")]
|
||||
CreatingAcpiTables(#[source] io::Error),
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Error adding fw_cfg item")]
|
||||
AddingFwCfgItem(#[source] io::Error),
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Error populating fw_cfg")]
|
||||
ErrorPopulatingFwCfg(#[source] io::Error),
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Error using fw_cfg while disabled")]
|
||||
FwCfgDisabled,
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
|
|
@ -741,11 +757,22 @@ impl Vm {
|
|||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
device_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.create_fw_cfg_device()
|
||||
.map_err(Error::DeviceManager)?;
|
||||
{
|
||||
let fw_cfg_config = config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.payload
|
||||
.as_ref()
|
||||
.map(|p| p.fw_cfg_config.is_some())
|
||||
.unwrap_or(false);
|
||||
if fw_cfg_config {
|
||||
device_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.create_fw_cfg_device()
|
||||
.map_err(Error::DeviceManager)?;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
let kernel = config
|
||||
|
|
@ -806,76 +833,85 @@ impl Vm {
|
|||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
fn populate_fw_cfg(
|
||||
fw_cfg_config: &FwCfgConfig,
|
||||
device_manager: &Arc<Mutex<DeviceManager>>,
|
||||
config: &Arc<Mutex<VmConfig>>,
|
||||
) -> Result<()> {
|
||||
device_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.fw_cfg()
|
||||
.expect("fw_cfg device must be present")
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_e820(config.lock().unwrap().memory.size as usize)
|
||||
.map_err(Error::CreatingE820Map)?;
|
||||
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 mut e820_option: Option<usize> = None;
|
||||
if fw_cfg_config.e820 {
|
||||
e820_option = Some(config.lock().unwrap().memory.size as usize);
|
||||
}
|
||||
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
|
||||
let mut kernel_option: Option<File> = None;
|
||||
if fw_cfg_config.kernel {
|
||||
let kernel = config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.fw_cfg()
|
||||
.expect("fw_cfg device must be present")
|
||||
.payload
|
||||
.as_ref()
|
||||
.map(|p| p.kernel.as_ref().map(File::open))
|
||||
.unwrap_or_default()
|
||||
.transpose()
|
||||
.map_err(Error::MissingFwCfgKernelFile)?;
|
||||
kernel_option = kernel;
|
||||
}
|
||||
let mut cmdline_option: Option<std::ffi::CString> = None;
|
||||
if fw_cfg_config.cmdline {
|
||||
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)?;
|
||||
cmdline_option = Some(cmdline);
|
||||
}
|
||||
let mut initramfs_option: Option<File> = None;
|
||||
if fw_cfg_config.initramfs {
|
||||
let initramfs = config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_initramfs_data(&initramfs_file)
|
||||
.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
|
||||
initramfs_option = initramfs;
|
||||
}
|
||||
let mut fw_cfg_item_list_option: Option<Vec<FwCfgItem>> = None;
|
||||
if let Some(fw_cfg_files) = &fw_cfg_config.items {
|
||||
let mut fw_cfg_item_list = vec![];
|
||||
for fw_cfg_file in fw_cfg_files.item_list.clone() {
|
||||
fw_cfg_item_list.push(FwCfgItem {
|
||||
name: fw_cfg_file.name,
|
||||
content: devices::legacy::fw_cfg::FwCfgContent::File(
|
||||
0,
|
||||
File::open(fw_cfg_file.file).map_err(Error::AddingFwCfgItem)?,
|
||||
),
|
||||
});
|
||||
}
|
||||
fw_cfg_item_list_option = Some(fw_cfg_item_list);
|
||||
}
|
||||
|
||||
let device_manager_binding = device_manager.lock().unwrap();
|
||||
let Some(fw_cfg) = device_manager_binding.fw_cfg() else {
|
||||
return Err(Error::FwCfgDisabled);
|
||||
};
|
||||
|
||||
fw_cfg
|
||||
.lock()
|
||||
.unwrap()
|
||||
.populate_fw_cfg(
|
||||
e820_option,
|
||||
kernel_option,
|
||||
initramfs_option,
|
||||
cmdline_option,
|
||||
fw_cfg_item_list_option,
|
||||
)
|
||||
.map_err(Error::ErrorPopulatingFwCfg)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -2370,15 +2406,37 @@ impl Vm {
|
|||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
{
|
||||
Self::populate_fw_cfg(&self.device_manager, &self.config)?;
|
||||
let tpm_enabled = self.config.lock().unwrap().tpm.is_some();
|
||||
crate::acpi::create_acpi_tables_for_fw_cfg(
|
||||
&self.device_manager,
|
||||
&self.cpu_manager,
|
||||
&self.memory_manager,
|
||||
&self.numa_nodes,
|
||||
tpm_enabled,
|
||||
)?
|
||||
let fw_cfg_enabled = self
|
||||
.config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.payload
|
||||
.as_ref()
|
||||
.map(|p| p.fw_cfg_config.is_some())
|
||||
.unwrap_or(false);
|
||||
if fw_cfg_enabled {
|
||||
let fw_cfg_config = self
|
||||
.config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.payload
|
||||
.as_ref()
|
||||
.map(|p| p.fw_cfg_config.clone())
|
||||
.unwrap_or_default()
|
||||
.ok_or(Error::VmMissingConfig)?;
|
||||
Self::populate_fw_cfg(&fw_cfg_config, &self.device_manager, &self.config)?;
|
||||
|
||||
if fw_cfg_config.acpi_tables {
|
||||
let tpm_enabled = self.config.lock().unwrap().tpm.is_some();
|
||||
crate::acpi::create_acpi_tables_for_fw_cfg(
|
||||
&self.device_manager,
|
||||
&self.cpu_manager,
|
||||
&self.memory_manager,
|
||||
&self.numa_nodes,
|
||||
tpm_enabled,
|
||||
)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do earlier to parallelise with loading kernel
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
//
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::path::PathBuf;
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
use std::str::FromStr;
|
||||
use std::{fs, result};
|
||||
|
||||
use net_util::MacAddr;
|
||||
|
|
@ -699,6 +701,79 @@ pub struct PayloadConfig {
|
|||
#[cfg(feature = "sev_snp")]
|
||||
#[serde(default)]
|
||||
pub host_data: Option<String>,
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
pub fw_cfg_config: Option<FwCfgConfig>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct FwCfgConfig {
|
||||
pub e820: bool,
|
||||
pub kernel: bool,
|
||||
pub cmdline: bool,
|
||||
pub initramfs: bool,
|
||||
pub acpi_tables: bool,
|
||||
pub items: Option<FwCfgItemList>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
impl Default for FwCfgConfig {
|
||||
fn default() -> Self {
|
||||
FwCfgConfig {
|
||||
e820: true,
|
||||
kernel: true,
|
||||
cmdline: true,
|
||||
initramfs: true,
|
||||
acpi_tables: true,
|
||||
items: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct FwCfgItemList {
|
||||
#[serde(default)]
|
||||
pub item_list: Vec<FwCfgItem>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct FwCfgItem {
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub file: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
pub enum FwCfgItemError {
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
impl FromStr for FwCfgItemList {
|
||||
type Err = FwCfgItemError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let body = s
|
||||
.trim()
|
||||
.strip_prefix('[')
|
||||
.and_then(|s| s.strip_suffix(']'))
|
||||
.ok_or_else(|| FwCfgItemError::InvalidValue(s.to_string()))?;
|
||||
|
||||
let mut fw_cfg_items: Vec<FwCfgItem> = vec![];
|
||||
let items: Vec<&str> = body.split(':').collect();
|
||||
for item in items {
|
||||
fw_cfg_items.push(
|
||||
FwCfgItem::parse(item)
|
||||
.map_err(|_| FwCfgItemError::InvalidValue(item.to_string()))?,
|
||||
);
|
||||
}
|
||||
Ok(FwCfgItemList {
|
||||
item_list: fw_cfg_items,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for PayloadConfig {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue