initial commit

This commit is contained in:
2025-10-01 16:45:04 -07:00
commit 17ca11f239
21 changed files with 610 additions and 0 deletions

36
.gitignore vendored Normal file
View File

@@ -0,0 +1,36 @@
/target
# Infrastructure
.env
.terraform
terraform.tfstate
terraform.tfstate.backup
aws-packer-config.json
override.install.sh
# Ignore the google cloud auth creds created in Actions workflows
gha-creds-*.json
crane
tok
# IDE
/.idea
/.vscode
# Benchmarks
/results
# cli markdown output
cli.md
# precommit hooks
.editorconfig
.markdownlint-cli2.jsonc
.pre-commit-config.yaml
.prettierrc
.shellcheckrc
# example outputs
/image-cache
/out

141
Cargo.lock generated Normal file
View File

@@ -0,0 +1,141 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bit_field"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]]
name = "cfg-if"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "ptr_meta"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "quote"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sprout"
version = "0.1.0"
dependencies = [
"uefi",
]
[[package]]
name = "syn"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "ucs2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79298e11f316400c57ec268f3c2c29ac3c4d4777687955cd3d4f3a35ce7eba"
dependencies = [
"bit_field",
]
[[package]]
name = "uefi"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7569ceafb898907ff764629bac90ac24ba4203c38c33ef79ee88c74aa35b11"
dependencies = [
"bitflags",
"cfg-if",
"log",
"ptr_meta",
"ucs2",
"uefi-macros",
"uefi-raw",
"uguid",
]
[[package]]
name = "uefi-macros"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3dad47b3af8f99116c0f6d4d669c439487d9aaf1c8d9480d686cda6f3a8aa23"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "uefi-raw"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cad96b8baaf1615d3fdd0f03d04a0b487d857c1b51b19dcbfe05e2e3c447b78"
dependencies = [
"bitflags",
"uguid",
]
[[package]]
name = "uguid"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab14ea9660d240e7865ce9d54ecdbd1cd9fa5802ae6f4512f093c7907e921533"
[[package]]
name = "unicode-ident"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"

8
Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "sprout"
version = "0.1.0"
edition = "2024"
[dependencies.uefi]
version = "0.35.0"
features = ["alloc"]

24
Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
# syntax=docker/dockerfile:1.7-labs
ARG RUST_PROFILE=release
ARG RUST_TARGET_SUBDIR=release
FROM --platform=$BUILDPLATFORM rustlang/rust:nightly-alpine@sha256:b8107fa66d3e5ad7f729d3347c7feedbd3f4b60b01006edce39eb6b994ff00bd AS build
RUN apk --no-cache add musl-dev busybox-static
ARG RUST_PROFILE
RUN adduser -S -s /bin/sh build
COPY \
--exclude=rust-toolchain.toml \
--chown=build:build \
. /build
WORKDIR /build
ARG TARGETPLATFORM
ARG RUST_TARGET_SUBDIR
RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ] || [ "${TARGETPLATFORM}" = "linux/x86_64" ]; then \
rustup target add x86_64-unknown-uefi; cargo build --bin sprout --profile "${RUST_PROFILE}" --target x86_64-unknown-uefi && \
cp "target/x86_64-unknown-uefi/${RUST_TARGET_SUBDIR}/sprout.efi" /sprout.efi; fi
RUN if [ "${TARGETPLATFORM}" = "linux/arm64" ] || [ "${TARGETPLATFORM}" = "linux/aarch64" ]; then \
rustup target add aarch64-unknown-uefi; cargo build --bin sprout --profile "${RUST_PROFILE}" --target aarch64-unknown-uefi && \
cp "target/aarch64-unknown-uefi/${RUST_TARGET_SUBDIR}/sprout.efi" /sprout.efi; fi
FROM scratch AS final
COPY --from=build /sprout.efi /sprout.efi

1
LICENSE Normal file
View File

@@ -0,0 +1 @@
This repository is proprietary and owned by Edera, Inc.

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# sprout
The Edera bootloader.

22
boot/Dockerfile Normal file
View File

