diff --git a/hack/dev/build.sh b/hack/dev/build.sh index 8a67c93..61984b9 100755 --- a/hack/dev/build.sh +++ b/hack/dev/build.sh @@ -83,8 +83,13 @@ if [ "${SKIP_VM_BUILD}" != "1" ]; then docker build --platform="${DOCKER_TARGET}" -t "${DOCKER_PREFIX}/sprout-initramfs-${TARGET_ARCH}:${DOCKER_TAG}" \ -f hack/dev/vm/Dockerfile.initramfs "${FINAL_DIR}" copy_from_image "${DOCKER_PREFIX}/sprout-initramfs-${TARGET_ARCH}" "initramfs" "${FINAL_DIR}/initramfs" - docker build --platform="${DOCKER_TARGET}" -t "${DOCKER_PREFIX}/sprout-xen-${TARGET_ARCH}:${DOCKER_TAG}" -f hack/dev/vm/Dockerfile.xen "${FINAL_DIR}" - copy_from_image "${DOCKER_PREFIX}/sprout-xen-${TARGET_ARCH}" "xen.efi" "${FINAL_DIR}/xen.efi" + + if [ -n "${SPROUT_XEN_EFI_OVERRIDE}" ]; then + cp "${SPROUT_XEN_EFI_OVERRIDE}" "${FINAL_DIR}/xen.efi" + else + docker build --platform="${DOCKER_TARGET}" -t "${DOCKER_PREFIX}/sprout-xen-${TARGET_ARCH}:${DOCKER_TAG}" -f hack/dev/vm/Dockerfile.xen "${FINAL_DIR}" + copy_from_image "${DOCKER_PREFIX}/sprout-xen-${TARGET_ARCH}" "xen.efi" "${FINAL_DIR}/xen.efi" + fi fi if [ "${SKIP_SPROUT_BUILD}" != "1" ]; then diff --git a/hack/dev/configs/edera.sprout.toml b/hack/dev/configs/edera.sprout.toml new file mode 100644 index 0000000..7bcb114 --- /dev/null +++ b/hack/dev/configs/edera.sprout.toml @@ -0,0 +1,15 @@ +version = 1 + +[extractors.boot.filesystem-device-match] +has-item = "\\EFI\\BOOT\\xen.efi" + +[actions.boot-edera] +edera.xen = "$boot\\EFI\\BOOT\\xen.efi" +edera.xen-options = ["clocksource=tsc", "smp=on", "smt=on", "ioapic_ack=new", "dom0_vcpus_pin=on", "spec-ctrl=gds-mit=no", "noreboot", "console=com1"] +edera.kernel = "$boot\\EFI\\BOOT\\kernel.efi" +edera.kernel-options = ["console=hvc0", "edera-xen=yes"] +edera.initrd = "$boot\\initramfs" + +[entries.edera] +title = "Boot Edera" +actions = ["boot-edera"] diff --git a/src/actions.rs b/src/actions.rs index 870331c..ea0a0e8 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use std::rc::Rc; pub mod chainload; +pub mod edera; pub mod print; #[cfg(feature = "splash")] @@ -18,6 +19,8 @@ pub struct ActionDeclaration { #[serde(default)] #[cfg(feature = "splash")] pub splash: Option, + #[serde(default, rename = "edera")] + pub edera: Option, } pub fn execute(context: Rc, name: impl AsRef) -> Result<()> { @@ -32,6 +35,8 @@ pub fn execute(context: Rc, name: impl AsRef) -> Result<()> } else if let Some(print) = &action.print { print::print(context.clone(), print)?; return Ok(()); + } else if let Some(edera) = &action.edera { + edera::edera(context.clone(), edera)?; } #[cfg(feature = "splash")] diff --git a/src/actions/edera.rs b/src/actions/edera.rs new file mode 100644 index 0000000..f308584 --- /dev/null +++ b/src/actions/edera.rs @@ -0,0 +1,97 @@ +use std::rc::Rc; + +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use uefi::Guid; + +use crate::{ + actions::{self, chainload::ChainloadConfiguration}, + context::SproutContext, + utils::{ + self, + media_loader::{ + MediaLoaderHandle, + constants::{ + XEN_EFI_CONFIG_MEDIA_GUID, XEN_EFI_KERNEL_MEDIA_GUID, XEN_EFI_RAMDISK_MEDIA_GUID, + }, + }, + }, +}; + +#[derive(Serialize, Deserialize, Default, Clone)] +pub struct EderaConfiguration { + pub xen: String, + pub kernel: String, + #[serde(default)] + pub initrd: Option, + #[serde(default, rename = "kernel-options")] + pub kernel_options: Vec, + #[serde(default, rename = "xen-options")] + pub xen_options: Vec, +} + +fn build_xen_config(configuration: &EderaConfiguration) -> String { + [ + "[global]".to_string(), + "default=sprout".to_string(), + "[sprout]".to_string(), + format!("options={}", configuration.xen_options.join(" ")), + format!("kernel=stub {}", configuration.kernel_options.join(" ")), + "".to_string(), // required or else the last line will be ignored + ] + .join("\n") +} + +fn register_media_loader_text(guid: Guid, what: &str, text: String) -> Result { + MediaLoaderHandle::register(guid, text.as_bytes().to_vec().into_boxed_slice()) + .context(format!("unable to register {} media loader", what)) /* */ +} + +fn register_media_loader_file( + context: &Rc, + guid: Guid, + what: &str, + path: &str, +) -> Result { + let path = context.stamp(path); + let content = utils::read_file_contents(context.root().loaded_image_path()?, &path) + .context(format!("unable to read {} file", what))?; + let handle = MediaLoaderHandle::register(guid, content.into_boxed_slice()) + .context(format!("unable to register {} media loader", what))?; + Ok(handle) +} + +pub fn edera(context: Rc, configuration: &EderaConfiguration) -> Result<()> { + let config = build_xen_config(configuration); + let config = register_media_loader_text(XEN_EFI_CONFIG_MEDIA_GUID, "config", config)?; + let kernel = register_media_loader_file( + &context, + XEN_EFI_KERNEL_MEDIA_GUID, + "kernel", + &configuration.kernel, + )?; + + let mut media_loaders = vec![config, kernel]; + + if let Some(ref initrd) = configuration.initrd { + let initrd = + register_media_loader_file(&context, XEN_EFI_RAMDISK_MEDIA_GUID, "initrd", initrd)?; + media_loaders.push(initrd); + } + + let result = actions::chainload::chainload( + context.clone(), + &ChainloadConfiguration { + path: configuration.xen.clone(), + options: vec![], + linux_initrd: None, + }, + ) + .context("unable to chainload to xen"); + + for media_loader in media_loaders { + media_loader.unregister()?; + } + + result +} diff --git a/src/utils/media_loader/constants.rs b/src/utils/media_loader/constants.rs index 9787642..33322ce 100644 --- a/src/utils/media_loader/constants.rs +++ b/src/utils/media_loader/constants.rs @@ -1,3 +1,7 @@ use uefi::{Guid, guid}; pub const LINUX_EFI_INITRD_MEDIA_GUID: Guid = guid!("5568e427-68fc-4f3d-ac74-ca555231cc68"); + +pub const XEN_EFI_CONFIG_MEDIA_GUID: Guid = guid!("bf61f458-a28e-46cd-93d7-07dac5e8cd66"); +pub const XEN_EFI_KERNEL_MEDIA_GUID: Guid = guid!("4010c8bf-6ced-40f5-a53f-e820aee8f34b"); +pub const XEN_EFI_RAMDISK_MEDIA_GUID: Guid = guid!("5db1fd01-c3cb-4812-b2ba-8791e52d4a89");