Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,29 @@ jobs:
--no-default-features --features arch-aarch64 \
-Z build-std=core,compiler_builtins,alloc \
-Z build-std-features=compiler-builtins-mem
# Also build the UEFI/Limine bootable ISO (the same render runtime is
# already staged in initramfs/). This is what boots in UTM / VMs / bare
# metal — a real ISO, not just the raw -kernel ELF. build-iso-aarch64.sh
# builds a higher-half limine-boot kernel and packages it with
# BOOTAA64.EFI; LIMINE_DIR was exported by the limine install step.
LIMINE_DIR="$HOME/limine" bash scripts/build-iso-aarch64.sh
ls -lh oscortex-aarch64.iso

- name: Stage release assets
run: |
set -euo pipefail
mkdir -p dist
tag="${{ steps.ver.outputs.tag }}"
# Arch-labelled names so the two artifacts are unambiguous:
# x86_64 → bootable ISO (Limine); aarch64 raw kernel ELF (-kernel).
# Arch-labelled names. Each arch ships a bootable ISO (UEFI) AND, for
# aarch64, the raw -kernel ELF for QEMU `-M virt -kernel` users.
cp oscortex.iso "dist/oscortex-x86_64-${tag}.iso"
cp oscortex-aarch64.iso "dist/oscortex-aarch64-${tag}.iso"
cp target/aarch64-unknown-none/release/kernel "dist/oscortex-aarch64-${tag}.kernel"
# Checksums computed from inside dist/ so each .sha256 lists a bare
# filename and `sha256sum -c` works after downloading just the pair.
( cd dist
sha256sum "oscortex-x86_64-${tag}.iso" > "oscortex-x86_64-${tag}.iso.sha256"
sha256sum "oscortex-aarch64-${tag}.iso" > "oscortex-aarch64-${tag}.iso.sha256"
sha256sum "oscortex-aarch64-${tag}.kernel" > "oscortex-aarch64-${tag}.kernel.sha256" )
ls -lh dist

Expand All @@ -146,19 +155,20 @@ jobs:
body: |
Automated production release built from `main`.

Two artifacts (one per architecture); they differ in type because the
arches boot differently — x86_64 via the Limine bootloader (a bootable
**ISO**), aarch64 via QEMU `-M virt -kernel` (a raw **kernel ELF**, no
bootloader/ISO).
Both architectures ship a **bootable UEFI ISO** — flash it or attach it
in UTM / a VM / bare metal. aarch64 additionally ships the raw `-kernel`
ELF for `qemu-system-aarch64 -M virt -kernel` users.

