17 Commits

Author SHA1 Message Date
34ac57d291 sprout: version 0.0.22 2025-11-03 14:47:59 -05:00
37abe49347 feat(sprout): implement custom logger which shortens output 2025-11-03 14:45:48 -05:00
79615f7436 chore(docs): add openSUSE Secure Boot guide 2025-11-03 04:49:55 -05:00
7a7d92ef70 chore(hack): add keyboard and mouse to dev qemu 2025-11-03 03:41:03 -05:00
b34c171ccb fix(hack): remove xen images during clean 2025-11-03 03:18:23 -05:00
384c1e7eaf chore(docker): swap rust for docker builds to 1.91.0 2025-11-03 03:08:09 -05:00
0b7b5066e4 chore(workflows): align on push/pull_request events across workflows 2025-11-03 03:01:57 -05:00
ba634ed68a fix(platform/timer): on x86_64, elide usage of asm!() and use _rdtsc() intrinsic 2025-11-03 02:57:22 -05:00
be63c5171b chore(doc): add clarifying comments in vercmp 2025-11-03 02:46:41 -05:00
f740c35568 fix(tpm): add clarifying parentheses to version check 2025-11-03 02:37:52 -05:00
8a0b70a99b chore(doc): add documentation to VariableController::remove 2025-11-03 02:35:57 -05:00
223a00563e chore(menu): add note as to why we match on the timer event 2025-11-03 02:35:01 -05:00
029e59b209 sprout: version 0.0.21 2025-11-03 02:15:23 -05:00
bde1cd01c8 Merge pull request #28 from edera-dev/experiment/no-std
feat(sprout): introduce no_std sprout which uses stable rust
2025-11-02 23:11:52 -08:00
0017d7874d feat(sprout): introduce no_std sprout which uses stable rust 2025-11-03 02:04:21 -05:00
1c2acdc568 chore(build): pin rust-toolchain to nightly-2025-11-03 2025-11-03 00:37:04 -05:00
1f322ff4bf chore(workflows): publish should upload and attest all artifacts in a single zip 2025-11-03 00:32:54 -05:00
65 changed files with 621 additions and 296 deletions

View File

@@ -1,10 +1,12 @@
name: zizmor name: zizmor
on: on:
push:
branches: ["main"]
pull_request: pull_request:
branches: ["**"] branches:
- main
push:
branches:
- main
permissions: permissions:
contents: read # Needed to checkout the repository. contents: read # Needed to checkout the repository.

View File

@@ -1,10 +1,12 @@
name: codeql name: codeql
on: on:
push:
branches: [ "main" ]
pull_request: pull_request:
branches: [ "main" ] branches:
- main
push:
branches:
- main
schedule: schedule:
- cron: '33 16 * * 0' - cron: '33 16 * * 0'

View File

