mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 12:30:17 +00:00
feat(bls): basic support for boot loader interface
This commit is contained in:
@@ -28,6 +28,7 @@ pub struct BootableEntry {
|
|||||||
context: Rc<SproutContext>,
|
context: Rc<SproutContext>,
|
||||||
declaration: EntryDeclaration,
|
declaration: EntryDeclaration,
|
||||||
default: bool,
|
default: bool,
|
||||||
|
pin_name: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BootableEntry {
|
impl BootableEntry {
|
||||||
@@ -44,6 +45,7 @@ impl BootableEntry {
|
|||||||
context,
|
context,
|
||||||
declaration,
|
declaration,
|
||||||
default: false,
|
default: false,
|
||||||
|
pin_name: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +74,11 @@ impl BootableEntry {
|
|||||||
self.default
|
self.default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch whether the entry is pinned, which prevents prefixing.
|
||||||
|
pub fn is_pin_name(&self) -> bool {
|
||||||
|
self.pin_name
|
||||||
|
}
|
||||||
|
|
||||||
/// Swap out the context of the entry.
|
/// Swap out the context of the entry.
|
||||||
pub fn swap_context(&mut self, context: Rc<SproutContext>) {
|
pub fn swap_context(&mut self, context: Rc<SproutContext>) {
|
||||||
self.context = context;
|
self.context = context;
|
||||||
@@ -87,6 +94,11 @@ impl BootableEntry {
|
|||||||
self.default = true;
|
self.default = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark this entry as being pinned, which prevents prefixing.
|
||||||
|
pub fn mark_pin_name(&mut self) {
|
||||||
|
self.pin_name = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Prepend the name of the entry with `prefix`.
|
/// Prepend the name of the entry with `prefix`.
|
||||||
pub fn prepend_name_prefix(&mut self, prefix: &str) {
|
pub fn prepend_name_prefix(&mut self, prefix: &str) {
|
||||||
self.name.insert_str(0, prefix);
|
self.name.insert_str(0, prefix);
|
||||||
|
|||||||
@@ -83,13 +83,16 @@ pub fn generate(context: Rc<SproutContext>, bls: &BlsConfiguration) -> Result<Ve
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the file name of the filesystem item.
|
// Get the file name of the filesystem item.
|
||||||
let name = entry.file_name().to_string();
|
let mut name = entry.file_name().to_string();
|
||||||
|
|
||||||
// Ignore files that are not .conf files.
|
// Ignore files that are not .conf files.
|
||||||
if !name.to_lowercase().ends_with(".conf") {
|
if !name.to_lowercase().ends_with(".conf") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the .conf extension.
|
||||||
|
name.truncate(name.len() - 5);
|
||||||
|
|
||||||
// Create a mutable path so we can append the file name to produce the full path.
|
// Create a mutable path so we can append the file name to produce the full path.
|
||||||
let mut full_entry_path = entries_path.to_path_buf();
|
let mut full_entry_path = entries_path.to_path_buf();
|
||||||
full_entry_path.push(entry.file_name());
|
full_entry_path.push(entry.file_name());
|
||||||
@@ -125,13 +128,21 @@ pub fn generate(context: Rc<SproutContext>, bls: &BlsConfiguration) -> Result<Ve
|
|||||||
context.set("options", options);
|
context.set("options", options);
|
||||||
context.set("initrd", initrd);
|
context.set("initrd", initrd);
|
||||||
|
|
||||||
// Add the entry to the list with a frozen context.
|
// Produce a new bootable entry.
|
||||||
entries.push(BootableEntry::new(
|
let mut entry = BootableEntry::new(
|
||||||
name,
|
name,
|
||||||
bls.entry.title.clone(),
|
bls.entry.title.clone(),
|
||||||
context.freeze(),
|
context.freeze(),
|
||||||
bls.entry.clone(),
|
bls.entry.clone(),
|
||||||
));
|
);
|
||||||
|
|
||||||
|
// Pin the entry name to prevent prefixing.
|
||||||
|
// This is needed as the bootloader interface requires the name to be
|
||||||
|
// the same as the entry file name, minus the .conf extension.
|
||||||
|
entry.mark_pin_name();
|
||||||
|
|
||||||
|
// Add the entry to the list with a frozen context.
|
||||||
|
entries.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(entries)
|
Ok(entries)
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use uefi::Guid;
|
use uefi::{CString16, Guid, guid};
|
||||||
|
use uefi_raw::table::runtime::{VariableAttributes, VariableVendor};
|
||||||
|
|
||||||
/// Bootloader Interface support.
|
/// Bootloader Interface support.
|
||||||
pub struct BootloaderInterface;
|
pub struct BootloaderInterface;
|
||||||
|
|
||||||
impl BootloaderInterface {
|
impl BootloaderInterface {
|
||||||
|
/// Bootloader Interface GUID from https://systemd.io/BOOT_LOADER_INTERFACE
|
||||||
|
const VENDOR: VariableVendor = VariableVendor(guid!("4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"));
|
||||||
|
|
||||||
/// Tell the system that Sprout was initialized at the current time.
|
/// Tell the system that Sprout was initialized at the current time.
|
||||||
pub fn mark_init() -> Result<()> {
|
pub fn mark_init() -> Result<()> {
|
||||||
// TODO(azenla): Implement support for LoaderTimeInitUSec here.
|
// TODO(azenla): Implement support for LoaderTimeInitUSec here.
|
||||||
@@ -18,26 +22,54 @@ impl BootloaderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Tell the system what the partition GUID of the ESP Sprout was booted from is.
|
/// Tell the system what the partition GUID of the ESP Sprout was booted from is.
|
||||||
pub fn set_partition_guid(_guid: &Guid) -> Result<()> {
|
pub fn set_partition_guid(guid: &Guid) -> Result<()> {
|
||||||
// TODO(azenla): Implement support for LoaderDevicePartUUID here.
|
Self::set_cstr16("LoaderDevicePartUUID", &guid.to_string())
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tell the system what boot entries are available.
|
/// Tell the system what boot entries are available.
|
||||||
pub fn set_entries<N: AsRef<str>>(_entries: impl Iterator<Item = N>) -> Result<()> {
|
pub fn set_entries<N: AsRef<str>>(entries: impl Iterator<Item = N>) -> Result<()> {
|
||||||
// TODO(azenla): Implement support for LoaderEntries here.
|
// Entries are stored as a null-terminated list of CString16 strings back to back.
|
||||||
Ok(())
|
// Iterate over the entries and convert them to CString16 placing them into data.
|
||||||
|
let mut data = Vec::new();
|
||||||
|
for entry in entries {
|
||||||
|
// Convert the entry to a CString16.
|
||||||
|
let entry = CString16::try_from(entry.as_ref())
|
||||||
|
.context("unable to convert entry to CString16")?;
|
||||||
|
// Write the bytes (including the null terminator) into the data buffer.
|
||||||
|
data.extend_from_slice(entry.as_bytes());
|
||||||
|
}
|
||||||
|
Self::set("LoaderEntries", &data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tell the system what the default boot entry is.
|
/// Tell the system what the default boot entry is.
|
||||||
pub fn set_default_entry(_entry: String) -> Result<()> {
|
pub fn set_default_entry(entry: String) -> Result<()> {
|
||||||
// TODO(azenla): Implement support for LoaderEntryDefault here.
|
Self::set_cstr16("LoaderEntryDefault", &entry)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tell the system what the selected boot entry is.
|
/// Tell the system what the selected boot entry is.
|
||||||
pub fn set_selected_entry(_entry: String) -> Result<()> {
|
pub fn set_selected_entry(entry: String) -> Result<()> {
|
||||||
// TODO(azenla): Implement support for LoaderEntrySelected here.
|
Self::set_cstr16("LoaderEntrySelected", &entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [VariableAttributes] for bootloader interface variables.
|
||||||
|
fn attributes() -> VariableAttributes {
|
||||||
|
VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a bootloader interface variable specified by `key` to `value`.
|
||||||
|
fn set(key: &str, value: &[u8]) -> Result<()> {
|
||||||
|
let name =
|
||||||
|
CString16::try_from(key).context("unable to convert variable name to CString16")?;
|
||||||
|
uefi::runtime::set_variable(&name, &Self::VENDOR, Self::attributes(), value)
|
||||||
|
.with_context(|| format!("unable to set efi variable {}", key))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a bootloader interface variable specified by `key` to `value`, converting the value to
|
||||||
|
/// a [CString16].
|
||||||
|
fn set_cstr16(key: &str, value: &str) -> Result<()> {
|
||||||
|
let value =
|
||||||
|
CString16::try_from(value).context("unable to convert variable value to CString16")?;
|
||||||
|
Self::set(key, value.as_bytes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,13 +182,17 @@ fn run() -> Result<()> {
|
|||||||
for (name, generator) in config.generators {
|
for (name, generator) in config.generators {
|
||||||
let context = context.fork().freeze();
|
let context = context.fork().freeze();
|
||||||
|
|
||||||
// We will prefix all entries with [name]-.
|
// We will prefix all entries with [name]-, provided the name is not pinned.
|
||||||
let prefix = format!("{}-", name);
|
let prefix = format!("{}-", name);
|
||||||
|
|
||||||
// Add all the entries generated by the generator to the entry list.
|
// Add all the entries generated by the generator to the entry list.
|
||||||
// The generator specifies the context associated with the entry.
|
// The generator specifies the context associated with the entry.
|
||||||
for mut entry in generators::generate(context.clone(), &generator)? {
|
for mut entry in generators::generate(context.clone(), &generator)? {
|
||||||
entry.prepend_name_prefix(&prefix);
|
// If the entry name is not pinned, prepend the name prefix.
|
||||||
|
if !entry.is_pin_name() {
|
||||||
|
entry.prepend_name_prefix(&prefix);
|
||||||
|
}
|
||||||
|
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user