**Artifacts:**
- `oscortex-x86_64-<tag>.iso` — x86_64 bootable kernel ISO (Limine BIOS +
UEFI), built via `cargo xtask iso`. **Platform-scope** (the full x86
Flutter shell is staged separately by `scripts/build-iso.sh`).
- `oscortex-aarch64-<tag>.kernel` — **render-capable** aarch64 kernel: it
embeds the full Flutter shell runtime (engine + AOT shell snapshot + ICU
+ fonts/assets), fetched from the `oscortex-engine-1` release at build
time. Boot it directly:
UEFI), built via `cargo xtask iso`.
- `oscortex-aarch64-<tag>.iso` — **render-capable** aarch64 UEFI ISO
(Limine → higher-half kernel), embedding the full Flutter shell runtime
(engine + AOT shell snapshot + ICU + fonts/assets). This is the image to
boot in **UTM / VMs / bare metal**. In UTM use a single CPU core
(OSCortex runs a cooperative single-core scheduler).
- `oscortex-aarch64-<tag>.kernel` — the same render-capable kernel as a raw
ELF for direct QEMU boot:
```
qemu-system-aarch64 -M virt -cpu cortex-a72 -smp 1 -m 2G \
-kernel oscortex-aarch64-<tag>.kernel -device ramfb \
Expand All @@ -171,5 +181,7 @@ jobs:
files: |
dist/oscortex-x86_64-${{ steps.ver.outputs.tag }}.iso
dist/oscortex-x86_64-${{ steps.ver.outputs.tag }}.iso.sha256
dist/oscortex-aarch64-${{ steps.ver.outputs.tag }}.iso
dist/oscortex-aarch64-${{ steps.ver.outputs.tag }}.iso.sha256
dist/oscortex-aarch64-${{ steps.ver.outputs.tag }}.kernel
dist/oscortex-aarch64-${{ steps.ver.outputs.tag }}.kernel.sha256
Binary file added iso_root_aarch64/EFI/BOOT/BOOTAA64.EFI
Binary file not shown.
9 changes: 9 additions & 0 deletions iso_root_aarch64/EFI/BOOT/limine.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# OSCortex aarch64 boot configuration (Limine UEFI)
timeout: 0
serial: yes
verbose: yes

/OSCortex AI-First Kernel
protocol: limine
path: boot():/boot/kernel
kaslr: no
Binary file added iso_root_aarch64/boot/kernel
Binary file not shown.
Binary file not shown.
9 changes: 9 additions & 0 deletions iso_root_aarch64/boot/limine/limine.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# OSCortex aarch64 boot configuration (Limine UEFI)
timeout: 0
serial: yes
verbose: yes

/OSCortex AI-First Kernel
protocol: limine
path: boot():/boot/kernel
kaslr: no
9 changes: 9 additions & 0 deletions iso_root_aarch64/limine.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# OSCortex aarch64 boot configuration (Limine UEFI)
timeout: 0
serial: yes
verbose: yes

/OSCortex AI-First Kernel
protocol: limine
path: boot():/boot/kernel
kaslr: no
4 changes: 4 additions & 0 deletions kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ default = ["arch-x86_64"]
arch-x86_64 = []
arch-aarch64 = []
arch-riscv64 = []
# Boot the aarch64 kernel via the Limine UEFI protocol (higher-half, MMU/HHDM/
# framebuffer set up by the bootloader) instead of the bare `-kernel`/DTB path.
# Selects the higher-half linker script + the Limine entry trampoline.
limine-boot = []
ai-cortex = []
driver-wasm = []
self-healing = []
9 changes: 9 additions & 0 deletions kernel/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ fn main() {
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let out = std::env::var("OUT_DIR").unwrap();

// The aarch64 port has TWO mutually-exclusive boot layouts:
// * `-kernel`/DTB boot → physical link @ 0x40080000, entry `_start`, MMU off
// (aarch64.ld, the default).
// * Limine UEFI boot → higher-half link @ -2 GiB, entry the Limine
// trampoline, MMU/HHDM/framebuffer already set up by the bootloader
// (aarch64-limine.ld), selected by the `limine-boot` feature.
let limine_boot = std::env::var("CARGO_FEATURE_LIMINE_BOOT").is_ok();
let ld = match arch.as_str() {
"aarch64" if limine_boot => "aarch64-limine.ld",
"aarch64" => "aarch64.ld",
"riscv64" => "riscv64.ld",
_ => "x86_64.ld", // default / x86_64
Expand All @@ -15,6 +23,7 @@ fn main() {
println!("cargo:rerun-if-changed=linker/{ld}");
println!("cargo:rerun-if-changed=linker/x86_64.ld");
println!("cargo:rerun-if-changed=linker/aarch64.ld");
println!("cargo:rerun-if-changed=linker/aarch64-limine.ld");
println!("cargo:rerun-if-changed=linker/riscv64.ld");
println!("cargo:rerun-if-changed=build.rs");

Expand Down
72 changes: 72 additions & 0 deletions kernel/linker/aarch64-limine.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* OSCortex aarch64 linker script — Limine higher-half kernel (UEFI boot).
*
* Unlike the bare `-kernel` path (aarch64.ld, linked to physical 0x40080000 with
* the MMU off and entry `_start`), the Limine boot protocol hands the kernel a
* machine that already has the MMU on, a higher-half mapping of the kernel, an
* HHDM direct-map of physical RAM, and a framebuffer. Limine loads each PT_LOAD
* segment at its higher-half virtual link address and jumps to the ELF entry
* point at EL1.
*
* We therefore link this variant to the conventional Limine higher-half base
* (-2 GiB) and set the entry to the aarch64 Limine entry trampoline, mirroring
* the x86_64 Limine kernel layout. The Limine request statics are discovered by
* the bootloader by scanning the loaded image for their magic numbers.
*/
OUTPUT_FORMAT("elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_limine_aarch64_entry)

PHDRS {
text PT_LOAD FLAGS(5); /* R-X */
rodata PT_LOAD FLAGS(4); /* R-- */
data PT_LOAD FLAGS(6); /* RW- */
}

SECTIONS {
. = 0xFFFFFFFF80000000;

.text : ALIGN(4096) {
__text_start = .;
KEEP(*(.text.limine_entry)) /* entry trampoline first */
*(.text .text.*)
__text_end = .;
} :text

. = ALIGN(4096);

.rodata : ALIGN(4096) {
__rodata_start = .;
*(.rodata .rodata.*)
__rodata_end = .;
} :rodata

/* Limine requests (scanned by the bootloader for their magic numbers). */
.limine_reqs : {
KEEP(*(.limine_requests*))
KEEP(*(.limine_requests_start*))
KEEP(*(.limine_requests_end*))
} :rodata

. = ALIGN(4096);

.data : ALIGN(4096) {
__data_start = .;
*(.data .data.*)
*(.got .got.*)
__data_end = .;
} :data

. = ALIGN(4096);
.bss (NOLOAD) : ALIGN(4096) {
__bss_start = .;
*(.bss .bss.*)
*(COMMON)
. = ALIGN(16);
__bss_end = .;
} :data

. = ALIGN(4096);
__kernel_end = .;

/DISCARD/ : { *(.eh_frame) *(.eh_frame_hdr) *(.note .note.*) *(.comment) }
}
Loading
Loading