@@ -1,19 +1,12 @@
name: publish name: publish
on: on:
push:
branches:
- main
pull_request: pull_request:
branches: branches:
- main - main
paths: push:
- bin/** branches:
- src/** - main
- Cargo.*
- rust-toolchain.toml
- .github/workflows/publish.yaml
permissions: permissions:
contents: read # Needed to checkout the repository. contents: read # Needed to checkout the repository.
@@ -48,28 +41,15 @@ jobs:
- name: 'assemble artifacts' - name: 'assemble artifacts'
run: ./hack/assemble.sh run: ./hack/assemble.sh
- name: 'upload sprout-x86_64.efi.zip artifact' - name: 'upload artifacts'
id: upload-sprout-x86_64-efi id: upload
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with: with:
name: sprout-x86_64.efi.zip name: artifacts
path: target/assemble/sprout-x86_64.efi path: target/assemble/*
- name: 'upload sprout-aarch64.efi.zip artifact' - name: 'attest artifacts'
id: upload-sprout-aarch64-efi
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: sprout-aarch64.efi.zip
path: target/assemble/sprout-aarch64.efi
- name: 'attest sprout-x86_64.efi.zip artifact'
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with: with:
subject-name: sprout-x86_64.efi.zip subject-name: artifacts.zip
subject-digest: "sha256:${{ steps.upload-sprout-x86_64-efi.outputs.artifact-digest }}" subject-digest: "sha256:${{ steps.upload.outputs.artifact-digest }}"
- name: 'attest sprout-aarch64.efi.zip artifact'
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with:
subject-name: sprout-aarch64.efi.zip
subject-digest: "sha256:${{ steps.upload-sprout-aarch64-efi.outputs.artifact-digest }}"

66
Cargo.lock generated
View File

@@ -66,7 +66,7 @@ dependencies = [
[[package]] [[package]]
name = "edera-sprout" name = "edera-sprout"
version = "0.0.20" version = "0.0.22"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags", "bitflags",
@@ -74,6 +74,8 @@ dependencies = [
"hex", "hex",
"log", "log",
"sha2", "sha2",
"shlex",
"spin",
"toml", "toml",
"uefi", "uefi",
"uefi-raw", "uefi-raw",
@@ -81,17 +83,11 @@ dependencies = [
[[package]] [[package]]
name = "edera-sprout-config" name = "edera-sprout-config"
version = "0.0.20" version = "0.0.22"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.9" version = "0.14.9"
@@ -102,34 +98,27 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]] [[package]]
name = "hex" name = "hex"
version = "0.4.3" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "indexmap"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.177" version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.28" version = "0.4.28"
@@ -174,6 +163,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@@ -224,6 +219,21 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "spin"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
dependencies = [
"lock_api",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.108" version = "2.0.108"
@@ -241,12 +251,10 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
dependencies = [ dependencies = [
"indexmap",
"serde_core", "serde_core",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"toml_parser", "toml_parser",
"toml_writer",
"winnow", "winnow",
] ]
@@ -268,12 +276,6 @@ dependencies = [
"winnow", "winnow",
] ]
[[package]]
name = "toml_writer"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.19.0" version = "1.19.0"

View File

@@ -7,22 +7,45 @@ resolver = "3"
[workspace.package] [workspace.package]
license = "Apache-2.0" license = "Apache-2.0"
version = "0.0.20" version = "0.0.22"
homepage = "https://sprout.edera.dev" homepage = "https://sprout.edera.dev"
repository = "https://github.com/edera-dev/sprout" repository = "https://github.com/edera-dev/sprout"
edition = "2024" edition = "2024"
[workspace.dependencies] [workspace.dependencies]
anyhow = "1.0.100"
bitflags = "2.10.0" bitflags = "2.10.0"
hex = "0.4.3"
log = "0.4.28" log = "0.4.28"
serde = "1.0.228" spin = "0.10.0"
sha2 = "0.10.9"
toml = "0.9.8"
uefi = "0.36.0" uefi = "0.36.0"
uefi-raw = "0.12.0" uefi-raw = "0.12.0"
[workspace.dependencies.anyhow]
version = "1.0.100"
default-features = false
[workspace.dependencies.hex]
version = "0.4.3"
default-features = false
features = ["alloc"]
[workspace.dependencies.serde]
version = "1.0.228"
default-features = false
features = ["alloc", "derive"]
[workspace.dependencies.sha2]
version = "0.10.9"
default-features = false
[workspace.dependencies.shlex]
version = "1.3.0"
default-features = false
[workspace.dependencies.toml]
version = "0.9.8"
default-features = false
features = ["serde", "parse"]
# Common build profiles # Common build profiles
# NOTE: We have to compile everything for opt-level = 2 due to optimization passes # NOTE: We have to compile everything for opt-level = 2 due to optimization passes
# which don't handle the UEFI target properly. # which don't handle the UEFI target properly.

View File

@@ -5,11 +5,7 @@ This guide is a work in progress.
## Development Setup ## Development Setup
You can use any Rust development environment to develop Sprout. You can use any Rust development environment to develop Sprout.
Rustup is recommended as the Rust toolchain manager to manage Rust versions and targets. Rustup is recommended as the Rust toolchain manager to manage Rust versions and targets.
Sprout currently requires Rust nightly to support uefi_std. See [uefi_std](https://doc.rust-lang.org/beta/rustc/platform-support/unknown-uefi.html) for more details.
We currently only support `x86_64-unknown-uefi` and `aarch64-unknown-uefi` targets. We currently only support `x86_64-unknown-uefi` and `aarch64-unknown-uefi` targets.
To test your changes in QEMU, please run `./hack/dev/boot.sh`, you can specify `x86_64` or `aarch64` To test your changes in QEMU, please run `./hack/dev/boot.sh`, you can specify `x86_64` or `aarch64`

View File

@@ -2,7 +2,7 @@
ARG RUST_PROFILE=release ARG RUST_PROFILE=release
ARG RUST_TARGET_SUBDIR=release ARG RUST_TARGET_SUBDIR=release
FROM --platform=$BUILDPLATFORM rustlang/rust:nightly-alpine@sha256:34532121803db17008af0cdc4e2e1210466cb257cc9d3840dac42d706640fee5 AS build FROM --platform=$BUILDPLATFORM rust:1.91.0-alpine@sha256:a3e3d30122c08c0ed85dcd8867d956f066be23c32ed67a0453bc04ce478ad69b AS build
RUN apk --no-cache add musl-dev busybox-static RUN apk --no-cache add musl-dev busybox-static
ARG RUST_PROFILE ARG RUST_PROFILE
RUN adduser -S -s /bin/sh build RUN adduser -S -s /bin/sh build

View File

@@ -49,6 +49,7 @@ We recommend running Sprout without Secure Boot for development, and with Secure
|------------------|---------------------|-------------------------------------------------------| |------------------|---------------------|-------------------------------------------------------|
| Ubuntu | ✅ | [Setup Guide](./docs/setup/signed/ubuntu.md) | | Ubuntu | ✅ | [Setup Guide](./docs/setup/signed/ubuntu.md) |
| Debian | ✅ | [Setup Guide](./docs/setup/signed/debian.md) | | Debian | ✅ | [Setup Guide](./docs/setup/signed/debian.md) |
| openSUSE | ✅ | [Setup Guide](./docs/setup/signed/opensuse.md) |
| Fedora | ❌ | [Setup Guide](./docs/setup/unsigned/fedora.md) | | Fedora | ❌ | [Setup Guide](./docs/setup/unsigned/fedora.md) |
| Alpine Edge | ❌ | [Setup Guide](./docs/setup/unsigned/alpine-edge.md) | | Alpine Edge | ❌ | [Setup Guide](./docs/setup/unsigned/alpine-edge.md) |
| Generic Linux | ❌ | [Setup Guide](./docs/setup/unsigned/generic-linux.md) | | Generic Linux | ❌ | [Setup Guide](./docs/setup/unsigned/generic-linux.md) |

View File

@@ -9,7 +9,7 @@ edition.workspace = true
[dependencies.serde] [dependencies.serde]
workspace = true workspace = true
features = ["derive"] default-features = false
[lib] [lib]
name = "edera_sprout_config" name = "edera_sprout_config"

View File

@@ -1,3 +1,5 @@
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// The configuration of the chainload action. /// The configuration of the chainload action.

View File

@@ -1,3 +1,5 @@
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// The configuration of the edera action which boots the Edera hypervisor. /// The configuration of the edera action which boots the Edera hypervisor.

View File

@@ -1,3 +1,4 @@
use alloc::string::String;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// The configuration of the print action. /// The configuration of the print action.

View File

@@ -1,3 +1,4 @@
use alloc::string::String;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Declares a driver configuration. /// Declares a driver configuration.

View File

@@ -1,5 +1,7 @@
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
/// Declares a boot entry to display in the boot menu. /// Declares a boot entry to display in the boot menu.
/// ///

View File

@@ -1,3 +1,4 @@
use alloc::string::String;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// The filesystem device match extractor. /// The filesystem device match extractor.

View File

@@ -1,4 +1,5 @@
use crate::entries::EntryDeclaration; use crate::entries::EntryDeclaration;
use alloc::string::{String, ToString};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// The default path to the BLS directory. /// The default path to the BLS directory.

View File

@@ -1,6 +1,8 @@
use crate::entries::EntryDeclaration; use crate::entries::EntryDeclaration;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
/// List generator configuration. /// List generator configuration.
/// The list generator produces multiple entries based /// The list generator produces multiple entries based

View File

@@ -1,6 +1,8 @@
use crate::entries::EntryDeclaration; use crate::entries::EntryDeclaration;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
/// Matrix generator configuration. /// Matrix generator configuration.
/// The matrix generator produces multiple entries based /// The matrix generator produces multiple entries based

View File

@@ -1,5 +1,7 @@
//! Sprout configuration descriptions. //! Sprout configuration descriptions.
//! This crate provides all the configuration structures for Sprout. //! This crate provides all the configuration structures for Sprout.
#![no_std]
extern crate alloc;
use crate::actions::ActionDeclaration; use crate::actions::ActionDeclaration;
use crate::drivers::DriverDeclaration; use crate::drivers::DriverDeclaration;
@@ -7,8 +9,9 @@ use crate::entries::EntryDeclaration;
use crate::extractors::ExtractorDeclaration; use crate::extractors::ExtractorDeclaration;
use crate::generators::GeneratorDeclaration; use crate::generators::GeneratorDeclaration;
use crate::phases::PhasesConfiguration; use crate::phases::PhasesConfiguration;
use alloc::collections::BTreeMap;
use alloc::string::String;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
pub mod actions; pub mod actions;
pub mod drivers; pub mod drivers;

View File

@@ -1,5 +1,7 @@
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
/// Configures the various phases of the boot process. /// Configures the various phases of the boot process.
/// This allows hooking various phases to run actions. /// This allows hooking various phases to run actions.

View File

@@ -13,12 +13,14 @@ bitflags.workspace = true
edera-sprout-config.path = "../config" edera-sprout-config.path = "../config"
hex.workspace = true hex.workspace = true
sha2.workspace = true sha2.workspace = true
shlex.workspace = true
spin.workspace = true
toml.workspace = true toml.workspace = true
log.workspace = true log.workspace = true
[dependencies.uefi] [dependencies.uefi]
workspace = true workspace = true
features = ["alloc", "logger"] features = ["alloc", "global_allocator", "panic_handler"]
[dependencies.uefi-raw] [dependencies.uefi-raw]
workspace = true workspace = true

View File

@@ -1,6 +1,6 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use alloc::rc::Rc;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use std::rc::Rc;
/// EFI chainloader action. /// EFI chainloader action.
pub mod chainload; pub mod chainload;

View File

@@ -4,10 +4,11 @@ use crate::integrations::shim::{ShimInput, ShimSupport};
use crate::utils; use crate::utils;
use crate::utils::media_loader::MediaLoaderHandle; use crate::utils::media_loader::MediaLoaderHandle;
use crate::utils::media_loader::constants::linux::LINUX_EFI_INITRD_MEDIA_GUID; use crate::utils::media_loader::constants::linux::LINUX_EFI_INITRD_MEDIA_GUID;
use alloc::boxed::Box;
use alloc::rc::Rc;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use edera_sprout_config::actions::chainload::ChainloadConfiguration; use edera_sprout_config::actions::chainload::ChainloadConfiguration;
use log::error; use log::error;
use std::rc::Rc;
use uefi::CString16; use uefi::CString16;
use uefi::proto::loaded_image::LoadedImage; use uefi::proto::loaded_image::LoadedImage;

View File

@@ -1,5 +1,3 @@
use std::rc::Rc;
use crate::{ use crate::{
actions, actions,
context::SproutContext, context::SproutContext,
@@ -13,6 +11,9 @@ use crate::{
}, },
}, },
}; };
use alloc::rc::Rc;
use alloc::string::{String, ToString};
use alloc::{format, vec};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use edera_sprout_config::actions::chainload::ChainloadConfiguration; use edera_sprout_config::actions::chainload::ChainloadConfiguration;
use edera_sprout_config::actions::edera::EderaConfiguration; use edera_sprout_config::actions::edera::EderaConfiguration;

View File

@@ -1,8 +1,8 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use alloc::rc::Rc;
use anyhow::Result; use anyhow::Result;
use edera_sprout_config::actions::print::PrintConfiguration; use edera_sprout_config::actions::print::PrintConfiguration;
use log::info; use log::info;
use std::rc::Rc;
/// Executes the print action with the specified `configuration` inside the provided `context`. /// Executes the print action with the specified `configuration` inside the provided `context`.
pub fn print(context: Rc<SproutContext>, configuration: &PrintConfiguration) -> Result<()> { pub fn print(context: Rc<SproutContext>, configuration: &PrintConfiguration) -> Result<()> {

View File

@@ -1,4 +1,6 @@
use crate::utils; use crate::utils;
use alloc::string::ToString;
use alloc::{format, vec};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use edera_sprout_config::RootConfiguration; use edera_sprout_config::RootConfiguration;
use edera_sprout_config::actions::ActionDeclaration; use edera_sprout_config::actions::ActionDeclaration;

View File

@@ -1,5 +1,9 @@
use crate::utils; use crate::utils;
use crate::utils::vercmp; use crate::utils::vercmp;
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use alloc::{format, vec};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use edera_sprout_config::RootConfiguration; use edera_sprout_config::RootConfiguration;
use edera_sprout_config::actions::ActionDeclaration; use edera_sprout_config::actions::ActionDeclaration;
@@ -7,7 +11,6 @@ use edera_sprout_config::actions::chainload::ChainloadConfiguration;
use edera_sprout_config::entries::EntryDeclaration; use edera_sprout_config::entries::EntryDeclaration;
use edera_sprout_config::generators::GeneratorDeclaration; use edera_sprout_config::generators::GeneratorDeclaration;
use edera_sprout_config::generators::list::ListConfiguration; use edera_sprout_config::generators::list::ListConfiguration;
use std::collections::BTreeMap;
use uefi::CString16; use uefi::CString16;
use uefi::fs::{FileSystem, Path, PathBuf}; use uefi::fs::{FileSystem, Path, PathBuf};
use uefi::proto::device_path::DevicePath; use uefi::proto::device_path::DevicePath;

View File

@@ -1,4 +1,6 @@
use crate::utils; use crate::utils;
use alloc::string::ToString;
use alloc::{format, vec};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use edera_sprout_config::RootConfiguration; use edera_sprout_config::RootConfiguration;
use edera_sprout_config::actions::ActionDeclaration; use edera_sprout_config::actions::ActionDeclaration;

View File

@@ -1,10 +1,11 @@
use crate::options::SproutOptions; use crate::options::SproutOptions;
use crate::platform::tpm::PlatformTpm; use crate::platform::tpm::PlatformTpm;
use crate::utils; use crate::utils;
use alloc::vec::Vec;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use core::ops::Deref;
use edera_sprout_config::{RootConfiguration, latest_version}; use edera_sprout_config::{RootConfiguration, latest_version};
use log::info; use log::info;
use std::ops::Deref;
use toml::Value; use toml::Value;
use uefi::proto::device_path::LoadedImageDevicePath; use uefi::proto::device_path::LoadedImageDevicePath;

View File

@@ -1,11 +1,15 @@
use crate::options::SproutOptions; use crate::options::SproutOptions;
use crate::platform::timer::PlatformTimer; use crate::platform::timer::PlatformTimer;
use alloc::boxed::Box;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::format;
use alloc::rc::Rc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use anyhow::anyhow; use anyhow::anyhow;
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use core::cmp::Reverse;
use edera_sprout_config::actions::ActionDeclaration; use edera_sprout_config::actions::ActionDeclaration;
use std::cmp::Reverse;
use std::collections::{BTreeMap, BTreeSet};
use std::rc::Rc;
use uefi::proto::device_path::DevicePath; use uefi::proto::device_path::DevicePath;
/// The maximum number of iterations that can be performed in [SproutContext::finalize]. /// The maximum number of iterations that can be performed in [SproutContext::finalize].

View File

@@ -1,11 +1,13 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use crate::integrations::shim::{ShimInput, ShimSupport}; use crate::integrations::shim::{ShimInput, ShimSupport};
use crate::utils; use crate::utils;
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::rc::Rc;
use alloc::string::String;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
pub(crate) use edera_sprout_config::drivers::DriverDeclaration; use edera_sprout_config::drivers::DriverDeclaration;
use log::info; use log::info;
use std::collections::BTreeMap;
use std::rc::Rc;
use uefi::boot::SearchType; use uefi::boot::SearchType;
/// Loads the driver specified by the `driver` declaration. /// Loads the driver specified by the `driver` declaration.

View File

@@ -1,6 +1,7 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use alloc::rc::Rc;
use alloc::string::{String, ToString};
use edera_sprout_config::entries::EntryDeclaration; use edera_sprout_config::entries::EntryDeclaration;
use std::rc::Rc;
/// Represents an entry that is stamped and ready to be booted. /// Represents an entry that is stamped and ready to be booted.
#[derive(Clone)] #[derive(Clone)]

View File

@@ -1,7 +1,8 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use alloc::rc::Rc;
use alloc::string::String;
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use edera_sprout_config::extractors::ExtractorDeclaration; use edera_sprout_config::extractors::ExtractorDeclaration;
use std::rc::Rc;
/// The filesystem device match extractor. /// The filesystem device match extractor.
pub mod filesystem_device_match; pub mod filesystem_device_match;

View File

@@ -1,10 +1,11 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use crate::utils; use crate::utils;
use alloc::rc::Rc;
use alloc::string::String;
use anyhow::{Context, Result, anyhow, bail}; use anyhow::{Context, Result, anyhow, bail};
use core::ops::Deref;
use core::str::FromStr;
use edera_sprout_config::extractors::filesystem_device_match::FilesystemDeviceMatchExtractor; use edera_sprout_config::extractors::filesystem_device_match::FilesystemDeviceMatchExtractor;
use std::ops::Deref;
use std::rc::Rc;
use std::str::FromStr;
use uefi::fs::{FileSystem, Path}; use uefi::fs::{FileSystem, Path};
use uefi::proto::device_path::DevicePath; use uefi::proto::device_path::DevicePath;
use uefi::proto::media::file::{File, FileSystemVolumeLabel}; use uefi::proto::media::file::{File, FileSystemVolumeLabel};

View File

@@ -1,9 +1,10 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use crate::entries::BootableEntry; use crate::entries::BootableEntry;
use alloc::rc::Rc;
use alloc::vec::Vec;
use anyhow::Result; use anyhow::Result;
use anyhow::bail; use anyhow::bail;
use edera_sprout_config::generators::GeneratorDeclaration; use edera_sprout_config::generators::GeneratorDeclaration;
use std::rc::Rc;
/// The BLS generator. /// The BLS generator.
pub mod bls; pub mod bls;

View File

@@ -3,11 +3,14 @@ use crate::entries::BootableEntry;
use crate::generators::bls::entry::BlsEntry; use crate::generators::bls::entry::BlsEntry;
use crate::utils; use crate::utils;
use crate::utils::vercmp; use crate::utils::vercmp;
use alloc::format;
use alloc::rc::Rc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use core::cmp::Ordering;
use core::str::FromStr;
use edera_sprout_config::generators::bls::BlsConfiguration; use edera_sprout_config::generators::bls::BlsConfiguration;
use std::cmp::Ordering;
use std::rc::Rc;
use std::str::FromStr;
use uefi::cstr16; use uefi::cstr16;
use uefi::fs::{FileSystem, PathBuf}; use uefi::fs::{FileSystem, PathBuf};
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};

View File

@@ -1,5 +1,6 @@
use alloc::string::{String, ToString};
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use std::str::FromStr; use core::str::FromStr;
/// Represents a parsed BLS entry. /// Represents a parsed BLS entry.
/// Fields unrelated to Sprout are not included. /// Fields unrelated to Sprout are not included.

View File

@@ -1,8 +1,10 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use crate::entries::BootableEntry; use crate::entries::BootableEntry;
use alloc::rc::Rc;
use alloc::string::ToString;
use alloc::vec::Vec;
use anyhow::Result; use anyhow::Result;
use edera_sprout_config::generators::list::ListConfiguration; use edera_sprout_config::generators::list::ListConfiguration;
use std::rc::Rc;
/// Generates a set of entries using the specified `list` configuration in the `context`. /// Generates a set of entries using the specified `list` configuration in the `context`.
pub fn generate( pub fn generate(

View File

@@ -1,11 +1,14 @@
use crate::context::SproutContext; use crate::context::SproutContext;
use crate::entries::BootableEntry; use crate::entries::BootableEntry;
use crate::generators::list; use crate::generators::list;
use alloc::collections::BTreeMap;
use alloc::rc::Rc;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use anyhow::Result; use anyhow::Result;
use edera_sprout_config::generators::list::ListConfiguration; use edera_sprout_config::generators::list::ListConfiguration;
use edera_sprout_config::generators::matrix::MatrixConfiguration; use edera_sprout_config::generators::matrix::MatrixConfiguration;
use std::collections::BTreeMap;
use std::rc::Rc;
/// Builds out multiple generations of `input` based on a matrix style. /// Builds out multiple generations of `input` based on a matrix style.
/// For example, if input is: {"x": ["a", "b"], "y": ["c", "d"]} /// For example, if input is: {"x": ["a", "b"], "y": ["c", "d"]}

View File

@@ -2,6 +2,9 @@ use crate::integrations::bootloader_interface::bitflags::LoaderFeatures;
use crate::platform::timer::PlatformTimer; use crate::platform::timer::PlatformTimer;
use crate::utils::device_path_subpath; use crate::utils::device_path_subpath;
use crate::utils::variables::{VariableClass, VariableController}; use crate::utils::variables::{VariableClass, VariableController};
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use uefi::proto::device_path::DevicePath; use uefi::proto::device_path::DevicePath;
use uefi::{Guid, guid}; use uefi::{Guid, guid};

View File

@@ -3,10 +3,13 @@ use crate::secure::SecureBoot;
use crate::utils; use crate::utils;
use crate::utils::ResolvedPath; use crate::utils::ResolvedPath;
use crate::utils::variables::{VariableClass, VariableController}; use crate::utils::variables::{VariableClass, VariableController};
use alloc::boxed::Box;
use alloc::string::ToString;
use alloc::vec::Vec;
use anyhow::{Context, Result, anyhow, bail}; use anyhow::{Context, Result, anyhow, bail};
use core::ffi::c_void;
use core::pin::Pin;
use log::warn; use log::warn;
use std::ffi::c_void;
use std::pin::Pin;
use uefi::Handle; use uefi::Handle;
use uefi::boot::LoadImageSource; use uefi::boot::LoadImageSource;
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};

View File

@@ -1,8 +1,9 @@
use crate::integrations::shim::{ShimInput, ShimSupport, ShimVerificationOutput}; use crate::integrations::shim::{ShimInput, ShimSupport, ShimVerificationOutput};
use crate::utils; use crate::utils;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result};
use core::slice;
use log::warn; use log::warn;
use std::sync::{LazyLock, Mutex}; use spin::{Lazy, Mutex};
use uefi::proto::device_path::FfiDevicePath; use uefi::proto::device_path::FfiDevicePath;
use uefi::proto::unsafe_protocol; use uefi::proto::unsafe_protocol;
use uefi::{Guid, guid}; use uefi::{Guid, guid};
@@ -45,8 +46,7 @@ struct SecurityHookState {
/// Global state for the security hook. /// Global state for the security hook.
/// This is messy, but it is safe given the mutex. /// This is messy, but it is safe given the mutex.
static GLOBAL_HOOK_STATE: LazyLock<Mutex<Option<SecurityHookState>>> = static GLOBAL_HOOK_STATE: Lazy<Mutex<Option<SecurityHookState>>> = Lazy::new(|| Mutex::new(None));
LazyLock::new(|| Mutex::new(None));
/// Security hook helper. /// Security hook helper.
pub struct SecurityHook; pub struct SecurityHook;
@@ -110,24 +110,13 @@ impl SecurityHook {
// Verify the input, if it fails, call the original hook. // Verify the input, if it fails, call the original hook.
if !Self::verify(input) { if !Self::verify(input) {
// Acquire the global hook state to grab the original hook. // Acquire the global hook state to grab the original hook.
let function = match GLOBAL_HOOK_STATE.lock() { let function = match GLOBAL_HOOK_STATE.lock().as_ref() {
// We have acquired the lock, so we can find the original hook. // The hook state is available, so we can acquire the original hook.
Ok(state) => match state.as_ref() { Some(state) => state.original_hook.file_authentication_state,
// The hook state is available, so we can acquire the original hook.
Some(state) => state.original_hook.file_authentication_state,
// The hook state is not available, so we can't call the original hook. // The hook state is not available, so we can't call the original hook.
None => { None => {
warn!("global hook state is not available, unable to call original hook"); warn!("global hook state is not available, unable to call original hook");
return Status::LOAD_ERROR;
}
},
Err(error) => {
warn!(
"unable to acquire global hook state lock to call original hook: {}",
error,
);
return Status::LOAD_ERROR; return Status::LOAD_ERROR;
} }
}; };
@@ -161,7 +150,7 @@ impl SecurityHook {
} }
// Construct a slice out of the file buffer and size. // Construct a slice out of the file buffer and size.
let buffer = unsafe { std::slice::from_raw_parts(file_buffer, file_size) }; let buffer = unsafe { slice::from_raw_parts(file_buffer, file_size) };
// Construct a shim input from the path. // Construct a shim input from the path.
let input = ShimInput::SecurityHookBuffer(Some(path), buffer); let input = ShimInput::SecurityHookBuffer(Some(path), buffer);
@@ -169,24 +158,13 @@ impl SecurityHook {
// Verify the input, if it fails, call the original hook. // Verify the input, if it fails, call the original hook.
if !Self::verify(input) { if !Self::verify(input) {
// Acquire the global hook state to grab the original hook. // Acquire the global hook state to grab the original hook.
let function = match GLOBAL_HOOK_STATE.lock() { let function = match GLOBAL_HOOK_STATE.lock().as_ref() {
// We have acquired the lock, so we can find the original hook. // The hook state is available, so we can acquire the original hook.
Ok(state) => match state.as_ref() { Some(state) => state.original_hook2.file_authentication,
// The hook state is available, so we can acquire the original hook.
Some(state) => state.original_hook2.file_authentication,
// The hook state is not available, so we can't call the original hook. // The hook state is not available, so we can't call the original hook.
None => { None => {
warn!("global hook state is not available, unable to call original hook"); warn!("global hook state is not available, unable to call original hook");
return Status::LOAD_ERROR;
}
},
Err(error) => {
warn!(
"unable to acquire global hook state lock to call original hook: {}",
error
);
return Status::LOAD_ERROR; return Status::LOAD_ERROR;
} }
}; };
@@ -237,9 +215,7 @@ impl SecurityHook {
}; };
// Acquire the lock to the global state and replace it. // Acquire the lock to the global state and replace it.
let Ok(mut global_state) = GLOBAL_HOOK_STATE.lock() else { let mut global_state = GLOBAL_HOOK_STATE.lock();
bail!("unable to acquire global hook state lock");
};
global_state.replace(state); global_state.replace(state);
// Install the hooks into the UEFI stack. // Install the hooks into the UEFI stack.
@@ -276,9 +252,7 @@ impl SecurityHook {
.context("unable to open security arch2 protocol")?; .context("unable to open security arch2 protocol")?;
// Acquire the lock to the global state. // Acquire the lock to the global state.
let Ok(mut global_state) = GLOBAL_HOOK_STATE.lock() else { let mut global_state = GLOBAL_HOOK_STATE.lock();
bail!("unable to acquire global hook state lock");
};
// Take the state and replace the original functions. // Take the state and replace the original functions.
let Some(state) = global_state.take() else { let Some(state) = global_state.take() else {

View File

@@ -0,0 +1,94 @@
//! Based on: https://github.com/rust-osdev/uefi-rs/blob/main/uefi/src/helpers/logger.rs
use alloc::format;
use core::fmt::Write;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
use log::{Log, Record};
use uefi::proto::console::text::Output;
/// The global logger object.
static LOGGER: Logger = Logger::new();
/// Logging mechanism for Sprout.
/// Must be initialized to be used, as we use atomic pointers to store the output to write to.
pub struct Logger {
writer: AtomicPtr<Output>,
}
impl Default for Logger {
/// Creates a default logger, which is uninitialized with an output.
fn default() -> Self {
Self::new()
}
}
impl Logger {
/// Create a new logger with an output not specified.
/// This will cause the logger to not print anything until it is configured.
pub const fn new() -> Self {
Self {
writer: AtomicPtr::new(ptr::null_mut()),
}
}
/// Retrieves the pointer to the output.
/// SAFETY: This pointer might be null, it should be checked before use.
#[must_use]
fn output(&self) -> *mut Output {
self.writer.load(Ordering::Acquire)
}
/// Sets the output to write to.
///
/// # Safety
/// This function is unsafe because the output is technically leaked and unmanaged.
pub unsafe fn set_output(&self, output: *mut Output) {
self.writer.store(output, Ordering::Release);
}
}
impl Log for Logger {
/// Enable the logger always.
fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool {
true
}
/// Log the specified `record` to the output if one is set.
fn log(&self, record: &Record) {
// Acquire the output. If one is not set, we do nothing.
let Some(output) = (unsafe { self.output().as_mut() }) else {
return;
};
// Format the log message.
let message = format!("{}", record.args());
// Iterate over every line, formatting the message and writing it to the output.
for line in message.lines() {
// The format writes the log level in front of every line of text.
let _ = writeln!(output, "[{:>5}] {}", record.level(), line);
}
}
/// This log is not buffered, so flushing isn't required.
fn flush(&self) {}
}
/// Initialize the logging environment, calling panic if something goes wrong.
pub fn init() {
// Retrieve the stdout handle and set it as the output for the global logger.
uefi::system::with_stdout(|stdout| unsafe {
// SAFETY: We are using the stdout handle to create a pointer to the output.
// The handle is global and is guaranteed to be valid for the lifetime of the program.
LOGGER.set_output(stdout);
});
// Set the logger to the global logger.
if let Err(error) = log::set_logger(&LOGGER) {
panic!("unable to set logger: {}", error);
}
// Set the max level to the level specified by the log features.
log::set_max_level(log::STATIC_MAX_LEVEL);
}

View File

@@ -1,8 +1,8 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![feature(uefi_std)] #![no_std]
#![no_main]
/// The delay to wait for when an error occurs in Sprout. extern crate alloc;
const DELAY_ON_ERROR: Duration = Duration::from_secs(10);
use crate::context::{RootContext, SproutContext}; use crate::context::{RootContext, SproutContext};
use crate::entries::BootableEntry; use crate::entries::BootableEntry;
@@ -14,13 +14,18 @@ use crate::platform::timer::PlatformTimer;
use crate::platform::tpm::PlatformTpm; use crate::platform::tpm::PlatformTpm;
use crate::secure::SecureBoot; use crate::secure::SecureBoot;
use crate::utils::PartitionGuidForm; use crate::utils::PartitionGuidForm;
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::ToString;
use alloc::vec::Vec;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use core::ops::Deref;
use core::time::Duration;
use edera_sprout_config::RootConfiguration; use edera_sprout_config::RootConfiguration;
use log::{error, info, warn}; use log::{error, info, warn};
use std::collections::BTreeMap; use uefi::entry;
use std::ops::Deref;
use std::time::Duration;
use uefi::proto::device_path::LoadedImageDevicePath; use uefi::proto::device_path::LoadedImageDevicePath;
use uefi_raw::Status;
/// actions: Code that can be configured and executed by Sprout. /// actions: Code that can be configured and executed by Sprout.
pub mod actions; pub mod actions;
@@ -46,18 +51,24 @@ pub mod extractors;
/// generators: Runtime code that can generate entries with specific values. /// generators: Runtime code that can generate entries with specific values.
pub mod generators; pub mod generators;
/// platform: Integration or support code for specific hardware platforms. /// integrations: Code that interacts with other systems.
pub mod platform; pub mod integrations;
/// logger: Code for the logging mechanism of Sprout.
pub mod logger;
/// menu: Display a boot menu to select an entry to boot. /// menu: Display a boot menu to select an entry to boot.
pub mod menu; pub mod menu;
/// integrations: Code that interacts with other systems. /// options: Parse the options of the Sprout executable.
pub mod integrations; pub mod options;
/// phases: Hooks into specific parts of the boot process. /// phases: Hooks into specific parts of the boot process.
pub mod phases; pub mod phases;
/// platform: Integration or support code for specific hardware platforms.
pub mod platform;
/// sbat: Secure Boot Attestation section. /// sbat: Secure Boot Attestation section.
pub mod sbat; pub mod sbat;
@@ -67,12 +78,12 @@ pub mod secure;
/// setup: Code that initializes the UEFI environment for Sprout. /// setup: Code that initializes the UEFI environment for Sprout.
pub mod setup; pub mod setup;
/// options: Parse the options of the Sprout executable.
pub mod options;
/// utils: Utility functions that are used by other parts of Sprout. /// utils: Utility functions that are used by other parts of Sprout.
pub mod utils; pub mod utils;
/// The delay to wait for when an error occurs in Sprout.
const DELAY_ON_ERROR: Duration = Duration::from_secs(10);
/// Run Sprout, returning an error if one occurs. /// Run Sprout, returning an error if one occurs.
fn run() -> Result<()> { fn run() -> Result<()> {
// For safety reasons, we will note that Secure Boot is in beta on Sprout. // For safety reasons, we will note that Secure Boot is in beta on Sprout.
@@ -373,23 +384,32 @@ fn run() -> Result<()> {
/// The main entrypoint of sprout. /// The main entrypoint of sprout.
/// It is possible this function will not return if actions that are executed /// It is possible this function will not return if actions that are executed
/// exit boot services or do not return control to sprout. /// exit boot services or do not return control to sprout.
fn main() -> Result<()> { #[entry]
fn efi_main() -> Status {
// Initialize the basic UEFI environment. // Initialize the basic UEFI environment.
setup::init()?; // If initialization fails, we will return ABORTED.
// NOTE: This function will also initialize the logger.
// The logger will panic if it is unable to initialize.
// It is guaranteed that if this returns, the logger is initialized.
if let Err(error) = setup::init() {
error!("unable to initialize environment: {}", error);
return Status::ABORTED;
}
// Run Sprout, then handle the error. // Run Sprout, then handle the error.
let result = run(); let result = run();
if let Err(ref error) = result { if let Err(ref error) = result {
// Print an error trace. // Print an error trace.
error!("sprout encountered an error"); error!("sprout encountered an error:");
for (index, stack) in error.chain().enumerate() { for (index, stack) in error.chain().enumerate() {
error!("[{}]: {}", index, stack); error!("[{}]: {}", index, stack);
} }
// Sleep to allow the user to read the error. // Sleep to allow the user to read the error.
uefi::boot::stall(DELAY_ON_ERROR); uefi::boot::stall(DELAY_ON_ERROR);
return Status::ABORTED;
} }
// Sprout doesn't necessarily guarantee anything was booted. // Sprout doesn't necessarily guarantee anything was booted.
// If we reach here, we will exit back to whoever called us. // If we reach here, we will exit back to whoever called us.
Ok(()) Status::SUCCESS
} }

View File

@@ -1,9 +1,10 @@
use crate::entries::BootableEntry; use crate::entries::BootableEntry;
use crate::integrations::bootloader_interface::BootloaderInterface; use crate::integrations::bootloader_interface::BootloaderInterface;
use crate::platform::timer::PlatformTimer; use crate::platform::timer::PlatformTimer;
use alloc::vec;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use core::time::Duration;
use log::{info, warn}; use log::{info, warn};
use std::time::Duration;
use uefi::ResultExt; use uefi::ResultExt;
use uefi::boot::TimerTrigger; use uefi::boot::TimerTrigger;
use uefi::proto::console::text::{Input, Key, ScanCode}; use uefi::proto::console::text::{Input, Key, ScanCode};
@@ -65,6 +66,7 @@ fn read(input: &mut Input, timeout: &Duration) -> Result<MenuOperation> {
// Close the timer event that we acquired. // Close the timer event that we acquired.
// We don't need to close the key event because it is owned globally. // We don't need to close the key event because it is owned globally.
// This should always be called in practice as events are not modified by wait_for_event.
if let Some(timer_event) = events.into_iter().next() { if let Some(timer_event) = events.into_iter().next() {
// Store the result of the close event so we can determine if we can safely assert it. // Store the result of the close event so we can determine if we can safely assert it.
let close_event_result = let close_event_result =

View File

@@ -1,6 +1,10 @@
use crate::options::parser::{OptionDescription, OptionForm, OptionsRepresentable}; use crate::options::parser::{OptionDescription, OptionForm, OptionsRepresentable};
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use std::collections::BTreeMap;
/// Acquire arguments from UEFI environment.
pub mod env;
/// The Sprout options parser. /// The Sprout options parser.
pub mod parser; pub mod parser;

View File

@@ -0,0 +1,77 @@
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use anyhow::{Context, Result, bail};
use uefi::proto::loaded_image::{LoadOptionsError, LoadedImage};
/// Loads the command-line arguments passed to Sprout.
pub fn args() -> Result<Vec<String>> {
// Acquire the image handle of Sprout.
let handle = uefi::boot::image_handle();
// Open the LoadedImage protocol for Sprout.
let loaded_image = uefi::boot::open_protocol_exclusive::<LoadedImage>(handle)
.context("unable to open loaded image protocol for sprout")?;
// Load the command-line argument string.
let options = match loaded_image.load_options_as_cstr16() {
// Load options were passed. We will return them for processing.
Ok(options) => options,
// No load options were passed. We will return an empty vector.
Err(LoadOptionsError::NotSet) => {
return Ok(Vec::new());
}
Err(LoadOptionsError::NotAligned) => {
bail!("load options are not properly aligned");
}
Err(LoadOptionsError::InvalidString(error)) => {
bail!("load options are not a valid string: {}", error);
}
};
// Convert the options to a string.
let options = options.to_string();
// Use shlex to parse the options.
// If shlex fails, we will fall back to a simple whitespace split.
let mut args = shlex::split(&options).unwrap_or_else(|| {
options
.split_ascii_whitespace()
.map(|string| string.to_string())
.collect::<Vec<_>>()
});
// If there is a first argument, check if it is not an option.
// If it is not, we will assume it is the path to the executable and remove it.
if let Some(arg) = args.first()
&& !arg.starts_with('-')
{
args.remove(0);
}
// Correct firmware that may add invalid arguments at the start.
// Witnessed this on a Dell Precision 5690 when direct booting.
loop {
// Grab the first argument or break.
let Some(arg) = args.first() else {
break;
};
// Check if the argument is a valid character.
// If it is not, remove it and continue.
let Some(first_character) = arg.chars().next() else {
break;
};
// If the character is not a printable character or a backtick, remove it and continue.
if first_character < 0x1f as char || first_character == '`' {
args.remove(0);
continue;
}
break;
}
Ok(args)
}

View File

@@ -1,6 +1,10 @@
use crate::options::env;
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use core::ptr::null_mut;
use log::info; use log::info;
use std::collections::BTreeMap; use uefi_raw::Status;
/// The type of option. This disambiguates different behavior /// The type of option. This disambiguates different behavior
/// of how options are handled. /// of how options are handled.
@@ -47,23 +51,7 @@ pub trait OptionsRepresentable {
// Collect all the arguments to Sprout. // Collect all the arguments to Sprout.
// Skip the first argument, which is the path to our executable. // Skip the first argument, which is the path to our executable.
let mut args = std::env::args().skip(1).collect::<Vec<_>>(); let args = env::args()?;
// Correct firmware that may add invalid arguments at the start.
// Witnessed this on a Dell Precision 5690 when direct booting.
loop {
// Grab the first argument or break.
let Some(arg) = args.first() else {
break;
};
// If the argument starts with a tilde, remove it.
if arg.starts_with("`") {
args.remove(0);
continue;
}
break;
}
// Represent options as key-value pairs. // Represent options as key-value pairs.
let mut options = BTreeMap::new(); let mut options = BTreeMap::new();
@@ -77,7 +65,7 @@ pub trait OptionsRepresentable {
break; break;
}; };
// If the doesn't start with --, that is invalid. // If the option doesn't start with --, that is invalid.
if !option.starts_with("--") { if !option.starts_with("--") {
bail!("invalid option: {option}"); bail!("invalid option: {option}");
} }
@@ -144,7 +132,9 @@ pub trait OptionsRepresentable {
); );
} }
// Exit because the help has been displayed. // Exit because the help has been displayed.
std::process::exit(0); unsafe {
uefi::boot::exit(uefi::boot::image_handle(), Status::SUCCESS, 0, null_mut());
};
} }
// Insert the option and the value into the map. // Insert the option and the value into the map.

View File

@@ -1,8 +1,9 @@
use crate::actions; use crate::actions;
use crate::context::SproutContext; use crate::context::SproutContext;
use alloc::format;
use alloc::rc::Rc;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use edera_sprout_config::phases::PhaseConfiguration; use edera_sprout_config::phases::PhaseConfiguration;
use std::rc::Rc;
/// Executes the specified [phase] of the boot process. /// Executes the specified [phase] of the boot process.
/// The value [phase] should be a reference of a specific phase in the [PhasesConfiguration]. /// The value [phase] should be a reference of a specific phase in the [PhasesConfiguration].

View File

@@ -1,7 +1,7 @@
// Referenced https://github.com/sheroz/tick_counter (MIT license) as a baseline. // Referenced https://github.com/sheroz/tick_counter (MIT license) as a baseline.
// Architecturally modified to support UEFI and remove x86 (32-bit) support. // Architecturally modified to support UEFI and remove x86 (32-bit) support.
use std::time::Duration; use core::time::Duration;
/// Support for aarch64 timers. /// Support for aarch64 timers.
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
@@ -17,7 +17,7 @@ pub enum TickFrequency {
/// The platform provides the tick frequency. /// The platform provides the tick frequency.
Hardware(u64), Hardware(u64),
/// The tick frequency is measured internally. /// The tick frequency is measured internally.
Measured(u64, Duration), Measured(u64),
} }
impl TickFrequency { impl TickFrequency {
@@ -25,7 +25,7 @@ impl TickFrequency {
fn ticks(&self) -> u64 { fn ticks(&self) -> u64 {
match self { match self {
TickFrequency::Hardware(frequency) => *frequency, TickFrequency::Hardware(frequency) => *frequency,
TickFrequency::Measured(frequency, _) => *frequency, TickFrequency::Measured(frequency) => *frequency,
} }
} }

View File

@@ -1,5 +1,5 @@
use crate::platform::timer::TickFrequency; use crate::platform::timer::TickFrequency;
use std::arch::asm; use core::arch::asm;
/// Reads the cntvct_el0 counter and returns the value. /// Reads the cntvct_el0 counter and returns the value.
pub fn ticks() -> u64 { pub fn ticks() -> u64 {
@@ -10,16 +10,6 @@ pub fn ticks() -> u64 {
counter counter
} }
/// We can use the actual ticks value as our start value.
pub fn start() -> u64 {
ticks()
}
/// We can use the actual ticks value as our stop value.
pub fn stop() -> u64 {
ticks()
}
/// Our frequency is provided by cntfrq_el0 on the platform. /// Our frequency is provided by cntfrq_el0 on the platform.
pub fn frequency() -> TickFrequency { pub fn frequency() -> TickFrequency {
let frequency: u64; let frequency: u64;

View File

@@ -1,6 +1,5 @@
use crate::platform::timer::TickFrequency; use crate::platform::timer::TickFrequency;
use core::arch::asm; use core::time::Duration;
use std::time::Duration;
/// We will measure the frequency of the timer based on 1000 microseconds. /// We will measure the frequency of the timer based on 1000 microseconds.
/// This will result in a call to BS->Stall(1000) in the end. /// This will result in a call to BS->Stall(1000) in the end.
@@ -8,52 +7,16 @@ const MEASURE_FREQUENCY_DURATION: Duration = Duration::from_micros(1000);
/// Read the number of ticks from the platform timer. /// Read the number of ticks from the platform timer.
pub fn ticks() -> u64 { pub fn ticks() -> u64 {
let mut eax: u32; // SAFETY: Reads the platform timer, which is safe in any context.
let mut edx: u32; unsafe { core::arch::x86_64::_rdtsc() }
unsafe {
asm!("rdtsc", out("eax") eax, out("edx") edx);
}
(edx as u64) << 32 | eax as u64
}
/// Read the starting number of ticks from the platform timer.
pub fn start() -> u64 {
let rax: u64;
unsafe {
asm!(
"mfence",
"lfence",
"rdtsc",
"shl rdx, 32",
"or rax, rdx",
out("rax") rax
);
}
rax
}
/// Read the ending number of ticks from the platform timer.
pub fn stop() -> u64 {
let rax: u64;
unsafe {
asm!(
"rdtsc",
"lfence",
"shl rdx, 32",
"or rax, rdx",
out("rax") rax
);
}
rax
} }
/// Measure the frequency of the platform timer. /// Measure the frequency of the platform timer.
/// NOTE: Intentionally, we do not synchronize rdtsc during measurement to match systemd behavior.
fn measure_frequency() -> u64 { fn measure_frequency() -> u64 {
let start = start(); let start = ticks();
uefi::boot::stall(MEASURE_FREQUENCY_DURATION); uefi::boot::stall(MEASURE_FREQUENCY_DURATION);
let stop = stop(); let stop = ticks();
let elapsed = stop.wrapping_sub(start) as f64; let elapsed = stop.wrapping_sub(start) as f64;
(elapsed / MEASURE_FREQUENCY_DURATION.as_secs_f64()) as u64 (elapsed / MEASURE_FREQUENCY_DURATION.as_secs_f64()) as u64
} }
@@ -62,5 +25,5 @@ fn measure_frequency() -> u64 {
/// On x86_64, this is slightly expensive, so it should be done once. /// On x86_64, this is slightly expensive, so it should be done once.
pub fn frequency() -> TickFrequency { pub fn frequency() -> TickFrequency {
let frequency = measure_frequency(); let frequency = measure_frequency();
TickFrequency::Measured(frequency, MEASURE_FREQUENCY_DURATION) TickFrequency::Measured(frequency)
} }

View File

@@ -85,7 +85,9 @@ impl PlatformTpm {
}; };
// Check if the TPM supports `GetActivePcrBanks`, and if it doesn't return zero. // Check if the TPM supports `GetActivePcrBanks`, and if it doesn't return zero.
if handle.version().major < 1 || handle.version().major == 1 && handle.version().minor < 1 { if (handle.version().major < 1)
|| (handle.version().major == 1 && (handle.version().minor < 1))
{
return Ok(0); return Ok(0);
} }

View File

@@ -1,27 +1,14 @@
use crate::logger;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::os::uefi as uefi_std;
/// Initializes the UEFI environment. /// Initializes the UEFI environment.
///
/// This fetches the system table and current image handle from uefi_std and injects
/// them into the uefi crate.
pub fn init() -> Result<()> { pub fn init() -> Result<()> {
// Acquire the system table and image handle from the uefi_std environment. // Initialize the logger for Sprout.
let system_table = uefi_std::env::system_table(); // NOTE: This cannot use a result type as errors need to be printed
let image_handle = uefi_std::env::image_handle(); // using the logger, which is not initialized until this returns.
logger::init();
// SAFETY: The UEFI variables above come from the Rust std. // Initialize further UEFI internals.
// These variables are not-null and calling the uefi crates with these values is validated uefi::helpers::init().context("unable to initialize uefi environment")?;
// to be corrected by hand.
unsafe {
// Set the system table and image handle.
uefi::table::set_system_table(system_table.as_ptr().cast());
let handle = uefi::Handle::from_ptr(image_handle.as_ptr().cast())
.context("unable to resolve image handle")?;
uefi::boot::set_image_handle(handle);
}
// Initialize the uefi logger mechanism and other helpers.
uefi::helpers::init().context("unable to initialize uefi")?;
Ok(()) Ok(())
} }

View File

@@ -1,6 +1,10 @@
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use core::ops::Deref;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::ops::Deref;
use uefi::boot::SearchType; use uefi::boot::SearchType;
use uefi::fs::{FileSystem, Path}; use uefi::fs::{FileSystem, Path};
use uefi::proto::device_path::text::{AllowShortcuts, DevicePathFromText, DisplayOnly}; use uefi::proto::device_path::text::{AllowShortcuts, DevicePathFromText, DisplayOnly};

View File

@@ -1,3 +1,5 @@
use alloc::vec;
use alloc::vec::Vec;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use uefi::proto::console::gop::{BltOp, BltPixel, BltRegion, GraphicsOutput}; use uefi::proto::console::gop::{BltOp, BltPixel, BltRegion, GraphicsOutput};

View File

@@ -1,5 +1,8 @@
use alloc::boxed::Box;
use alloc::vec::Vec;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use std::ffi::c_void; use core::ffi::c_void;
use core::ptr;
use uefi::proto::device_path::DevicePath; use uefi::proto::device_path::DevicePath;
use uefi::proto::device_path::build::DevicePathBuilder; use uefi::proto::device_path::build::DevicePathBuilder;
use uefi::proto::device_path::build::media::Vendor; use uefi::proto::device_path::build::media::Vendor;
@@ -261,8 +264,7 @@ impl MediaLoaderHandle {
let protocol = Box::from_raw(self.protocol); let protocol = Box::from_raw(self.protocol);
// Retrieve a box for the data we passed in. // Retrieve a box for the data we passed in.
let slice = let slice = ptr::slice_from_raw_parts_mut(protocol.address as *mut u8, protocol.length);
std::ptr::slice_from_raw_parts_mut(protocol.address as *mut u8, protocol.length);
let data = Box::from_raw(slice); let data = Box::from_raw(slice);
// Drop all the allocations explicitly, as we don't want to leak them. // Drop all the allocations explicitly, as we don't want to leak them.

View File

@@ -1,4 +1,7 @@
use crate::utils; use crate::utils;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use log::warn; use log::warn;
use uefi::{CString16, guid}; use uefi::{CString16, guid};
@@ -142,6 +145,8 @@ impl VariableController {
self.set(key, &value.to_le_bytes(), class) self.set(key, &value.to_le_bytes(), class)
} }
/// Remove the variable specified by `key`.
/// This can fail if the variable is not set.
pub fn remove(&self, key: &str) -> Result<()> { pub fn remove(&self, key: &str) -> Result<()> {
let name = Self::name(key)?; let name = Self::name(key)?;

View File

@@ -1,5 +1,5 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::iter::Peekable; use core::iter::Peekable;
/// Handles single character advancement and comparison. /// Handles single character advancement and comparison.
macro_rules! handle_single_char { macro_rules! handle_single_char {
@@ -23,9 +23,9 @@ pub fn compare_versions_optional(a: Option<&str>, b: Option<&str>) -> Ordering {
match (a, b) { match (a, b) {
// If both have values, compare them. // If both have values, compare them.
(Some(a), Some(b)) => compare_versions(a, b), (Some(a), Some(b)) => compare_versions(a, b),
// If the second value is None, return that it is less than the first. // If the second value is None, then `a` is less than `b`.
(Some(_a), None) => Ordering::Less, (Some(_a), None) => Ordering::Less,
// If the first value is None, return that it is greater than the second. // If the first value is None, the `a` is greater than `b`.
(None, Some(_b)) => Ordering::Greater, (None, Some(_b)) => Ordering::Greater,
// If both values are None, return that they are equal. // If both values are None, return that they are equal.
(None, None) => Ordering::Equal, (None, None) => Ordering::Equal,

View File

@@ -4,7 +4,6 @@
- Modern Debian release: tested on Debian 13 ARM64 - Modern Debian release: tested on Debian 13 ARM64
- EFI System Partition mounted on `/boot/efi` (the default) - EFI System Partition mounted on `/boot/efi` (the default)
- ext4 or FAT32/exFAT formatted `/boot` partition
- You will need the following packages installed: `openssl`, `shim-signed`, `mokutil`, `sbsigntool` - You will need the following packages installed: `openssl`, `shim-signed`, `mokutil`, `sbsigntool`
## Step 1: Generate and Install Secure Boot Key ## Step 1: Generate and Install Secure Boot Key
@@ -16,9 +15,9 @@ $ mkdir -p /etc/sprout/secure-boot
$ cd /etc/sprout/secure-boot $ cd /etc/sprout/secure-boot
# Generate a MOK key and certificate. # Generate a MOK key and certificate.
$ openssl req \ $ openssl req \
-newkey rsa:4096 -nodes -keyout mok.key \ -newkey rsa:4096 -nodes -keyout mok.key \
-new -x509 -sha256 -days 3650 -subj '/CN=Sprout Secure Boot/' \ -new -x509 -sha256 -days 3650 -subj '/CN=Sprout Secure Boot/' \
-out mok.crt -out mok.crt
# Generate a DER encoded certificate for enrollment. # Generate a DER encoded certificate for enrollment.
$ openssl x509 -outform DER -in mok.crt -out mok.cer $ openssl x509 -outform DER -in mok.crt -out mok.cer
# Import the certificate into the Secure Boot environment. # Import the certificate into the Secure Boot environment.

View File

@@ -0,0 +1,139 @@
# Setup Sprout for openSUSE with Secure Boot
**NOTE:** This guide may not function as written if the system validates hashes.
If your system validates hashes in the shim, you will need to use MokManager to enroll the hashes
of every EFI file involved, such as Sprout and any EFI drivers.
## Prerequisites
- Modern openSUSE release: tested on openSUSE Tumbleweed ARM64
- EFI System Partition mounted on `/boot/efi` (the default)
- You will need the following packages installed: `openssl`, `shim`, `mokutil`, `sbsigntools`
## Step 1: Generate and Install Secure Boot Key
```bash
# Create a directory to store the Secure Boot MOK key and certificates.
$ mkdir -p /etc/sprout/secure-boot
# Change to the created directory.
$ cd /etc/sprout/secure-boot
# Generate a MOK key and certificate.
$ openssl req \
-newkey rsa:4096 -nodes -keyout mok.key \
-new -x509 -sha256 -days 3650 -subj '/CN=Sprout Secure Boot/' \
-out mok.crt
# Generate a DER encoded certificate for enrollment.
$ openssl x509 -outform DER -in mok.crt -out mok.cer
# Import the certificate into the Secure Boot environment.
# This will ask you to make a password that will be used during enrollment.
$ mokutil --import mok.cer
# Reboot your machine.
# During boot, MOK enrollment should appear. If it doesn't, ensure you are booting into the shim.
# Press any key to begin MOK management. Select "Enroll MOK".
# Select "View key 0", and ensure the subject says "CN=Sprout Secure Boot".
# If the subject does not match, something has gone wrong with MOK enrollment.
# Press Enter to continue, then select the "Continue" option.
# When it asks to enroll the key, select the "Yes" option.
# Enter the password that you created during the mokutil --import step.
# Select "Reboot" to boot back into your Operating System.
```
## Step 2: Prepare the Secure Boot Environment
```bash
# Create a directory for Sprout EFI artifacts.
$ mkdir -p /boot/efi/EFI/sprout
# For x86_64, copy the following artifacts to the Sprout EFI directory.
$ cp /usr/share/efi/x86_64/shim.efi /boot/efi/EFI/sprout/shim.efi
$ cp /usr/share/efi/x86_64/MokManager.efi /boot/efi/EFI/sprout/MokManager.efi
$ cp /usr/share/efi/x86_64/fallback.efi /boot/efi/EFI/sprout/fallback.efi
# For aarch64, copy the following artifacts to the Sprout EFI directory.
$ cp /usr/share/efi/aarch64/shim.efi /boot/efi/EFI/sprout/shim.efi
$ cp /usr/share/efi/aarch64/MokManager.efi /boot/efi/EFI/sprout/MokManager.efi
$ cp /usr/share/efi/aarch64/fallback.efi /boot/efi/EFI/sprout/fallback.efi
```
## Step 3: Install Unsigned Sprout
Download the latest sprout.efi release from the [GitHub releases page](https://github.com/edera-dev/sprout/releases).
For x86_64 systems, download the `sprout-x86_64.efi` file, and for ARM64 systems, download the `sprout-aarch64.efi`
file.
Copy the downloaded `sprout.efi` file to `/boot/efi/EFI/sprout/sprout.unsigned.efi` on your EFI System Partition.
## Step 4: Sign Sprout for Secure Boot
```bash
# Sign the unsigned Sprout artifact and name it grub.efi which is what the shim will call.
$ sbsign \
--key /etc/sprout/secure-boot/mok.key \
--cert /etc/sprout/secure-boot/mok.crt \
--output /boot/efi/EFI/sprout/grub.efi \
/boot/efi/EFI/sprout/sprout.unsigned.efi
```
## Step 5: Install and Sign EFI Drivers
You will need a filesystem EFI driver if `/boot` is not FAT32 or ExFAT.
If `/boot` is FAT32 or ExFAT, you can skip this step.
Most Debian systems use an ext4 filesystem for `/boot`.
You can download an EFI filesystem driver from [EfiFs releases](https://github.com/pbatard/EfiFs/releases).
For ext4, download the `ext2` file for your platform. It should work for ext4 filesystems too.
If you have an EFI driver, copy the driver to `/boot/efi/EFI/sprout/DRIVER_NAME.unsigned.efi` for signing.
For example, the `ext4` driver, copy the `ext4.efi` file to `/boot/efi/EFI/sprout/ext4.unsigned.efi`.
Then sign the driver with the Sprout Secure Boot key:
```bash
# Sign the ext4 driver at ext4.unsigned.efi, placing it at ext4.efi, which will be used in the configuration.
$ sbsign \
--key /etc/sprout/secure-boot/mok.key \
--cert /etc/sprout/secure-boot/mok.crt \
--output /boot/efi/EFI/sprout/ext4.efi \
/boot/efi/EFI/sprout/ext4.unsigned.efi
```
## Step 6: Create Sprout Configuration
Write the following to the file `/boot/efi/sprout.toml`:
```toml
# sprout configuration: version 1
version = 1
# global values.
[values]
# your linux kernel command line.
linux-options = "root=UUID=MY_ROOT_UUID"
# load an ext4 EFI driver.
# skip this if you do not have a filesystem driver.
# if your filesystem driver is not named ext4, change accordingly.
[drivers.ext4]
path = "\\EFI\\sprout\\ext4.efi"
# global options.
[options]
# enable autoconfiguration by detecting bls enabled filesystems
# or linux kernels and generating boot entries for them.
autoconfigure = true
```
Ensure you add the signed driver paths to the configuration, not the unsigned ones.
If you do not have any drivers, exclude the drivers section entirely.
## Step 7: Configure Sprout Boot Entry
In the following commands, replace /dev/ESP_PARTITION with the actual path to the ESP partition block device.
```bash
# Run this command to add Sprout as the default boot entry.
$ efibootmgr -d /dev/ESP_PARTITION -c -L 'Sprout' -l '\EFI\sprout\shim.efi'
```
Reboot your machine and it should boot into Sprout.
If Sprout fails to boot, it should boot into the original bootloader.

View File

@@ -4,7 +4,6 @@
- Modern Ubuntu release: tested on Ubuntu 25.10 ARM64 - Modern Ubuntu release: tested on Ubuntu 25.10 ARM64
- EFI System Partition mounted on `/boot/efi` (the default) - EFI System Partition mounted on `/boot/efi` (the default)
- ext4 or FAT32/exFAT formatted `/boot` partition
## Step 1: Generate and Install Secure Boot Key ## Step 1: Generate and Install Secure Boot Key
@@ -15,9 +14,9 @@ $ mkdir -p /etc/sprout/secure-boot
$ cd /etc/sprout/secure-boot $ cd /etc/sprout/secure-boot
# Generate a MOK key and certificate. # Generate a MOK key and certificate.
$ openssl req \ $ openssl req \
-newkey rsa:4096 -nodes -keyout mok.key \ -newkey rsa:4096 -nodes -keyout mok.key \
-new -x509 -sha256 -days 3650 -subj '/CN=Sprout Secure Boot/' \ -new -x509 -sha256 -days 3650 -subj '/CN=Sprout Secure Boot/' \
-out mok.crt -out mok.crt
# Generate a DER encoded certificate for enrollment. # Generate a DER encoded certificate for enrollment.
$ openssl x509 -outform DER -in mok.crt -out mok.cer $ openssl x509 -outform DER -in mok.crt -out mok.cer
# Import the certificate into the Secure Boot environment. # Import the certificate into the Secure Boot environment.

View File

@@ -27,4 +27,6 @@ if command -v docker >/dev/null 2>&1; then
delete_image sprout-kernel-build-aarch64 || true delete_image sprout-kernel-build-aarch64 || true
delete_image sprout-boot-x86_64 || true delete_image sprout-boot-x86_64 || true
delete_image sprout-boot-aarch64 || true delete_image sprout-boot-aarch64 || true
delete_image sprout-xen-x86_64 || true
delete_image sprout-xen-aarch64 || true
fi fi

View File

@@ -55,6 +55,13 @@ else
fi fi
fi fi
if [ "${NO_INPUT}" != "1" ]; then
set -- "${@}" \
-device qemu-xhci \
-device usb-kbd \
-device usb-mouse
fi
rm -f "${FINAL_DIR}/ovmf-boot.fd" rm -f "${FINAL_DIR}/ovmf-boot.fd"
cp "${FINAL_DIR}/ovmf.fd" "${FINAL_DIR}/ovmf-boot.fd" cp "${FINAL_DIR}/ovmf.fd" "${FINAL_DIR}/ovmf-boot.fd"
if [ "${TARGET_ARCH}" = "aarch64" ]; then if [ "${TARGET_ARCH}" = "aarch64" ]; then

View File

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