Skip to content

Commit

Permalink
feat: remove NASM dependency
Browse files Browse the repository at this point in the history
This migrates the x86-64 application processor boot code from NASM to GNU assembly language.

`boot.asm` is replaced by `boot.s`.
Instead of NASM directives, their GNU assembler (`as`) equivalents are used.
This assembly file is embedded in an LLVM assembly language file with
only one module level (global) assembly statement.
Rust bundles `llvm-as`, which we use to translate the LLVM assembly to LLVM bitcode.
The resulting file is linked to binary using the bundled `rust-lld`.
The final binary is exactly the same as before, bit for bit.

This finally removes NASM as the last non-Rust toolchain dependency.

Signed-off-by: Martin Kröning <[email protected]>
  • Loading branch information
mkroening committed Sep 21, 2023
1 parent daa9682 commit 1d47cd4
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 264 deletions.
28 changes: 6 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: taiki-e/install-action@cargo-hack
- name: Install NASM
run: |
sudo apt-get update
sudo apt-get install nasm
- uses: mkroening/rust-toolchain-toml@main
- uses: Swatinem/rust-cache@v2
- name: Check each feature
Expand All @@ -38,10 +34,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install NASM
run: |
sudo apt-get update
sudo apt-get install nasm
- uses: mkroening/rust-toolchain-toml@main
- run: rustup component add clippy
- uses: Swatinem/rust-cache@v2
Expand All @@ -61,10 +53,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install NASM
run: |
sudo apt-get update
sudo apt-get install nasm
- uses: mkroening/rust-toolchain-toml@main
- run: rustup target add aarch64-unknown-none-softfloat
- uses: Swatinem/rust-cache@v2
Expand All @@ -78,10 +66,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install NASM
run: |
sudo apt-get update
sudo apt-get install nasm
- uses: mkroening/rust-toolchain-toml@main
- uses: Swatinem/rust-cache@v2
- name: Build minimal kernel
Expand All @@ -94,10 +78,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install QEMU, NASM
- name: Install QEMU
run: |
sudo apt-get update
sudo apt-get install qemu-system-x86 nasm
sudo apt-get install qemu-system-x86
- uses: mkroening/rust-toolchain-toml@main
- uses: Swatinem/rust-cache@v2
- name: Unit tests
Expand Down Expand Up @@ -133,10 +117,10 @@ jobs:
uses: actions/checkout@v4
with:
path: kernel
- name: Install QEMU, NASM
- name: Install QEMU
run: |
sudo apt-get update
sudo apt-get install qemu-system-x86 nasm libcap-ng-dev libseccomp-dev socat
sudo apt-get install qemu-system-x86 libcap-ng-dev libseccomp-dev socat
- uses: mkroening/rust-toolchain-toml@main
- uses: mkroening/rust-toolchain-toml@main
with:
Expand Down Expand Up @@ -238,10 +222,10 @@ jobs:
uses: actions/checkout@v4
with:
path: kernel
- name: Install QEMU, NASM
- name: Install QEMU
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends qemu-system-x86 nasm
sudo apt-get install -y --no-install-recommends qemu-system-x86
- name: Check KVM availability
shell: bash
run: |
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/publish_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ jobs:
uses: actions/checkout@v4
- name: Rustup (apply rust-toolchain.toml)
run: rustup show
- name: Install NASM
run: |
sudo apt-get update
sudo apt-get install nasm
- name: Generate documentation
run: cargo doc --target x86_64-unknown-none --package hermit-kernel
- name: Generate index.html
Expand Down
83 changes: 2 additions & 81 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fsgsbase = []
newlib = []
pci = []
rtl8139 = ["tcp", "pci"]
smp = ["include-transformed"]
smp = []
tcp = ["smoltcp", "smoltcp/socket-tcp"]
udp = ["smoltcp", "smoltcp/socket-udp"]
trace = []
Expand All @@ -73,7 +73,6 @@ dyn-clone = "1.0"
hashbrown = { version = "0.14", default-features = false }
hermit-entry = { version = "0.9", features = ["kernel"] }
hermit-sync = "0.1"
include-transformed = { version = "0.2", optional = true }
lock_api = "0.4"
log = { version = "0.4", default-features = false }
num = { version = "0.4", default-features = false }
Expand Down Expand Up @@ -130,6 +129,10 @@ float-cmp = "0.9"
num-traits = { version = "0.2", default-features = false }
x86 = { version = "0.52", default-features = false }

