# minos — minimal multi-arch kernel (M1) # One clang, three ISAs. Build all in parallel: make -j # # make build all three (x86, riscv, arm) # make x86 build one arch # make run-riscv build + boot that arch under QEMU (-nographic; Ctrl-A X to quit) # make sizes compare code size across arches # make clean CC := clang LD := ld.lld OBJCOPY := llvm-objcopy ARCHES := x86 riscv arm # Shared flags. Per-arch target/flags below. CFLAGS := -ffreestanding -nostdlib -fno-pic -O2 -Wall -Wextra -Iinclude -g -MMD -MP COMMON := common/main.c common/console.c # --- per-arch knobs ------------------------------------------------------- x86_TARGET := --target=i386-unknown-none -m32 x86_LDEMU := elf_i386 x86_SRC := arch/x86/boot.S arch/x86/arch.c $(COMMON) x86_QEMU := qemu-system-i386 -kernel build/x86/minos.elf -nographic riscv_TARGET := --target=riscv64-unknown-none-elf -march=rv64imac -mabi=lp64 -mcmodel=medany riscv_LDEMU := elf64lriscv riscv_SRC := arch/riscv/boot.S arch/riscv/trap.S arch/riscv/arch.c $(COMMON) riscv_QEMU := qemu-system-riscv64 -machine virt -bios none -nographic -kernel build/riscv/minos.elf arm_TARGET := --target=arm-none-eabi -mcpu=cortex-a9 -marm arm_LDEMU := armelf arm_SRC := arch/arm/boot.S arch/arm/vectors.S arch/arm/arch.c $(COMMON) arm_QEMU := qemu-system-arm -machine xilinx-zynq-a9 -cpu cortex-a9 -nographic -kernel build/arm/minos.elf .PHONY: all clean sizes $(ARCHES) $(addprefix run-,$(ARCHES)) all: $(ARCHES) # Generate per-arch build rules. define ARCH_rules $(1)_OBJS := $$(patsubst %,build/$(1)/%.o,$$(basename $$(notdir $$($(1)_SRC)))) build/$(1)/%.o: arch/$(1)/%.S | build/$(1) $$(CC) $$($(1)_TARGET) $$(CFLAGS) -c $$< -o $$@ build/$(1)/%.o: arch/$(1)/%.c | build/$(1) $$(CC) $$($(1)_TARGET) $$(CFLAGS) -c $$< -o $$@ build/$(1)/%.o: common/%.c | build/$(1) $$(CC) $$($(1)_TARGET) $$(CFLAGS) -c $$< -o $$@ build/$(1): mkdir -p $$@ build/$(1)/minos.elf: $$($(1)_OBJS) arch/$(1)/link.ld $$(LD) -m $$($(1)_LDEMU) -T arch/$(1)/link.ld -nostdlib $$($(1)_OBJS) -o $$@ @echo " [$(1)] linked -> $$@" $(1): build/$(1)/minos.elf run-$(1): build/$(1)/minos.elf @echo "== booting $(1) (Ctrl-A X to quit) ==" $$($(1)_QEMU) -include $$($(1)_OBJS:.o=.d) endef $(foreach a,$(ARCHES),$(eval $(call ARCH_rules,$(a)))) sizes: all @echo "arch text+data+bss (bytes)"; \ for a in $(ARCHES); do \ printf '%-7s ' $$a; \ $(OBJCOPY) -O binary build/$$a/minos.elf /tmp/_sz.bin 2>/dev/null; \ stat -c '%s' /tmp/_sz.bin; \ done; rm -f /tmp/_sz.bin # --- real hardware: Allwinner D1 / Lichee RV via xfel ------------------------ # Same RISC-V sources, rebuilt with -DBOARD_D1 (UART 0x02500000) and linked # for DRAM 0x40000000, emitted as a flat binary xfel can load. XFEL ?= xfel D1_TARGET := $(riscv_TARGET) -DBOARD_D1 D1_OBJS := $(patsubst %,build/lichee/%.o,$(basename $(notdir $(riscv_SRC)))) build/lichee/%.o: arch/riscv/%.S | build/lichee $(CC) $(D1_TARGET) $(CFLAGS) -c $< -o $@ build/lichee/%.o: arch/riscv/%.c | build/lichee $(CC) $(D1_TARGET) $(CFLAGS) -c $< -o $@ build/lichee/%.o: common/%.c | build/lichee $(CC) $(D1_TARGET) $(CFLAGS) -c $< -o $@ build/lichee: mkdir -p $@ build/lichee/minos.elf: $(D1_OBJS) arch/riscv/link-d1.ld $(LD) -m $(riscv_LDEMU) -T arch/riscv/link-d1.ld -nostdlib $(D1_OBJS) -o $@ build/lichee/minos.bin: build/lichee/minos.elf $(OBJCOPY) -O binary $< $@ @echo " [lichee] flat binary -> $@ ($$(stat -c%s $@) bytes)" .PHONY: lichee run-lichee lichee: build/lichee/minos.bin ## build the D1 flat binary # Load & run on the board (must be in FEL mode; needs sudo for USB). run-lichee: build/lichee/minos.bin @echo "== loading minos onto D1 via xfel ==" $(XFEL) ddr d1 $(XFEL) write 0x40000000 build/lichee/minos.bin $(XFEL) exec 0x40000000 @echo "== running. Watch /dev/ttyUSB0 @115200 for output. ==" # --- real hardware: Zynq-7000 PS (Cortex-A9) via JTAG/OpenOCD ---------------- # Same ARM sources, re-linked into on-chip RAM (OCM at 0x0) so no FSBL/DDR # init is needed. Loaded over JTAG with OpenOCD (the on-board FT2232H is a # generic 0403:6010 that Xilinx hw_server won't auto-detect, but OpenOCD does). OPENOCD ?= openocd OOCD_CFG := zynq/openocd-zynq.cfg ZYNQ_OBJS := $(patsubst %,build/zynq/%.o,$(basename $(notdir $(arm_SRC)))) build/zynq/%.o: arch/arm/%.S | build/zynq $(CC) $(arm_TARGET) $(CFLAGS) -c $< -o $@ build/zynq/%.o: arch/arm/%.c | build/zynq $(CC) $(arm_TARGET) $(CFLAGS) -c $< -o $@ build/zynq/%.o: common/%.c | build/zynq $(CC) $(arm_TARGET) $(CFLAGS) -c $< -o $@ build/zynq: mkdir -p $@ build/zynq/minos.elf: $(ZYNQ_OBJS) arch/arm/link-zynq-ocm.ld $(LD) -m $(arm_LDEMU) -T arch/arm/link-zynq-ocm.ld -nostdlib $(ZYNQ_OBJS) -o $@ @echo " [zynq] OCM ELF -> $@" .PHONY: zynq run-zynq zynq: build/zynq/minos.elf ## build the Zynq OCM ELF # Halt A9 core0, load the ELF into OCM, set PC to _start, resume. # Watch the UART console (ttyUSB1 @115200) for output. # # Critical: the on-board boot firmware leaves the core with MMU + D-cache + # I-cache enabled (SCTLR bits 0/2/12). If we resume like that, the stale # TLB/cache shadow our freshly loaded OCM and nothing runs. So we clear those # bits (SCTLR -> 0x08C54C78) and force SVC mode before resuming. We keep the # firmware's PS clock setup (incl. UART) by halting rather than resetting. run-zynq: build/zynq/minos.elf @echo "== loading minos into Zynq OCM via OpenOCD ==" $(OPENOCD) -f $(OOCD_CFG) \ -c "init" \ -c "targets zynq.cpu0" \ -c "halt" \ -c "load_image build/zynq/minos.elf" \ -c "arm mcr 15 0 1 0 0 0x08C54C78" \ -c "reg pc 0x00000000" \ -c "reg cpsr 0x000001d3" \ -c "resume" \ -c "shutdown" @echo "== running. Watch /dev/ttyUSB1 @115200 for output. ==" clean: rm -rf build