Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
1381 lines
60 KiB
Makefile
1381 lines
60 KiB
Makefile
# ==============================================================================
|
|
# kernel/Makefile — StarKernel UEFI Build System
|
|
# Multi-architecture: amd64 (x86_64), aarch64 (ARM64), riscv64 (RISC-V 64)
|
|
#
|
|
# Quick start:
|
|
# make -f kernel/Makefile — build for host arch
|
|
# make -f kernel/Makefile ARCH=amd64 qemu — build + boot (x86_64)
|
|
# make -f kernel/Makefile ARCH=aarch64 qemu — build + boot (aarch64)
|
|
# make -f kernel/Makefile ARCH=riscv64 qemu — build + boot (riscv64)
|
|
# make -f kernel/Makefile help — show all targets
|
|
# ==============================================================================
|
|
|
|
# ==============================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================
|
|
|
|
# Architecture normalization — accept common aliases
|
|
# x86_64 → amd64
|
|
# arm64 → aarch64
|
|
# riscv → riscv64
|
|
canon_arch = $(strip \
|
|
$(if $(filter x86_64,$1),amd64, \
|
|
$(if $(filter amd64,$1),amd64, \
|
|
$(if $(filter arm64,$1),aarch64, \
|
|
$(if $(filter aarch64,$1),aarch64, \
|
|
$(if $(filter riscv riscv64,$1),riscv64,$1))))))
|
|
|
|
UNAME_M := $(shell uname -m)
|
|
ARCH ?= $(call canon_arch,$(UNAME_M))
|
|
override ARCH := $(call canon_arch,$(ARCH))
|
|
|
|
ifeq ($(filter amd64 aarch64 riscv64,$(ARCH)),)
|
|
$(error Unsupported ARCH='$(ARCH)'. Use: amd64, aarch64, riscv64)
|
|
endif
|
|
|
|
# Kconfig bridge (menuconfig/xconfig/config/oldconfig/*_defconfig). See
|
|
# mk/Kconfig.mk for why KCONFIG_ARCH_DIR must be set here, before include.
|
|
# .DEFAULT_GOAL is pinned explicitly because mk/Kconfig.mk's first rule
|
|
# would otherwise silently become Make's default goal, since it's included
|
|
# before this Makefile's own `all:` target is defined below.
|
|
.DEFAULT_GOAL := all
|
|
KCONFIG_ARCH_DIR := $(ARCH)
|
|
include mk/Kconfig.mk
|
|
|
|
# Target type (default: kernel)
|
|
TARGET ?= kernel
|
|
|
|
# Kernel-only flags (Phase 6): repointed through the Kconfig bridge, same
|
|
# drop-in-replacement-for-`?=` behavior as every other family in this
|
|
# migration. Kconfig.kernel's symbols only exist for a kernel-variant
|
|
# .config (`if STARFORTH_VARIANT_KERNEL`), so a hosted-variant .config
|
|
# leaves CONFIG_PARITY_MODE/etc undefined and kconfig_bool falls back to
|
|
# its $2 default exactly as if Kconfig were inactive -- this file is never
|
|
# built from a hosted-variant config in practice, but the fallback is
|
|
# correct either way.
|
|
|
|
# Parity mode (0 = normal, 1 = parity harness)
|
|
$(eval $(call kconfig_bool,PARITY_MODE,0))
|
|
|
|
# StarForth VM integration (default: enabled)
|
|
$(eval $(call kconfig_bool,STARFORTH_ENABLE_VM,1))
|
|
|
|
# Monolithic build — loader + kernel compiled together (default: enabled)
|
|
MONOLITHIC ?= 1
|
|
|
|
# Parallel build — use all available cores when not cleaning
|
|
# (adding -j when 'clean' is also a goal causes a race on v3/include/version.h)
|
|
NPROC := $(shell nproc 2>/dev/null || echo 4)
|
|
ifeq ($(filter clean,$(MAKECMDGOALS)),)
|
|
MAKEFLAGS += -j$(NPROC)
|
|
endif
|
|
|
|
# Version
|
|
# Versioning policy (FABRIC-3.5.md §XXX, ratified 2026-09-19, replaces the old
|
|
# X.0.0/X.5.0 QEMU/hardware encoding this comment used to carry):
|
|
# Major: ODD = LTS line (non-breaking fixes only). EVEN = working line
|
|
# (breaking changes land here). Minor: release within the line.
|
|
# Patch: working builds within a release.
|
|
# v1.0.x — serial-only production (released, historical encoding)
|
|
# v1.5.x — framebuffer VT100 console milestone (released, historical encoding)
|
|
# v2.0.0 — QEMU release: three-arch QEMU story complete (released)
|
|
# v2.0.1 — SER5 hardware-track line (historical; superseded by this policy)
|
|
# v2.1.0 — Tripod/kernel reshuffle: Hermes into the kernel, Tripod = Hera/Artemis/Hestia
|
|
# v2.2.0 — amd64 bare-metal bring-up (Beelink SER5)
|
|
# v2.x — further working releases; riscv64 / aarch64 board bring-up
|
|
# v3.0.0 — FIRST LTS, when and only when FABRIC-3.5.md §XXX.5's 5 criteria are met
|
|
# LITHOS_VERSION (kernel) and VERSION (embedded StarForth engine) are independent
|
|
# and do not auto-sync (§XXX.4) — do not infer a relationship if they ever coincide.
|
|
# Engine VERSION bumps at tag time by what actually changed (§XXX.6): major = any
|
|
# FORTH-79-visible word semantics change; minor = words added/removed/relocated;
|
|
# patch = build-only. This reshuffle is a minor bump: BIRTH's registration widened,
|
|
# the messaging layer was replaced by kernel-Hermes, PLOT/FB-* relocated to Hestia —
|
|
# all dictionary-visible, no FORTH-79 word semantics changed.
|
|
VERSION ?= 3.2.0
|
|
LITHOS_VERSION ?= 2.1.0
|
|
|
|
# ==============================================================================
|
|
# BUILD PATHS
|
|
# ==============================================================================
|
|
|
|
BUILD_DIR := build/$(ARCH)/$(TARGET)
|
|
OBJ_DIR := $(BUILD_DIR)/obj
|
|
LOADER_OBJ_DIR := $(OBJ_DIR)/loader
|
|
KERNEL_OBJ_DIR := $(OBJ_DIR)/kernel
|
|
|
|
LOADER_EFI := $(BUILD_DIR)/starkernel_loader.efi
|
|
KERNEL_ELF := $(BUILD_DIR)/starkernel_kernel.elf
|
|
|
|
KERNEL_SRC := kernel/src
|
|
KERNEL_INC := kernel/include/starkernel
|
|
STARFORTH_CONFIG_HEADER := v3/include/starforth_config.h
|
|
|
|
QEMU_LOG_DIR := logs
|
|
QEMU_ISO := $(BUILD_DIR)/starkernel.iso
|
|
DOE_RUNS_DIR := experiments/bare_metal/runs
|
|
DOE_LATEST_DIR := experiments/bare_metal/latest
|
|
|
|
CAPSULES_DIR ?= capsules
|
|
ARTDISK ?= disk/artemis.img
|
|
# XHCI_PORTS -- real top-level port count the qemu-xhci controller exposes on the
|
|
# xhci0.0 bus (p2 == p3 == this value; these are the SAME physical dual-role ports
|
|
# counted from the USB2 and USB3 register sets respectively, NOT additive to p2+p3 --
|
|
# confirmed live 2026-09-06, the stock default p2=4,p3=4 exposes exactly 4 real ports,
|
|
# not 8). Every usb-storage device attached below or via QEMU_EXTRA must get its own
|
|
# explicit port=N less than this value: exceed it and QEMU silently drops the overflow
|
|
# onto an auto-inserted internal USB2 hub, which the xHCI/BOT driver correctly refuses
|
|
# to treat as Mass Storage (root cause of the FABRIC-3.md §IX.5 "4th-device enumeration
|
|
# failure" -- misread as a driver bug at the time, it never was one). Sized with real
|
|
# headroom above the current 9-device identity roster (Zuse + 8 minted identities)
|
|
# rather than matching it exactly, per Bob's standing ruling against hardcoding a bound
|
|
# to today's scale (FABRIC-3.md §VII.4). Override if a future test needs more, e.g.
|
|
# `make -f kernel/Makefile qemu XHCI_PORTS=32`.
|
|
XHCI_PORTS ?= 16
|
|
# ZUSEDISK -- Zuse's own minted USB thumbdrive, attached by default so a
|
|
# plain `make qemu` lands already authenticated into the Zuse identity
|
|
# (Captain Bob, 2026-08-28). Attached at QEMU launch time via -drive/
|
|
# -device on the qemu-xhci controller each arch's qemu target already
|
|
# creates; xhci_bringup()'s initial port scan (kernel/src/usb/xhci.c)
|
|
# is what makes an already-connected-at-launch device visible at all --
|
|
# xhci_poll_events() alone is purely hotplug-event-driven and would never
|
|
# see a device present before controller reset. Override to "" (empty) to
|
|
# boot without Zuse attached, e.g. `make -f kernel/Makefile qemu ZUSEDISK=`.
|
|
ZUSEDISK ?= disk/thumbdrives/zuse-thumb-ident.img
|
|
# Precomputed (not inlined as $(if ...,...)) because the drive/device specs
|
|
# below are comma-heavy and GNU make's $(if) function splits its own
|
|
# arguments on every top-level comma -- inlining it silently truncated the
|
|
# flags to just "-drive if=none" with everything past the next comma
|
|
# dropped, confirmed live via `ps aux` showing the truncated qemu command
|
|
# line (2026-08-28).
|
|
ifneq ($(strip $(ZUSEDISK)),)
|
|
ZUSEDISK_QEMU_ARGS := -drive if=none,id=zusedrv,file=$(ZUSEDISK),format=raw -device usb-storage,bus=xhci0.0,port=1,drive=zusedrv
|
|
else
|
|
ZUSEDISK_QEMU_ARGS :=
|
|
endif
|
|
MKCAPSULE_SRC = tools/mkcapsule.c tools/pkcs8_ed25519.c \
|
|
kernel/src/crypto/ed25519.c \
|
|
kernel/src/crypto/fe25519.c \
|
|
kernel/src/crypto/scalar25519.c \
|
|
kernel/src/crypto/sha512.c
|
|
MKCAPSULE_BIN = $(BUILD_DIR)/tools/mkcapsule
|
|
# Milestone 6 (Phase 8): the snakeoil intermediate's private key, generated
|
|
# offline outside this repo entirely (see FABRIC-2.md's Phase 8 §Milestone 6
|
|
# writeup) -- not present in CI or a fresh checkout, so signing is skipped
|
|
# gracefully ($(wildcard ...) below) rather than failing the build. Override
|
|
# with `make SIGN_KEY=/path/to/key.pem ...` on a machine that holds it.
|
|
SIGN_KEY ?= /home/rajames/CLionProjects/lithosananke-ca/intermediate/snakeoil-intermediate.key
|
|
SIGN_KEY_ARGS = $(if $(wildcard $(SIGN_KEY)),--sign-key $(SIGN_KEY),)
|
|
CAPSULE_GENERATED = $(BUILD_DIR)/capsule_generated.c
|
|
CAPSULE_GENERATED_OBJ = $(BUILD_DIR)/capsule_generated.o
|
|
CAPSULE_GENERATED_KOBJ = $(KERNEL_OBJ_DIR)/capsule_generated.o
|
|
|
|
# ==============================================================================
|
|
# BOARD (boot_image)
|
|
# ==============================================================================
|
|
# BOARD names a directory under boards/ (ser5, raspi, milkv, ...). Its
|
|
# board.mk sets BOARD_ARCH, BOARD_BOOT (how the firmware finds us) and
|
|
# BOARD_IMAGE (the single file written to the boot medium). The root
|
|
# Makefile's `make boot_image TARGET=<NAME>` maps NAME to BOARD and passes
|
|
# ARCH=$(BOARD_ARCH); the kernel objects themselves are the ordinary
|
|
# build/$(ARCH)/kernel ones, shared with `make qemu`.
|
|
BOARD ?=
|
|
ifneq ($(strip $(BOARD)),)
|
|
ifeq ($(wildcard boards/$(BOARD)/board.mk),)
|
|
$(error Unknown BOARD='$(BOARD)': no boards/$(BOARD)/board.mk)
|
|
endif
|
|
include boards/$(BOARD)/board.mk
|
|
ifneq ($(BOARD_ARCH),$(ARCH))
|
|
$(error BOARD=$(BOARD) is ARCH=$(BOARD_ARCH), but ARCH=$(ARCH) was requested)
|
|
endif
|
|
BOARD_OUT := build/boards/$(BOARD)
|
|
endif
|
|
BOOT_IMAGE_SIZE_MIB ?= 128
|
|
|
|
# ==============================================================================
|
|
# TOOLCHAIN
|
|
# ==============================================================================
|
|
|
|
ifeq ($(ARCH),amd64)
|
|
ifneq ($(shell which x86_64-elf-gcc 2>/dev/null),)
|
|
CC := x86_64-elf-gcc
|
|
LD := x86_64-elf-ld
|
|
OBJCOPY := x86_64-elf-objcopy
|
|
else
|
|
CC := gcc
|
|
LD := ld
|
|
OBJCOPY := objcopy
|
|
endif
|
|
# -fno-pic -fno-pie: this is a fixed-base, statically-linked freestanding
|
|
# image with no dynamic linker to populate a GOT at load time. -fPIC (set
|
|
# in COMMON_CFLAGS, needed by riscv64's -shared link pipeline) makes GCC
|
|
# emit R_X86_64_REX_GOTPCRELX (GOT-indirect) addressing for some cross-TU
|
|
# extern globals depending on per-call-site codegen heuristics; the "GOT
|
|
# slot" is just an unpopulated .bss cell here, so those reads silently
|
|
# return NULL instead of the real address. Root-caused 2026-08-06 via
|
|
# dict_find_word_heat_aware() reading sf_fc_count as NULL while
|
|
# vm_find_word() (same globals, same TU as the definition) read it
|
|
# correctly. These flags come after COMMON_CFLAGS on the command line so
|
|
# they win for amd64 only; riscv64/aarch64 keep -fPIC.
|
|
ARCH_CFLAGS := -m64 -march=x86-64 -mno-red-zone -DARCH_AMD64 -fno-pic -fno-pie
|
|
LOADER_LINKER_SCRIPT := kernel/linker/starkernel-loader-amd64.ld
|
|
KERNEL_LINKER_SCRIPT := kernel/linker/starkernel-kernel-amd64.ld
|
|
|
|
else ifeq ($(ARCH),aarch64)
|
|
ifneq ($(shell which aarch64-linux-gnu-gcc 2>/dev/null),)
|
|
CC := aarch64-linux-gnu-gcc
|
|
LD := aarch64-linux-gnu-ld
|
|
OBJCOPY := aarch64-linux-gnu-objcopy
|
|
else ifneq ($(shell which aarch64-none-elf-gcc 2>/dev/null),)
|
|
CC := aarch64-none-elf-gcc
|
|
LD := aarch64-none-elf-ld
|
|
OBJCOPY := aarch64-none-elf-objcopy
|
|
else
|
|
$(error No ARM64 toolchain found. Install aarch64-linux-gnu-gcc or aarch64-none-elf-gcc)
|
|
endif
|
|
# aarch64 loader uses clang + lld-link (COFF target — GCC cannot produce aarch64 PE)
|
|
# Any clang with the aarch64 COFF backend works; the version is not
|
|
# pinned (Ubuntu 26.04 ships clang-21, older hosts clang-18).
|
|
LOADER_CC := $(shell which clang 2>/dev/null || ls /usr/bin/clang-[0-9]* 2>/dev/null | sort -V | tail -1 || echo clang)
|
|
# Debian/Ubuntu package only installs the versioned lld-link-NN on PATH by
|
|
# default (unversioned lld-link lives under /usr/lib/llvm-NN/bin, which
|
|
# isn't on PATH unless explicitly prepended) — detect whichever resolves.
|
|
LOADER_LD := $(shell which lld-link 2>/dev/null || ls /usr/bin/lld-link-[0-9]* /usr/lib/llvm-*/bin/lld-link 2>/dev/null | sort -V | tail -1 || echo lld-link)
|
|
ARCH_CFLAGS := -march=armv8-a -mcpu=cortex-a72 -DARCH_AARCH64 -mno-outline-atomics
|
|
LOADER_LINKER_SCRIPT := kernel/linker/starkernel-loader-aarch64.ld
|
|
KERNEL_LINKER_SCRIPT := kernel/linker/starkernel-kernel-aarch64.ld
|
|
|
|
else ifeq ($(ARCH),riscv64)
|
|
ifneq ($(shell which riscv64-unknown-elf-gcc 2>/dev/null),)
|
|
CC := riscv64-unknown-elf-gcc
|
|
LD := riscv64-unknown-elf-ld
|
|
OBJCOPY := riscv64-unknown-elf-objcopy
|
|
else ifneq ($(shell which riscv64-linux-gnu-gcc 2>/dev/null),)
|
|
CC := riscv64-linux-gnu-gcc
|
|
LD := riscv64-linux-gnu-ld
|
|
OBJCOPY := riscv64-linux-gnu-objcopy
|
|
else
|
|
$(error No RISC-V toolchain found. Install riscv64-unknown-elf-gcc or riscv64-linux-gnu-gcc)
|
|
endif
|
|
ARCH_CFLAGS := -march=rv64gc -mabi=lp64d -DARCH_RISCV64
|
|
LOADER_LINKER_SCRIPT := kernel/linker/starkernel-loader-riscv64.ld
|
|
KERNEL_LINKER_SCRIPT := kernel/linker/starkernel-kernel-riscv64.ld
|
|
endif
|
|
|
|
# Default loader toolchain = kernel toolchain (overridden for aarch64 above)
|
|
LOADER_CC ?= $(CC)
|
|
LOADER_LD ?= $(LD)
|
|
|
|
# ==============================================================================
|
|
# COMPILER FLAGS
|
|
# ==============================================================================
|
|
|
|
# Common flags for all arches / both loader and kernel.
|
|
# -fPIC + -fvisibility=hidden: needed by riscv64's -shared loader link
|
|
# pipeline (amd64 overrides back to -fno-pic/-fno-pie in its ARCH_CFLAGS
|
|
# above -- see that comment for why: -fPIC made GCC emit GOT-indirect
|
|
# addressing for some cross-TU extern globals, and this freestanding image
|
|
# has no dynamic linker to populate a GOT, so those reads silently returned
|
|
# NULL. PLATFORM_TIME_NO_INLINE (removed 2026-08-06) was a symbol-specific
|
|
# workaround for the same underlying bug, made unnecessary once amd64 got
|
|
# the real fix; see FABRIC-0.md for the write-up).
|
|
COMMON_CFLAGS := \
|
|
-std=c99 -Wall -Werror -Wextra \
|
|
-O2 -U_FORTIFY_SOURCE \
|
|
-ffreestanding -nostdlib -fno-builtin \
|
|
-fPIC -fvisibility=hidden -fno-stack-protector -fshort-wchar \
|
|
$(ARCH_CFLAGS) \
|
|
-I$(KERNEL_INC) -Iv3/include -Ikernel/include -Iv3/src \
|
|
-include $(STARFORTH_CONFIG_HEADER) \
|
|
-DPARITY_MODE=$(PARITY_MODE)
|
|
|
|
VMCORE_CFLAGS_COMMON := \
|
|
$(filter-out -I$(KERNEL_INC),$(COMMON_CFLAGS)) \
|
|
-Iv3/include -Ikernel/include -I. -Iv3/src/word_source -Iv3/src/test_runner/include \
|
|
-Wno-error=unused-parameter -Wno-error=shift-negative-value \
|
|
-Wno-error=sign-compare -Wno-error=missing-field-initializers
|
|
|
|
# aarch64 loader flags: clang COFF target — drop ELF-only flags (-fPIC,
|
|
# -fvisibility=hidden), add COFF-specific flags instead.
|
|
ifeq ($(ARCH),aarch64)
|
|
LOADER_BASE_CFLAGS := \
|
|
-std=c99 -Wall -Werror -Wextra \
|
|
--target=aarch64-pc-windows-msvc \
|
|
-ffreestanding -fno-builtin \
|
|
-fno-stack-protector -fshort-wchar \
|
|
-mno-stack-arg-probe \
|
|
-march=armv8-a \
|
|
-DARCH_AARCH64 \
|
|
-I$(KERNEL_INC) -Iv3/include -Ikernel/include -Iv3/src \
|
|
-Ikernel/include/starkernel/freestanding \
|
|
-include $(STARFORTH_CONFIG_HEADER) \
|
|
-DPARITY_MODE=$(PARITY_MODE) \
|
|
-DPLATFORM_TIME_NO_INLINE
|
|
else
|
|
LOADER_BASE_CFLAGS := $(COMMON_CFLAGS)
|
|
endif
|
|
|
|
DOE_INJECT ?= 0
|
|
DOE_REPS ?= 3
|
|
DOE_SEED ?= 12345
|
|
# EMERGENCY_CONSOLE_ENABLED is blanket-forwarded (unlike the opt-in-only
|
|
# knobs below), so it's repointed through the Kconfig bridge the same way
|
|
# as the hosted Makefile's STRICT_PTR (Phase 3) -- Phase 4 heartbeat family.
|
|
# Default flipped 0 (was 1) 2026-09-05: headless-until-login is now the
|
|
# production default (Kconfig.heartbeat's own default n, same decision).
|
|
$(eval $(call kconfig_bool,EMERGENCY_CONSOLE_ENABLED,0))
|
|
LOADER_CFLAGS := $(LOADER_BASE_CFLAGS) -I$(KERNEL_SRC)/vm
|
|
KERNEL_CFLAGS := $(COMMON_CFLAGS) -D__STARKERNEL__ -I$(KERNEL_SRC)/vm
|
|
|
|
# Linker flags
|
|
ifeq ($(ARCH),aarch64)
|
|
LOADER_LDFLAGS_PE := # lld-link called directly in link rule
|
|
else ifeq ($(ARCH),riscv64)
|
|
LOADER_LDFLAGS_PE := # ELF→objcopy pipeline in link rule
|
|
else
|
|
LOADER_LINKER_SCRIPT_PE := kernel/linker/starkernel-loader-amd64-pe.ld
|
|
LOADER_LDFLAGS_PE := \
|
|
-T $(LOADER_LINKER_SCRIPT_PE) -m i386pep -nostdlib \
|
|
--enable-reloc-section --image-base 0 --subsystem 10 -e efi_main
|
|
endif
|
|
|
|
LOADER_LDFLAGS := \
|
|
-T $(LOADER_LINKER_SCRIPT) -nostdlib --build-id=none \
|
|
--emit-relocs -shared -Bsymbolic -znocombreloc -e efi_main
|
|
KERNEL_LDFLAGS := \
|
|
-T $(KERNEL_LINKER_SCRIPT) -nostdlib --build-id=none \
|
|
--emit-relocs -e kernel_main
|
|
|
|
# ==============================================================================
|
|
# VM INTEGRATION FLAGS
|
|
# ==============================================================================
|
|
|
|
ifeq ($(STARFORTH_ENABLE_VM),1)
|
|
KERNEL_CFLAGS += -DSTARFORTH_ENABLE_VM=1 -DSTARFORTH_MINIMAL=1
|
|
LOADER_CFLAGS += -DSTARFORTH_ENABLE_VM=1 -DSTARFORTH_MINIMAL=1
|
|
|
|
# Physics/SSM knob family: pre-assign from the Kconfig-fed CONFIG_* value
|
|
# when a build/$(ARCH)/.config exists (Kconfig migration Phase 3). This is
|
|
# additive to, not a replacement for, this Makefile's existing opt-in
|
|
# philosophy below: a plain `:=`/derived assignment here makes
|
|
# `origin($1)` "file" (not "undefined"), so `add_vm_flag` picks it up and
|
|
# forwards it -- exactly as if a developer had passed it on the command
|
|
# line. When no .config exists (KCONFIG_ACTIVE unset), none of this fires,
|
|
# and a knob is only forwarded if a developer explicitly sets it, same as
|
|
# before this migration touched the kernel build at all. Command-line
|
|
# values always win regardless, per ordinary Make semantics.
|
|
ifeq ($(KCONFIG_ACTIVE),1)
|
|
$(eval $(call kconfig_bool,STRICT_PTR,1))
|
|
$(eval $(call kconfig_bool,ENABLE_HOTWORDS_CACHE,0))
|
|
$(eval $(call kconfig_bool,ENABLE_PIPELINING,0))
|
|
$(eval $(call kconfig_int,ROLLING_WINDOW_SIZE,4096))
|
|
$(eval $(call kconfig_int,TRANSITION_WINDOW_SIZE,8))
|
|
$(eval $(call kconfig_int,ADAPTIVE_SHRINK_RATE,50))
|
|
$(eval $(call kconfig_int,ADAPTIVE_MIN_WINDOW_SIZE,256))
|
|
$(eval $(call kconfig_int,ADAPTIVE_CHECK_FREQUENCY,512))
|
|
$(eval $(call kconfig_int,ADAPTIVE_GROWTH_THRESHOLD,5))
|
|
$(eval $(call kconfig_int,INITIAL_DECAY_SLOPE_Q48,21845))
|
|
$(eval $(call kconfig_int,DECAY_MIN_INTERVAL,500))
|
|
$(eval $(call kconfig_int,DECAY_RATE_PER_US_Q16,1))
|
|
$(eval $(call kconfig_int,HEARTBEAT_INFERENCE_FREQUENCY,1000))
|
|
$(eval $(call kconfig_int,STADIUM_MAX_VM_COUNT,4))
|
|
$(eval $(call kconfig_int,STADIUM_CONTAINS_DEPTH_MAX,5))
|
|
$(eval $(call kconfig_int,STADIUM_CAPACITY_TICK,1000))
|
|
$(eval $(call kconfig_int,STADIUM_MEMORY_PERCENT,1))
|
|
$(eval $(call kconfig_int,STADIUM_WORD_HEAT_QUANTUM,2048))
|
|
$(eval $(call kconfig_int,STADIUM_WORD_COOL_RATE_Q48,21845))
|
|
$(eval $(call kconfig_int,STADIUM_BLOCK_HEAT_QUANTUM,2048))
|
|
$(eval $(call kconfig_int,STADIUM_BLOCK_COOL_RATE_Q48,21845))
|
|
$(eval $(call kconfig_int,STADIUM_BLOCK_TRACK_CAP_MULT,2))
|
|
$(eval $(call kconfig_int,SSM_ENTROPY_HIGH_THRESHOLD,0.75))
|
|
$(eval $(call kconfig_int,SSM_CV_HIGH_THRESHOLD,0.15))
|
|
$(eval $(call kconfig_int,SSM_TEMPORAL_DECAY_THRESHOLD,0.5))
|
|
$(eval $(call kconfig_int,SSM_TEMPORAL_DECAY_LOW_THRESHOLD,0.3))
|
|
$(eval $(call kconfig_int,SSM_HYSTERESIS_TICKS,5))
|
|
# Heartbeat family (Phase 4). HEARTBEAT_THREAD_ENABLED/HEARTBEAT_TICK_NS
|
|
# `depends on STARFORTH_VARIANT_HOSTED` in Kconfig.heartbeat, so for a
|
|
# kernel-variant .config CONFIG_HEARTBEAT_THREAD_ENABLED is simply absent
|
|
# and kconfig_bool resolves it to 0 -- correctly, since LithosAnanke has no
|
|
# pthreads -- with no special case needed here. The unconditional
|
|
# `VM_FEATURE_OVERRIDES += -DHEARTBEAT_THREAD_ENABLED=0` below is kept as a
|
|
# deliberate second, independent enforcement of the same fact; see
|
|
# Kconfig.heartbeat's help text for why both layers exist.
|
|
$(eval $(call kconfig_bool,HEARTBEAT_THREAD_ENABLED,0))
|
|
$(eval $(call kconfig_int,HEARTBEAT_TICK_NS,10000))
|
|
$(eval $(call kconfig_int,HEARTBEAT_CHECK_FREQUENCY,256))
|
|
$(eval $(call kconfig_int,HEARTBEAT_WINDOW_TUNING_FREQUENCY,1000))
|
|
$(eval $(call kconfig_int,HEARTBEAT_SLOPE_VALIDATION_FREQUENCY,5000))
|
|
# Remaining physics_pipelining_metrics.h constants (Phase 5). Previously
|
|
# hardcoded, non-overridable #defines; see Kconfig.physics for the exact
|
|
# hex/decimal defaults. MINIMUM_PREFETCH_ROI's default was corrected from
|
|
# the long-shipped 0x11999A to 0x1199A (rev y) -- the former was ~17.6 in
|
|
# Q48.16, not the documented 1.10.
|
|
$(eval $(call kconfig_int,SPECULATION_THRESHOLD_Q48,0x8000))
|
|
$(eval $(call kconfig_int,SPECULATION_DEPTH,1))
|
|
$(eval $(call kconfig_int,MIN_SAMPLES_FOR_SPECULATION,10))
|
|
$(eval $(call kconfig_int,MISPREDICTION_COST_Q48,0x190000))
|
|
$(eval $(call kconfig_int,MINIMUM_PREFETCH_ROI,0x1199A))
|
|
# SK_PARITY_DEBUG (Phase 6): previously opt-in-only via VM_FEATURE_FLAG_VARS
|
|
# below with no Kconfig presence at all.
|
|
$(eval $(call kconfig_bool,SK_PARITY_DEBUG,0))
|
|
endif
|
|
|
|
VM_FEATURE_FLAG_VARS := \
|
|
STRICT_PTR ENABLE_HOTWORDS_CACHE ENABLE_PIPELINING \
|
|
ROLLING_WINDOW_SIZE TRANSITION_WINDOW_SIZE \
|
|
ADAPTIVE_SHRINK_RATE ADAPTIVE_MIN_WINDOW_SIZE \
|
|
ADAPTIVE_CHECK_FREQUENCY ADAPTIVE_GROWTH_THRESHOLD \
|
|
INITIAL_DECAY_SLOPE_Q48 DECAY_MIN_INTERVAL \
|
|
DECAY_RATE_PER_US_Q16 HEARTBEAT_THREAD_ENABLED \
|
|
HEARTBEAT_TICK_NS HEARTBEAT_INFERENCE_FREQUENCY \
|
|
HEARTBEAT_CHECK_FREQUENCY HEARTBEAT_WINDOW_TUNING_FREQUENCY \
|
|
HEARTBEAT_SLOPE_VALIDATION_FREQUENCY SK_PARITY_DEBUG \
|
|
EMERGENCY_CONSOLE_ENABLED STADIUM_MAX_VM_COUNT \
|
|
STADIUM_CONTAINS_DEPTH_MAX STADIUM_CAPACITY_TICK \
|
|
STADIUM_MEMORY_PERCENT \
|
|
SSM_ENTROPY_HIGH_THRESHOLD SSM_CV_HIGH_THRESHOLD \
|
|
SSM_TEMPORAL_DECAY_THRESHOLD SSM_TEMPORAL_DECAY_LOW_THRESHOLD \
|
|
SSM_HYSTERESIS_TICKS \
|
|
SPECULATION_THRESHOLD_Q48 SPECULATION_DEPTH \
|
|
MIN_SAMPLES_FOR_SPECULATION MISPREDICTION_COST_Q48 \
|
|
MINIMUM_PREFETCH_ROI
|
|
|
|
VM_FEATURE_OVERRIDES :=
|
|
define add_vm_flag
|
|
ifneq ($$(origin $1),undefined)
|
|
VM_FEATURE_OVERRIDES += -D$1=$$($1)
|
|
endif
|
|
endef
|
|
$(foreach f,$(VM_FEATURE_FLAG_VARS),$(eval $(call add_vm_flag,$f)))
|
|
VM_FEATURE_OVERRIDES += -DHEARTBEAT_THREAD_ENABLED=0
|
|
|
|
KERNEL_CFLAGS += $(VM_FEATURE_OVERRIDES)
|
|
LOADER_CFLAGS += $(VM_FEATURE_OVERRIDES)
|
|
endif
|
|
|
|
# SK_CMD — optional startup FORTH script injected before the interactive REPL.
|
|
# Usage: make -f kernel/Makefile qemu SK_CMD="TIME-TICKS . BYE"
|
|
ifdef SK_CMD
|
|
KERNEL_CFLAGS += -DSK_STARTUP_FORTH='"$(SK_CMD)"'
|
|
LOADER_CFLAGS += -DSK_STARTUP_FORTH='"$(SK_CMD)"'
|
|
endif
|
|
|
|
# KERNEL_ARGS — runtime boot flags written into starforth.cfg on the ESP image.
|
|
# The loader reads this file from the EFI partition and parses it at boot.
|
|
# Supported flags: --doe --log-level=<debug|info|warn|error>
|
|
# --stack=<N>[MG] --heap=<N>[MG]
|
|
# Usage: make -f kernel/Makefile qemu KERNEL_ARGS="--doe --log-level=info"
|
|
KERNEL_ARGS ?=
|
|
|
|
# ==============================================================================
|
|
# SOURCE FILES
|
|
# ==============================================================================
|
|
|
|
ifeq ($(MONOLITHIC),1)
|
|
LOADER_CFLAGS += -DMONOLITHIC_BUILD -D__STARKERNEL__
|
|
|
|
LOADER_SRCS_BASE := \
|
|
$(KERNEL_SRC)/boot/uefi_loader.c \
|
|
$(KERNEL_SRC)/boot/elf_loader.c \
|
|
$(KERNEL_SRC)/boot/reloc_stub.c \
|
|
$(KERNEL_SRC)/boot/cmdline.c \
|
|
$(KERNEL_SRC)/kernel_main.c \
|
|
$(wildcard $(KERNEL_SRC)/hal/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/memory/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/math/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/hash/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/crypto/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/capsule/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/pci/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/virtio/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/rng/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/usb/*.c) \
|
|
$(KERNEL_SRC)/repl.c \
|
|
$(KERNEL_SRC)/doe_log.c \
|
|
$(KERNEL_SRC)/heartbeat.c \
|
|
$(KERNEL_SRC)/homeblocks_sig.c \
|
|
$(KERNEL_SRC)/artemis_sig.c \
|
|
$(KERNEL_SRC)/log_region.c
|
|
|
|
LOADER_ASM := \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/boot.S \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/isr.S \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/switch.S
|
|
|
|
ifeq ($(ARCH),amd64)
|
|
LOADER_ASM += $(KERNEL_SRC)/arch/$(ARCH)/kernel_entry.S
|
|
endif
|
|
ifeq ($(ARCH),riscv64)
|
|
LOADER_ASM += $(KERNEL_SRC)/arch/$(ARCH)/kernel_entry.S
|
|
endif
|
|
|
|
LOADER_ARCH_SRCS := \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/arch.c \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/interrupts.c \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/apic.c \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/timer.c
|
|
|
|
ifeq ($(ARCH),amd64)
|
|
LOADER_ARCH_SRCS += \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/ioapic.c \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/i8042.c
|
|
endif
|
|
ifeq ($(ARCH),riscv64)
|
|
LOADER_ARCH_SRCS += \
|
|
$(KERNEL_SRC)/arch/$(ARCH)/plic.c
|
|
endif
|
|
else
|
|
LOADER_SRCS_BASE := \
|
|
$(KERNEL_SRC)/boot/uefi_loader.c \
|
|
$(KERNEL_SRC)/boot/elf_loader.c \
|
|
$(KERNEL_SRC)/boot/reloc_stub.c
|
|
LOADER_ASM :=
|
|
LOADER_ARCH_SRCS := $(KERNEL_SRC)/arch/$(ARCH)/arch.c
|
|
endif
|
|
|
|
KERNEL_SRCS_BASE := \
|
|
$(KERNEL_SRC)/kernel_main.c \
|
|
$(KERNEL_SRC)/boot/cmdline.c \
|
|
$(wildcard $(KERNEL_SRC)/hal/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/memory/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/math/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/hash/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/crypto/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/vm_arbiter/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/capsule/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/pci/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/virtio/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/rng/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/usb/*.c) \
|
|
$(wildcard $(KERNEL_SRC)/arch/$(ARCH)/*.c) \
|
|
$(KERNEL_SRC)/repl.c \
|
|
$(KERNEL_SRC)/doe_log.c \
|
|
$(KERNEL_SRC)/heartbeat.c \
|
|
$(KERNEL_SRC)/homeblocks_sig.c \
|
|
$(KERNEL_SRC)/artemis_sig.c \
|
|
$(KERNEL_SRC)/log_region.c
|
|
|
|
KERNEL_ASM := $(wildcard $(KERNEL_SRC)/arch/$(ARCH)/*.S)
|
|
|
|
ifeq ($(STARFORTH_ENABLE_VM),1)
|
|
VM_ALL_SRCS := \
|
|
$(wildcard v3/src/*.c) \
|
|
$(wildcard v3/src/word_source/*.c) \
|
|
$(wildcard v3/src/test_runner/*.c) \
|
|
$(wildcard v3/src/test_runner/modules/*.c)
|
|
|
|
VM_EXCLUDE := \
|
|
v3/src/main.c v3/src/cli.c v3/src/repl.c \
|
|
v3/src/platform/% \
|
|
v3/src/blkio_factory.c v3/src/blkio_file.c \
|
|
v3/src/log.c v3/src/doe_metrics.c \
|
|
v3/src/vm.c v3/src/vm_core.c v3/src/vm_bootstrap.c v3/src/vm_runtime.c v3/src/vm_time.c \
|
|
v3/src/word_source/q48_16_words.c \
|
|
v3/src/test_runner/modules/break_me_tests.c
|
|
|
|
VM_CORE_SRCS := $(filter-out $(VM_EXCLUDE),$(VM_ALL_SRCS))
|
|
|
|
LOADER_EXTRA_SRCS := \
|
|
$(KERNEL_SRC)/vm/arena.c \
|
|
$(KERNEL_SRC)/vm/switch.c \
|
|
$(KERNEL_SRC)/vm/parity.c \
|
|
$(KERNEL_SRC)/vm/bootstrap/sk_vm_bootstrap.c \
|
|
$(KERNEL_SRC)/vm/host/shim.c \
|
|
$(KERNEL_SRC)/vm/vm_core.c \
|
|
$(KERNEL_SRC)/vm/vm_bootstrap.c \
|
|
$(KERNEL_SRC)/vm/vm_runtime.c \
|
|
$(KERNEL_SRC)/vm/alloc_kernel.c \
|
|
$(KERNEL_SRC)/vm/q48_stubs.c \
|
|
$(KERNEL_SRC)/vm/stadium.c \
|
|
$(KERNEL_SRC)/vm/stadium_words.c \
|
|
$(KERNEL_SRC)/vm/stadium_blocks.c \
|
|
$(KERNEL_SRC)/vm/session.c \
|
|
$(KERNEL_SRC)/vm/kernel_hermes.c
|
|
|
|
KERNEL_EXTRA_SRCS := $(LOADER_EXTRA_SRCS)
|
|
|
|
LOADER_VM_OBJS := $(patsubst v3/src/%.c,$(LOADER_OBJ_DIR)/vmcore/%.o,$(VM_CORE_SRCS))
|
|
KERNEL_VM_OBJS := $(patsubst v3/src/%.c,$(KERNEL_OBJ_DIR)/vmcore/%.o,$(VM_CORE_SRCS))
|
|
endif
|
|
|
|
LOADER_SRCS := $(LOADER_SRCS_BASE) $(LOADER_EXTRA_SRCS)
|
|
KERNEL_SRCS := $(KERNEL_SRCS_BASE) $(KERNEL_EXTRA_SRCS)
|
|
|
|
LOADER_OBJS := \
|
|
$(patsubst $(KERNEL_SRC)/%.c,$(LOADER_OBJ_DIR)/%.o,$(LOADER_SRCS)) \
|
|
$(patsubst $(KERNEL_SRC)/%.c,$(LOADER_OBJ_DIR)/%.o,$(LOADER_ARCH_SRCS)) \
|
|
$(patsubst $(KERNEL_SRC)/%.S,$(LOADER_OBJ_DIR)/%.o,$(LOADER_ASM)) \
|
|
$(LOADER_VM_OBJS) \
|
|
$(CAPSULE_GENERATED_OBJ)
|
|
|
|
KERNEL_OBJS := \
|
|
$(patsubst $(KERNEL_SRC)/%.c,$(KERNEL_OBJ_DIR)/%.o,$(KERNEL_SRCS)) \
|
|
$(patsubst $(KERNEL_SRC)/%.S,$(KERNEL_OBJ_DIR)/%.o,$(KERNEL_ASM)) \
|
|
$(KERNEL_VM_OBJS) \
|
|
$(CAPSULE_GENERATED_KOBJ)
|
|
|
|
# ==============================================================================
|
|
# PHONY TARGETS
|
|
# ==============================================================================
|
|
|
|
.PHONY: all clean clean-kernel kernel kernel-all
|
|
.PHONY: qemu qemu-esp qemu-gdb
|
|
.PHONY: thumbdrive iso-usb
|
|
.PHONY: info help
|
|
.PHONY: FORCE
|
|
|
|
# ==============================================================================
|
|
# MAIN TARGETS
|
|
# ==============================================================================
|
|
|
|
all: v3/include/version.h $(CAPSULE_GENERATED_OBJ) $(CAPSULE_GENERATED_KOBJ) $(LOADER_EFI) $(KERNEL_ELF)
|
|
@echo "StarKernel built successfully for $(ARCH)"
|
|
@echo " Loader: $(LOADER_EFI)"
|
|
@echo " Kernel: $(KERNEL_ELF)"
|
|
|
|
kernel: all
|
|
|
|
kernel-all:
|
|
@$(MAKE) -f kernel/Makefile ARCH=amd64 TARGET=$(TARGET)
|
|
@$(MAKE) -f kernel/Makefile ARCH=aarch64 TARGET=$(TARGET)
|
|
@$(MAKE) -f kernel/Makefile ARCH=riscv64 TARGET=$(TARGET)
|
|
|
|
clean:
|
|
rm -rf build/$(ARCH)/$(TARGET)
|
|
rm -f v3/include/version.h
|
|
|
|
clean-kernel:
|
|
@echo "Cleaning all StarKernel build artifacts..."
|
|
@rm -rf build/*/kernel
|
|
@echo "Done."
|
|
|
|
# ==============================================================================
|
|
# BUILD RULES
|
|
# ==============================================================================
|
|
|
|
FORCE:
|
|
|
|
# FORCE prerequisite (matches the hosted Makefile's own pattern, Makefile:613/626):
|
|
# this file is also written by the hosted Makefile with different, incompatible
|
|
# content (no LITHOS_VERSION/LITHOS_VERSION_STR) -- without FORCE, a bare build
|
|
# here after a hosted `make` run silently reuses that stale/wrong-flavored file
|
|
# and fails with "LITHOS_VERSION_STR undeclared" deep in kernel_main.c, instead
|
|
# of regenerating its own correct version.
|
|
v3/include/version.h: FORCE
|
|
@mkdir -p v3/include
|
|
@BUILD_TS=$$(date -Iseconds 2>/dev/null || echo "unknown"); \
|
|
printf '#ifndef STARFORTH_VERSION_H\n#define STARFORTH_VERSION_H\n\n' > $@; \
|
|
printf '#define STARFORTH_VERSION "%s"\n' "$(VERSION)" >> $@; \
|
|
printf '#define STARFORTH_ARCH "%s"\n' "$(ARCH)" >> $@; \
|
|
printf '#define STARFORTH_TARGET "%s"\n' "kernel" >> $@; \
|
|
printf '#define STARFORTH_TIMESTAMP "%s"\n' "$$BUILD_TS" >> $@; \
|
|
printf '#define STARFORTH_VERSION_FULL "StarForth v%s %s kernel %s"\n\n' "$(VERSION)" "$(ARCH)" "$$BUILD_TS" >> $@; \
|
|
printf '#define LITHOS_VERSION "%s"\n' "$(LITHOS_VERSION)" >> $@; \
|
|
printf '#define LITHOS_VERSION_STR "LithosAnanke v%s"\n\n' "$(LITHOS_VERSION)" >> $@; \
|
|
printf '#endif /* STARFORTH_VERSION_H */\n' >> $@
|
|
|
|
# Host tool: capsule packer
|
|
$(MKCAPSULE_BIN): $(MKCAPSULE_SRC)
|
|
@mkdir -p $(dir $@)
|
|
@echo "HOSTCC $(MKCAPSULE_SRC)"
|
|
@cc -std=c99 -Wall -Wextra -O2 -Iv3/include -Ikernel/include -Itools -o $@ $(MKCAPSULE_SRC)
|
|
|
|
# Generate capsule_generated.c from capsules/
|
|
CAPSULE_SRCS := $(shell find $(CAPSULES_DIR) -type f ! -name '.*' 2>/dev/null)
|
|
$(CAPSULE_GENERATED): $(MKCAPSULE_BIN) $(CAPSULE_SRCS)
|
|
@mkdir -p $(dir $@)
|
|
@echo " MKCAP $(CAPSULES_DIR) -> $@"
|
|
@$(MKCAPSULE_BIN) $(SIGN_KEY_ARGS) $(CAPSULES_DIR) $@
|
|
@echo " MKCAP $(CAPSULES_DIR) -> $(CAPSULES_DIR)/BLOCK_MAP.md"
|
|
@$(MKCAPSULE_BIN) --manifest $(SIGN_KEY_ARGS) $(CAPSULES_DIR) $(CAPSULES_DIR)/BLOCK_MAP.md
|
|
|
|
# manifest — regenerate capsules/BLOCK_MAP.md without a full kernel build
|
|
.PHONY: manifest
|
|
manifest: $(MKCAPSULE_BIN)
|
|
@$(MKCAPSULE_BIN) --manifest $(SIGN_KEY_ARGS) $(CAPSULES_DIR) $(CAPSULES_DIR)/BLOCK_MAP.md
|
|
@echo " Wrote $(CAPSULES_DIR)/BLOCK_MAP.md"
|
|
|
|
$(CAPSULE_GENERATED_OBJ): $(CAPSULE_GENERATED)
|
|
@echo " CC $<"
|
|
@$(LOADER_CC) $(LOADER_CFLAGS) -c $< -o $@
|
|
|
|
$(CAPSULE_GENERATED_KOBJ): $(CAPSULE_GENERATED) | $(KERNEL_OBJ_DIR)
|
|
@echo " CC (kernel) $<"
|
|
@$(CC) $(KERNEL_CFLAGS) -c $< -o $@
|
|
|
|
# Create build directories
|
|
$(BUILD_DIR) $(LOADER_OBJ_DIR) $(KERNEL_OBJ_DIR):
|
|
@mkdir -p \
|
|
$(LOADER_OBJ_DIR)/boot $(LOADER_OBJ_DIR)/arch/$(ARCH) \
|
|
$(LOADER_OBJ_DIR)/vm $(LOADER_OBJ_DIR)/capsule $(LOADER_OBJ_DIR)/hash
|
|
@mkdir -p \
|
|
$(KERNEL_OBJ_DIR)/boot $(KERNEL_OBJ_DIR)/hal $(KERNEL_OBJ_DIR)/memory \
|
|
$(KERNEL_OBJ_DIR)/vm_arbiter $(KERNEL_OBJ_DIR)/capsule \
|
|
$(KERNEL_OBJ_DIR)/hash $(KERNEL_OBJ_DIR)/arch/$(ARCH) $(KERNEL_OBJ_DIR)/vm
|
|
|
|
# Compile loader C sources
|
|
$(LOADER_OBJ_DIR)/%.o: $(KERNEL_SRC)/%.c | $(LOADER_OBJ_DIR)
|
|
@mkdir -p $(dir $@)
|
|
@echo "CC (loader) $<"
|
|
@$(LOADER_CC) $(LOADER_CFLAGS) -c $< -o $@
|
|
|
|
# Compile loader VM core sources
|
|
$(LOADER_OBJ_DIR)/vmcore/%.o: v3/src/%.c v3/include/version.h | $(LOADER_OBJ_DIR)
|
|
@mkdir -p $(dir $@)
|
|
@echo "CC (loader) $<"
|
|
ifeq ($(ARCH),aarch64)
|
|
@$(LOADER_CC) $(LOADER_CFLAGS) \
|
|
-Iv3/include -Ikernel/include -I. -Iv3/src/test_runner/include \
|
|
-Wno-error=unused-parameter -Wno-error=shift-negative-value \
|
|
-Wno-error=sign-compare -Wno-error=missing-field-initializers \
|
|
-Wno-unused-function \
|
|
-c $< -o $@
|
|
else
|
|
@$(LOADER_CC) $(VMCORE_CFLAGS_COMMON) $(filter-out -I$(KERNEL_INC),$(LOADER_CFLAGS)) -c $< -o $@
|
|
endif
|
|
|
|
# Compile loader assembly sources
|
|
$(LOADER_OBJ_DIR)/%.o: $(KERNEL_SRC)/%.S | $(LOADER_OBJ_DIR)
|
|
@mkdir -p $(dir $@)
|
|
@echo "AS (loader) $<"
|
|
@$(LOADER_CC) $(LOADER_CFLAGS) -c $< -o $@
|
|
|
|
# Compile kernel C sources
|
|
$(KERNEL_OBJ_DIR)/%.o: $(KERNEL_SRC)/%.c | $(KERNEL_OBJ_DIR)
|
|
@mkdir -p $(dir $@)
|
|
@echo "CC (kernel) $<"
|
|
@$(CC) $(KERNEL_CFLAGS) -c $< -o $@
|
|
|
|
# Compile kernel VM core sources
|
|
$(KERNEL_OBJ_DIR)/vmcore/%.o: v3/src/%.c v3/include/version.h | $(KERNEL_OBJ_DIR)
|
|
@mkdir -p $(dir $@)
|
|
@echo "CC (kernel) $<"
|
|
@$(CC) $(VMCORE_CFLAGS_COMMON) $(filter-out -I$(KERNEL_INC),$(KERNEL_CFLAGS)) -Ikernel/src/vm -c $< -o $@
|
|
|
|
# Compile kernel assembly sources
|
|
$(KERNEL_OBJ_DIR)/%.o: $(KERNEL_SRC)/%.S | $(KERNEL_OBJ_DIR)
|
|
@mkdir -p $(dir $@)
|
|
@echo "AS (kernel) $<"
|
|
@$(CC) $(KERNEL_CFLAGS) -c $< -o $@
|
|
|
|
# ==============================================================================
|
|
# LINK RULES
|
|
# ==============================================================================
|
|
|
|
# Link loader to PE32+
|
|
# amd64: ld -m i386pep → PE directly
|
|
# aarch64: lld-link links COFF objects → PE (auto-generates .reloc)
|
|
# riscv64: ELF → gen_pe_reloc.py patches addends → objcopy → pei-riscv64-little
|
|
$(LOADER_EFI): $(LOADER_OBJS) | $(BUILD_DIR)
|
|
ifeq ($(ARCH),aarch64)
|
|
@echo "LLD-LINK (loader PE) $@"
|
|
@$(LOADER_LD) /subsystem:EFI_APPLICATION /entry:efi_main /nodefaultlib \
|
|
/align:4096 /base:0 /out:$@ $(LOADER_OBJS)
|
|
@echo "Loader built: $@"
|
|
else ifeq ($(ARCH),riscv64)
|
|
@echo "LD+OBJCOPY (loader ELF→PE) $@"
|
|
@$(LD) -T $(LOADER_LINKER_SCRIPT) -nostdlib --build-id=none \
|
|
-shared -Bsymbolic -znocombreloc -e efi_main -m elf64lriscv \
|
|
$(LOADER_OBJS) -o $(BUILD_DIR)/starkernel_loader_tmp.elf
|
|
@python3 scripts/gen_pe_reloc.py \
|
|
$(BUILD_DIR)/starkernel_loader_tmp.elf \
|
|
$(BUILD_DIR)/starkernel_loader_patched.elf \
|
|
$(BUILD_DIR)/reloc_rv64.bin
|
|
@$(OBJCOPY) \
|
|
--remove-section=.rela.dyn \
|
|
--remove-section=.dynstr \
|
|
--remove-section=.hash \
|
|
--remove-section=.gnu.hash \
|
|
--remove-section=.dynsym \
|
|
--remove-section=.dynamic \
|
|
--remove-section=.riscv.attributes \
|
|
--update-section=.reloc=$(BUILD_DIR)/reloc_rv64.bin \
|
|
$(BUILD_DIR)/starkernel_loader_patched.elf \
|
|
$(BUILD_DIR)/starkernel_loader_stripped.elf
|
|
@$(OBJCOPY) -O pei-riscv64-little --subsystem 10 \
|
|
$(BUILD_DIR)/starkernel_loader_stripped.elf $@
|
|
@echo "Loader built: $@"
|
|
else
|
|
@echo "LD (loader PE) $@"
|
|
@$(LD) $(LOADER_LDFLAGS_PE) $(LOADER_OBJS) -o $@
|
|
@echo "Loader built: $@"
|
|
endif
|
|
|
|
# Keep ELF for debugging
|
|
$(BUILD_DIR)/starkernel_loader.elf: $(LOADER_OBJS) $(LOADER_LINKER_SCRIPT) | $(BUILD_DIR)
|
|
@echo "LD (loader ELF) $@"
|
|
@$(LD) $(LOADER_LDFLAGS) $(LOADER_OBJS) -o $@
|
|
|
|
# Link kernel ELF
|
|
$(KERNEL_ELF): $(KERNEL_OBJS) $(KERNEL_LINKER_SCRIPT) | $(BUILD_DIR)
|
|
@echo "LD (kernel) $@"
|
|
@$(LD) $(KERNEL_LDFLAGS) $(KERNEL_OBJS) -o $@
|
|
@echo "Kernel built: $@"
|
|
|
|
# ==============================================================================
|
|
# QEMU TARGETS
|
|
# ==============================================================================
|
|
|
|
# qemu — full build, pack boot media, boot under QEMU, serial → timestamped log
|
|
#
|
|
# amd64: El Torito ISO + OVMF pflash
|
|
# aarch64: El Torito ISO + AAVMF -bios
|
|
# riscv64: GPT/FAT32 disk image + EDK2 pflash (cdrom/bios don't work on virt)
|
|
#
|
|
# Requires: qemu-system-* ovmf/qemu-efi-* xorriso mtools gdisk dosfstools
|
|
OVMF_CODE := /usr/share/OVMF/OVMF_CODE_4M.fd
|
|
OVMF_VARS_RO := /usr/share/OVMF/OVMF_VARS_4M.fd
|
|
|
|
# QEMU_DISPLAY — window backend for the framebuffer console (gtk/sdl/none/...).
|
|
# Usage: make -f kernel/Makefile qemu QEMU_DISPLAY=none # headless/CI
|
|
QEMU_DISPLAY ?= gtk
|
|
|
|
# QEMU_EXTRA — extra raw qemu-system-* args appended verbatim to every arch's
|
|
# qemu invocation below. Empty by default (no behavior change for a normal
|
|
# boot). Existing use: attaching a disposable USB test image for WRITE(10)
|
|
# validation, e.g.
|
|
# QEMU_EXTRA="-drive if=none,id=usbtest,file=disk/usbwrite-test.img,format=raw \
|
|
# -device usb-storage,bus=xhci0.0,port=2,drive=usbtest"
|
|
# Give every extra usb-storage device its own explicit port= (ZUSEDISK_QEMU_ARGS
|
|
# above already claims port 1), distinct from every other device attached on
|
|
# bus=xhci0.0 -- without it, QEMU's own port auto-assignment fills the
|
|
# controller's real top-level ports in device order and then silently drops
|
|
# any further device behind an auto-inserted internal USB2 hub, which the
|
|
# xHCI/BOT driver correctly refuses to treat as Mass Storage (confirmed live,
|
|
# 2026-09-06, root cause of the FABRIC-3.md §IX.5 "4th-device enumeration
|
|
# failure" -- it was never a driver bug, the emulated controller only exposes
|
|
# 4 real ports for p2=4,p3=4 despite the port count parameters implying 8).
|
|
# The qemu-xhci controller below is sized via XHCI_PORTS (see its own doc comment
|
|
# above) to give real ports to Zuse plus every identity drive attached this way.
|
|
QEMU_EXTRA ?=
|
|
|
|
qemu: all
|
|
ifeq ($(ARCH),amd64)
|
|
@echo "=== Building ISO (amd64) ==="
|
|
@mkdir -p $(BUILD_DIR)/iso
|
|
@dd if=/dev/zero of=$(BUILD_DIR)/iso/efi.img bs=512 count=8192 2>/dev/null
|
|
@mformat -i $(BUILD_DIR)/iso/efi.img ::
|
|
@mmd -i $(BUILD_DIR)/iso/efi.img ::/EFI
|
|
@mmd -i $(BUILD_DIR)/iso/efi.img ::/EFI/BOOT
|
|
@mcopy -i $(BUILD_DIR)/iso/efi.img $(LOADER_EFI) ::/EFI/BOOT/BOOTX64.EFI
|
|
$(if $(KERNEL_ARGS),\
|
|
@printf '$(KERNEL_ARGS)\n' > $(BUILD_DIR)/iso/starforth.cfg && \
|
|
mcopy -i $(BUILD_DIR)/iso/efi.img $(BUILD_DIR)/iso/starforth.cfg ::/starforth.cfg,)
|
|
@xorriso -as mkisofs -r -J \
|
|
-e efi.img -no-emul-boot \
|
|
-o $(QEMU_ISO) $(BUILD_DIR)/iso 2>&1
|
|
@if [ ! -f $(OVMF_CODE) ]; then echo "Error: $(OVMF_CODE) not found"; exit 1; fi
|
|
@if [ ! -f $(OVMF_VARS_RO) ]; then echo "Error: $(OVMF_VARS_RO) not found"; exit 1; fi
|
|
@if [ ! -f $(BUILD_DIR)/OVMF_VARS.fd ]; then \
|
|
cp $(OVMF_VARS_RO) $(BUILD_DIR)/OVMF_VARS.fd; \
|
|
fi
|
|
@mkdir -p $(QEMU_LOG_DIR) $(DOE_RUNS_DIR) $(DOE_LATEST_DIR)
|
|
@TS=$$(date +%Y%m%d-%H%M%S); \
|
|
SESSION=$(QEMU_LOG_DIR)/$$TS; \
|
|
mkdir -p $$SESSION/amd64; \
|
|
LOG=$$SESSION/amd64/qemu-amd64-$$TS.log; \
|
|
CSV=$(DOE_RUNS_DIR)/doe-amd64-$$TS.csv; \
|
|
SERIAL_SOCK=$(BUILD_DIR)/qemu-serial-amd64-$$TS.sock; \
|
|
QMP_SOCK=$(BUILD_DIR)/qemu-qmp-amd64-$$TS.sock; \
|
|
echo "=== Launching QEMU (amd64) ==="; \
|
|
echo " Serial log: $$LOG"; \
|
|
echo " QMP socket: $$QMP_SOCK"; \
|
|
echo " (interactive — close the window or QEMU monitor 'quit' to exit)"; \
|
|
echo ""; \
|
|
touch $$LOG; \
|
|
tail -n +1 -f $$LOG & TAILPID=$$!; \
|
|
qemu-system-x86_64 \
|
|
-machine q35,accel=tcg \
|
|
-cpu qemu64 \
|
|
-m 1024 \
|
|
-vga std \
|
|
-drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \
|
|
-drive if=pflash,format=raw,file=$(BUILD_DIR)/OVMF_VARS.fd \
|
|
-cdrom $(QEMU_ISO) -boot d \
|
|
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
|
|
-device virtio-blk-pci,drive=artdisk \
|
|
-device qemu-xhci,id=xhci0,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
|
|
$(ZUSEDISK_QEMU_ARGS) \
|
|
-object rng-random,id=rng0,filename=/dev/urandom \
|
|
-device virtio-rng-pci,rng=rng0 \
|
|
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
|
|
-serial chardev:cserial \
|
|
-qmp unix:$$QMP_SOCK,server=on,wait=off \
|
|
$(QEMU_EXTRA) \
|
|
-display $(QEMU_DISPLAY) \
|
|
-no-reboot; \
|
|
kill $$TAILPID 2>/dev/null; wait $$TAILPID 2>/dev/null || true; \
|
|
rm -f $$SERIAL_SOCK $$QMP_SOCK; \
|
|
echo ""; \
|
|
echo "=== Serial log: $$LOG ==="; \
|
|
echo "=== Extracting DOE CSV ==="; \
|
|
bash scripts/extract_doe.sh $$LOG $$CSV || true; \
|
|
cp $$CSV $(DOE_LATEST_DIR)/amd64.csv 2>/dev/null || true
|
|
|
|
else ifeq ($(ARCH),aarch64)
|
|
@echo "=== Building ISO (aarch64) ==="
|
|
@mkdir -p $(BUILD_DIR)/iso/EFI/BOOT
|
|
@cp $(LOADER_EFI) $(BUILD_DIR)/iso/EFI/BOOT/BOOTAA64.EFI
|
|
@printf "FS0:\\EFI\\BOOT\\BOOTAA64.EFI\r\n" > $(BUILD_DIR)/iso/startup.nsh
|
|
@dd if=/dev/zero of=$(BUILD_DIR)/iso/efi.img bs=512 count=8192 2>/dev/null
|
|
@mformat -i $(BUILD_DIR)/iso/efi.img ::
|
|
@mmd -i $(BUILD_DIR)/iso/efi.img ::/EFI
|
|
@mmd -i $(BUILD_DIR)/iso/efi.img ::/EFI/BOOT
|
|
@mcopy -i $(BUILD_DIR)/iso/efi.img $(LOADER_EFI) ::/EFI/BOOT/BOOTAA64.EFI
|
|
$(if $(KERNEL_ARGS),\
|
|
@printf '$(KERNEL_ARGS)\n' > $(BUILD_DIR)/iso/starforth.cfg && \
|
|
mcopy -i $(BUILD_DIR)/iso/efi.img $(BUILD_DIR)/iso/starforth.cfg ::/starforth.cfg,)
|
|
@xorriso -as mkisofs -r -J \
|
|
-e efi.img -no-emul-boot \
|
|
-o $(QEMU_ISO) $(BUILD_DIR)/iso 2>&1
|
|
@mkdir -p $(QEMU_LOG_DIR) $(DOE_RUNS_DIR) $(DOE_LATEST_DIR)
|
|
@if [ -f /usr/share/AAVMF/AAVMF_CODE.fd ]; then \
|
|
AAVMF_CODE=/usr/share/AAVMF/AAVMF_CODE.fd; \
|
|
AAVMF_VARS_RO=/usr/share/AAVMF/AAVMF_VARS.fd; \
|
|
elif [ -f /usr/share/qemu-efi-aarch64/QEMU_EFI.fd ]; then \
|
|
AAVMF_CODE=/usr/share/qemu-efi-aarch64/QEMU_EFI.fd; \
|
|
AAVMF_VARS_RO=/usr/share/qemu-efi-aarch64/vars-template-pflash.raw; \
|
|
elif [ -f /usr/share/qemu/edk2-aarch64-code.fd ]; then \
|
|
AAVMF_CODE=/usr/share/qemu/edk2-aarch64-code.fd; \
|
|
AAVMF_VARS_RO=/usr/share/qemu/edk2-aarch64-vars.fd; \
|
|
else \
|
|
echo "Error: aarch64 UEFI firmware not found. Install qemu-efi-aarch64."; \
|
|
exit 1; \
|
|
fi; \
|
|
if [ -f "$$AAVMF_VARS_RO" ]; then \
|
|
if [ ! -f $(BUILD_DIR)/AAVMF_VARS.fd ]; then \
|
|
cp $$AAVMF_VARS_RO $(BUILD_DIR)/AAVMF_VARS.fd; \
|
|
fi; \
|
|
AAVMF_VARS=$(BUILD_DIR)/AAVMF_VARS.fd; \
|
|
else \
|
|
echo " (no split VARS template found for this firmware -- NVRAM will not persist across runs)"; \
|
|
AAVMF_VARS=""; \
|
|
fi; \
|
|
TS=$$(date +%Y%m%d-%H%M%S); \
|
|
SESSION=$(QEMU_LOG_DIR)/$$TS; \
|
|
mkdir -p $$SESSION/aarch64; \
|
|
LOG=$$SESSION/aarch64/qemu-aarch64-$$TS.log; \
|
|
CSV=$(DOE_RUNS_DIR)/doe-aarch64-$$TS.csv; \
|
|
SERIAL_SOCK=$(BUILD_DIR)/qemu-serial-aarch64-$$TS.sock; \
|
|
QMP_SOCK=$(BUILD_DIR)/qemu-qmp-aarch64-$$TS.sock; \
|
|
echo "=== Launching QEMU (aarch64) ==="; \
|
|
echo " ISO: $(QEMU_ISO)"; \
|
|
echo " Serial log: $$LOG"; \
|
|
echo " QMP socket: $$QMP_SOCK"; \
|
|
echo " (interactive — close the window or QEMU monitor 'quit' to exit)"; \
|
|
echo ""; \
|
|
touch $$LOG; \
|
|
tail -n +1 -f $$LOG & TAILPID=$$!; \
|
|
qemu-system-aarch64 \
|
|
-machine virt \
|
|
-cpu cortex-a57 \
|
|
$$(if [ -n "$$AAVMF_VARS" ]; then \
|
|
echo "-drive if=pflash,format=raw,readonly=on,file=$$AAVMF_CODE -drive if=pflash,format=raw,file=$$AAVMF_VARS"; \
|
|
else \
|
|
echo "-bios $$AAVMF_CODE"; \
|
|
fi) \
|
|
-drive if=virtio,format=raw,file=$(QEMU_ISO),media=cdrom \
|
|
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
|
|
-device virtio-blk-pci,drive=artdisk \
|
|
-device virtio-keyboard-pci,addr=0x3 \
|
|
-device qemu-xhci,id=xhci0,addr=0x4,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
|
|
$(ZUSEDISK_QEMU_ARGS) \
|
|
-object rng-random,id=rng0,filename=/dev/urandom \
|
|
-device virtio-rng-pci,rng=rng0,addr=0x5 \
|
|
-device ramfb \
|
|
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
|
|
-serial chardev:cserial \
|
|
-qmp unix:$$QMP_SOCK,server=on,wait=off \
|
|
$(QEMU_EXTRA) \
|
|
-display $(QEMU_DISPLAY) \
|
|
-m 4096 \
|
|
-no-reboot \
|
|
-d guest_errors; \
|
|
kill $$TAILPID 2>/dev/null; wait $$TAILPID 2>/dev/null || true; \
|
|
rm -f $$SERIAL_SOCK $$QMP_SOCK; \
|
|
echo ""; \
|
|
echo "=== Serial log: $$LOG ==="; \
|
|
echo "=== Extracting DOE CSV ==="; \
|
|
bash scripts/extract_doe.sh $$LOG $$CSV || true; \
|
|
cp $$CSV $(DOE_LATEST_DIR)/aarch64.csv 2>/dev/null || true
|
|
|
|
else ifeq ($(ARCH),riscv64)
|
|
@echo "=== Building GPT disk image (riscv64) ==="
|
|
@# riscv64 QEMU virt: pflash firmware + virtio-blk-pci GPT disk.
|
|
@# El Torito ISO and -bios mode do NOT work on riscv64 virt.
|
|
@mkdir -p $(QEMU_LOG_DIR) $(DOE_RUNS_DIR) $(DOE_LATEST_DIR)
|
|
@if ! which sgdisk >/dev/null 2>&1; then echo "Error: sgdisk not found. Install gdisk (apt-get install gdisk)."; exit 1; fi
|
|
@if ! which mkfs.fat >/dev/null 2>&1; then echo "Error: mkfs.fat not found. Install dosfstools (apt-get install dosfstools)."; exit 1; fi
|
|
@if [ -f /usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd ]; then \
|
|
EDK2_RISCV_CODE=/usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd; \
|
|
elif [ -f /usr/share/qemu/edk2-riscv64-code.fd ]; then \
|
|
EDK2_RISCV_CODE=/usr/share/qemu/edk2-riscv64-code.fd; \
|
|
else \
|
|
echo "Error: riscv64 UEFI firmware not found. Install qemu-efi-riscv64."; \
|
|
exit 1; \
|
|
fi; \
|
|
EDK2_RISCV_VARS=""; \
|
|
if [ -f /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd ]; then \
|
|
if [ ! -f $(BUILD_DIR)/RISCV_VARS.fd ]; then \
|
|
cp /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd $(BUILD_DIR)/RISCV_VARS.fd; \
|
|
fi; \
|
|
EDK2_RISCV_VARS=$(BUILD_DIR)/RISCV_VARS.fd; \
|
|
fi; \
|
|
DISK=$(BUILD_DIR)/starkernel_rv64.img; \
|
|
dd if=/dev/zero of=$$DISK bs=1M count=64 2>/dev/null; \
|
|
sgdisk -n 1:2048:131038 -t 1:ef00 -c 1:"EFI System" $$DISK >/dev/null 2>&1; \
|
|
dd if=/dev/zero of=$(BUILD_DIR)/esp_rv64.img bs=512 count=128991 2>/dev/null; \
|
|
mkfs.fat -F 32 -n "ESP" $(BUILD_DIR)/esp_rv64.img >/dev/null 2>&1; \
|
|
mmd -i $(BUILD_DIR)/esp_rv64.img ::/EFI; \
|
|
mmd -i $(BUILD_DIR)/esp_rv64.img ::/EFI/BOOT; \
|
|
mcopy -i $(BUILD_DIR)/esp_rv64.img $(LOADER_EFI) ::/EFI/BOOT/BOOTRISCV64.EFI; \
|
|
mcopy -i $(BUILD_DIR)/esp_rv64.img $(KERNEL_ELF) ::/kernel.elf; \
|
|
printf 'FS0:\\EFI\\BOOT\\BOOTRISCV64.EFI\r\n' > $(BUILD_DIR)/startup.nsh; \
|
|
mcopy -i $(BUILD_DIR)/esp_rv64.img $(BUILD_DIR)/startup.nsh ::/startup.nsh; \
|
|
if [ -n "$(KERNEL_ARGS)" ]; then \
|
|
printf '$(KERNEL_ARGS)\n' > $(BUILD_DIR)/starforth.cfg; \
|
|
mcopy -i $(BUILD_DIR)/esp_rv64.img $(BUILD_DIR)/starforth.cfg ::/starforth.cfg; \
|
|
fi; \
|
|
dd if=$(BUILD_DIR)/esp_rv64.img of=$$DISK bs=512 seek=2048 conv=notrunc 2>/dev/null; \
|
|
echo " Disk: $$DISK"; \
|
|
echo ""; \
|
|
PFLASH_VARS_ARG=""; \
|
|
if [ -n "$$EDK2_RISCV_VARS" ]; then \
|
|
PFLASH_VARS_ARG="-drive if=pflash,format=raw,unit=1,file=$$EDK2_RISCV_VARS"; \
|
|
fi; \
|
|
TS=$$(date +%Y%m%d-%H%M%S); \
|
|
SESSION=$(QEMU_LOG_DIR)/$$TS; \
|
|
mkdir -p $$SESSION/riscv64; \
|
|
LOG=$$SESSION/riscv64/qemu-riscv64-$$TS.log; \
|
|
CSV=$(DOE_RUNS_DIR)/doe-riscv64-$$TS.csv; \
|
|
SERIAL_SOCK=$(BUILD_DIR)/qemu-serial-riscv64-$$TS.sock; \
|
|
QMP_SOCK=$(BUILD_DIR)/qemu-qmp-riscv64-$$TS.sock; \
|
|
echo "=== Launching QEMU (riscv64) ==="; \
|
|
echo " Serial log: $$LOG"; \
|
|
echo " QMP socket: $$QMP_SOCK"; \
|
|
echo " (interactive — close the window or QEMU monitor 'quit' to exit)"; \
|
|
echo ""; \
|
|
touch $$LOG; \
|
|
tail -n +1 -f $$LOG & TAILPID=$$!; \
|
|
qemu-system-riscv64 \
|
|
-machine virt \
|
|
-cpu rv64 \
|
|
-m 1024 \
|
|
-drive if=pflash,format=raw,unit=0,file=$$EDK2_RISCV_CODE,readonly=on \
|
|
$$PFLASH_VARS_ARG \
|
|
-drive id=hd0,file=$$DISK,format=raw,if=none \
|
|
-device virtio-blk-pci,drive=hd0,addr=0x2 \
|
|
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
|
|
-device virtio-blk-pci,drive=artdisk,addr=0x1 \
|
|
-device virtio-keyboard-pci,addr=0x3 \
|
|
-device qemu-xhci,id=xhci0,addr=0x4,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
|
|
$(ZUSEDISK_QEMU_ARGS) \
|
|
-object rng-random,id=rng0,filename=/dev/urandom \
|
|
-device virtio-rng-pci,rng=rng0,addr=0x5 \
|
|
-device ramfb \
|
|
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
|
|
-serial chardev:cserial \
|
|
-qmp unix:$$QMP_SOCK,server=on,wait=off \
|
|
$(QEMU_EXTRA) \
|
|
-display $(QEMU_DISPLAY) \
|
|
-no-reboot \
|
|
-d guest_errors; \
|
|
kill $$TAILPID 2>/dev/null; wait $$TAILPID 2>/dev/null || true; \
|
|
rm -f $$SERIAL_SOCK $$QMP_SOCK; \
|
|
echo ""; \
|
|
echo "=== Serial log: $$LOG ==="; \
|
|
echo "=== Extracting DOE CSV ==="; \
|
|
bash scripts/extract_doe.sh $$LOG $$CSV || true; \
|
|
cp $$CSV $(DOE_LATEST_DIR)/riscv64.csv 2>/dev/null || true
|
|
endif
|
|
|
|
# thumbdrive — build a generic UEFI-bootable GPT/FAT32 disk image that can be
|
|
# written directly to a USB thumbdrive (dd) and boots on real amd64 UEFI
|
|
# hardware (e.g. Beelink SER5) as well as under QEMU. This is the "generic
|
|
# thumbdrive bootable OS" image for the v2.5.0/SER5 bare-metal path: the
|
|
# monolithic loader embeds the whole kernel, so the ESP needs only the UEFI
|
|
# fallback boot path EFI/BOOT/BOOT<ARCH>.EFI. Storage on real hardware is the
|
|
# USB BOT/xHCI path (already live), not virtio.
|
|
thumbdrive: all
|
|
@printf 'FS0:\\EFI\\BOOT\\%s\r\n' "$(EFI_BOOTNAME)" > $(BUILD_DIR)/thumbdrive-startup.nsh
|
|
@bash scripts/mkdiskimage.sh --out $(BUILD_DIR)/starkernel-thumbdrive.img --table gpt \
|
|
--size-mib 128 --label STARKERNEL \
|
|
$(LOADER_EFI):/EFI/BOOT/$(EFI_BOOTNAME) \
|
|
$(BUILD_DIR)/thumbdrive-startup.nsh:/startup.nsh
|
|
@rm -f $(BUILD_DIR)/thumbdrive-startup.nsh
|
|
@echo "Write to a USB stick with: dd if=$(BUILD_DIR)/starkernel-thumbdrive.img of=/dev/sdX bs=4M status=progress"
|
|
|
|
# ==============================================================================
|
|
# BOOT IMAGES (one per board)
|
|
# ==============================================================================
|
|
# boot_image -- the single file written to a board's boot medium. Driven by
|
|
# BOARD (boards/<board>/board.mk); normally invoked from the repo root as
|
|
# make boot_image TARGET=SER5|RASPI|MILKV|ZYNQ7020
|
|
# Output: build/boards/<board>/$(BOARD_IMAGE).
|
|
#
|
|
# BOARD_BOOT selects the recipe:
|
|
# uefi-esp -- GPT disk, one FAT32 EFI System Partition holding the
|
|
# monolithic loader as EFI/BOOT/BOOT<ARCH>.EFI (+ startup.nsh
|
|
# for the UEFI shell, + starforth.cfg when KERNEL_ARGS is set).
|
|
# Real UEFI firmware (SER5) and U-Boot's bootefi (Milk-V) both
|
|
# boot it through the removable-media fallback path.
|
|
# rpi-native -- MBR disk, one FAT32 (LBA) partition holding config.txt,
|
|
# kernel_2712.img (kernel relinked at 0x80000 and flattened)
|
|
# and the stock BCM2712 DTB (+ cmdline.txt, which firmware
|
|
# copies into /chosen/bootargs, when KERNEL_ARGS is set).
|
|
ifeq ($(ARCH),amd64)
|
|
EFI_BOOTNAME := BOOTX64.EFI
|
|
else ifeq ($(ARCH),aarch64)
|
|
EFI_BOOTNAME := BOOTAA64.EFI
|
|
else ifeq ($(ARCH),riscv64)
|
|
EFI_BOOTNAME := BOOTRISCV64.EFI
|
|
endif
|
|
|
|
RPI5_NATIVE_LINKER_SCRIPT := kernel/linker/starkernel-native-rpi5.ld
|
|
RPI5_NATIVE_ELF := $(BUILD_DIR)/starkernel_rpi5.elf
|
|
RPI5_NATIVE_IMG := $(BUILD_DIR)/kernel_2712.img
|
|
|
|
$(RPI5_NATIVE_ELF): $(KERNEL_OBJS) $(RPI5_NATIVE_LINKER_SCRIPT) | $(BUILD_DIR)
|
|
@echo "LD (rpi5 native) $@"
|
|
@$(LD) -T $(RPI5_NATIVE_LINKER_SCRIPT) -nostdlib --build-id=none \
|
|
-e rpi5_native_start $(KERNEL_OBJS) -o $@
|
|
|
|
$(RPI5_NATIVE_IMG): $(RPI5_NATIVE_ELF)
|
|
@echo "OBJCOPY (raw) $@"
|
|
@$(OBJCOPY) -O binary $< $@
|
|
|
|
.PHONY: boot_image
|
|
ifeq ($(strip $(BOARD)),)
|
|
boot_image:
|
|
@echo "boot_image needs a board. From the repo root:"
|
|
@echo " make boot_image TARGET=SER5|RASPI|MILKV|ZYNQ7020"
|
|
@exit 1
|
|
else ifeq ($(BOARD_BOOT),uefi-esp)
|
|
boot_image: all
|
|
@mkdir -p $(BOARD_OUT)
|
|
@printf 'FS0:\\EFI\\BOOT\\%s\r\n' "$(EFI_BOOTNAME)" > $(BOARD_OUT)/startup.nsh
|
|
@rm -f $(BOARD_OUT)/starforth.cfg
|
|
$(if $(KERNEL_ARGS),@printf '%s\n' '$(KERNEL_ARGS)' > $(BOARD_OUT)/starforth.cfg)
|
|
@bash scripts/mkdiskimage.sh --out $(BOARD_OUT)/$(BOARD_IMAGE) --table gpt \
|
|
--size-mib $(BOOT_IMAGE_SIZE_MIB) --label LITHOS \
|
|
$(LOADER_EFI):/EFI/BOOT/$(EFI_BOOTNAME) \
|
|
$(BOARD_OUT)/startup.nsh:/startup.nsh \
|
|
$(if $(KERNEL_ARGS),$(BOARD_OUT)/starforth.cfg:/starforth.cfg)
|
|
@echo "$(BOARD_DESC)"
|
|
@echo "Write with: dd if=$(BOARD_OUT)/$(BOARD_IMAGE) of=/dev/sdX bs=4M conv=fsync status=progress"
|
|
else ifeq ($(BOARD_BOOT),rpi-native)
|
|
$(RPI5_DTB):
|
|
@mkdir -p $(dir $@)
|
|
@echo " FETCH $(RPI5_DTB_URL)"
|
|
@curl -fsSL -o $@.tmp $(RPI5_DTB_URL) || { rm -f $@.tmp; \
|
|
echo "Error: could not fetch the Pi 5 DTB. Copy bcm2712-rpi-5-b.dtb from a Raspberry Pi"; \
|
|
echo "firmware release and pass RPI5_DTB=/path/to/bcm2712-rpi-5-b.dtb"; exit 1; }
|
|
@mv $@.tmp $@
|
|
|
|
boot_image: v3/include/version.h $(RPI5_NATIVE_IMG) $(RPI5_DTB)
|
|
@mkdir -p $(BOARD_OUT)
|
|
@rm -f $(BOARD_OUT)/cmdline.txt
|
|
$(if $(KERNEL_ARGS),@printf '%s\n' '$(KERNEL_ARGS)' > $(BOARD_OUT)/cmdline.txt)
|
|
@bash scripts/mkdiskimage.sh --out $(BOARD_OUT)/$(BOARD_IMAGE) --table mbr \
|
|
--size-mib $(BOOT_IMAGE_SIZE_MIB) --label LITHOS \
|
|
boards/$(BOARD)/config.txt:/config.txt \
|
|
$(RPI5_NATIVE_IMG):/kernel_2712.img \
|
|
$(RPI5_DTB):/bcm2712-rpi-5-b.dtb \
|
|
$(if $(KERNEL_ARGS),$(BOARD_OUT)/cmdline.txt:/cmdline.txt)
|
|
@echo "$(BOARD_DESC)"
|
|
@echo "Write with: dd if=$(BOARD_OUT)/$(BOARD_IMAGE) of=/dev/mmcblkX bs=4M conv=fsync status=progress"
|
|
else
|
|
boot_image:
|
|
@echo "Error: BOARD=$(BOARD) has BOARD_BOOT='$(BOARD_BOOT)', which has no boot_image recipe"
|
|
@exit 1
|
|
endif
|
|
|
|
# iso-usb — build a PURE UEFI isohybrid ISO for the Iso Image Writer workflow
|
|
# (GNOME Disks "Restore Disk Image...", or dd). Writes a single .iso to a USB
|
|
# stick as a raw image; the ISO carries a clean GPT with an EFI System
|
|
# Partition holding EFI/BOOT/BOOT<ARCH>.EFI, so real UEFI firmware (e.g.
|
|
# Beelink SER5) scans the ESP and boots it. No GRUB, no isolinux, no MBR boot
|
|
# code — the protective MBR template is zeroed (partition metadata only).
|
|
# Also still boots under QEMU via -cdrom. This is the novice path: one ISO,
|
|
# picked with a GUI, written to the stick.
|
|
iso-usb: all
|
|
@mkdir -p $(BUILD_DIR)
|
|
@if ! which xorriso >/dev/null 2>&1; then echo "Error: xorriso not found. Install xorriso (apt-get install xorriso)."; exit 1; fi
|
|
@if ! which mformat >/dev/null 2>&1 || ! which mmd >/dev/null 2>&1 || ! which mcopy >/dev/null 2>&1; then echo "Error: mtools not found. Install mtools (apt-get install mtools)."; exit 1; fi
|
|
@case "$(ARCH)" in \
|
|
amd64) BOOTNAME="BOOTX64.EFI" ;; \
|
|
aarch64) BOOTNAME="BOOTAA64.EFI" ;; \
|
|
*) echo "Error: iso-usb EFI boot name only defined for amd64/aarch64 yet"; exit 1 ;; \
|
|
esac; \
|
|
ISODIR=$(BUILD_DIR)/iso-usb; \
|
|
rm -rf $$ISODIR; mkdir -p $$ISODIR; \
|
|
dd if=/dev/zero of=$$ISODIR/efi.img bs=512 count=8192 2>/dev/null; \
|
|
mformat -i $$ISODIR/efi.img :: >/dev/null 2>&1; \
|
|
mmd -i $$ISODIR/efi.img ::/EFI; mmd -i $$ISODIR/efi.img ::/EFI/BOOT; \
|
|
mcopy -i $$ISODIR/efi.img $(LOADER_EFI) ::/EFI/BOOT/$$BOOTNAME; \
|
|
printf 'FS0:\\EFI\\BOOT\\%s\r\n' "$$BOOTNAME" > $$ISODIR/startup.nsh; \
|
|
mcopy -i $$ISODIR/efi.img $$ISODIR/startup.nsh ::/startup.nsh; \
|
|
head -c 432 /dev/zero > $$ISODIR/protmbr.bin; \
|
|
ISO=$(BUILD_DIR)/starkernel-iso-usb.iso; \
|
|
rm -f $$ISO; \
|
|
xorriso -as mkisofs \
|
|
-V STARKERNEL -r -J \
|
|
-isohybrid-mbr $$ISODIR/protmbr.bin \
|
|
-eltorito-alt-boot -e efi.img -no-emul-boot \
|
|
-isohybrid-gpt-basdat \
|
|
-o $$ISO $$ISODIR >/dev/null 2>&1; \
|
|
rm -rf $$ISODIR; \
|
|
echo "Iso Image Writer ISO (pure UEFI): $$ISO"; \
|
|
echo " In GNOME Disks, pick the ISO with 'Restore Disk Image...' and select your USB stick."; \
|
|
echo " Or: dd if=$$ISO of=/dev/sdX bs=4M status=progress"
|
|
|
|
|
|
# qemu-esp — interactive dev boot from FAT directory (no disk image rebuild,
|
|
# no auto-kill/timeout/DOE-injection logic — stays up until you quit it
|
|
# yourself with Ctrl-A X or by closing the window).
|
|
# amd64/aarch64: serial → stdio (bidirectional, you can type into the REPL);
|
|
# riscv64: uses proper GPT (esp dir not supported)
|
|
qemu-esp: v3/include/version.h $(LOADER_EFI) $(KERNEL_ELF)
|
|
ifeq ($(ARCH),amd64)
|
|
@rm -rf $(BUILD_DIR)/esp
|
|
@mkdir -p $(BUILD_DIR)/esp/EFI/BOOT
|
|
@cp $(LOADER_EFI) $(BUILD_DIR)/esp/EFI/BOOT/BOOTX64.EFI
|
|
@cp $(KERNEL_ELF) $(BUILD_DIR)/esp/kernel.elf
|
|
@printf "FS0:\\EFI\\BOOT\\BOOTX64.EFI\r\n" > $(BUILD_DIR)/esp/startup.nsh
|
|
$(if $(KERNEL_ARGS),@printf '$(KERNEL_ARGS)\n' > $(BUILD_DIR)/esp/starforth.cfg,)
|
|
@OVMF_CODE=$(OVMF_CODE); \
|
|
if [ ! -f $$OVMF_CODE ]; then echo "Error: $$OVMF_CODE not found"; exit 1; fi; \
|
|
cp $(OVMF_VARS_RO) $(BUILD_DIR)/OVMF_VARS.fd; \
|
|
qemu-system-x86_64 \
|
|
-nodefaults -display $(QEMU_DISPLAY) -vga std \
|
|
-machine q35,accel=tcg \
|
|
-cpu qemu64 \
|
|
-m 1024 \
|
|
-chardev stdio,id=ser0,signal=off \
|
|
-device isa-serial,chardev=ser0,iobase=0x3f8 \
|
|
-drive if=pflash,format=raw,readonly=on,file=$$OVMF_CODE \
|
|
-drive if=pflash,format=raw,file=$(BUILD_DIR)/OVMF_VARS.fd \
|
|
-drive format=raw,file=fat:rw:$(BUILD_DIR)/esp \
|
|
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
|
|
-device virtio-blk-pci,drive=artdisk \
|
|
-no-reboot
|
|
|
|
else ifeq ($(ARCH),aarch64)
|
|
@rm -rf $(BUILD_DIR)/esp
|
|
@mkdir -p $(BUILD_DIR)/esp/EFI/BOOT
|
|
@cp $(LOADER_EFI) $(BUILD_DIR)/esp/EFI/BOOT/BOOTAA64.EFI
|
|
@cp $(KERNEL_ELF) $(BUILD_DIR)/esp/kernel.elf
|
|
@printf "FS0:\\EFI\\BOOT\\BOOTAA64.EFI\r\n" > $(BUILD_DIR)/esp/startup.nsh
|
|
@if [ -f /usr/share/AAVMF/AAVMF_CODE.fd ]; then \
|
|
AAVMF_CODE=/usr/share/AAVMF/AAVMF_CODE.fd; \
|
|
elif [ -f /usr/share/qemu-efi-aarch64/QEMU_EFI.fd ]; then \
|
|
AAVMF_CODE=/usr/share/qemu-efi-aarch64/QEMU_EFI.fd; \
|
|
elif [ -f /usr/share/qemu/edk2-aarch64-code.fd ]; then \
|
|
AAVMF_CODE=/usr/share/qemu/edk2-aarch64-code.fd; \
|
|
else \
|
|
echo "Error: aarch64 UEFI firmware not found. Install qemu-efi-aarch64."; exit 1; \
|
|
fi; \
|
|
qemu-system-aarch64 \
|
|
-machine virt \
|
|
-cpu cortex-a57 \
|
|
-bios $$AAVMF_CODE \
|
|
-drive format=raw,file=fat:rw:$(BUILD_DIR)/esp \
|
|
-serial mon:stdio \
|
|
-nographic \
|
|
-m 2048 \
|
|
-d guest_errors
|
|
|
|
else ifeq ($(ARCH),riscv64)
|
|
@echo "riscv64: qemu-esp not supported (FAT dir mode broken on virt); use 'make qemu'"
|
|
@exit 1
|
|
endif
|
|
|
|
# qemu-gdb — launch QEMU with GDB stub on port 1234
|
|
qemu-gdb: v3/include/version.h $(LOADER_EFI) $(KERNEL_ELF)
|
|
ifeq ($(ARCH),amd64)
|
|
@mkdir -p $(BUILD_DIR)/esp/EFI/BOOT
|
|
@cp $(LOADER_EFI) $(BUILD_DIR)/esp/EFI/BOOT/BOOTX64.EFI
|
|
@cp $(KERNEL_ELF) $(BUILD_DIR)/esp/kernel.elf
|
|
@echo "Launching QEMU with GDB server on :1234 (amd64)"
|
|
@echo " gdb $(KERNEL_ELF) -ex 'target remote :1234'"
|
|
@qemu-system-x86_64 \
|
|
-bios $(OVMF_CODE) \
|
|
-drive format=raw,file=fat:rw:$(BUILD_DIR)/esp \
|
|
-serial stdio \
|
|
-m 1024 \
|
|
-s -S
|
|
else ifeq ($(ARCH),aarch64)
|
|
@mkdir -p $(BUILD_DIR)/esp/EFI/BOOT
|
|
@cp $(LOADER_EFI) $(BUILD_DIR)/esp/EFI/BOOT/BOOTAA64.EFI
|
|
@cp $(KERNEL_ELF) $(BUILD_DIR)/esp/kernel.elf
|
|
@echo "Launching QEMU with GDB server on :1234 (aarch64)"
|
|
@echo " gdb $(KERNEL_ELF) -ex 'target remote :1234'"
|
|
@qemu-system-aarch64 \
|
|
-machine virt -cpu cortex-a57 \
|
|
-bios /usr/share/AAVMF/AAVMF_CODE.fd \
|
|
-drive format=raw,file=fat:rw:$(BUILD_DIR)/esp \
|
|
-serial stdio -m 2048 -s -S
|
|
else
|
|
@echo "Error: qemu-gdb not yet implemented for $(ARCH)"; exit 1
|
|
endif
|
|
|
|
# ==============================================================================
|
|
# INFO / HELP
|
|
# ==============================================================================
|
|
|
|
|
|
info:
|
|
@echo "StarKernel Build Configuration"
|
|
@echo "==============================="
|
|
@echo "Architecture: $(ARCH)"
|
|
@echo "Target: $(TARGET)"
|
|
@echo "Compiler: $(CC)"
|
|
@echo "Loader CC: $(LOADER_CC)"
|
|
@echo "Linker: $(LD)"
|
|
@echo "Build dir: $(BUILD_DIR)"
|
|
@echo "Loader script: $(LOADER_LINKER_SCRIPT)"
|
|
@echo "Kernel script: $(KERNEL_LINKER_SCRIPT)"
|
|
@echo "Loader output: $(LOADER_EFI)"
|
|
@echo "Kernel output: $(KERNEL_ELF)"
|
|
@echo "VM integration: $(STARFORTH_ENABLE_VM)"
|
|
@echo "Monolithic: $(MONOLITHIC)"
|
|
|
|
help:
|
|
@echo "StarKernel Build System"
|
|
@echo "======================="
|
|
@echo ""
|
|
@echo "Usage: make -f kernel/Makefile [ARCH=<arch>] [TARGET=<target>] <goal>"
|
|
@echo ""
|
|
@echo "Architectures (aliases accepted):"
|
|
@echo " ARCH=amd64 / x86_64 — x86-64 (default on x86 host)"
|
|
@echo " ARCH=aarch64 / arm64 — ARM64"
|
|
@echo " ARCH=riscv64 / riscv — RISC-V 64-bit"
|
|
@echo ""
|
|
@echo "Build goals:"
|
|
@echo " all — build loader EFI + kernel ELF (default)"
|
|
@echo " kernel — alias for all"
|
|
@echo " kernel-all — build all three architectures"
|
|
@echo " clean — remove build/\$(ARCH)/\$(TARGET)"
|
|
@echo " clean-kernel — remove build/*/kernel for all arches"
|
|
@echo ""
|
|
@echo "QEMU goals:"
|
|
@echo " qemu — clean boot, serial tee'd live + logs/<session>/<arch>/ + DOE CSV → doe/ (all arches)"
|
|
@echo " qemu-esp — quick boot from FAT directory (amd64, aarch64)"
|
|
@echo " qemu-gdb — boot with GDB stub on :1234"
|
|
@echo " thumbdrive — build generic GPT/FAT32 UEFI disk image (write to a USB stick with dd; real-hardware/SER5 path)"
|
|
@echo " iso-usb — build UEFI isohybrid ISO for Iso Image Writer / GNOME Disks 'Restore Disk Image...' (novice path; real-hardware/SER5)"
|
|
@echo ""
|
|
@echo "QEMU display:"
|
|
@echo " QEMU_DISPLAY=gtk — framebuffer window backend for 'qemu' goal (default: gtk)"
|
|
@echo " QEMU_DISPLAY=none — headless (no window), e.g. for CI"
|
|
@echo ""
|
|
@echo "Info goals:"
|
|
@echo " info — show build configuration"
|
|
@echo " help — show this message"
|
|
@echo ""
|
|
@echo "Examples:"
|
|
@echo " make -f kernel/Makefile ARCH=amd64 qemu"
|
|
@echo " make -f kernel/Makefile ARCH=aarch64 clean qemu"
|
|
@echo " make -f kernel/Makefile ARCH=riscv64 clean qemu"
|
|
@echo " make -f kernel/Makefile ARCH=x86_64 qemu # alias works"
|
|
@echo " make -f kernel/Makefile kernel-all"
|