Skip to content

Commit fdefdf1

Browse files
feat: drop userland dependency from kernel (#742)
* feat: userland is bin crate; awkernel is lib crate * feat(x86): compile mpboot with kernel/build.rs * Add template external userland as example * feat(aarch64): add assembly via global_asm! * feat(userland_extern): add support for Aarch64
1 parent 61bc126 commit fdefdf1

29 files changed

Lines changed: 746 additions & 117 deletions

File tree

‎.cargo/config.toml‎

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[alias]
2-
x86 = "build --package awkernel --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
3-
aarch64 = "build --package awkernel --no-default-features --features aarch64 --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
4-
rv32 = "build --package awkernel --no-default-features --features rv32 --target riscv32imac-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
5-
rv64 = "build --package awkernel --no-default-features --features rv64 --target riscv64gc-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
6-
std = "build --package awkernel --no-default-features --features std"
2+
x86 = "build --package userland --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
3+
aarch64 = "build --package userland --no-default-features --features aarch64 --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
4+
rv32 = "build --package userland --no-default-features --features rv32 --target riscv32imac-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
5+
rv64 = "build --package userland --no-default-features --features rv64 --target riscv64gc-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
6+
std = "build --package userland --no-default-features --features std"
77

88
test_awkernel_lib = "test --package awkernel_lib --no-default-features --features std"
99
test_awkernel_async_lib = "test --package awkernel_async_lib --no-default-features --features std"
@@ -12,25 +12,25 @@ test_smoltcp = "test --package smoltcp --no-default-features --features std --fe
1212
test_affinity_btree_queue = "test --package affinity_btree_queue"
1313
test_rd_gen_to_dags = "test --package rd_gen_to_dags --no-default-features --features std --features milliseconds"
1414

15-
clippy_x86 = "clippy --package awkernel --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
16-
clippy_raspi = "clippy --package awkernel --no-default-features --features raspi --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
17-
clippy_raspi5 = "clippy --package awkernel --no-default-features --features raspi5 --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
18-
clippy_aarch64_virt = "clippy --package awkernel --no-default-features --features aarch64_virt --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
19-
clippy_std = "clippy --package awkernel --no-default-features --features std"
20-
clippy_rv32 = "clippy --package awkernel --no-default-features --features rv32 --target riscv32imac-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
21-
clippy_rv64 = "clippy --package awkernel --no-default-features --features rv64 --target riscv64gc-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
15+
clippy_x86 = "clippy --package userland --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
16+
clippy_raspi = "clippy --package userland --no-default-features --features raspi --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
17+
clippy_raspi5 = "clippy --package userland --no-default-features --features raspi5 --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
18+
clippy_aarch64_virt = "clippy --package userland --no-default-features --features aarch64_virt --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
19+
clippy_std = "clippy --package userland --no-default-features --features std"
20+
clippy_rv32 = "clippy --package userland --no-default-features --features rv32 --target riscv32imac-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
21+
clippy_rv64 = "clippy --package userland --no-default-features --features rv64 --target riscv64gc-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
2222
clippy_rd_gen_to_dags = "clippy --package rd_gen_to_dags --no-default-features --features std --features milliseconds"
2323

24-
check_aarch64 = "check --package awkernel --no-default-features --features aarch64 --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
25-
check_x86 = "check --package awkernel --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
26-
check_rv32 = "check --package awkernel --no-default-features --features rv32 --target riscv32imac-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
27-
check_rv64 = "check --package awkernel --no-default-features --features rv64 --target riscv64gc-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
28-
check_no_std = "check --package awkernel -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem --quiet --message-format=json -Zjson-target-spec"
29-
check_std = "check --package awkernel --no-default-features --features std"
24+
check_aarch64 = "check --package userland --no-default-features --features aarch64 --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
25+
check_x86 = "check --package userland --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
26+
check_rv32 = "check --package userland --no-default-features --features rv32 --target riscv32imac-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
27+
check_rv64 = "check --package userland --no-default-features --features rv64 --target riscv64gc-unknown-none-elf -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
28+
check_no_std = "check --package userland -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem --quiet --message-format=json -Zjson-target-spec"
29+
check_std = "check --package userland --no-default-features --features std"
3030

31-
doc_raspi = "doc --package awkernel --no-default-features --features raspi --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
32-
doc_aarch64_virt = "doc --package awkernel --no-default-features --features aarch64_virt --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
33-
doc_x86 = "doc --package awkernel --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
31+
doc_raspi = "doc --package userland --no-default-features --features raspi --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
32+
doc_aarch64_virt = "doc --package userland --no-default-features --features aarch64_virt --target targets/aarch64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
33+
doc_x86 = "doc --package userland --no-default-features --features x86 --target targets/x86_64-kernel.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -Zjson-target-spec"
3434

3535
udeps_x86 = "udeps --package awkernel --no-default-features --features x86 --target x86_64-unknown-linux-gnu"
3636
udeps_raspi = "udeps --package awkernel --no-default-features --features x86 --target x86_64-unknown-linux-gnu"

‎.vscode/settings.json‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@
22
"rust-analyzer.showUnlinkedFileNotification": false,
33
"tlaplus.tlc.modelChecker.options": "-workers auto",
44
"editor.formatOnSave": true,
5+
// The cargo aliases build `--package userland`, which has no default
6+
// architecture: pass it explicitly (the features are forwarded to `awkernel`).
57
// For AArch64 Virt
68
// "rust-analyzer.cargo.buildScripts.overrideCommand": [
79
// "cargo",
810
// "+nightly-2026-07-20",
911
// "check_no_std",
1012
// "--target=targets/aarch64-kernel.json",
13+
// "--features=aarch64_virt",
1114
// ],
1215
// "rust-analyzer.check.overrideCommand": [
1316
// "cargo",
1417
// "+nightly-2026-07-20",
1518
// "check_no_std",
1619
// "--target=targets/aarch64-kernel.json",
20+
// "--features=aarch64_virt",
1721
// ],
1822
// "rust-analyzer.cargo.features": [
1923
// "aarch64_virt"
@@ -24,12 +28,14 @@
2428
"+nightly-2026-07-20",
2529
"check_no_std",
2630
"--target=targets/x86_64-kernel.json",
31+
"--features=x86",
2732
],
2833
"rust-analyzer.check.overrideCommand": [
2934
"cargo",
3035
"+nightly-2026-07-20",
3136
"check_no_std",
3237
"--target=targets/x86_64-kernel.json",
38+
"--features=x86",
3339
],
3440
"rust-analyzer.cargo.features": [
3541
"x86"
@@ -39,17 +45,19 @@
3945
// "cargo",
4046
// "+nightly-2026-07-20",
4147
// "check_std",
48+
// "--features=std",
4249
// "--quiet",
4350
// "--message-format=json"
4451
// ],
4552
// "rust-analyzer.check.overrideCommand": [
4653
// "cargo",
4754
// "+nightly-2026-07-20",
4855
// "check_std",
56+
// "--features=std",
4957
// "--quiet",
5058
// "--message-format=json"
5159
// ],
5260
// "rust-analyzer.cargo.features": [
5361
// "std"
5462
// ],
55-
}
63+
}