[build-dependencies]
anyhow = "1"
llvm-tools = "0.1"

[workspace]
members = [
"xtask",
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ This is the kernel of the [Hermit](https://github.com/hermitcore) unikernel proj
## Requirements

* [`rustup`](https://www.rust-lang.org/tools/install)
* [NASM](https://nasm.us/) (only for SMP on x86_64)

## Building the kernel

Expand Down
87 changes: 87 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};

use anyhow::{anyhow, Context, Result};
use llvm_tools::LlvmTools;

fn main() -> Result<()> {
if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64"
&& env::var_os("CARGO_FEATURE_SMP").is_some()
{
assemble_x86_64_smp_boot()?;
}

Ok(())
}

fn assemble_x86_64_smp_boot() -> Result<()> {
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());

let boot_s = Path::new("src/arch/x86_64/kernel/boot.s");
let boot_ll = out_dir.join("boot.ll");
let boot_bc = out_dir.join("boot.bc");
let boot_bin = out_dir.join("boot.bin");

let llvm_as = binutil("llvm-as")?;
let rust_lld = binutil("rust-lld")?;

let assembly = fs::read_to_string(boot_s)?;

let mut llvm_file = File::create(&boot_ll)?;
writeln!(
&mut llvm_file,
r#"target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-none-elf"
module asm "
{assembly}
"
"#
)?;
llvm_file.flush()?;
drop(llvm_file);

let status = Command::new(&llvm_as)
.arg("-o")
.arg(&boot_bc)
.arg(boot_ll)
.status()
.with_context(|| format!("Failed to run llvm-as from {}", llvm_as.display()))?;
assert!(status.success());

let status = Command::new(&rust_lld)
.arg("-flavor")
.arg("gnu")
.arg("--section-start=.text=0x8000")
.arg("--oformat=binary")
.arg("-o")
.arg(&boot_bin)
.arg(&boot_bc)
.status()
.with_context(|| format!("Failed to run rust-lld from {}", rust_lld.display()))?;
assert!(status.success());

println!("cargo:rerun-if-changed={}", boot_s.display());
Ok(())
}

fn binutil(name: &str) -> Result<PathBuf> {
let exe = format!("{name}{}", env::consts::EXE_SUFFIX);

let path = LlvmTools::new()
.map_err(|err| match err {
llvm_tools::Error::NotFound => anyhow!(
"Could not find llvm-tools component\n\
\n\
Maybe the rustup component `llvm-tools` is missing? Install it through: `rustup component add llvm-tools`"
),
err => anyhow!("{err:?}"),
})?
.tool(&exe)
.ok_or_else(|| anyhow!("could not find {exe}"))?;

Ok(path)
}
4 changes: 1 addition & 3 deletions src/arch/x86_64/kernel/apic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,11 +682,9 @@ pub fn init_next_processor_variables() {
pub fn boot_application_processors() {
use core::hint;

use include_transformed::include_nasm_bin;

use super::{raw_boot_info, start};

let smp_boot_code = include_nasm_bin!("boot.asm");
let smp_boot_code = include_bytes!(concat!(core::env!("OUT_DIR"), "/boot.bin"));

// We shouldn't have any problems fitting the boot code into a single page, but let's better be sure.
assert!(
Expand Down
Loading

0 comments on commit 1d47cd4

Please sign in to comment.