Copyright (c) 2026 Muhammad Anisur Rahman. All rights reserved.
Each supported core has one script that builds the kernel, links it with a board support package, and boots it. There is no separate configure step and no makefile to edit.
sh hardware/CPU/<arch>/<board>/run_kernel.sh
| Target | Toolchain | Where the script looks |
|---|---|---|
| AArch64 | aarch64-none-elf-gcc | toolchains/xpack-aarch64-none-elf-gcc-*/bin |
| RISC-V 64 | riscv-none-elf-gcc | toolchains/xpack-riscv-none-elf-gcc-*/bin |
| x86-64 | host mingw-w64 gcc | on PATH |
| i386 | host mingw-w64 gcc -m32 | on PATH |
Plus QEMU for the emulated boards. The scripts say which binary they could not find rather than failing further along, so a missing toolchain is a one-line message and not a wall of compiler errors.
The two x86 targets need no cross-toolchain at all: the kernel is built as a UEFI application by the same mingw gcc that is already on the machine, and QEMU's bundled OVMF firmware loads it.
sh hardware/CPU/arm64/qemu_virt/run_kernel.sh # AArch64, QEMU virt
sh hardware/CPU/x86_64/qemu_uefi/run_kernel.sh # x86-64, QEMU q35 + OVMF
sh hardware/CPU/riscv/qemu_virt/run_kernel.sh # RISC-V64, QEMU virt
sh hardware/CPU/i386/qemu_uefi/run_kernel.sh # i386, QEMU q35 + OVMF
ALL-SCHEDULABLE-TASKS-RAN the scheduler preempted real tasks
RAMFS-MOUNTED a FAT12 volume was formatted and mounted
MODULE-LOADED-FROM-FILE a module was read from that volume and run
PICFMT2_SELFTEST: ALL PASSED the module loader passed its self-test
SHELL-FS: ALL PASSED the shell's filesystem commands work
TUWA-KERNEL-RUNNING the kernel reached steady state
All six appear on all four targets. If one is missing, that subsystem is where to look - they are printed in the order the boot sequence reaches them, so the last line you see is the last thing that worked.
ALL-SCHEDULABLE-TASKS-RAN is the load-bearing one. Two tasks spin without ever yielding or blocking, so neither can make progress unless the timer tick takes the CPU away from the other. A test that only proved tasks start would pass on a system that never switches.
Every port's script sets flags whose absence produces a subtly broken kernel rather than a build error. They are worth knowing before you copy a command line into a project of your own.
| Flag | Why |
|---|---|
-ffreestanding -nostdlib | There is no host C library. |
-fno-common | Two translation units defining the same global must be a link error, not a silent merge. |
-mgeneral-regs-only | Keeps GCC out of the vector registers, which the context switch does not save. Without it a vectorised memcpy in kernel code is corrupted by the very next context switch. |
-DLITTLE_ENDIAN | Selects the correct byte order in the hex formatters. Without it every %X diagnostic prints byte-reversed - the code is already right, it just has to be selected. |
-fno-tree-loop-distribute-patterns | GCC may recognise the copy loop inside memcpy as a memcpy and replace it with a call to itself. That links cleanly and hangs on first use. |
-mno-red-zone (x86) | An interrupt would destroy the 128 bytes below RSP that a leaf function is otherwise entitled to use. |
-ffixed-x18 (AArch64) | Reserves the platform register the module entry convention uses. |
One flag decides which container the kernel can load:
| Flag | Kernel loads |
|---|---|
-DTUWA_MODFMT_ELF | ELF shared objects (.so) |
-DTUWA_MODFMT_PE | PE DLLs (.dll) |
| neither | picfmt2 (.twa) only |
The rule is to match the toolchain that built the kernel. A gcc-built kernel loads ELF, a mingw-built kernel loads PE. That way the module and the kernel agree about calling convention and name decoration by construction rather than by inspection. See 06-modules.md.
Four source files and two flags, identical on every target:
FSFLAGS="-DTUWA_FS_AVAILABLE -fno-tree-loop-distribute-patterns"
FS_SRC="filesys/fsio.c filesys/fat/fatfs.c devices/ramdev.c clib/freestanding.c"
and one call in the board's boot sequence:
LoadRamFileSystem(0, "/"); /* formats and mounts FAT12 in RAM */
Without TUWA_FS_AVAILABLE, TuwaLoadModuleFromFile() returns MODERR_NO_FILESYSTEM - a specific answer naming the real problem, rather than "file not found" sending you to look for a file.
SHELL_SRC="shell/tuwashel.c shell/tuwaprsr.c"
The shell references four board-supplied functions, and the link fails at the last step without them: ShowAsicReg, TuwaReadPort, TuwaWritePort and UserShellCmd. They inspect hardware, so only the board can implement them. 04-bsp-guide.md gives the shapes and says what to do on a board that has no I/O port space.
Under a minute per target on an ordinary desktop, including the QEMU boot. There is no incremental build: every run compiles everything, which for a kernel this size is faster than being wrong about what needed rebuilding.