‎Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ members = [
1919
"applications/tests/*",
2020
"smoltcp",
2121
"awkernel_futures_macro",
22+
"awkernel_macros",
2223
]
2324

2425
resolver = "2"

‎Makefile‎

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,22 @@ else
66
OPT = --features debug
77
endif
88

9-
# 2MiB Stack
10-
STACKSIZE = 1024 * 1024 * 2
11-
129
ifeq ($(BSP),raspi3)
1310
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
14-
INITADDR = 0x80000
1511
AARCH64_OPT = $(OPT) --features raspi
1612
else ifeq ($(BSP),raspi4)
1713
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
18-
INITADDR = 0x80000
1914
AARCH64_OPT = $(OPT) --features raspi
2015
else ifeq ($(BSP),raspi5)
2116
RUSTC_MISC_ARGS = -C target-cpu=cortex-a76
22-
INITADDR = 0x80000
2317
AARCH64_OPT = $(OPT) --features raspi5
2418
else ifeq ($(BSP),aarch64_virt)
2519
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
26-
INITADDR = 0x40080000
2720
AARCH64_OPT = $(OPT) --features aarch64_virt
2821
endif
2922

3023
RUSTC_MISC_ARGS += -C panic=unwind
3124

32-
ASM_FILE_DEP_AARCH64=kernel/asm/aarch64/exception.S
33-
ASM_FILE_AARCH64=kernel/asm/aarch64/boot.S
34-
ASM_OBJ_AARCH64=boot_aarch64.o
35-
36-
X86ASM=kernel/asm/x86
37-
38-
ifndef $(CC)
39-
CC = clang
40-
endif
41-
4225
ifndef $(LD)
4326
LD = rust-lld -flavor gnu
4427
# LD = ld.gold
@@ -49,8 +32,6 @@ QEMUPORT=5556
4932

