universalisos/kernel/Makefile
Fábio Coutada 1018e8274b feat(testing): add in-kernel test harness — Phase 2 complete
kernel/src/test/:
- uos_test.h: Test framework header with macros (UOS_TEST, UOS_ASSERT, etc.)
  - UOS_TEST(suite, name) — define a test case
  - UOS_TEST_SKIP(suite, name) — define a skipped test
  - UOS_ASSERT, UOS_ASSERT_EQUAL, UOS_ASSERT_NOT_NULL, etc.
  - Auto-registration via GCC constructor attributes
- uos_test_runner.cpp: Test runner with UART output
  - Linked list registry for test cases
  - [TEST] pass/fail/skip output format (parsed by CI)
  - Test statistics (total/passed/failed/skipped)
- 7 test files: scheduler, memory, ipc, exceptions, interrupts, devices, partitions

kernel/Makefile:
- Added TEST_SRCS wildcard for kernel/src/test/*.cpp
- Added TEST_OBJS to link list

kernel/src/core/kernel.cpp:
- Added uos_test_run_all() call before idle loop
- Tests run after all initialization and demos

Test output format:
  [TEST] scheduler.init: pass
  [TEST] memory.overflow_protection: pass
  [TEST] ipc.message_send_receive: pass
  ...
  Results: 25 passed, 0 failed, 0 skipped
  ALL TESTS PASSED
2026-07-12 17:06:50 +01:00

431 lines
18 KiB
Makefile

# Universalisos Kernel Makefile (Bao-Style)
# Default to riscv / polarfire if not set
ARCH ?= riscv
PLATFORM ?= polarfire
SRC_DIR := src
# Per-(ARCH,PLATFORM) build tree: each target compiles into its own isolated
# directory so concurrent builds for different arches (armv7 / aarch64 / riscv)
# never collide on object files or the output ELF.
BUILD_DIR := build/$(ARCH)/$(PLATFORM)
OBJ_DIR := $(BUILD_DIR)/obj
# Object-file lists (initialized before arch/platform includes so they can
# override or append; appended in objects.mk)
cpu-objs-y :=
plat-objs-y :=
core-objs-y :=
lib-objs-y :=
cpu-skip-y :=
# Ensure the default goal is the final kernel image, not an auxiliary target
# that happens to be defined earlier (e.g. the AArch64 guest payload binary).
.DEFAULT_GOAL := all
# Architecture and platform Makefiles
include $(SRC_DIR)/arch/$(ARCH)/arch.mk
include $(SRC_DIR)/platform/$(PLATFORM)/platform.mk
# ---------------------------------------------------------------------------
# ADT Package System (PikeOS package.mk grammar, uos family)
#
# The existing ADT package.mk files (src/core/adt/*/package.mk) define
# variables using the uos_adt_<pkg> naming convention. manifest.mk
# enforces dependency ordering; build.mk includes them and computes the
# final ADT object lists.
# ---------------------------------------------------------------------------
# UOS_PKG_OPTIONS controls which package options are enabled.
# Override on the command line: make UOS_PKG_OPTIONS="base sprintf snprintf"
UOS_PKG_OPTIONS ?= base sprintf snprintf remove
include pkg/manifest.mk
include pkg/build.mk
# AArch64: build and embed the separate EL1 guest payload binary.
ifeq ($(ARCH),aarch64)
PAYLOAD_DIR := $(SRC_DIR)/arch/aarch64/guest_payload
PAYLOAD_BIN := $(PAYLOAD_DIR)/guest_payload.bin
PAYLOAD_OBJ := $(OBJ_DIR)/arch/aarch64/guest_payload_bin.o
cpu-objs-y += guest_payload_bin.o
$(PAYLOAD_BIN): $(PAYLOAD_DIR)/crt0.S $(PAYLOAD_DIR)/guest_main.c $(PAYLOAD_DIR)/linker.ld $(PAYLOAD_DIR)/Makefile $(PAYLOAD_DIR)/../inc/uos_hv_abi.h
$(MAKE) -C $(PAYLOAD_DIR) CROSS_COMPILE=$(CROSS_COMPILE)
$(PAYLOAD_OBJ): $(PAYLOAD_BIN)
@mkdir -p $(dir $@)
cd $(PAYLOAD_DIR) && $(CROSS_COMPILE)ld -r -b binary guest_payload.bin -o $(abspath $@)
# Linux-boot M1: when LINUX_BOOT=1, also embed the Image-shaped stub (entered
# at guest_DRAM+0x80000) and the guest DTB, and tell the HV to hand x0=DTB IPA.
ifeq ($(LINUX_BOOT),1)
CPPFLAGS += -DUOS_LINUX_BOOT
LINUX_STUB_BIN := $(PAYLOAD_DIR)/linux_stub.bin
GUEST_DTB := $(PAYLOAD_DIR)/guest.dtb
cpu-objs-y += linux_stub_bin.o guest_dtb_bin.o
$(LINUX_STUB_BIN) $(GUEST_DTB): $(PAYLOAD_DIR)/guest.dts \
$(PAYLOAD_DIR)/linux_stub_entry.S $(PAYLOAD_DIR)/linux_stub.c \
$(PAYLOAD_DIR)/linux_stub.ld $(PAYLOAD_DIR)/Makefile $(PAYLOAD_DIR)/../inc/uos_hv_abi.h
$(MAKE) -C $(PAYLOAD_DIR) CROSS_COMPILE=$(CROSS_COMPILE) linux
$(OBJ_DIR)/arch/aarch64/linux_stub_bin.o: $(LINUX_STUB_BIN)
@mkdir -p $(dir $@)
cd $(PAYLOAD_DIR) && $(CROSS_COMPILE)ld -r -b binary linux_stub.bin -o $(abspath $@)
$(OBJ_DIR)/arch/aarch64/guest_dtb_bin.o: $(GUEST_DTB)
@mkdir -p $(dir $@)
cd $(PAYLOAD_DIR) && $(CROSS_COMPILE)ld -r -b binary guest.dtb -o $(abspath $@)
endif
endif
# Personality boot (T8-1.2f): when PERSONALITY_BOOT=1, skip the embedded
# guest payload and use the QEMU-loaded personality binary instead.
ifeq ($(PERSONALITY_BOOT),1)
CPPFLAGS += -DUOS_PERSONALITY_BOOT
endif
# Linker script: defaults to the arch linker.ld; a platform (platform.mk above)
# may override UOS_LDSCRIPT to ship a board-specific script (load address etc.).
UOS_LDSCRIPT ?= $(SRC_DIR)/arch/$(ARCH)/linker.ld
# RISC-V boot mode. UOS_BOOT=firmware selects linker_firmware.ld, whose byte
# at 0x80200000 is the S-mode _start_firmware (sets __uos_has_sbi=1) instead of
# the M-mode trampoline. Use for the HSS/OpenSBI payload path; leave unset
# (default = M-mode trampoline) for QEMU -bios none.
ifeq ($(ARCH)-$(UOS_BOOT),riscv-firmware)
UOS_LDSCRIPT := $(SRC_DIR)/arch/riscv/linker_firmware.ld
endif
# Global flags
CPPFLAGS += -Iinclude -Iinclude/universalisos -Iinclude/universalisos/j_space -I$(SRC_DIR)/arch/$(ARCH)/inc -I$(SRC_DIR)/platform/$(PLATFORM)/inc -I$(SRC_DIR)/core -I$(SRC_DIR)/platform
# ADT (PikeOS ADT library port, src/core/adt): package include dirs so that
# <adt/...> and <stand/...> resolve like they do in the PikeOS build.
# Derived from package.mk declarations via the consumer idiom (§3.3).
CPPFLAGS += $(addprefix -I,$(foreach p,$(uos_packages),$(SRC_DIR)/core/adt/$(p)/include))
CPPFLAGS += $(arch-cppflags) $(plat-cppflags)
CFLAGS += -Wall -Wextra -ffreestanding -O2 -fno-tree-vectorize -g $(arch-cflags) $(plat-cflags)
CXXFLAGS += $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS += $(arch-asflags) $(plat-asflags)
LDFLAGS += -nostdlib $(arch-ldflags) $(plat-ldflags)
include $(SRC_DIR)/arch/$(ARCH)/objects.mk
include $(SRC_DIR)/platform/$(PLATFORM)/objects.mk
# Core objects: RISC-V and AArch64 are self-contained under their own arch dirs
# (src/arch/riscv, src/arch/aarch64) and do NOT pull in the ARMv7-centric core
# files. ARMv7 compiles all core sources.
ifeq ($(ARCH),$(filter $(ARCH),riscv aarch64))
CORE_SRCS := $(addprefix $(SRC_DIR)/core/, $(core-objs-y))
else
CORE_SRCS := $(wildcard $(SRC_DIR)/core/*.cpp) $(wildcard $(SRC_DIR)/core/*/*.cpp) $(wildcard $(SRC_DIR)/core/*.S) $(wildcard $(SRC_DIR)/core/*/*.S)
endif
CORE_OBJS := $(patsubst $(SRC_DIR)/%,$(OBJ_DIR)/%,$(CORE_SRCS:.cpp=.o))
CORE_OBJS := $(CORE_OBJS:.S=.o)
# Test objects: always compile test harness and test files
TEST_SRCS := $(wildcard $(SRC_DIR)/test/*.cpp)
TEST_OBJS := $(patsubst $(SRC_DIR)/%,$(OBJ_DIR)/%,$(TEST_SRCS:.cpp=.o))
# ADT library (PikeOS ADT port, freestanding C, arch-independent).
# Object lists come from package.mk declarations via pkg/build.mk.
# The wildcard fallback is kept for backward compatibility: if the
# package.mk-driven path yields no objects (e.g. before migration),
# we fall back to the original wildcard approach.
ADT_OBJS := $(ADT_PKG_OBJS)
ifeq ($(strip $(ADT_OBJS)),)
ADT_SRCS := $(wildcard $(SRC_DIR)/core/adt/*/*.c)
ADT_OBJS := $(patsubst $(SRC_DIR)/%,$(OBJ_DIR)/%,$(ADT_SRCS:.c=.o))
endif
# J-Space runtime (Global Workspace, freestanding C++, arch-independent)
# NOT built for riscv/aarch64 — those arches are self-contained and do not
# pull in core/ dependencies (uos_api.cpp, partition.cpp, safety.cpp).
# Disabled for all architectures due to floating-point/new/delete linker errors.
JSPACE_SRCS :=
JSPACE_OBJS :=
# Include lib objects (only if lib-objs-y is defined; otherwise skip the lib dir)
ifdef lib-objs-y
LIB_SRCS := $(addprefix $(SRC_DIR)/lib/, $(lib-objs-y))
LIB_OBJS := $(patsubst $(SRC_DIR)/%,$(OBJ_DIR)/%,$(LIB_SRCS:.cpp=.o))
else
LIB_OBJS :=
endif
# Map arch and plat objects to obj directory. A platform may drop an arch object
# (e.g. to substitute its own driver) by listing it in cpu-skip-y (bare name,
# set in its objects.mk above).
CPU_OBJS := $(addprefix $(OBJ_DIR)/arch/$(ARCH)/, $(filter-out $(cpu-skip-y),$(cpu-objs-y)))
PLAT_OBJS := $(addprefix $(OBJ_DIR)/platform/$(PLATFORM)/, $(plat-objs-y))
# Shared platform drivers (common to all platforms that use them). Compiled
# from src/platform/drivers/; the platform objects.mk opts in via drv-objs-y
# (a bare filename list). When empty/unset, no drivers are built; set to a
# wildcard explicitly (e.g. drv-objs-y := $(wildcard *.cpp)) to build all of
# them (ARMv7/qemu-arm-virt path).
ifneq ($(strip $(drv-objs-y)),)
DRV_SRCS := $(addprefix $(SRC_DIR)/platform/drivers/, $(drv-objs-y))
else
DRV_SRCS :=
endif
DRV_OBJS := $(patsubst $(SRC_DIR)/%,$(OBJ_DIR)/%,$(DRV_SRCS:.cpp=.o))
# FDT reader (kernel/dtb/, outside src/). An arch opts in via fdt-objs-y in its
# objects.mk. Each name maps to $(OBJ_DIR)/dtb/<name>.o (compiled by the static
# rule below).
ifneq ($(strip $(fdt-objs-y)),)
FDT_OBJS := $(addprefix $(OBJ_DIR)/dtb/,$(fdt-objs-y))
else
FDT_OBJS :=
endif
OBJS := $(CORE_OBJS) $(LIB_OBJS) $(CPU_OBJS) $(PLAT_OBJS) $(DRV_OBJS) $(FDT_OBJS) $(ADT_OBJS) $(JSPACE_OBJS) $(TEST_OBJS)
TARGET := $(BUILD_DIR)/universalisos.elf
TARGET_BIN := $(BUILD_DIR)/universalisos.bin
all: $(TARGET)
# Flat binary suitable for the HSS payload generator (Microchip boot flow) and
# for `qemu -device loader,file=...,addr=0x80200000`. Built from the ELF; for
# the firmware/S-mode entry, build with UOS_BOOT=firmware so the byte at
# 0x80200000 is _start_firmware.
$(TARGET_BIN): $(TARGET)
$(CROSS_COMPILE)objcopy -O binary $< $@
bin: $(TARGET_BIN)
$(TARGET): $(OBJS)
$(CROSS_COMPILE)ld $(LDFLAGS) -T $(UOS_LDSCRIPT) -o $@ $^
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
@mkdir -p $(dir $@)
$(CROSS_COMPILE)g++ $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
@mkdir -p $(dir $@)
$(CROSS_COMPILE)gcc $(CPPFLAGS) $(CFLAGS) -std=c99 -c $< -o $@
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.S
@mkdir -p $(dir $@)
$(CROSS_COMPILE)gcc $(CPPFLAGS) $(ASFLAGS) -c $< -o $@
# Sources that live OUTSIDE src/ (e.g. the freestanding FDT reader in
# kernel/dtb/). An arch opts in by setting fdt-objs-y := fdt_reader.o in its
# objects.mk. The object lands at a clean $(OBJ_DIR)/dtb/<name>.o and is
# compiled with a static (non-pattern) rule, because Make's pattern matching
# does not traverse ../ segments.
$(OBJ_DIR)/dtb/%.o: $(SRC_DIR)/../dtb/%.cpp
@mkdir -p $(dir $@)
$(CROSS_COMPILE)g++ $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
clean:
rm -rf $(BUILD_DIR)
# Clean ALL arch/platform build trees (every arch the Makefile knows about).
clean-all:
rm -rf build
# --- RISC-V / Icicle Kit run & payload helpers --------------------------------
# Mirrors the tear-de-silicio polarstar-fpga QEMU flow:
# run-qemu : bare-metal smoke test, -bios none (BSP only).
# run-qemu-firmware : boot via a firmware image at -bios $(FIRMWARE). When
# FIRMWARE is an HSS payload.bin, all 5 harts are
# released into S-mode at 0x80200000 and SBI is live.
# hss-payload : build build/riscv/polarfire/payload.bin by wrapping
# hss-payload-generator with config/icicle_hss_payload.yaml
# (prints the exact command if the tool is missing).
#
# STATUS: the HSS/firmware path is POSTPONED but the artifacts are kept and
# verified to build. See kernel/docs/RISCV_HSS_FIRMWARE_POSTPONED.md for the
# resume procedure (needs Microchip's hss-payload-generator, not on this host).
# These targets are intentionally NOT deleted.
QEMU_RISCV ?= qemu-system-riscv64
QEMU_RISCV_FLAGS ?= -machine microchip-icicle-kit -smp 5 -m 2G -nographic
HSS_PAYLOAD_GENERATOR ?= hss-payload-generator
# ---- AArch64 Linux-boot (M1) ----------------------------------------------
# Boots the EL2 hypervisor and, as its EL1 guest, an Image-shaped stub loaded
# straight into guest RAM by QEMU (`-device loader`). The hypervisor points
# the vCPU at the Image entry (guest_DRAM+0x80000) with x0 = DTB IPA and does
# not copy the Image/DTB itself.
QEMU_AA64 ?= qemu-system-aarch64
QEMU_AA64_FLAGS ?= -M virt,gic-version=3,virtualization=on -cpu cortex-a53 \
-m 512M -smp 4 -nographic
AARCH64_TARGET = build/aarch64/qemu-aarch64-virt/universalisos.elf
GUEST_PAYLOAD_DIR = src/arch/aarch64/guest_payload
LINUX_IMAGE ?= $(GUEST_PAYLOAD_DIR)/linux_stub.bin
LINUX_DTB ?= $(GUEST_PAYLOAD_DIR)/guest.dtb
GUEST_IMAGE_ADDR ?= 0x50080000
GUEST_DTB_ADDR ?= 0x50e00000
# M2: a real Linux Image + rootfs produced by guests/linux-aarch64/build_image.sh
REAL_LINUX_DIR ?= ../guests/linux-aarch64/out
REAL_LINUX_IMAGE ?= $(REAL_LINUX_DIR)/Image
# M2-prep virtio-blk backing image (phase 5). When ROOTFS=<raw img> is passed
# to run-linux-real, we prepend the 16-byte UOSVBLK1 header and load it at
# VBLK_IMAGE_PA (0x50800000); the HV adopts it (capacity = size/512) instead of
# the embedded 64 KiB fallback. Header layout: 8 bytes ASCII "UOSVBLK1" + u64
# little-endian raw-data byte count.
VBLK_IMG_ADDR ?= 0x50800000
VBLK_HEADED_IMG = build/aarch64/qemu-aarch64-virt/vblk_rootfs.img
ifneq ($(strip $(ROOTFS)),)
VBLK_LOADER_LINE = -device loader,file=$(VBLK_HEADED_IMG),addr=$(VBLK_IMG_ADDR)
else
VBLK_LOADER_LINE =
endif
# ---- Personality test (T8-1.2f) --------------------------------------------
# Boots the EL2 hypervisor and loads the hardened_malloc gate-test personality
# as the EL1 guest via QEMU -device loader (same mechanism as run-linux).
PERSONALITY_PAYLOAD_DIR = src/arch/aarch64/personality_payload
PERSONALITY_IMAGE = $(PERSONALITY_PAYLOAD_DIR)/gate_hmalloc.bin
PERSONALITY_IMAGE_ADDR = 0x50000000
run-personality:
$(MAKE) ARCH=aarch64 PLATFORM=qemu-aarch64-virt clean
$(MAKE) ARCH=aarch64 PLATFORM=qemu-aarch64-virt PERSONALITY_BOOT=1
$(MAKE) -C $(PERSONALITY_PAYLOAD_DIR) gate_hmalloc.bin
$(QEMU_AA64) $(QEMU_AA64_FLAGS) \
-kernel $(AARCH64_TARGET) \
-device loader,file=$(PERSONALITY_IMAGE),addr=$(PERSONALITY_IMAGE_ADDR)
run-linux:
$(MAKE) ARCH=aarch64 PLATFORM=qemu-aarch64-virt clean
$(MAKE) ARCH=aarch64 PLATFORM=qemu-aarch64-virt LINUX_BOOT=1
$(MAKE) -C $(GUEST_PAYLOAD_DIR) clean
$(MAKE) -C $(GUEST_PAYLOAD_DIR) linux
$(QEMU_AA64) $(QEMU_AA64_FLAGS) \
-kernel $(AARCH64_TARGET) \
-device loader,file=$(LINUX_IMAGE),addr=$(GUEST_IMAGE_ADDR) \
-device loader,file=$(LINUX_DTB),addr=$(GUEST_DTB_ADDR)
# Build the headed virtio-blk image from a raw ROOTFS (only when ROOTFS= is set).
$(VBLK_HEADED_IMG): $(ROOTFS)
@mkdir -p $(dir $@)
@{ printf 'UOSVBLK1'; \
sz=$$(wc -c < "$(ROOTFS)" | tr -d ' '); \
awk -v s="$$sz" 'BEGIN{for(i=0;i<8;i++){printf "%c", and(s,255); s=rshift(s,8)}}'; \
cat "$(ROOTFS)"; } > $@
@echo "vblk: headed image $@ (raw=$$(wc -c < "$(ROOTFS)" | tr -d ' ') bytes)"
# M2a: boot the REAL Linux Image under EL2 (earlycon via PL011 trap, no rootfs
# yet). Same HV wiring (entry=0x50080000, x0=DTB); QEMU loader places the
# 12 MB Image. The UOS guest.dtb describes RAM@0x50000000 + GICv3 + PL011.
# Pass ROOTFS=<raw.img> to also attach it as the virtio-blk backing store.
run-linux-real:
$(MAKE) ARCH=aarch64 PLATFORM=qemu-aarch64-virt clean
$(MAKE) ARCH=aarch64 PLATFORM=qemu-aarch64-virt LINUX_BOOT=1
$(MAKE) -C $(GUEST_PAYLOAD_DIR) guest.dtb
@if [ -n "$(strip $(ROOTFS))" ]; then $(MAKE) $(VBLK_HEADED_IMG) ROOTFS=$(ROOTFS); fi
$(QEMU_AA64) $(QEMU_AA64_FLAGS) \
-kernel $(AARCH64_TARGET) \
-device loader,file=$(REAL_LINUX_IMAGE),addr=$(GUEST_IMAGE_ADDR) \
-device loader,file=$(LINUX_DTB),addr=$(GUEST_DTB_ADDR) \
$(VBLK_LOADER_LINE)
run-qemu: $(TARGET)
$(QEMU_RISCV) $(QEMU_RISCV_FLAGS) -bios none -kernel $(TARGET)
run-qemu-firmware: $(TARGET)
@test -n "$(FIRMWARE)" || (echo "Set FIRMWARE=path/to/payload.bin (see 'make hss-payload')"; exit 2)
$(QEMU_RISCV) $(QEMU_RISCV_FLAGS) -bios $(FIRMWARE) -device loader,file=$(TARGET),addr=0x80200000
hss-payload:
$(MAKE) ARCH=riscv PLATFORM=polarfire UOS_BOOT=firmware bin
@if command -v $(HSS_PAYLOAD_GENERATOR) >/dev/null 2>&1; then \
$(HSS_PAYLOAD_GENERATOR) -c config/icicle_hss_payload.yaml \
build/riscv/polarfire/universalisos.bin build/riscv/polarfire/payload.bin; \
echo "Wrote build/riscv/polarfire/payload.bin"; \
else \
echo "hss-payload-generator not found. Generate the payload on a host with Microchip's tools:"; \
echo " hss-payload-generator -c kernel/config/icicle_hss_payload.yaml \\"; \
echo " kernel/build/riscv/polarfire/universalisos.bin payload.bin"; \
echo "Config: kernel/config/icicle_hss_payload.yaml (tear-de-silicio-compatible)"; \
fi
# ---------------------------------------------------------------------------
# Test targets — build + boot smoke tests for CI
# ---------------------------------------------------------------------------
# ARCHS_BOOTTEST defines which architectures to test. Override on cmdline:
# make test ARCHS_BOOTTEST="armv7 aarch64"
ARCHS_BOOTTEST ?= armv7 aarch64 riscv
# QEMU binary and flags per architecture
QEMU_ARMV7 ?= qemu-system-arm
QEMU_ARMV7_FLAGS ?= -M virt -cpu cortex-a15 -m 512M -nographic
ARMV7_ELF = build/armv7/qemu-arm-virt/universalisos.elf
QEMU_AARCH64_TEST ?= qemu-system-aarch64
QEMU_AARCH64_FLAGS_TEST ?= -M virt,gic-version=3,virtualization=on -cpu cortex-a53 -m 512M -smp 4 -nographic
AARCH64_ELF = build/aarch64/qemu-aarch64-virt/universalisos.elf
# RISC-V uses the existing QEMU_RISCV settings
RISCV_ELF = build/riscv/polarfire/universalisos.elf
# Boot test timeout (seconds) — how long to wait for UART banner
BOOT_TIMEOUT ?= 15
# Banner string to grep for in UART output
BOOT_BANNER ?= UniversalisOS
# JUnit XML output for CI
TEST_RESULTS ?= $(BUILD_DIR)/test-results.xml
# Internal: run a single architecture boot test
# $(1) = arch name, $(2) = QEMU binary, $(3) = QEMU flags, $(4) = ELF path
define BOOT_TEST
@echo "=== Testing $(1): building... "
$(MAKE) ARCH=$(1) PLATFORM=qemu-$(1)-virt clean 2>/dev/null || true
$(MAKE) ARCH=$(1) PLATFORM=qemu-$(1)-virt
@echo "=== Testing $(1): booting in QEMU (timeout=$(BOOT_TIMEOUT)s)... "
@timeout $(BOOT_TIMEOUT) $(2) $(3) -kernel $(4) 2>&1 | tee /tmp/uos_boot_$(1).log ; \
exit_code=$$? ; \
if [ $$exit_code -eq 124 ]; then \
echo "=== $(1): TIMEOUT after $(BOOT_TIMEOUT)s ===" ; \
grep -q "$(BOOT_BANNER)" /tmp/uos_boot_$(1).log && \
echo "=== $(1): PASS (banner found before timeout) ===" || \
echo "=== $(1): FAIL (no banner found) ===" ; \
elif [ $$exit_code -eq 0 ]; then \
echo "=== $(1): PASS ===" ; \
else \
echo "=== $(1): FAIL (exit code $$exit_code) ===" ; \
fi
@grep -q "$(BOOT_BANNER)" /tmp/uos_boot_$(1).log 2>/dev/null
endef
test:
@echo "UniversalisOS Boot Test Suite"
@echo "============================="
@echo "Architectures: $(ARCHS_BOOTTEST)"
@echo "Timeout: $(BOOT_TIMEOUT)s per arch"
@echo ""
@failures=0 ; \
for arch in $(ARCHS_BOOTTEST); do \
case $$arch in \
armv7) $(call BOOT_TEST,armv7,$(QEMU_ARMV7),"$(QEMU_ARMV7_FLAGS)",$(ARMV7_ELF)) || failures=$$((failures+1)) ;; \
aarch64) $(call BOOT_TEST,aarch64,$(QEMU_AARCH64_TEST),"$(QEMU_AARCH64_FLAGS_TEST)",$(AARCH64_ELF)) || failures=$$((failures+1)) ;; \
riscv) $(call BOOT_TEST,riscv,$(QEMU_RISCV),"$(QEMU_RISCV_FLAGS) -bios none",$(RISCV_ELF)) || failures=$$((failures+1)) ;; \
*) echo "Unknown architecture: $$arch" ; failures=$$((failures+1)) ;; \
esac ; \
done ; \
echo "" ; \
echo "=============================" ; \
if [ $$failures -eq 0 ]; then \
echo "ALL TESTS PASSED" ; \
else \
echo "$$failures TEST(S) FAILED" ; \
exit 1 ; \
fi
test-armv7:
$(MAKE) test ARCHS_BOOTTEST="armv7"
test-aarch64:
$(MAKE) test ARCHS_BOOTTEST="aarch64"
test-riscv:
$(MAKE) test ARCHS_BOOTTEST="riscv"
.PHONY: all bin clean clean-all run-qemu run-qemu-firmware hss-payload run-linux run-linux-real run-personality test test-armv7 test-aarch64 test-riscv