Skip to content

openhcl, vm: Support loading static elf files into VTL0 #1379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion openhcl/underhill_core/src/loader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ pub enum LoadKind {
Uefi,
Pcat,
Linux,
StaticElf,
}

impl From<LoadKind> for FirmwareType {
fn from(value: LoadKind) -> Self {
match value {
LoadKind::None | LoadKind::Linux => FirmwareType::None,
LoadKind::None | LoadKind::Linux | LoadKind::StaticElf => FirmwareType::None,
LoadKind::Uefi => FirmwareType::Uefi,
LoadKind::Pcat => FirmwareType::Pcat,
}
Expand Down Expand Up @@ -117,6 +118,16 @@ pub fn load(
tracing::info!("loading nothing into VTL0");
VpContext::Vbs(Vec::new())
}
LoadKind::StaticElf => {
tracing::info!("loading static ELF into VTL0");

// Static ELF image is already loaded into guest memory.
let static_elf_info = vtl0_info
.supports_static_elf
.as_ref()
.ok_or(Error::LinuxSupport)?;
static_elf_info.vp_context.clone()
}
LoadKind::Uefi => {
tracing::info!("loading UEFI into VTL0");
// UEFI image is already loaded into guest memory, so only the
Expand Down
51 changes: 51 additions & 0 deletions openhcl/underhill_core/src/loader/vtl0_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,20 @@ pub struct LinuxInfo {
pub command_line: Option<CString>,
}

#[derive(Debug)]
pub struct StaticElfInfo {
/// The region of memory used by the static ELF.
pub elf_range: MemoryRange,
/// The VP context for the static ELF.
pub vp_context: VpContext,
}

#[derive(Debug)]
pub struct MeasuredVtl0Info {
pub supports_pcat: bool,
pub supports_uefi: Option<UefiInfo>,
pub supports_linux: Option<LinuxInfo>,
pub supports_static_elf: Option<StaticElfInfo>,
}

impl MeasuredVtl0Info {
Expand Down Expand Up @@ -155,6 +164,35 @@ impl MeasuredVtl0Info {
None
};

let supports_static_elf = if measured_config.supported_vtl0.static_elf_supported() {
let static_elf = &measured_config.static_elf;
let vp_context = match static_elf.vp_context.pages() {
Some((vtl0_vp_context_page_base, vtl0_vp_context_page_count)) => {
assert!(vtl0_vp_context_page_base != 0);
assert_eq!(vtl0_vp_context_page_count, 1);

let mut vtl0_vp_context_raw: Vec<u8> = vec![0; HV_PAGE_SIZE as usize];
gm.read_at(
vtl0_vp_context_page_base * HV_PAGE_SIZE,
vtl0_vp_context_raw.as_mut_slice(),
)
.map_err(Error::GuestMemoryAccess)?;
config_pages.push(vtl0_vp_context_page_base);

parse_vtl0_vp_context(vtl0_vp_context_raw)?
}
None => VpContext::Vbs(Vec::new()),
};

Some(StaticElfInfo {
elf_range: memory_range_from_page_region(&static_elf.region)
.ok_or(Error::UefiFirmwareRegion)?,
vp_context,
})
} else {
None
};

// Clear measured info from VTL0 memory.
gm.zero_range(
&PagedRange::new(0, config_pages.len() * HV_PAGE_SIZE as usize, &config_pages)
Expand All @@ -166,6 +204,7 @@ impl MeasuredVtl0Info {
supports_pcat,
supports_uefi,
supports_linux,
supports_static_elf,
})
}

Expand Down Expand Up @@ -200,6 +239,18 @@ impl MeasuredVtl0Info {
}
}

if let Some(static_elf) = &self.supports_static_elf {
if load_kind != LoadKind::StaticElf {
// Clear out the memory used by the static ELF.
gm.fill_at(
static_elf.elf_range.start(),
0,
static_elf.elf_range.len() as usize,
)
.map_err(Error::GuestMemoryAccess)?;
}
}

Ok(())
}
}
Expand Down
3 changes: 2 additions & 1 deletion openhcl/underhill_core/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ pub struct UnderhillEnvCfg {
/// Force load the specified image in VTL0. The image must support the
/// option specified.
///
/// Valid options are "pcat, uefi, linux".
/// Valid options are "pcat, uefi, linux, static_elf".
pub force_load_vtl0_image: Option<String>,
/// Use the user-mode NVMe driver.
pub nvme_vfio: bool,
Expand Down Expand Up @@ -1674,6 +1674,7 @@ async fn new_underhill_vm(
"pcat" => LoadKind::Pcat,
"uefi" => LoadKind::Uefi,
"linux" => LoadKind::Linux,
"static_elf" => LoadKind::StaticElf,
_ => anyhow::bail!("unexpected force load vtl0 type {kind}"),
}
} else {
Expand Down
98 changes: 98 additions & 0 deletions vm/loader/igvmfilegen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ use igvmfilegen_config::LinuxImage;
use igvmfilegen_config::ResourceType;
use igvmfilegen_config::Resources;
use igvmfilegen_config::SnpInjectionType;
use igvmfilegen_config::StaticElfImage;
use igvmfilegen_config::UefiConfigType;
use loader::importer::Aarch64Register;
use loader::importer::GuestArch;
use loader::importer::GuestArchKind;
use loader::importer::ImageLoad;
use loader::importer::X86Register;
use loader::linux::InitrdConfig;
use loader::linux::StaticElfLoadInfo;
use loader::paravisor::CommandLineType;
use loader::paravisor::Vtl0Config;
use loader::paravisor::Vtl0Linux;
Expand Down Expand Up @@ -411,6 +413,17 @@ trait IgvmfilegenRegister: IgvmLoaderRegister + 'static {
F: std::io::Read + std::io::Seek,
Self: GuestArch;

fn load_static_elf<F>(
importer: &mut impl ImageLoad<Self>,
image: &mut F,
minimum_start_address: u64,
load_offset: u64,
assume_pic: bool,
) -> Result<StaticElfLoadInfo, loader::linux::Error>
where
F: std::io::Read + std::io::Seek,
Self: GuestArch;

fn load_openhcl<F>(
importer: &mut dyn ImageLoad<Self>,
kernel_image: &mut F,
Expand Down Expand Up @@ -453,6 +466,26 @@ impl IgvmfilegenRegister for X86Register {
)
}

fn load_static_elf<F>(
importer: &mut impl ImageLoad<Self>,
image: &mut F,
minimum_start_address: u64,
load_offset: u64,
assume_pic: bool,
) -> Result<StaticElfLoadInfo, loader::linux::Error>
where
F: std::io::Read + std::io::Seek,
Self: GuestArch,
{
loader::linux::load_static_elf_x64(
importer,
image,
minimum_start_address,
load_offset,
assume_pic,
)
}

fn load_openhcl<F>(
importer: &mut dyn ImageLoad<Self>,
kernel_image: &mut F,
Expand Down Expand Up @@ -509,6 +542,26 @@ impl IgvmfilegenRegister for Aarch64Register {
)
}

fn load_static_elf<F>(
importer: &mut impl ImageLoad<Self>,
image: &mut F,
minimum_start_address: u64,
load_offset: u64,
assume_pic: bool,
) -> Result<StaticElfLoadInfo, loader::linux::Error>
where
F: std::io::Read + std::io::Seek,
Self: GuestArch,
{
loader::linux::load_static_elf_arm64(
importer,
image,
minimum_start_address,
load_offset,
assume_pic,
)
}

fn load_openhcl<F>(
importer: &mut dyn ImageLoad<Self>,
kernel_image: &mut F,
Expand Down Expand Up @@ -561,6 +614,7 @@ fn load_image<'a, R: IgvmfilegenRegister + GuestArch + 'static>(
memory_page_count,
uefi,
ref linux,
ref static_elf,
} => {
if uefi && linux.is_some() {
anyhow::bail!("cannot include both UEFI and Linux images in OpenHCL image");
Expand Down Expand Up @@ -614,6 +668,7 @@ fn load_image<'a, R: IgvmfilegenRegister + GuestArch + 'static>(
supports_pcat: loader.loader().arch() == GuestArchKind::X86_64,
supports_uefi: Some((load_info, vp_context)),
supports_linux: None,
supports_static_elf: None,
}
} else if let Some(linux) = linux {
let load_info = load_linux(&mut loader.nested_loader(), linux, resources)?;
Expand All @@ -624,12 +679,29 @@ fn load_image<'a, R: IgvmfilegenRegister + GuestArch + 'static>(
command_line: &linux.command_line,
load_info,
}),
supports_static_elf: None,
}
} else if let Some(static_elf) = static_elf {
let mut inner_loader = loader.nested_loader();
let StaticElfLoadInfo { gpa, size } =
load_static_elf(&mut inner_loader, static_elf, resources)?;
let vp_context = inner_loader.take_vp_context();
Vtl0Config {
supports_pcat: false,
supports_uefi: None,
supports_linux: None,
supports_static_elf: Some(loader::paravisor::Vtl0StaticElf {
gpa,
size,
vp_context,
}),
}
} else {
Vtl0Config {
supports_pcat: false,
supports_uefi: None,
supports_linux: None,
supports_static_elf: None,
}
};

Expand Down Expand Up @@ -722,3 +794,29 @@ fn load_linux<R: IgvmfilegenRegister + GuestArch + 'static>(
.context("loading linux kernel and initrd")?;
Ok(load_info)
}