5033
# Link scripts
5134
LINKERDIR=kernel/ld
52-
AARCH64_LD=$(LINKERDIR)/aarch64-link.lds
53-
AARCH64_BSP_LD=$(LINKERDIR)/aarch64-link-bsp.lds
5435
X86_64_LD=$(LINKERDIR)/x86_64-link.lds
5536
RV32_LD=$(LINKERDIR)/rv32-link.lds
5637
RV64_LD=$(LINKERDIR)/rv64-link.lds
@@ -61,7 +42,7 @@ all: aarch64 x86_64 riscv32 riscv64 std
6142

6243
check: check_aarch64 check_x86_64 check_std check_riscv32 check_riscv64
6344

64-
clippy: $(X86ASM)
45+
clippy:
6546
cargo +$(RUSTV) clippy_x86
6647
cargo +$(RUSTV) clippy_raspi
6748
cargo +$(RUSTV) clippy_raspi5
@@ -71,7 +52,7 @@ clippy: $(X86ASM)
7152
cargo +$(RUSTV) clippy_std
7253
cargo +$(RUSTV) clippy_rd_gen_to_dags
7354

74-
udeps: $(X86ASM)
55+
udeps:
7556
cargo +$(RUSTV) udeps_x86
7657

7758
cargo: target/aarch64-kernel/$(BUILD)/awkernel kernel-x86_64.elf std
@@ -84,19 +65,13 @@ aarch64: kernel8.img
8465
check_aarch64: FORCE
8566
cargo +$(RUSTV) check_aarch64 $(AARCH64_OPT)
8667

87-
target/aarch64-kernel/$(BUILD)/awkernel: $(ASM_OBJ_AARCH64) $(AARCH64_BSP_LD) FORCE
68+
target/aarch64-kernel/$(BUILD)/awkernel: FORCE
8869
RUSTFLAGS="$(RUSTC_MISC_ARGS)" cargo +$(RUSTV) aarch64 $(AARCH64_OPT)
8970
python3 scripts/embed_debug_info.py $@
9071

9172
kernel8.img: target/aarch64-kernel/$(BUILD)/awkernel
9273
rust-objcopy -O binary target/aarch64-kernel/$(BUILD)/awkernel $@
9374

94-
$(ASM_OBJ_AARCH64): $(ASM_FILE_AARCH64) $(ASM_FILE_DEP_AARCH64)
95-
$(CC) --target=aarch64-elf -c $< -o $@ -DSTACKSIZE="$(STACKSIZE)"
96-
97-
$(AARCH64_BSP_LD): $(AARCH64_LD)
98-
sed "s/#INITADDR#/$(INITADDR)/" $(AARCH64_LD) > $@
99-
10075
QEMU_AARCH64_ARGS= -kernel kernel8.img
10176
QEMU_AARCH64_ARGS+= -serial stdio
10277
QEMU_AARCH64_ARGS+= -monitor telnet::$(QEMUPORT),server,nowait # -d int
@@ -139,10 +114,10 @@ gdb-aarch64-virt:
139114

140115
x86_64: x86_64_uefi.img
141116

142-
check_x86_64: $(X86ASM)
117+
check_x86_64:
143118
cargo +$(RUSTV) check_x86
144119

145-
kernel-x86_64.elf: $(X86ASM) FORCE
120+
kernel-x86_64.elf: FORCE
146121
RUSTFLAGS="$(RUSTC_MISC_ARGS)" cargo +$(RUSTV) x86 $(OPT)
147122
python3 scripts/embed_debug_info.py $@
148123

@@ -152,9 +127,6 @@ x86_64_boot.img: kernel-x86_64.elf
152127
x86_64_uefi.img: kernel-x86_64.elf
153128
RUSTFLAGS="$(RUSTC_MISC_ARGS)" cargo +$(RUSTV) run --release --package x86bootdisk --no-default-features --features uefi -- --kernel $< --output $@ --pxe x86_64_uefi_pxe_boot --boot-type uefi
154129

155-
$(X86ASM): FORCE
156-
$(MAKE) -C $@
157-
158130
OVMF_PATH := $(shell cat ${HOME}/.ovmfpath)
159131

160132
QEMU_X86_ARGS= -drive if=pflash,format=raw,readonly=on,file=${OVMF_PATH}/code.fd
@@ -247,7 +219,7 @@ check_std: FORCE
247219
cargo +$(RUSTV) check_std
248220

249221
run-std:
250-
cargo +$(RUSTV) run --package awkernel --no-default-features --features std $(OPT)
222+
cargo +$(RUSTV) run --package userland --no-default-features --features std $(OPT)
251223

