Merge pull request #31 from nix-community/aarch64

This commit is contained in:
Ryan Lahfa 2023-09-29 02:41:12 +02:00 committed by GitHub
commit 644dc8a269
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 147 additions and 22 deletions

View File

@ -106,11 +106,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1689951833, "lastModified": 1695859332,
"narHash": "sha256-wdpIgb5X0p85RRne74TeUOp9ti7a1k9KDSe4NzsaAGk=", "narHash": "sha256-w2a7NW3VtI5FgFPUKslYRGAj5Qb7y4i0I2QO0S/lBMQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "ebf4e87429ce7faa51a86a36a7b2e615c8bcc735", "rev": "248a83fffc10b627da67fa6b25d2c13fc7542628",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -74,11 +74,12 @@
"x86_64-linux" "x86_64-linux"
# Not actively tested, but may work: # Not actively tested, but may work:
# "aarch64-linux" "aarch64-linux"
]; ];
perSystem = { config, system, pkgs, ... }: perSystem = { config, system, pkgs, ... }:
let let
rustTarget = "${pkgs.hostPlatform.qemuArch}-unknown-uefi";
pkgs = import nixpkgs { pkgs = import nixpkgs {
system = system; system = system;
overlays = [ overlays = [
@ -89,7 +90,7 @@
inherit (pkgs) lib; inherit (pkgs) lib;
uefi-rust-stable = pkgs.rust-bin.fromRustupToolchainFile ./rust/uefi/rust-toolchain.toml; uefi-rust-stable = pkgs.rust-bin.fromRustupToolchainFile ./rust/uefi/rust-toolchain.toml;
craneLib = crane.lib.x86_64-linux.overrideToolchain uefi-rust-stable; craneLib = crane.lib.${system}.overrideToolchain uefi-rust-stable;
# Build attributes for a Rust application. # Build attributes for a Rust application.
buildRustApp = lib.makeOverridable ( buildRustApp = lib.makeOverridable (
@ -145,7 +146,7 @@
stubCrane = buildRustApp { stubCrane = buildRustApp {
pname = "lanzaboote-stub"; pname = "lanzaboote-stub";
src = craneLib.cleanCargoSource ./rust/uefi; src = craneLib.cleanCargoSource ./rust/uefi;
target = "x86_64-unknown-uefi"; target = rustTarget;
doCheck = false; doCheck = false;
}; };
@ -234,6 +235,7 @@
let let
systemdUkify = pkgs.systemdMinimal.override { systemdUkify = pkgs.systemdMinimal.override {
withEfi = true; withEfi = true;
withBootloader = true;
withUkify = true; withUkify = true;
}; };
in in

View File

@ -103,7 +103,10 @@ in
${sbctlWithPki}/bin/sbctl enroll-keys --yes-this-might-brick-my-machine ${sbctlWithPki}/bin/sbctl enroll-keys --yes-this-might-brick-my-machine
''} ''}
# Use the system from the kernel's hostPlatform because this should
# always, even in the cross compilation case, be the right system.
${cfg.package}/bin/lzbt install \ ${cfg.package}/bin/lzbt install \
--system ${config.boot.kernelPackages.stdenv.hostPlatform.system} \
--systemd ${config.systemd.package} \ --systemd ${config.systemd.package} \
--systemd-boot-loader-config ${loaderConfigFile} \ --systemd-boot-loader-config ${loaderConfigFile} \
--public-key ${cfg.publicKeyFile} \ --public-key ${cfg.publicKeyFile} \

View File

@ -0,0 +1,35 @@
use std::path::PathBuf;
use anyhow::{bail, Result};
/// Supported system
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Architecture {
X86,
AArch64,
}
impl Architecture {
pub fn efi_representation(&self) -> &str {
match self {
Self::X86 => "x64",
Self::AArch64 => "aa64",
}
}
pub fn efi_fallback_filename(&self) -> PathBuf {
format!("BOOT{}.EFI", self.efi_representation().to_ascii_uppercase()).into()
}
}
impl Architecture {
/// Converts from a NixOS system double to a supported system
pub fn from_nixos_system(system_double: &str) -> Result<Self> {
Ok(match system_double {
"x86_64-linux" => Self::X86,
"aarch64-linux" => Self::AArch64,
_ => bail!(format!("Unsupported NixOS system: {}.", system_double)),
})
}
}

View File

@ -3,14 +3,16 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use anyhow::{Context, Result}; use anyhow::{bail, Context, Result};
use indoc::indoc;
use crate::architecture::Architecture;
use crate::generation::Generation; use crate::generation::Generation;
/// Generic ESP paths which can be specific to a bootloader /// Generic ESP paths which can be specific to a bootloader
pub trait EspPaths<const N: usize> { pub trait EspPaths<const N: usize> {
/// Build an ESP path structure out of the ESP root directory /// Build an ESP path structure out of the ESP root directory
fn new(esp: impl AsRef<Path>) -> Self; fn new(esp: impl AsRef<Path>, arch: Architecture) -> Self;
/// Return the used file paths to store as garbage collection roots. /// Return the used file paths to store as garbage collection roots.
fn iter(&self) -> std::array::IntoIter<&PathBuf, N>; fn iter(&self) -> std::array::IntoIter<&PathBuf, N>;
@ -33,8 +35,17 @@ impl EspGenerationPaths {
pub fn new<const N: usize, P: EspPaths<N>>( pub fn new<const N: usize, P: EspPaths<N>>(
esp_paths: &P, esp_paths: &P,
generation: &Generation, generation: &Generation,
system: Architecture,
) -> Result<Self> { ) -> Result<Self> {
let bootspec = &generation.spec.bootspec.bootspec; let bootspec = &generation.spec.bootspec.bootspec;
let bootspec_system: Architecture = Architecture::from_nixos_system(&bootspec.system)?;
if system != bootspec_system {
bail!(indoc! {r#"
The CPU architecture declared in your module differs from the one declared in the
bootspec of the current generation.
"#})
}
Ok(Self { Ok(Self {
kernel: esp_paths kernel: esp_paths

View File

@ -1,3 +1,4 @@
pub mod architecture;
pub mod esp; pub mod esp;
pub mod gc; pub mod gc;
pub mod generation; pub mod generation;

View File

@ -0,0 +1,19 @@
use std::path::PathBuf;
use lanzaboote_tool::architecture::Architecture;
/// Systemd-specific architecture helpers
pub trait SystemdArchitectureExt {
fn systemd_stub_filename(&self) -> PathBuf;
fn systemd_filename(&self) -> PathBuf;
}
impl SystemdArchitectureExt for Architecture {
fn systemd_stub_filename(&self) -> PathBuf {
format!("linux{}.efi.stub", self.efi_representation()).into()
}
fn systemd_filename(&self) -> PathBuf {
format!("systemd-boot{}.efi", self.efi_representation()).into()
}
}

View File

@ -4,6 +4,7 @@ use anyhow::{Context, Result};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use crate::install; use crate::install;
use lanzaboote_tool::architecture::Architecture;
use lanzaboote_tool::signature::KeyPair; use lanzaboote_tool::signature::KeyPair;
/// The default log level. /// The default log level.
@ -30,6 +31,10 @@ enum Commands {
#[derive(Parser)] #[derive(Parser)]
struct InstallCommand { struct InstallCommand {
/// System for lanzaboote binaries, e.g. defines the EFI fallback path
#[arg(long)]
system: String,
/// Systemd path /// Systemd path
#[arg(long)] #[arg(long)]
systemd: PathBuf, systemd: PathBuf,
@ -90,6 +95,7 @@ fn install(args: InstallCommand) -> Result<()> {
install::Installer::new( install::Installer::new(
PathBuf::from(lanzaboote_stub), PathBuf::from(lanzaboote_stub),
Architecture::from_nixos_system(&args.system)?,
args.systemd, args.systemd,
args.systemd_boot_loader_config, args.systemd_boot_loader_config,
key_pair, key_pair,

View File

@ -1,5 +1,7 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use crate::architecture::SystemdArchitectureExt;
use lanzaboote_tool::architecture::Architecture;
use lanzaboote_tool::esp::EspPaths; use lanzaboote_tool::esp::EspPaths;
/// Paths to the boot files that are not specific to a generation. /// Paths to the boot files that are not specific to a generation.
@ -18,7 +20,7 @@ pub struct SystemdEspPaths {
} }
impl EspPaths<10> for SystemdEspPaths { impl EspPaths<10> for SystemdEspPaths {
fn new(esp: impl AsRef<Path>) -> Self { fn new(esp: impl AsRef<Path>, architecture: Architecture) -> Self {
let esp = esp.as_ref(); let esp = esp.as_ref();
let efi = esp.join("EFI"); let efi = esp.join("EFI");
let efi_nixos = efi.join("nixos"); let efi_nixos = efi.join("nixos");
@ -34,9 +36,9 @@ impl EspPaths<10> for SystemdEspPaths {
nixos: efi_nixos, nixos: efi_nixos,
linux: efi_linux, linux: efi_linux,
efi_fallback_dir: efi_efi_fallback_dir.clone(), efi_fallback_dir: efi_efi_fallback_dir.clone(),
efi_fallback: efi_efi_fallback_dir.join("BOOTX64.EFI"), efi_fallback: efi_efi_fallback_dir.join(architecture.efi_fallback_filename()),
systemd: efi_systemd.clone(), systemd: efi_systemd.clone(),
systemd_boot: efi_systemd.join("systemd-bootx64.efi"), systemd_boot: efi_systemd.join(architecture.systemd_filename()),
loader, loader,
systemd_boot_loader_config, systemd_boot_loader_config,
} }

View File

@ -10,8 +10,10 @@ use anyhow::{anyhow, Context, Result};
use nix::unistd::syncfs; use nix::unistd::syncfs;
use tempfile::TempDir; use tempfile::TempDir;
use crate::architecture::SystemdArchitectureExt;
use crate::esp::SystemdEspPaths; use crate::esp::SystemdEspPaths;
use crate::version::SystemdVersion; use crate::version::SystemdVersion;
use lanzaboote_tool::architecture::Architecture;
use lanzaboote_tool::esp::{EspGenerationPaths, EspPaths}; use lanzaboote_tool::esp::{EspGenerationPaths, EspPaths};
use lanzaboote_tool::gc::Roots; use lanzaboote_tool::gc::Roots;
use lanzaboote_tool::generation::{Generation, GenerationLink}; use lanzaboote_tool::generation::{Generation, GenerationLink};
@ -30,11 +32,14 @@ pub struct Installer {
configuration_limit: usize, configuration_limit: usize,
esp_paths: SystemdEspPaths, esp_paths: SystemdEspPaths,
generation_links: Vec<PathBuf>, generation_links: Vec<PathBuf>,
arch: Architecture,
} }
impl Installer { impl Installer {
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
lanzaboote_stub: PathBuf, lanzaboote_stub: PathBuf,
arch: Architecture,
systemd: PathBuf, systemd: PathBuf,
systemd_boot_loader_config: PathBuf, systemd_boot_loader_config: PathBuf,
key_pair: KeyPair, key_pair: KeyPair,
@ -43,7 +48,7 @@ impl Installer {
generation_links: Vec<PathBuf>, generation_links: Vec<PathBuf>,
) -> Self { ) -> Self {
let mut gc_roots = Roots::new(); let mut gc_roots = Roots::new();
let esp_paths = SystemdEspPaths::new(esp); let esp_paths = SystemdEspPaths::new(esp, arch);
gc_roots.extend(esp_paths.iter()); gc_roots.extend(esp_paths.iter());
Self { Self {
@ -56,6 +61,7 @@ impl Installer {
configuration_limit, configuration_limit,
esp_paths, esp_paths,
generation_links, generation_links,
arch,
} }
} }
@ -238,7 +244,7 @@ impl Installer {
let bootspec = &generation.spec.bootspec.bootspec; let bootspec = &generation.spec.bootspec.bootspec;
let esp_gen_paths = EspGenerationPaths::new(&self.esp_paths, generation)?; let esp_gen_paths = EspGenerationPaths::new(&self.esp_paths, generation, self.arch)?;
self.gc_roots.extend(esp_gen_paths.to_iter()); self.gc_roots.extend(esp_gen_paths.to_iter());
let initrd_content = fs::read( let initrd_content = fs::read(
@ -284,7 +290,7 @@ impl Installer {
let bootspec = &generation.spec.bootspec.bootspec; let bootspec = &generation.spec.bootspec.bootspec;
let esp_gen_paths = EspGenerationPaths::new(&self.esp_paths, generation)?; let esp_gen_paths = EspGenerationPaths::new(&self.esp_paths, generation, self.arch)?;
let kernel_cmdline = let kernel_cmdline =
assemble_kernel_cmdline(&bootspec.init, bootspec.kernel_params.clone()); assemble_kernel_cmdline(&bootspec.init, bootspec.kernel_params.clone());
@ -335,7 +341,8 @@ impl Installer {
fn install_systemd_boot(&self) -> Result<()> { fn install_systemd_boot(&self) -> Result<()> {
let systemd_boot = self let systemd_boot = self
.systemd .systemd
.join("lib/systemd/boot/efi/systemd-bootx64.efi"); .join("lib/systemd/boot/efi")
.join(self.arch.systemd_filename());
let paths = [ let paths = [
(&systemd_boot, &self.esp_paths.efi_fallback), (&systemd_boot, &self.esp_paths.efi_fallback),

View File

@ -0,0 +1 @@
pub mod architecture;

View File

@ -1,3 +1,4 @@
mod architecture;
mod cli; mod cli;
mod esp; mod esp;
mod install; mod install;

View File

@ -17,6 +17,23 @@ use rand::{thread_rng, Rng};
use serde_json::json; use serde_json::json;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use lanzaboote_tool::architecture::Architecture;
use lzbt_systemd::architecture::SystemdArchitectureExt;
/// Returns the host platform system
/// in the system double format for
/// our usual targets.
#[cfg(target_arch = "aarch64")]
pub static SYSTEM: &str = "aarch64-linux";
// We do not actually care much about 32 bit. However we can use this to easily test that lzbt
// works with another architecture.
#[cfg(target_arch = "x86")]
pub static SYSTEM: &str = "i686-linux";
#[cfg(target_arch = "x86_64")]
pub static SYSTEM: &str = "x86_64-linux";
/// Create a mock generation link. /// Create a mock generation link.
/// ///
/// Works like `setup_generation_link_from_toplevel` but already sets up toplevel. /// Works like `setup_generation_link_from_toplevel` but already sets up toplevel.
@ -55,7 +72,7 @@ pub fn setup_generation_link_from_toplevel(
], ],
"label": "LanzaOS", "label": "LanzaOS",
"toplevel": toplevel, "toplevel": toplevel,
"system": "x86_64-linux", "system": SYSTEM,
}, },
"org.nixos-community.lanzaboote": { "osRelease": toplevel.join("os-release") } "org.nixos-community.lanzaboote": { "osRelease": toplevel.join("os-release") }
}); });
@ -78,13 +95,18 @@ pub fn setup_generation_link_from_toplevel(
/// Accepts the temporary directory as a parameter so that the invoking function retains control of /// Accepts the temporary directory as a parameter so that the invoking function retains control of
/// it (and when it goes out of scope). /// it (and when it goes out of scope).
pub fn setup_toplevel(tmpdir: &Path) -> Result<PathBuf> { pub fn setup_toplevel(tmpdir: &Path) -> Result<PathBuf> {
let system = Architecture::from_nixos_system(SYSTEM)?;
// Generate a random toplevel name so that multiple toplevel paths can live alongside each // Generate a random toplevel name so that multiple toplevel paths can live alongside each
// other in the same directory. // other in the same directory.
let toplevel = tmpdir.join(format!("toplevel-{}", random_string(8))); let toplevel = tmpdir.join(format!("toplevel-{}", random_string(8)));
fs::create_dir(&toplevel)?; fs::create_dir(&toplevel)?;
let test_systemd = systemd_location_from_env()?; let test_systemd = systemd_location_from_env()?;
let test_systemd_stub = format!("{test_systemd}/lib/systemd/boot/efi/linuxx64.efi.stub"); let systemd_stub_filename = system.systemd_stub_filename();
let test_systemd_stub = format!(
"{test_systemd}/lib/systemd/boot/efi/{systemd_stub_filename}",
systemd_stub_filename = systemd_stub_filename.display()
);
let initrd_path = toplevel.join("initrd"); let initrd_path = toplevel.join("initrd");
let kernel_path = toplevel.join("kernel"); let kernel_path = toplevel.join("kernel");
@ -119,8 +141,13 @@ pub fn lanzaboote_install(
) -> Result<Output> { ) -> Result<Output> {
// To simplify the test setup, we use the systemd stub here instead of the lanzaboote stub. See // To simplify the test setup, we use the systemd stub here instead of the lanzaboote stub. See
// the comment in setup_toplevel for details. // the comment in setup_toplevel for details.
let system = Architecture::from_nixos_system(SYSTEM)?;
let test_systemd = systemd_location_from_env()?; let test_systemd = systemd_location_from_env()?;
let test_systemd_stub = format!("{test_systemd}/lib/systemd/boot/efi/linuxx64.efi.stub"); let systemd_stub_filename = system.systemd_stub_filename();
let test_systemd_stub = format!(
"{test_systemd}/lib/systemd/boot/efi/{systemd_stub_filename}",
systemd_stub_filename = systemd_stub_filename.display()
);
let test_loader_config_path = tempfile::NamedTempFile::new()?; let test_loader_config_path = tempfile::NamedTempFile::new()?;
let test_loader_config = r"timeout 0\nconsole-mode 1\n"; let test_loader_config = r"timeout 0\nconsole-mode 1\n";
@ -131,6 +158,8 @@ pub fn lanzaboote_install(
.env("LANZABOOTE_STUB", test_systemd_stub) .env("LANZABOOTE_STUB", test_systemd_stub)
.arg("-vv") .arg("-vv")
.arg("install") .arg("install")
.arg("--system")
.arg(SYSTEM)
.arg("--systemd") .arg("--systemd")
.arg(test_systemd) .arg(test_systemd)
.arg("--systemd-boot-loader-config") .arg("--systemd-boot-loader-config")

View File

@ -2,11 +2,13 @@ use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use lanzaboote_tool::architecture::Architecture;
use lzbt_systemd::architecture::SystemdArchitectureExt;
use tempfile::tempdir; use tempfile::tempdir;
mod common; mod common;
use common::{hash_file, mtime, remove_signature, verify_signature}; use common::{hash_file, mtime, remove_signature, verify_signature, SYSTEM};
#[test] #[test]
fn keep_systemd_boot_binaries() -> Result<()> { fn keep_systemd_boot_binaries() -> Result<()> {
@ -113,9 +115,15 @@ fn overwrite_unsigned_systemd_boot_binaries() -> Result<()> {
} }
fn systemd_boot_path(esp: &tempfile::TempDir) -> PathBuf { fn systemd_boot_path(esp: &tempfile::TempDir) -> PathBuf {
esp.path().join("EFI/systemd/systemd-bootx64.efi") let arch = Architecture::from_nixos_system(SYSTEM).unwrap();
esp.path()
.join("EFI/systemd/")
.join(arch.systemd_filename())
} }
fn systemd_boot_fallback_path(esp: &tempfile::TempDir) -> PathBuf { fn systemd_boot_fallback_path(esp: &tempfile::TempDir) -> PathBuf {
esp.path().join("EFI/BOOT/BOOTX64.EFI") let arch = Architecture::from_nixos_system(SYSTEM).unwrap();
esp.path()
.join("EFI/BOOT/")
.join(arch.efi_fallback_filename())
} }

View File

@ -1,4 +1,4 @@
[toolchain] [toolchain]
channel = "1.70.0" channel = "1.70.0"
components = [ "rust-src" ] components = [ "rust-src" ]
targets = [ "x86_64-unknown-uefi" ] targets = [ "x86_64-unknown-uefi", "aarch64-unknown-uefi" ]