2. Build guide, per target core

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

What you need first

TargetToolchainWhere the script looks
AArch64aarch64-none-elf-gcctoolchains/xpack-aarch64-none-elf-gcc-*/bin
RISC-V 64riscv-none-elf-gcctoolchains/xpack-riscv-none-elf-gcc-*/bin
x86-64host mingw-w64 gccon PATH
i386host mingw-w64 gcc -m32on 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.

The four commands

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

What a good run prints

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.

Flags that are not decoration

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.

FlagWhy
-ffreestanding -nostdlibThere is no host C library.
-fno-commonTwo translation units defining the same global must be a link error, not a silent merge.
-mgeneral-regs-onlyKeeps 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_ENDIANSelects 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-patternsGCC 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.

Choosing the module format

One flag decides which container the kernel can load:

FlagKernel loads
-DTUWA_MODFMT_ELFELF shared objects (.so)
-DTUWA_MODFMT_PEPE DLLs (.dll)
neitherpicfmt2 (.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.

Adding the filesystem to a port

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.

Adding the shell to a port

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.

Build times

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.