252224
# Docs
253225
docs: FORCE
@@ -271,9 +243,8 @@ fmt: FORCE
271243
# Clean
272244

273245
clean: FORCE
274-
rm -rf *.o *.elf $(AARCH64_BSP_LD) *.img kernel/asm/x86/*.o x86_64_uefi_pxe_boot
246+
rm -rf *.o *.elf *.img x86_64_uefi_pxe_boot
275247
cargo clean
276-
$(MAKE) -C $(X86ASM) clean
277248

278249

279250
## QEMU Monitor

‎README.md‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ $ cargo binstall mdbook-mermaid
4141
- verified library for awkernel_async_lib
4242
- pure Rust (no dependencies on external functions and no inline assembly)
4343
- [awkernel_futures_macro](./awkernel_futures_macro/)
44+
- [awkernel_macros](./awkernel_macros/)
45+
- `#[awkernel::main]` entry-point macro
4446
- [awkernel_drivers](./awkernel_drivers/)
4547
- [awkernel_aarch64](./awkernel_aarch64/)
4648
- [userland](./userland/)
@@ -60,7 +62,8 @@ graph TD;
6062
kernel-->awkernel_aarch64;
6163
kernel-->awkernel_drivers;
6264
awkernel_drivers-->awkernel_lib;
63-
kernel-->userland;
65+
userland-->kernel;
66+
kernel-->awkernel_macros;
6467
```
6568

6669
Applications can use `awkernel_async_lib`, `awkernel_lib`, and `awkernel_drivers`.
@@ -95,7 +98,6 @@ etc
9598
### x86_64
9699

97100
```text
98-
$ make kernel/asm/x86
99101
$ cargo doc_x86
100102
$ ls target/x86_64-kernel/doc/awkernel/index.html
101103
$ ls target/aarch64-kernel/doc/awkernel_lib/index.html
@@ -120,28 +122,29 @@ Debug build.
120122
$ make x86_64
121123
```
122124

123-
#### Developing for x86_64 on macOS
125+
#### Building x86_64 on Apple Silicon (or another non-x86_64 host)
124126

125-
`kernel/asm/x86/mpboot.S` mixes 16/32/64-bit code and needs a real GNU assembler and linker.
126-
By default, Apple systems provide an alias so `gcc` points to `clang`.
127-
Also, modern Apple Silicon processors are aarch64.
128-
Therefore, we must install a GNU x86_64 ELF cross toolchain:
127+
The x86_64 build includes `kernel/asm/x86/mpboot.S`, an assembly file that mixes 16-, 32-, and 64-bit code. The build script assembles and links it with GNU tools, then uses `objcopy` to create a binary image. A Rust target alone does not provide these tools.
128+
129+
On Apple Silicon, install an x86_64 ELF GNU toolchain:
129130

130131
```text
131132
$ brew install x86_64-elf-gcc x86_64-elf-binutils
132133
```
133134

134-
Then, when building your application, point to this specific cross toolchain:
135+
The build script automatically uses the `x86_64-elf-` tool prefix if `x86_64-elf-gcc` is available. Otherwise, it tries the host tools (`gcc`, `ld`, and `objcopy`), which may not support this build on a non-x86_64 host.
136+
137+
To select a toolchain with a different prefix, pass that prefix as `CROSS_COMPILE`. For example, if its tools are named `my-x86_64-elf-gcc`, `my-x86_64-elf-ld`, and `my-x86_64-elf-objcopy`, run:
135138

136139
```text
137-
$ make x86_64 RELEASE=1 CROSS_COMPILE=x86_64-elf-
140+
$ make x86_64 RELEASE=1 CROSS_COMPILE=my-x86_64-elf-
138141
```
139142

140143
> [!IMPORTANT]
141144
>
142-
> Do not remove the trailing `-`
143-
144-
This way, Awkernel will use the `x86_64-elf-gcc` compiler and the `x86_64-elf-ld` linker.
145+
> `CROSS_COMPILE` is a prefix, not a path to the compiler. Keep the trailing `-` so the build can append `gcc`, `ld`, and `objcopy` to it.
146+
>
147+
> For example, `CROSS_COMPILE=x86_64-elf-` selects `x86_64-elf-gcc`, `x86_64-elf-ld`, and `x86_64-elf-objcopy`.
145148
146149
### Boot
147150

‎awkernel_macros/Cargo.toml‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "awkernel_macros"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
proc-macro = true
8+
9+
[dependencies]
10+
proc-macro2 = "1.0.60"
11+
quote = "1.0"
12+
syn = { version = "2.0.52", features = ["full"] }

0 commit comments

Comments
 (0)