@@ -0,0 +1,22 @@
FROM --platform=$BUILDPLATFORM debian:trixie@sha256:fd8f5a1df07b5195613e4b9a0b6a947d3772a151b81975db27d47f093f60c6e6 AS build
ARG BUILDPLATFORM
ARG EFI_NAME
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y \
parted dosfstools mtools && \
rm -rf /var/lib/apt/lists/*
WORKDIR /work
COPY sprout.efi /work/${EFI_NAME}.EFI
COPY kernel.efi /work/KERNEL.EFI
RUN truncate -s256MiB sprout.img && \
parted --script sprout.img mklabel gpt > /dev/null 2>&1 && \
parted --script sprout.img mkpart primary fat32 1MiB 100% > /dev/null 2>&1 && \
parted --script sprout.img set 1 esp on > /dev/null 2>&1 && \
mkfs.vfat -F32 -n EFI sprout.img && \
mmd -i sprout.img ::/EFI && \
mmd -i sprout.img ::/EFI/BOOT && \
mcopy -i sprout.img ${EFI_NAME}.EFI ::/EFI/BOOT/ && \
mcopy -i sprout.img KERNEL.EFI ::/EFI/BOOT/ && \
mv sprout.img /sprout.img
FROM scratch AS final
COPY --from=build /sprout.img /sprout.img

17
hack/autofix.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
set -e
cd "$(dirname "${0}")/.." || exit 1
NATIVE_ARCH="$(uname -m)"
[ "${NATIVE_ARCH}" = "arm64" ] && NATIVE_ARCH="aarch64"
[ "${NATIVE_ARCH}" = "amd64" ] && NATIVE_ARCH="x86_64"
if [ "$(uname)" != "Linux" ]; then
cargo clippy --workspace --fix --allow-dirty --allow-staged \
--target "${NATIVE_ARCH}-unknown-uefi"
else
cargo clippy --workspace --fix --allow-dirty --allow-staged
fi
./hack/format.sh

48
hack/boot.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/bin/sh
set -e
cd "$(dirname "${0}")/.." || exit 1
. "hack/common.sh"
./hack/build.sh "${TARGET_ARCH}" "${RUST_PROFILE}"
clear
set --
if [ "${TARGET_ARCH}" = "x86_64" ]; then
set -- "${@}" qemu-system-x86_64 -M q35
elif [ "${TARGET_ARCH}" = "aarch64" ]; then
set -- "${@}" qemu-system-aarch64 -M virt -cpu cortex-a57
fi
set -- "${@}" -smp 2 -m 4096
if [ "${NO_GRAPHICAL_BOOT}" = "1" ]; then
set -- "${@}" -nographic
else
set -- "${@}" -serial stdio -vga none -device "virtio-gpu,edid=on,xres=1024,yres=768"
fi
rm -f "${FINAL_DIR}/ovmf-boot.fd"
cp "${FINAL_DIR}/ovmf.fd" "${FINAL_DIR}/ovmf-boot.fd"
if [ "${TARGET_ARCH}" = "aarch64" ]; then
dd if=/dev/zero of="${FINAL_DIR}/ovmf-boot.fd" bs=1 count=1 seek=67108863 >/dev/null 2>&1
fi
# shellcheck disable=SC2086
set -- "${@}" \
-drive "if=pflash,file=${FINAL_DIR}/ovmf-boot.fd,format=raw,readonly=on" \
-device nvme,drive=disk1,serial=cafebabe
if [ "${DISK_BOOT}" = "1" ]; then
set -- "${@}" \
-drive "if=none,file=${FINAL_DIR}/sprout.img,format=raw,id=disk1,readonly=on"
else
set -- "${@}" \
-drive "if=none,file=fat:rw:${FINAL_DIR}/efi,format=raw,id=disk1"
fi
set -- "${@}" -name "sprout ${TARGET_ARCH}"
exec "${@}"

72
hack/build.sh Executable file
View File

@@ -0,0 +1,72 @@
#!/bin/sh
set -e
cd "$(dirname "${0}")/.." || exit 1
. "hack/common.sh"
EFI_NAME="BOOTX64"
if [ "${TARGET_ARCH}" = "aarch64" ]; then
EFI_NAME="BOOTAA64"
fi
echo "[build] ${TARGET_ARCH} ${RUST_PROFILE}"
if ! command -v docker >/dev/null 2>&1; then
echo "ERROR: docker is required to build sprout." >/dev/stderr
exit 1
fi
export DOCKER_CLI_HINTS="0"
if [ "${SKIP_CLEANUP}" != 1 ]; then
rm -rf "${FINAL_DIR}"
fi
mkdir -p "${FINAL_DIR}"
if [ "${SKIP_KERNEL_BUILD}" != "1" ] || [ "${SKIP_VM_BUILD}" != "1" ] || [ "${SKIP_SPROUT_BUILD}" != "1" ]; then
docker build -t "${DOCKER_PREFIX}/sprout-utils-copy:${DOCKER_TAG}" -f hack/utils/Dockerfile.copy hack
fi
if [ "${SKIP_KERNEL_BUILD}" != "1" ]; then
echo "[kernel build] ${TARGET_ARCH} ${RUST_PROFILE}"
docker build --platform="${DOCKER_TARGET}" -t "${DOCKER_PREFIX}/sprout-kernel-${TARGET_ARCH}:${DOCKER_TAG}" -f kernel/Dockerfile kernel
if [ "${KERNEL_BUILD_TAG}" = "1" ]; then
docker build --platform="${DOCKER_TARGET}" -t "${DOCKER_PREFIX}/sprout-kernel-build-${TARGET_ARCH}:${DOCKER_TAG}" -f kernel/Dockerfile --target
build kernel
fi
docker run --rm -i \
--mount="type=image,source=${DOCKER_PREFIX}/sprout-kernel-${TARGET_ARCH}:${DOCKER_TAG},target=/image" \
"${DOCKER_PREFIX}/sprout-utils-copy:${DOCKER_TAG}" cat /image/kernel.efi >"${FINAL_DIR}/kernel.efi"
fi
if [ "${SKIP_VM_BUILD}" != "1" ]; then
echo "[vm build] ${TARGET_ARCH} ${RUST_PROFILE}"
docker build --platform="${DOCKER_TARGET}" -t "${DOCKER_PREFIX}/sprout-ovmf-${TARGET_ARCH}:${DOCKER_TAG}" -f vm/Dockerfile.ovmf "${FINAL_DIR}"
docker run --rm -i \
--mount="type=image,source=${DOCKER_PREFIX}/sprout-ovmf-${TARGET_ARCH}:${DOCKER_TAG},target=/image" \
"${DOCKER_PREFIX}/sprout-utils-copy:${DOCKER_TAG}" cat /image/ovmf.fd >"${FINAL_DIR}/ovmf.fd"
fi
if [ "${SKIP_SPROUT_BUILD}" != "1" ]; then
echo "[sprout build] ${TARGET_ARCH} ${RUST_PROFILE}"
docker build --platform="${DOCKER_TARGET}" -t "${DOCKER_PREFIX}/sprout-${TARGET_ARCH}:${DOCKER_TAG}" --build-arg="RUST_TARGET_SUBDIR=${RUST_TARGET_SUBDIR}" -f Dockerfile .
docker run --rm -i \
--mount="type=image,source=${DOCKER_PREFIX}/sprout-${TARGET_ARCH}:${DOCKER_TAG},target=/image" \
"${DOCKER_PREFIX}/sprout-utils-copy:${DOCKER_TAG}" cat /image/sprout.efi >"${FINAL_DIR}/sprout.efi"
mkdir -p "${FINAL_DIR}/efi/EFI/BOOT"
cp "${FINAL_DIR}/sprout.efi" "${FINAL_DIR}/efi/EFI/BOOT/${EFI_NAME}.EFI"
if [ -f "${FINAL_DIR}/kernel.efi" ]; then
cp "${FINAL_DIR}/kernel.efi" "${FINAL_DIR}/efi/EFI/BOOT/KERNEL.EFI"
fi
fi
if [ "${SKIP_BOOT_BUILD}" != "1" ]; then
echo "[boot build] ${TARGET_ARCH} ${RUST_PROFILE}"
docker build --platform="${DOCKER_TARGET}" -t "${DOCKER_PREFIX}/sprout-boot-${TARGET_ARCH}:${DOCKER_TAG}" --build-arg "EFI_NAME=${EFI_NAME}" -f boot/Dockerfile "${FINAL_DIR}"
docker run --rm -i \
--mount="type=image,source=${DOCKER_PREFIX}/sprout-boot-${TARGET_ARCH}:${DOCKER_TAG},target=/image" \
"${DOCKER_PREFIX}/sprout-utils-copy:${DOCKER_TAG}" cat /image/sprout.img >"${FINAL_DIR}/sprout.img"
fi

20
hack/clean.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/sh
set -e
cd "$(dirname "${0}")/.." || exit 1
. "hack/common.sh"
delete_image() {
IMAGE="${1}"
docker image ls -q --no-trunc --filter "reference=${DOCKER_PREFIX}/${IMAGE}" | xargs -rn1 docker image rm
}
cargo clean || true
if command -v docker >/dev/null 2>&1; then
delete_image sprout-utils-copy || true
delete_image sprout-ovmf || true
delete_image sprout-x86_64 || true
delete_image sprout-aarch64 || true
fi

31
hack/common.sh Normal file
View File

@@ -0,0 +1,31 @@
#!/bin/sh
# shellcheck disable=SC2034
set -e
DOCKER_PREFIX="ghcr.io/edera-dev/sprout"
DEFAULT_RUST_PROFILE="release"
DEFAULT_DOCKER_TAG="latest"
[ -z "${TARGET_ARCH}" ] && TARGET_ARCH="${1}"
{ [ -z "${TARGET_ARCH}" ] || [ "${TARGET_ARCH}" = "native" ]; } && TARGET_ARCH="$(uname -m)"
[ -z "${RUST_PROFILE}" ] && RUST_PROFILE="${2}"
[ -z "${RUST_PROFILE}" ] && RUST_PROFILE="${DEFAULT_RUST_PROFILE}"
[ "${TARGET_ARCH}" = "arm64" ] && TARGET_ARCH="aarch64"
[ "${TARGET_ARCH}" = "amd64" ] && TARGET_ARCH="x86_64"
if [ "${TARGET_ARCH}" != "x86_64" ] && [ "${TARGET_ARCH}" != "aarch64" ]; then
echo "Unsupported Architecture: ${TARGET_ARCH}" >/dev/stderr
exit 1
fi
[ "${RUST_PROFILE}" = "debug" ] && RUST_PROFILE="dev"
RUST_TARGET_SUBDIR="${RUST_PROFILE}"
[ "${RUST_PROFILE}" = "dev" ] && RUST_TARGET_SUBDIR="debug"
RUST_TARGET="${TARGET_ARCH}-unknown-uefi"
[ -z "${DOCKER_TAG}" ] && DOCKER_TAG="${DEFAULT_DOCKER_TAG}"
DOCKER_TARGET="linux/${TARGET_ARCH}"
FINAL_DIR="target/final/${TARGET_ARCH}"

7
hack/format.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
cd "$(dirname "${0}")/.." || exit 1
cargo fmt --all
shfmt -w hack/*.sh

View File

@@ -0,0 +1 @@
FROM --platform=$BUILDPLATFORM debian:trixie@sha256:fd8f5a1df07b5195613e4b9a0b6a947d3772a151b81975db27d47f093f60c6e6

37
kernel/Dockerfile Normal file
View File

@@ -0,0 +1,37 @@
ARG KERNEL_SOURCE_URL=https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.16.9.tar.xz
ARG KERNEL_CHECKSUM=sha256:7ac8c8a3cf05476375deaaa85dfcee095a826ffe557b437f43774fc3b64ce58d
FROM --platform=$BUILDPLATFORM debian:trixie@sha256:fd8f5a1df07b5195613e4b9a0b6a947d3772a151b81975db27d47f093f60c6e6 AS buildenv
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y \
build-essential squashfs-tools python3-yaml \
patch diffutils sed mawk findutils zstd \
python3 python3-packaging curl rsync cpio \
flex bison pahole libssl-dev libelf-dev bc kmod && \
rm -rf /var/lib/apt/lists/*
ARG BUILDPLATFORM
RUN if [ "${BUILDPLATFORM}" = "linux/amd64" ] || [ "${BUILDPLATFORM}" = "linux/x86_64" ]; then \
apt-get update && apt-get install -y linux-headers-amd64 gcc-aarch64-linux-gnu && rm -rf /var/lib/apt/lists/*; fi
RUN if [ "${BUILDPLATFORM}" = "linux/arm64" ] || [ "${BUILDPLATFORM}" = "linux/aarch64" ]; then \
apt-get update && apt-get install -y linux-headers-arm64 gcc-x86-64-linux-gnu && rm -rf /var/lib/apt/lists/*; fi
RUN useradd -ms /bin/sh build
COPY --chown=build:build docker-build.sh /build/docker-build.sh
USER build
WORKDIR /build
FROM scratch AS source
ARG KERNEL_SOURCE_URL
ARG KERNEL_CHECKSUM
ADD --checksum=${KERNEL_CHECKSUM} ${KERNEL_SOURCE_URL} /src.tar.xz
FROM --platform=$BUILDPLATFORM buildenv AS build
COPY --from=source --chown=build:build /src.tar.xz /build/src.tar.xz
RUN mkdir /build/src && tar -C /build/src --strip-components=1 -xf /build/src.tar.xz && rm /build/src.tar.xz
ARG BUILDPLATFORM
ARG TARGETPLATFORM
ENV BUILDPLATFORM=${BUILDPLATFORM}
ENV TARGETPLATFORM=${TARGETPLATFORM}
WORKDIR /build/src
RUN /build/docker-build.sh
FROM scratch AS final
COPY --from=build /build/src/kernel.image /kernel.efi

42
kernel/docker-build.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/sh
set -e
TARGET_KARCH=""
TARGET_SARCH=""
MAYBE_CROSS_COMPILE=""
CURRENT_SARCH="$(uname -m)"
[ "${CURRENT_SARCH}" = "amd64" ] && CURRENT_SARCH="x86_64"
[ "${CURRENT_SARCH}" = "arm64" ] && CURRENT_SARCH="aarch64"
if [ "${TARGETPLATFORM}" = "linux/aarch64" ] || [ "${TARGETPLATFORM}" = "linux/arm64" ]; then
TARGET_KARCH="arm64"
TARGET_SARCH="aarch64"
if [ "${CURRENT_SARCH}" != "${TARGET_SARCH}" ]; then
MAYBE_CROSS_COMPILE="aarch64-linux-gnu-"
fi
elif [ "${TARGETPLATFORM}" = "linux/x86_64" ] || [ "${TARGETPLATFORM}" = "linux/amd64" ]; then
TARGET_KARCH="x86_64"
TARGET_SARCH="x86_64"
if [ "${CURRENT_SARCH}" != "${TARGET_SARCH}" ]; then
MAYBE_CROSS_COMPILE="x86_64-linux-gnu-"
fi
else
echo "Unknown platform: ${TARGETPLATFORM}" >/dev/stderr
exit 1
fi
make CROSS_COMPILE="${MAYBE_CROSS_COMPILE}" ARCH="${TARGET_KARCH}" defconfig
make CROSS_COMPILE="${MAYBE_CROSS_COMPILE}" ARCH="${TARGET_KARCH}" mod2yesconfig
./scripts/config -e DRM_VIRTIO_GPU
./scripts/config -e FRAMEBUFFER_CONSOLE
./scripts/config -e FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
make "-j$(nproc)" CROSS_COMPILE="${MAYBE_CROSS_COMPILE}" ARCH="${TARGET_KARCH}"
[ -f "arch/x86/boot/bzImage" ] && cp "arch/x86/boot/bzImage" kernel.image
[ -f "arch/arm64/boot/Image.gz" ] && gzip -d < "arch/arm64/boot/Image.gz" > kernel.image
exit 0

4
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "nightly"
components = ["rustfmt", "rust-std", "clippy"]
targets = ["x86_64-unknown-uefi", "aarch64-unknown-uefi"]

61
src/chainload.rs Normal file
View File

@@ -0,0 +1,61 @@
use uefi::{
CString16,
proto::device_path::{
DevicePath, LoadedImageDevicePath, PoolDevicePath,
text::{AllowShortcuts, DevicePathFromText, DisplayOnly},
},
};
fn text_to_device_path(path: &str) -> PoolDevicePath {
let path = CString16::try_from(path).expect("unable to convert path to CString16");
let device_path_from_text = uefi::boot::open_protocol_exclusive::<DevicePathFromText>(
uefi::boot::get_handle_for_protocol::<DevicePathFromText>()
.expect("no device path from text protocol"),
)
.expect("unable to open device path from text protocol");
device_path_from_text
.convert_text_to_device_path(&path)
.expect("unable to convert text to device path")
}
pub fn chainload(path: &str) {
let sprout_image = uefi::boot::image_handle();
let image_device_path_protocol =
uefi::boot::open_protocol_exclusive::<LoadedImageDevicePath>(sprout_image)
.expect("unable to open loaded image protocol");
let image_device_path: &DevicePath = &image_device_path_protocol;
let mut full_path = image_device_path
.node_iter()
.filter_map(|item| {
let item = item
.to_string(DisplayOnly(false), AllowShortcuts(false))
.expect("unable to convert device path to string");
if item.to_string().contains("(") {
Some(item)
} else {
None
}
})
.map(|item| item.to_string())
.collect::<Vec<_>>()
.join("/");
full_path.push('/');
full_path.push_str(path);
println!("chainload: {}", full_path);
let device_path = text_to_device_path(&full_path);
let image = uefi::boot::load_image(
sprout_image,
uefi::boot::LoadImageSource::FromDevicePath {
device_path: &device_path,
boot_policy: uefi::proto::BootPolicy::ExactMatch,
},
)
.expect("failed to load image");
uefi::boot::start_image(image).expect("failed to start image");
panic!("chainloaded image exited");
}

10
src/main.rs Normal file
View File

@@ -0,0 +1,10 @@
#![feature(uefi_std)]
pub mod chainload;
pub mod setup;
const CHAINLOADER_TARGET: &str = "\\EFI\\BOOT\\KERNEL.efi";
fn main() {
setup::init();
chainload::chainload(CHAINLOADER_TARGET);
}

16
src/setup.rs Normal file
View File

@@ -0,0 +1,16 @@
use std::os::uefi as uefi_std;
pub fn init() {
let system_table = uefi_std::env::system_table();
let image_handle = uefi_std::env::image_handle();
// SAFETY: The uefi variables above come from the Rust std.
// These variables are nonnull and calling the uefi crates with these values is validated
// to be corrected by hand.
unsafe {
uefi::table::set_system_table(system_table.as_ptr().cast());
let handle = uefi::Handle::from_ptr(image_handle.as_ptr().cast())
.expect("unable to resolve image handle");
uefi::boot::set_image_handle(handle);
}
}

9
vm/Dockerfile.ovmf Normal file
View File

@@ -0,0 +1,9 @@
FROM alpine:3.22@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1 AS build
ARG TARGETPLATFORM
RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ] || [ "${TARGETPLATFORM}" = "linux/x86_64" ]; then \
apk --no-cache add ovmf; cp /usr/share/ovmf/bios.bin /ovmf.fd; fi
RUN if [ "${TARGETPLATFORM}" = "linux/arm64" ] || [ "${TARGETPLATFORM}" = "linux/aarch64" ]; then \
apk --no-cache add aavmf; cp /usr/share/AAVMF/QEMU_EFI.fd /ovmf.fd; fi
FROM scratch AS final
COPY --from=build /ovmf.fd /ovmf.fd