runtime-rs: ch: Enable initrd usage

Allow an initrd/initramfs image to be used with Cloud Hypervisor, which
is handled differently to the default rootfs image type.

Fixes: #6335.

Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
This commit is contained in:
James O. D. Hunt 2023-02-21 11:56:28 +00:00
parent fbee6c820e
commit 3483272bbd
4 changed files with 81 additions and 47 deletions

View File

@ -7,7 +7,7 @@ use crate::NamedHypervisorConfig;
use crate::VmConfig;
use crate::{
ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpuTopology, CpusConfig, MacAddr, MemoryConfig,
PayloadConfig, RngConfig, VsockConfig,
PayloadConfig, PmemConfig, RngConfig, VsockConfig,
};
use anyhow::{anyhow, Context, Result};
use kata_types::config::default::DEFAULT_CH_ENTROPY_SOURCE;
@ -20,6 +20,8 @@ use std::path::PathBuf;
// 1 MiB
const MIB: u64 = 1024 * 1024;
const PMEM_ALIGN_BYTES: u64 = 2 * MIB;
const DEFAULT_CH_MAX_PHYS_BITS: u8 = 46;
impl TryFrom<NamedHypervisorConfig> for VmConfig {
@ -31,14 +33,42 @@ impl TryFrom<NamedHypervisorConfig> for VmConfig {
let vsock_socket_path = n.vsock_socket_path;
let sandbox_path = n.sandbox_path;
let fs = n.shared_fs_devices;
let pmem = n.pmem_devices;
let cpus = CpusConfig::try_from(cfg.cpu_info)?;
let rng = RngConfig::try_from(cfg.machine_info)?;
// Note that PmemConfig replaces the PayloadConfig.initrd.
let payload = PayloadConfig::try_from((cfg.boot_info, kernel_params))?;
// Note how CH handles the different image types:
//
// - An image is specified in PmemConfig.
// - An initrd/initramfs is specified in PayloadConfig.
let boot_info = cfg.boot_info;
let use_initrd = !boot_info.initrd.is_empty();
let use_image = !boot_info.image.is_empty();
if use_initrd && use_image {
return Err(anyhow!("cannot specify image and initrd"));
}
if !use_initrd && !use_image {
return Err(anyhow!("missing boot file (no image or initrd)"));
}
let initrd = if use_initrd {
Some(PathBuf::from(boot_info.initrd.clone()))
} else {
None
};
let pmem = if use_initrd {
None
} else {
let pmem = PmemConfig::try_from(&boot_info)?;
Some(vec![pmem])
};
let payload = PayloadConfig::try_from((boot_info, kernel_params, initrd))?;
let serial = get_serial_cfg()?;
let console = get_console_cfg()?;
@ -90,13 +120,18 @@ impl TryFrom<MemoryInfo> for MemoryConfig {
.ok_or("failed to calculate max hotplug size for CH")
.map_err(|e| anyhow!(e))?;
let aligned_hotplug_size_bytes =
checked_next_multiple_of(hotplug_size_bytes, PMEM_ALIGN_BYTES)
.ok_or("cannot handle pmem alignment for CH")
.map_err(|e| anyhow!(e))?;
let cfg = MemoryConfig {
size: mem_bytes,
// Required
shared: true,
hotplug_size: Some(hotplug_size_bytes),
hotplug_size: Some(aligned_hotplug_size_bytes),
..Default::default()
};
@ -105,6 +140,20 @@ impl TryFrom<MemoryInfo> for MemoryConfig {
}
}
// Return the next multiple of 'multiple' starting from the specified value
// (aka align value to multiple).
//
// This is a temporary solution until checked_next_multiple_of() integer
// method is available in the rust language.
//
// See: https://github.com/rust-lang/rust/issues/88581
fn checked_next_multiple_of(value: u64, multiple: u64) -> Option<u64> {
match value.checked_rem(multiple) {
None => Some(value),
Some(r) => value.checked_add(multiple - r),
}
}
impl TryFrom<CpuInfo> for CpusConfig {
type Error = anyhow::Error;
@ -153,20 +202,23 @@ impl TryFrom<String> for CpuFeatures {
}
// The 2nd tuple element is the space separated kernel parameters list.
// The 3rd tuple element is an optional initramfs image to use.
// This cannot be created only from BootInfo since that contains the
// user-specified kernel parameters only.
impl TryFrom<(BootInfo, String)> for PayloadConfig {
impl TryFrom<(BootInfo, String, Option<PathBuf>)> for PayloadConfig {
type Error = anyhow::Error;
fn try_from(args: (BootInfo, String)) -> Result<Self, Self::Error> {
fn try_from(args: (BootInfo, String, Option<PathBuf>)) -> Result<Self, Self::Error> {
let b = args.0;
let cmdline = args.1;
let initramfs = args.2;
let kernel = PathBuf::from(b.kernel);
let payload = PayloadConfig {
kernel: Some(kernel),
cmdline: Some(cmdline),
initramfs,
..Default::default()
};
@ -195,6 +247,27 @@ impl TryFrom<MachineInfo> for RngConfig {
}
}
impl TryFrom<&BootInfo> for PmemConfig {
type Error = anyhow::Error;
fn try_from(b: &BootInfo) -> Result<Self, Self::Error> {
let file = if b.image.is_empty() {
return Err(anyhow!("CH PmemConfig only used for images"));
} else {
b.image.clone()
};
let cfg = PmemConfig {
file: PathBuf::from(file),
discard_writes: true,
..Default::default()
};
Ok(cfg)
}
}
fn get_serial_cfg() -> Result<ConsoleConfig> {
let cfg = ConsoleConfig {
file: None,

View File

@ -492,5 +492,4 @@ pub struct NamedHypervisorConfig {
pub cfg: HypervisorConfig,
pub shared_fs_devices: Option<Vec<FsConfig>>,
pub pmem_devices: Option<Vec<PmemConfig>>,
}

View File

@ -10,7 +10,7 @@ use crate::HybridVsockConfig;
use crate::VmmState;
use anyhow::{anyhow, Context, Result};
use ch_config::ch_api::cloud_hypervisor_vm_fs_add;
use ch_config::{FsConfig, PmemConfig};
use ch_config::FsConfig;
use safe_path::scoped_join;
use std::convert::TryFrom;
use std::path::PathBuf;
@ -148,41 +148,6 @@ impl CloudHypervisorInner {
Ok(None)
}
}
pub(crate) async fn get_boot_file(&mut self) -> Result<PathBuf> {
if let Some(ref config) = self.config {
let boot_info = &config.boot_info;
let file = if !boot_info.initrd.is_empty() {
boot_info.initrd.clone()
} else if !boot_info.image.is_empty() {
boot_info.image.clone()
} else {
return Err(anyhow!("missing boot file (no image or initrd)"));
};
Ok(PathBuf::from(file))
} else {
Err(anyhow!("no hypervisor config"))
}
}
pub(crate) async fn get_pmem_devices(&mut self) -> Result<Option<Vec<PmemConfig>>> {
let file = self.get_boot_file().await?;
let pmem_cfg = PmemConfig {
file,
size: None,
iommu: false,
discard_writes: true,
id: None,
pci_segment: 0,
};
let pmem_devices = vec![pmem_cfg];
Ok(Some(pmem_devices))
}
}
#[derive(Debug)]

View File

@ -96,8 +96,6 @@ impl CloudHypervisorInner {
async fn boot_vm(&mut self) -> Result<()> {
let shared_fs_devices = self.get_shared_fs_devices().await?;
let pmem_devices = self.get_pmem_devices().await?;
let socket = self
.api_socket
.as_ref()
@ -129,7 +127,6 @@ impl CloudHypervisorInner {
vsock_socket_path,
cfg: hypervisor_config.clone(),
shared_fs_devices,
pmem_devices,
};
let cfg = VmConfig::try_from(named_cfg)?;