fn load_static_elf<R: IgvmfilegenRegister + GuestArch + 'static>(
loader: &mut IgvmVtlLoader<'_, R>,
static_elf: &StaticElfImage,
resources: &Resources,
) -> Result<StaticElfLoadInfo, anyhow::Error> {
let path = resources
.get(ResourceType::StaticElf)
.expect("validated present");
let mut image = fs_err::File::open(path)
.context(format!("reading vtl0 kernel image at {}", path.display()))?;
let StaticElfImage {
start_address: minimum_start_address,
load_offset,
assume_pic,
} = *static_elf;
let load_info = R::load_static_elf(
loader,
&mut image,
minimum_start_address as u64,
load_offset as u64,
assume_pic,
)
.context("loading static elf")?;
Ok(load_info)
}
13 changes: 13 additions & 0 deletions vm/loader/igvmfilegen_config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub enum Image {
/// Include the Linux kernel for loading into the guest.
#[serde(skip_serializing_if = "Option::is_none")]
linux: Option<LinuxImage>,
static_elf: Option<StaticElfImage>,
},
/// Load the Linux kernel.
/// TODO: Currently, this only works with underhill.
Expand All @@ -110,6 +111,17 @@ pub struct LinuxImage {
pub command_line: CString,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub struct StaticElfImage {
/// Load address.
pub start_address: usize,
/// Load offset.
pub load_offset: usize,
/// Whether the code is position independent or not.
pub assume_pic: bool,
}

impl Image {
/// Get the required resources for this image config.
pub fn required_resources(&self) -> Vec<ResourceType> {
Expand Down Expand Up @@ -194,6 +206,7 @@ impl Config {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum ResourceType {
StaticElf,
Uefi,
UnderhillKernel,
OpenhclBoot,
Expand Down
18 changes: 17 additions & 1 deletion vm/loader/loader_defs/src/paravisor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,17 @@ pub struct LinuxInfo {
pub command_line: PageRegionDescriptor,
}

/// Measured config about ELF loaded into VTL0.
#[repr(C)]
#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
#[cfg_attr(feature = "inspect", derive(Inspect))]
pub struct ElfInfo {
/// The memory the image was loaded into.
pub region: PageRegionDescriptor,
/// The VP context for the image.
pub vp_context: PageRegionDescriptor,
}

/// Measured config about UEFI loaded into VTL0.
#[repr(C)]
#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
Expand All @@ -315,8 +326,11 @@ pub struct SupportedVtl0LoadInfo {
/// This image supports Linux Direct.
#[bits(1)]
pub linux_direct_supported: bool,
/// This image supports a static ELF.
#[bits(1)]
pub static_elf_supported: bool,
/// Currently reserved.
#[bits(61)]
#[bits(60)]
pub reserved: u64,
}

Expand All @@ -336,6 +350,8 @@ pub struct ParavisorMeasuredVtl0Config {
pub uefi_info: UefiInfo,
/// If Linux is supported, information about Linux for VTL0.
pub linux_info: LinuxInfo,
/// If a static ELF is supported, information about it for VTL0.
pub static_elf: ElfInfo,
}

impl ParavisorMeasuredVtl0Config {
Expand Down
Loading