Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
This commit is contained in:
commit
d0f7acf9eb
275 changed files with 51417 additions and 0 deletions
806
Makefile
Normal file
806
Makefile
Normal file
|
|
@ -0,0 +1,806 @@
|
|||
BUILD = build
|
||||
SYSROOT = sysroot
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Parallelism
|
||||
#
|
||||
# Nothing here is order-dependent beyond what the rules already declare, so
|
||||
# there is no reason to compile one file at a time. Override with `make -j1`
|
||||
# when you want readable output from a failing build.
|
||||
# -----------------------------------------------------------------------
|
||||
NPROC := $(shell nproc 2>/dev/null || echo 4)
|
||||
MAKEFLAGS += -j$(NPROC)
|
||||
|
||||
# Kernel object files — organized by subsystem
|
||||
KERN_ARCH = $(addprefix $(BUILD)/kernel/arch/x86_64/, \
|
||||
acpi.o e820.o entry.o trapasm.o swtch.o \
|
||||
trap.o vm.o lapic.o ioapic.o mp.o)
|
||||
|
||||
KERN_CORE = $(addprefix $(BUILD)/kernel/core/, \
|
||||
main.o kalloc.o dma.o spinlock.o sleeplock.o string.o console.o \
|
||||
panic.o uaccess.o qr.o time.o)
|
||||
|
||||
KERN_DEV = $(addprefix $(BUILD)/kernel/dev/, \
|
||||
bio.o cdev.o device.o fb.o ide.o input.o irq.o kbd.o mouse.o \
|
||||
pci.o text.o ttf.o uart.o vga.o ramdisk.o procfs.o)
|
||||
|
||||
# Drivers for hardware that has to be *found* rather than assumed. Anything
|
||||
# discovered by walking a bus belongs here; the fixed-address legacy devices
|
||||
# (console, keyboard, PIT, the IDE ports) stay in dev/.
|
||||
#
|
||||
# drivers/<class>/<driver>/ -- one directory per driver, so a driver that grows
|
||||
# a register header, a second source file or a firmware blob has somewhere to
|
||||
# put it. Classes match include/device.h's DEVCLASS_*. Everything flat was
|
||||
# already eight files with four called usb_something, and each driver added made
|
||||
# the directory harder to read. The compile rule uses `mkdir -p $(dir $@)`, so
|
||||
# the depth costs nothing.
|
||||
KERN_DRIVERS = $(BUILD)/kernel/drivers/registry.o
|
||||
KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/vga/vga_pci.o
|
||||
KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/virtio_gpu/virtio_gpu.o
|
||||
KERN_DRIVERS += $(BUILD)/kernel/drivers/net/e1000/e1000.o
|
||||
KERN_DRIVERS += $(BUILD)/kernel/drivers/bus/xhci/xhci.o
|
||||
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/core/usb.o
|
||||
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/hid/usb_hid.o
|
||||
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/storage/usb_storage.o
|
||||
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/hub/usb_hub.o
|
||||
|
||||
KERN_FS = $(addprefix $(BUILD)/kernel/fs/, \
|
||||
exec.o file.o fs.o log.o pipe.o procfs.o)
|
||||
|
||||
# SchFS. Every one of these is the very same source the host tools compile, so
|
||||
# the kernel and mkschfs cannot disagree about the on-disk format: there is one
|
||||
# implementation, found and fixed on the host where a test takes a second.
|
||||
KERN_FS += $(addprefix $(BUILD)/kernel/fs/schfs/, \
|
||||
btree.o space.o vol.o obj.o objw.o kmount.o vfs.o)
|
||||
|
||||
# Sockets. Unix domain only for now; an IP stack becomes more files here
|
||||
# rather than a reshaping of what is already written.
|
||||
KERN_NET = $(addprefix $(BUILD)/kernel/net/, \
|
||||
unix.o)
|
||||
|
||||
KERN_PROC = $(addprefix $(BUILD)/kernel/proc/, \
|
||||
proc.o)
|
||||
|
||||
KERN_SYS = $(addprefix $(BUILD)/kernel/sys/, \
|
||||
syscall.o sys_proc.o sys_fs.o sys_mem.o sys_net.o \
|
||||
sys_time.o sys_sig.o sys_compat.o sys_poll.o linux_abi.o)
|
||||
|
||||
# vectors.o lives alongside trapasm in the arch dir
|
||||
KERN_ARCH += $(BUILD)/kernel/arch/x86_64/vectors.o
|
||||
|
||||
KERNOBJS = $(KERN_ARCH) $(KERN_CORE) $(KERN_DEV) $(KERN_DRIVERS) \
|
||||
$(KERN_FS) $(KERN_NET) $(KERN_PROC) $(KERN_SYS)
|
||||
# entry.o must be first in link order; remove it from KERNOBJS for the rule
|
||||
KERN_NOENTRY = $(filter-out $(BUILD)/kernel/arch/x86_64/entry.o, $(KERNOBJS))
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# First-party userland
|
||||
#
|
||||
# lib/ startup glue + syscall stubs every cmd/ program links against
|
||||
# cmd/ one directory per program: cmd/<name>/main.c -> sysroot/bin/<name>
|
||||
#
|
||||
# To add a program: mkdir cmd/foo, write cmd/foo/main.c, add "foo" to CMDS.
|
||||
# Third-party software does NOT go here — see ports/.
|
||||
# -----------------------------------------------------------------------
|
||||
ULIB = $(addprefix $(BUILD)/lib/, start.o glue.o usys.o)
|
||||
CMDS = init fbinfo shmtest socktest inputtest
|
||||
CMDBINS = $(addprefix $(BUILD)/cmd/, $(CMDS))
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Toolchain
|
||||
# -----------------------------------------------------------------------
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_P := $(shell uname -p)
|
||||
|
||||
# Windows, under Git Bash or MSYS2 — WINHOST.
|
||||
#
|
||||
# Building natively on Windows rather than in WSL, because WSL2 is a real Linux
|
||||
# kernel in a Hyper-V VM and so hard-requires virtualisation; turning that on
|
||||
# puts the Hyper-V hypervisor underneath the whole machine and demotes VMware
|
||||
# to running nested on top of it, which is a real and permanent cost to pay for
|
||||
# a build environment.
|
||||
#
|
||||
# The usual answer is a cross binutils+gcc, which is a day of work that then
|
||||
# rots. clang does not need any of it: it is a cross-compiler by construction,
|
||||
# so one ordinary LLVM install targets both the bare-metal kernel and Linux
|
||||
# userland, and llvm-objcopy/objdump stand in for binutils. Everything else the
|
||||
# build shells out to — perl, sed, dd, sha256sum — comes with Git Bash.
|
||||
ifneq (,$(filter MINGW% MSYS% CYGWIN%,$(UNAME_S)))
|
||||
WINHOST := 1
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(UNAME_P),aarch64)
|
||||
TOOLPREFIX = x86_64-linux-gnu-
|
||||
else
|
||||
TOOLPREFIX =
|
||||
endif
|
||||
else ifeq ($(UNAME_S),Darwin)
|
||||
TOOLPREFIX = x86_64-elf-
|
||||
endif
|
||||
|
||||
QEMU = qemu-system-x86_64
|
||||
|
||||
# Display for every QEMU target.
|
||||
#
|
||||
# virtio-gpu is what the display class binds, and what a compositor should be
|
||||
# developed against: presenting is explicit, so the host never shows a
|
||||
# half-drawn frame.
|
||||
#
|
||||
# stdvga stays alongside it and is not optional. The bootloader draws the splash
|
||||
# through VBE in 16-bit real mode and there is no virtio path that early, so a
|
||||
# machine with only virtio-gpu boots blind until the kernel is up. Both devices:
|
||||
# virtio-gpu wins the display class, stdvga carries the splash and text console.
|
||||
QEMU_DISPLAY = -vga std -device virtio-gpu-pci
|
||||
|
||||
ifdef WINHOST
|
||||
# An explicit --target is what makes clang emit ELF instead of the COFF it
|
||||
# would default to on this host. Without it every object is silently the
|
||||
# wrong format and the first thing to complain is the linker.
|
||||
CC = clang --target=x86_64-elf
|
||||
CC32 = clang --target=i386-elf
|
||||
LD = ld.lld
|
||||
OBJCOPY = llvm-objcopy
|
||||
OBJDUMP = llvm-objdump
|
||||
# ld.lld has no --no-warn-rwx-segments: it does not emit that warning, so
|
||||
# there is nothing to suppress and the flag is a hard error.
|
||||
LDFLAGS = -m elf_x86_64 -nostdlib
|
||||
# The bootloader, the AP trampoline, initcode and the cmd/ programs are all
|
||||
# linked at addresses below one megabyte. GNU ld places a section wherever
|
||||
# -Ttext says; lld first checks it against a default image base of 0x400000
|
||||
# and refuses, because for a hosted program that would be a bug. Saying the
|
||||
# base is zero is how you tell it this is not a hosted program.
|
||||
LD_LOWBASE = --image-base=0
|
||||
# Host tools (mkfs, mkschfs, the SchFS suites) are ordinary Windows programs.
|
||||
# They must be built with mingw-w64 rather than clang's default MSVC target:
|
||||
# tools/mkfs.c includes <unistd.h> and <dirent.h>, and the Windows SDK has
|
||||
# neither.
|
||||
HOSTCC = gcc
|
||||
# mingw-w64's gcc warns about things glibc's does not (notably %lld/%zu in
|
||||
# printf against msvcrt). Those warnings are about the host build only and
|
||||
# say nothing about the image, so they must not stop it.
|
||||
HOSTWARN = -Wall
|
||||
# MinGW gcc appends .exe when the -o name has no extension, so a rule
|
||||
# whose target lacks it is never considered built -- mkfs was recompiled on
|
||||
# every single invocation of make, forever, because of this.
|
||||
EXE = .exe
|
||||
# `python3` is not on PATH from the python.org installer; `python` is.
|
||||
PYTHON = python
|
||||
# Compiler for ports/, which build *Linux userland* against musl -- a third
|
||||
# target, distinct from both the kernel (bare-metal ELF) and the host tools
|
||||
# (Windows PE). mingw's gcc cannot do it: it targets Windows, so linking it
|
||||
# against musl gives undefined `stderr` and `__errno_location`.
|
||||
#
|
||||
# -nostdlib with the crt files named explicitly, because clang otherwise
|
||||
# reaches for gcc's crtbeginT.o and libgcc, which do not exist on this host.
|
||||
MUSLPREFIX = $(abspath $(BUILD))/ports/musl/prefix
|
||||
PORTCC = clang --target=x86_64-unknown-linux-musl -fuse-ld=lld
|
||||
PORTCRT = -nostdlib $(MUSLPREFIX)/lib/crt1.o $(MUSLPREFIX)/lib/crti.o
|
||||
PORTEND = $(MUSLPREFIX)/lib/crtn.o
|
||||
else
|
||||
CC = $(TOOLPREFIX)gcc
|
||||
CC32 = $(CC) -m32
|
||||
LD = $(TOOLPREFIX)ld
|
||||
OBJCOPY = $(TOOLPREFIX)objcopy
|
||||
OBJDUMP = $(TOOLPREFIX)objdump
|
||||
LDFLAGS = -m elf_x86_64 -nostdlib --no-warn-rwx-segments
|
||||
HOSTCC = gcc
|
||||
HOSTWARN = -Werror -Wall
|
||||
EXE =
|
||||
PYTHON = python3
|
||||
LD_LOWBASE =
|
||||
PORTCC = gcc
|
||||
PORTCRT =
|
||||
PORTEND =
|
||||
endif
|
||||
|
||||
# Host tools are generated output and live under build/, per docs/layout.md.
|
||||
# $(EXE) matters: MinGW appends it, and a rule whose target lacks it never
|
||||
# looks satisfied.
|
||||
MKFS = $(BUILD)/tools/mkfs$(EXE)
|
||||
MKSCHFS = $(BUILD)/tools/mkschfs$(EXE)
|
||||
SCHFS_TEST = $(BUILD)/tools/schfs_test$(EXE)
|
||||
SCHFS_OBJT = $(BUILD)/tools/schfs_objtest$(EXE)
|
||||
SCHFS_FSCK = $(BUILD)/tools/schfs_fsck$(EXE)
|
||||
|
||||
OPT ?= -O0
|
||||
# -ftls-model=local-exec: the kernel is *the* executable, never a shared
|
||||
# object, so its __thread variables (cpu, proc) are always at a fixed negative
|
||||
# offset from the thread pointer. Saying so lets the compiler emit a direct
|
||||
# `movq %rcx, %fs:-0x10` and never materialise the thread pointer as a value.
|
||||
#
|
||||
# GCC picked local-exec on its own for this code; clang did not, and generated
|
||||
# `movq %fs:0x0, %rax; addq $-0x10, %rax; ...` instead -- which reads the TCB
|
||||
# self-pointer at %fs:0, a word this kernel does not maintain and something
|
||||
# else has apparently always been scribbling on. Harmless for fifteen years
|
||||
# because nothing ever read it; instant triple fault the moment a compiler did.
|
||||
XFLAGS = -m64 -DX64 -mcmodel=large -mtls-direct-seg-refs -mno-red-zone
|
||||
XFLAGS += -ftls-model=local-exec
|
||||
|
||||
# Probed once, with :=, not on every expansion of CFLAGS. CFLAGS is a
|
||||
# recursively-expanded variable, so a $(shell) appended to it re-ran the probe
|
||||
# on every single compile -- one extra gcc process per object file, for an
|
||||
# answer that cannot change during a build.
|
||||
NO_SSP := $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
|
||||
|
||||
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -Wall -MD -ggdb -fno-omit-frame-pointer
|
||||
CFLAGS += -ffreestanding -fno-common -nostdlib -I./include $(XFLAGS) $(OPT)
|
||||
CFLAGS += $(NO_SSP) $(CFLAGS_EXTRA)
|
||||
|
||||
# sys/ also needs to find table.inc via a relative include
|
||||
CFLAGS_SYS = $(CFLAGS) -I./kernel/sys
|
||||
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Ramdisk root (opt-in): make RAMROOT=1
|
||||
#
|
||||
# Links a complete SCHFS image into the kernel and boots from it instead of
|
||||
# the IDE disk, so the physical disk can be reformatted from a running system.
|
||||
# Off by default because it adds RAMDISK_BLOCKS KB to the kernel, and stage2
|
||||
# loads the kernel by PIO one sector at a time -- a 6 MB kernel takes visibly
|
||||
# longer to boot than a 350 KB one.
|
||||
# -----------------------------------------------------------------------
|
||||
RAMDISK_BLOCKS ?= 6000
|
||||
|
||||
# Inodes for the ramdisk root. The inode table is preallocated, so mkfs's
|
||||
# default of 12000 cost 1501 of those 6000 blocks -- a quarter of the image, to
|
||||
# describe about 260 files. 2000 is still an order of magnitude of headroom and
|
||||
# costs 251 blocks.
|
||||
RAMDISK_NINODES ?= 2000
|
||||
|
||||
# Kept out of the ramdisk root.
|
||||
#
|
||||
# Empty right now, and the machinery is kept anyway, because this is a licensing
|
||||
# rule before it is a size one: the ramdisk image is linked *into the kernel
|
||||
# binary*, so anything staged in sysroot/ when it is built becomes part of a
|
||||
# distributable artifact. Anything that arrives in sysroot/ under a licence that
|
||||
# cannot be combined with Apache-2.0, or that simply is not ours to redistribute,
|
||||
# belongs on this list rather than in someone's memory.
|
||||
#
|
||||
# This is not hypothetical: a 33 MB test payload once landed here and overflowed
|
||||
# the 6 MB image, and mkfs's "increase FSSIZE" message then pointed at a constant
|
||||
# that had nothing to do with it.
|
||||
# usb.ids is 730 KB of a 6 MB image -- 12% of the ramdisk root -- and nothing
|
||||
# needs it to boot. It is a lookup table for a userspace tool, so it stays on
|
||||
# the disk image and out of the kernel binary.
|
||||
RAMDISK_EXCLUDE = usr/share/usb.ids
|
||||
RAMDISK_EXCLUDE_ARGS = $(foreach p,$(RAMDISK_EXCLUDE),--exclude $(p))
|
||||
|
||||
ifdef RAMROOT
|
||||
CFLAGS += -DRAMDISK_ROOT
|
||||
RAMDISK_OBJ = $(BUILD)/kernel/ramdisk_obj
|
||||
else
|
||||
RAMDISK_OBJ =
|
||||
endif
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Top-level targets
|
||||
# -----------------------------------------------------------------------
|
||||
all: schunix.img
|
||||
|
||||
schunix.img: $(BUILD)/boot/bootblock $(BUILD)/boot/stage2 $(BUILD)/kernel/kernel $(BUILD)/schfs.img
|
||||
dd if=/dev/zero of=schunix.img count=10000
|
||||
dd if=$(BUILD)/boot/bootblock of=schunix.img conv=notrunc
|
||||
dd if=$(BUILD)/boot/stage2 of=schunix.img seek=1 conv=notrunc
|
||||
# seek is 1 + STAGE2_SECTORS, not a literal. It was 25 against a 24-sector
|
||||
# stage2; raising STAGE2_SECTORS for the boot logo without touching it wrote
|
||||
# the kernel straight over stage2's own tail.
|
||||
dd if=$(BUILD)/kernel/kernel of=schunix.img seek=$$(( 1 + $(STAGE2_SECTORS) )) conv=notrunc
|
||||
|
||||
kernel: $(BUILD)/kernel/kernel
|
||||
|
||||
# Build the ramdisk-root variant. This is a target rather than just
|
||||
# "make RAMROOT=1" because toggling RAMROOT changes CFLAGS (-DRAMDISK_ROOT),
|
||||
# and make has no way to notice that: objects compiled for one setting would be
|
||||
# silently reused for the other, leaving ROOTDEV pointing at the wrong device.
|
||||
# Dropping the compiled objects first makes the switch always correct.
|
||||
ramroot:
|
||||
rm -rf $(BUILD)/kernel $(BUILD)/lib $(BUILD)/cmd
|
||||
$(MAKE) RAMROOT=1
|
||||
|
||||
cmd: $(CMDBINS)
|
||||
|
||||
ports:
|
||||
$(MAKE) -C ports
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Bootable ISO
|
||||
#
|
||||
# Always a ramdisk-root build. An ISO that needed a second disk to find its
|
||||
# root filesystem would not be an install medium, it would be a puzzle -- and
|
||||
# read-only media cannot host a writable root anyway. Same object-wiping
|
||||
# preamble as the `ramroot` target, for the same reason: RAMROOT changes
|
||||
# CFLAGS and make cannot see that.
|
||||
# -----------------------------------------------------------------------
|
||||
iso:
|
||||
rm -rf $(BUILD)/kernel $(BUILD)/lib $(BUILD)/cmd
|
||||
$(MAKE) RAMROOT=1 schunix.iso
|
||||
|
||||
schunix.iso: $(BUILD)/boot/cdboot $(BUILD)/boot/stage2 $(BUILD)/kernel/kernel
|
||||
STAGE2_SECTORS=$(STAGE2_SECTORS) sh tools/mkiso.sh $@
|
||||
|
||||
# Boot the ISO the way a machine booting off a disc would: no hard disk at
|
||||
# all, so a kernel that still secretly depends on one fails here rather than
|
||||
# in someone's optical drive.
|
||||
qemu-iso: schunix.iso
|
||||
$(QEMU) -serial mon:stdio $(QEMU_DISPLAY) -cpu qemu64,+rdtscp \
|
||||
-cdrom schunix.iso -boot d \
|
||||
-m 1G -smp 2 -machine pc-i440fx-9.2
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Bootloader — two stages. Stage 1 (bootblock) is the actual 512-byte MBR
|
||||
# sector: BIOS-imposed hard limit, so it only loads stage 2 off disk and
|
||||
# jumps to it. Stage 2 does VBE/A20/protected-mode setup, draws the boot
|
||||
# splash, and loads the kernel — not size-constrained the same way, just
|
||||
# padded out to a fixed STAGE2_SECTORS*512 bytes so the kernel's starting
|
||||
# sector on disk is predictable (kept in sync with STAGE2_SECTORS in
|
||||
# boot/bootasm.S and boot/stage2main.c).
|
||||
# -----------------------------------------------------------------------
|
||||
# STAGE2_SECTORS used to be written out three times -- here, in boot/bootasm.S
|
||||
# and in boot/stage2main.c -- and docs/layout.md carried a warning about keeping
|
||||
# them in step. They are now all fed from this one variable via -D, so the
|
||||
# warning describes a problem that can no longer happen. Both C/asm files keep
|
||||
# an #ifndef fallback purely so they still read sensibly on their own.
|
||||
#
|
||||
# The ceiling is memory, not disk: stage2 is loaded at 0x7E00 and the multiboot
|
||||
# kernel-search buffer begins at 0x10000, so 65 sectors is the hard maximum.
|
||||
# stage2main.c static-asserts this. padstage2.pl fails the build if the compiled
|
||||
# binary overflows the budget, rather than truncating it -- a short stage2 would
|
||||
# shift the kernel's start sector and fail in a way that looks nothing like its
|
||||
# cause.
|
||||
STAGE2_SECTORS = 48
|
||||
STAGE2_DEF = -DSTAGE2_SECTORS=$(STAGE2_SECTORS)
|
||||
# Byte budget is computed in the recipe with shell arithmetic, not $(shell expr
|
||||
# ... \* 512): make hands the backslash through untouched, sh then treats the
|
||||
# `*` as a glob, and expr gets a directory listing. It "worked" to the extent
|
||||
# that make carried on and padstage2.pl received nonsense.
|
||||
STAGE2_BYTES = $$(( $(STAGE2_SECTORS) * 512 ))
|
||||
|
||||
$(BUILD)/boot/bootblock: boot/bootasm.S
|
||||
@mkdir -p $(BUILD)/boot
|
||||
$(CC32) $(STAGE2_DEF) -fno-builtin -fno-pic -nostdinc -I./include -c boot/bootasm.S -o $(BUILD)/boot/bootasm.o
|
||||
$(LD) -m elf_i386 -nostdlib $(LD_LOWBASE) -n -N -e start -Ttext 0x7C00 \
|
||||
-o $(BUILD)/boot/bootblocktmp.o $(BUILD)/boot/bootasm.o
|
||||
$(OBJCOPY) -S -O binary -j .text $(BUILD)/boot/bootblocktmp.o $(BUILD)/boot/bootblock
|
||||
./tools/sign.pl $(BUILD)/boot/bootblock
|
||||
|
||||
# Stage 1 for optical media. Same job as bootblock, far less of it: El Torito
|
||||
# has already loaded stage2 alongside this sector by the time it runs, so it
|
||||
# only records that we came from a CD and jumps. See boot/cdboot.S.
|
||||
$(BUILD)/boot/cdboot: boot/cdboot.S
|
||||
@mkdir -p $(BUILD)/boot
|
||||
$(CC32) -fno-builtin -fno-pic -nostdinc -I./include -c boot/cdboot.S -o $(BUILD)/boot/cdbootasm.o
|
||||
$(LD) -m elf_i386 -nostdlib $(LD_LOWBASE) -n -N -e start -Ttext 0x7C00 \
|
||||
-o $(BUILD)/boot/cdboottmp.o $(BUILD)/boot/cdbootasm.o
|
||||
$(OBJCOPY) -S -O binary -j .text $(BUILD)/boot/cdboottmp.o $@
|
||||
./tools/sign.pl $@
|
||||
|
||||
$(BUILD)/boot/stage2: boot/stage2.S boot/stage2main.c include/logo.h
|
||||
@mkdir -p $(BUILD)/boot
|
||||
$(CC32) $(STAGE2_DEF) -fno-builtin -fno-pic -nostdinc -I./include -c boot/stage2.S -o $(BUILD)/boot/stage2asm.o
|
||||
$(CC32) $(STAGE2_DEF) -fno-builtin -fno-pic -O -nostdinc -I./include -c boot/stage2main.c -o $(BUILD)/boot/stage2main.o
|
||||
$(LD) -m elf_i386 -nostdlib $(LD_LOWBASE) -n -N -e stage2start -Ttext 0x7E00 \
|
||||
-o $(BUILD)/boot/stage2tmp.o $(BUILD)/boot/stage2asm.o $(BUILD)/boot/stage2main.o
|
||||
$(OBJCOPY) -S -O binary -j .text -j .rodata -j .data $(BUILD)/boot/stage2tmp.o $(BUILD)/boot/stage2
|
||||
./tools/padstage2.pl $(BUILD)/boot/stage2 $(STAGE2_BYTES)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Kernel binary
|
||||
# -----------------------------------------------------------------------
|
||||
$(BUILD)/kernel/kernel: $(KERNOBJS) $(BUILD)/kernel/initcode_obj $(BUILD)/kernel/entryother_obj $(RAMDISK_OBJ) kernel/kernel.ld
|
||||
@mkdir -p $(BUILD)/kernel
|
||||
$(LD) $(LDFLAGS) -T kernel/kernel.ld -o $@ \
|
||||
$(BUILD)/kernel/arch/x86_64/entry.o $(KERN_NOENTRY) \
|
||||
$(BUILD)/kernel/initcode_obj $(BUILD)/kernel/entryother_obj $(RAMDISK_OBJ)
|
||||
$(OBJDUMP) -S $@ > $(BUILD)/kernel/kernel.asm
|
||||
|
||||
# The ramdisk root image, and the object wrapping it for the linker. Built from
|
||||
# the same staged sysroot as the main image, just capped at RAMDISK_BLOCKS --
|
||||
# mkfs's content dedup means the whole userland still fits in a few MB.
|
||||
$(BUILD)/fs-ram.img: $(MKFS) $(BUILD)/sysroot.stamp
|
||||
@mkdir -p $(BUILD)
|
||||
@rm -f $@
|
||||
$(MKFS) $@ --size $(RAMDISK_BLOCKS) --ninodes $(RAMDISK_NINODES) \
|
||||
$(RAMDISK_EXCLUDE_ARGS) --sysroot $(SYSROOT)
|
||||
|
||||
$(BUILD)/kernel/ramdisk_obj: $(BUILD)/fs-ram.img
|
||||
@mkdir -p $(BUILD)/kernel
|
||||
$(LD) -r -b binary -m elf_x86_64 -o $@ $<
|
||||
$(OBJCOPY) \
|
||||
--redefine-sym _binary_build_fs_ram_img_start=_binary_ramdisk_start \
|
||||
--redefine-sym _binary_build_fs_ram_img_end=_binary_ramdisk_end \
|
||||
--redefine-sym _binary_build_fs_ram_img_size=_binary_ramdisk_size \
|
||||
$@
|
||||
|
||||
# AP startup blob
|
||||
$(BUILD)/kernel/entryother: kernel/arch/x86_64/entryother.S
|
||||
@mkdir -p $(BUILD)/kernel
|
||||
$(CC) $(CFLAGS) -fno-pic -nostdinc -I./include -c $< -o $(BUILD)/kernel/entryother.o
|
||||
$(LD) $(LDFLAGS) $(LD_LOWBASE) -e start -Ttext 0x7000 -o $(BUILD)/kernel/entryothertmp.o $(BUILD)/kernel/entryother.o
|
||||
$(OBJCOPY) -S -O binary -j .text $(BUILD)/kernel/entryothertmp.o $@
|
||||
|
||||
# NOTE: ld -r -b binary derives its symbol names from the *path* of the input
|
||||
# file, so these --redefine-sym pairs must be kept in sync with the build
|
||||
# directory layout above. Move the file, and the left-hand names change.
|
||||
$(BUILD)/kernel/entryother_obj: $(BUILD)/kernel/entryother
|
||||
$(LD) -r -b binary -m elf_x86_64 -o $@ $<
|
||||
$(OBJCOPY) \
|
||||
--redefine-sym _binary_build_kernel_entryother_start=_binary_entryother_start \
|
||||
--redefine-sym _binary_build_kernel_entryother_end=_binary_entryother_end \
|
||||
--redefine-sym _binary_build_kernel_entryother_size=_binary_entryother_size \
|
||||
$@
|
||||
|
||||
# Init stub (first user-space code, embedded in the kernel image)
|
||||
$(BUILD)/lib/initcode: lib/initcode.S
|
||||
@mkdir -p $(BUILD)/lib
|
||||
$(CC) $(CFLAGS) -fno-pic -nostdinc -I./include -c $< -o $(BUILD)/lib/initcode.o
|
||||
$(LD) -m elf_x86_64 -nostdlib $(LD_LOWBASE) -N -e start -Ttext 0 -o $(BUILD)/lib/initcodetmp.o $(BUILD)/lib/initcode.o
|
||||
$(OBJCOPY) -S -O binary $(BUILD)/lib/initcodetmp.o $@
|
||||
|
||||
$(BUILD)/kernel/initcode_obj: $(BUILD)/lib/initcode
|
||||
@mkdir -p $(BUILD)/kernel
|
||||
$(LD) -r -b binary -m elf_x86_64 -o $@ $<
|
||||
$(OBJCOPY) \
|
||||
--redefine-sym _binary_build_lib_initcode_start=_binary_initcode_start \
|
||||
--redefine-sym _binary_build_lib_initcode_end=_binary_initcode_end \
|
||||
--redefine-sym _binary_build_lib_initcode_size=_binary_initcode_size \
|
||||
$@
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# vectors.S — generated from perl script
|
||||
# -----------------------------------------------------------------------
|
||||
kernel/arch/x86_64/vectors.S: tools/vectors.pl
|
||||
perl tools/vectors.pl > $@
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Compile rules for kernel/
|
||||
# -----------------------------------------------------------------------
|
||||
$(BUILD)/kernel/arch/x86_64/%.o: kernel/arch/x86_64/%.c
|
||||
@mkdir -p $(BUILD)/kernel/arch/x86_64
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel/arch/x86_64/%.o: kernel/arch/x86_64/%.S
|
||||
@mkdir -p $(BUILD)/kernel/arch/x86_64
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel/core/%.o: kernel/core/%.c
|
||||
@mkdir -p $(BUILD)/kernel/core
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel/dev/%.o: kernel/dev/%.c
|
||||
@mkdir -p $(BUILD)/kernel/dev
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# `$(dir $@)` rather than a fixed path: drivers are grouped into one directory
|
||||
# per device class (gpu/, net/, bus/, usb/), so the output directory varies with
|
||||
# the target and a hardcoded mkdir only creates the top one.
|
||||
$(BUILD)/kernel/drivers/%.o: kernel/drivers/%.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel/fs/%.o: kernel/fs/%.c
|
||||
@mkdir -p $(BUILD)/kernel/fs
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel/fs/schfs/%.o: kernel/fs/schfs/%.c
|
||||
@mkdir -p $(BUILD)/kernel/fs/schfs
|
||||
$(CC) $(CFLAGS) -I./kernel/fs/schfs -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel/net/%.o: kernel/net/%.c
|
||||
@mkdir -p $(BUILD)/kernel/net
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel/proc/%.o: kernel/proc/%.c
|
||||
@mkdir -p $(BUILD)/kernel/proc
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# sys/ gets an extra -I so syscall.c can do #include "table.inc"
|
||||
$(BUILD)/kernel/sys/%.o: kernel/sys/%.c
|
||||
@mkdir -p $(BUILD)/kernel/sys
|
||||
$(CC) $(CFLAGS_SYS) -c $< -o $@
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# First-party userland: lib/ + cmd/
|
||||
# -----------------------------------------------------------------------
|
||||
$(BUILD)/lib/%.o: lib/%.c
|
||||
@mkdir -p $(BUILD)/lib
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/lib/%.o: lib/%.S
|
||||
@mkdir -p $(BUILD)/lib
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/cmd/%: cmd/%/main.c $(ULIB)
|
||||
@mkdir -p $(BUILD)/cmd
|
||||
$(CC) $(CFLAGS) -c $< -o $(BUILD)/cmd/$*.o
|
||||
$(LD) $(LDFLAGS) $(LD_LOWBASE) -e _start -Ttext 0x4000 -o $@ $(BUILD)/cmd/$*.o $(ULIB)
|
||||
$(OBJCOPY) -S $@
|
||||
|
||||
$(SYSROOT)/bin/%: $(BUILD)/cmd/% | $(SYSROOT)/bin
|
||||
cp $< $@
|
||||
chmod 755 $@
|
||||
|
||||
$(SYSROOT)/bin:
|
||||
mkdir -p $@
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Filesystem image
|
||||
# -----------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------
|
||||
# SchFS B-tree torture test (host program).
|
||||
#
|
||||
# The B-tree underpins every record type in SchFS, so it is developed and
|
||||
# tested here -- against a reference model, with simulated crashes -- before
|
||||
# it is linked into the kernel. Run it after any change to
|
||||
# kernel/fs/schfs/btree.c.
|
||||
# -----------------------------------------------------------------------
|
||||
schfs-test: $(SCHFS_TEST)
|
||||
$(SCHFS_TEST)
|
||||
|
||||
# Use ASan/UBSan when the runtime libraries are actually installed; they catch
|
||||
# exactly the pointer-arithmetic mistakes this code is most exposed to, but not
|
||||
# every machine has libasan, so fall back rather than failing the build.
|
||||
SAN := $(shell echo 'int main(void){return 0;}' > /tmp/.santest.c 2>/dev/null && \
|
||||
gcc -fsanitize=address,undefined -o /tmp/.santest /tmp/.santest.c \
|
||||
>/dev/null 2>&1 && echo -fsanitize=address,undefined)
|
||||
|
||||
SCHFS_SRC = kernel/fs/schfs/btree.c kernel/fs/schfs/btree.h \
|
||||
kernel/fs/schfs/space.c kernel/fs/schfs/vol.c \
|
||||
kernel/fs/schfs/obj.c kernel/fs/schfs/objw.c \
|
||||
kernel/fs/schfs/schfs_int.h include/schfs.h
|
||||
|
||||
# -iquote, not -I: include/ has its own errno.h, stat.h, fcntl.h and types.h,
|
||||
# and -I would put them ahead of the system ones for a host program that needs
|
||||
# the real thing. -iquote applies only to "quoted" includes, so btree.h still
|
||||
# finds schfs.h while <errno.h> still means the C library's.
|
||||
HOSTCFLAGS = -Wall -Wextra -O1 -g $(SAN) -iquote ./include
|
||||
|
||||
$(SCHFS_TEST): tools/schfs_test.c $(SCHFS_SRC)
|
||||
@mkdir -p $(BUILD)/tools
|
||||
@mkdir -p $(BUILD)
|
||||
$(HOSTCC) $(HOSTCFLAGS) -o $@ tools/schfs_test.c
|
||||
|
||||
# The object layer -- files, directories, and whether deleting things gives the
|
||||
# space back. Runs against a memory-backed volume, so a full create/write/
|
||||
# delete/remount cycle takes milliseconds instead of a rebuild and a boot.
|
||||
$(SCHFS_OBJT): tools/schfs_objtest.c $(SCHFS_SRC)
|
||||
@mkdir -p $(BUILD)/tools
|
||||
@mkdir -p $(BUILD)
|
||||
$(HOSTCC) $(HOSTCFLAGS) -o $@ tools/schfs_objtest.c
|
||||
|
||||
schfs-objtest: $(SCHFS_OBJT)
|
||||
$(SCHFS_OBJT)
|
||||
|
||||
# The image writer and checker. Both #include the real filesystem sources, so
|
||||
# there is exactly one implementation of the on-disk format: a host tool and the
|
||||
# kernel cannot drift apart because there is nothing to drift from.
|
||||
$(MKSCHFS): tools/mkschfs.c $(SCHFS_SRC)
|
||||
@mkdir -p $(BUILD)/tools
|
||||
@mkdir -p $(BUILD)
|
||||
$(HOSTCC) $(HOSTCFLAGS) -o $@ tools/mkschfs.c
|
||||
|
||||
$(SCHFS_FSCK): tools/schfs_fsck.c $(SCHFS_SRC)
|
||||
@mkdir -p $(BUILD)/tools
|
||||
@mkdir -p $(BUILD)
|
||||
$(HOSTCC) $(HOSTCFLAGS) -o $@ tools/schfs_fsck.c
|
||||
|
||||
mkschfs: $(MKSCHFS)
|
||||
schfs-fsck: $(SCHFS_FSCK)
|
||||
|
||||
# End-to-end filesystem test: build an image from a directory tree, read it
|
||||
# back with schfs_fsck, and diff the result against the original.
|
||||
#
|
||||
# The oracle here is the host's own filesystem, which knows nothing about SchFS.
|
||||
# schfs-test proves the B-tree matches a model written alongside it; this proves
|
||||
# the *filesystem* matches a tree that exists independently of it, so a writer
|
||||
# and reader that agree on something wrong are still caught.
|
||||
schfs-image-test: $(MKSCHFS) $(SCHFS_FSCK)
|
||||
@sh tools/schfs_roundtrip.sh $(MKSCHFS) $(SCHFS_FSCK)
|
||||
|
||||
# Every SchFS suite. Run this after touching anything under kernel/fs/schfs.
|
||||
schfs-check: schfs-test schfs-objtest schfs-image-test
|
||||
|
||||
# Crash the kernel on purpose and photograph the result.
|
||||
#
|
||||
# The crash screen is the one part of the system you cannot look at by using
|
||||
# the system, which is exactly why it rots. This makes "does it still render"
|
||||
# a question with a thirty-second answer. Needs a ramroot build first.
|
||||
crashshot:
|
||||
pwsh -File tools/crashshot.ps1
|
||||
|
||||
# Read the QR code back off a captured crash screen. End-to-end on purpose: it
|
||||
# reads the pixels that were painted, so payload construction in panic context,
|
||||
# the encoder, the module scaling and the quiet zone are all covered by whether
|
||||
# text comes back. Run `make crashshot` first. Needs the same Python packages
|
||||
# as `qrcheck`.
|
||||
crashqr:
|
||||
$(PYTHON) tools/crashqr.py
|
||||
|
||||
$(MKFS): tools/mkfs.c include/fs.h include/stat.h include/param.h
|
||||
@mkdir -p $(BUILD)/tools
|
||||
$(HOSTCC) $(HOSTWARN) -I. -o $(MKFS) tools/mkfs.c
|
||||
|
||||
# Stage everything a running system needs into sysroot/, then image it.
|
||||
#
|
||||
# ports/ is built via its own Makefile rather than per-port from here, because
|
||||
# that is where the inter-port ordering lives (mksh and toybox both link
|
||||
# against musl, whose install prefix is under build/ — so `make clean` wipes it
|
||||
# and musl must be rebuildable as part of the normal chain, not out-of-band).
|
||||
#
|
||||
# On Windows the ports are NOT rebuilt. musl, mksh and toybox are Linux
|
||||
# userland built by configure scripts and kconfig, and getting those to run
|
||||
# under Git Bash against a clang cross-target is a separate project from
|
||||
# building the OS. Their outputs are already staged in sysroot/ and change
|
||||
# perhaps twice a year, so the cost of not rebuilding them here is close to
|
||||
# zero -- and it is stated out loud rather than silently skipped, because a
|
||||
# port that quietly does not rebuild is exactly the kind of thing that wastes
|
||||
# an afternoon later.
|
||||
sysroot-install: $(addprefix $(SYSROOT)/bin/, $(CMDS))
|
||||
ifdef WINHOST
|
||||
@echo "ports: skipped (Windows host) -- musl/mksh/toybox are used as already staged in $(SYSROOT)"
|
||||
@test -f $(SYSROOT)/bin/sh || { echo "ports: $(SYSROOT)/bin/sh is missing and cannot be built here; build ports on a Linux host once" >&2; exit 1; }
|
||||
else
|
||||
$(MAKE) -C ports CC=gcc
|
||||
endif
|
||||
|
||||
# A stamp whose timestamp advances only when the staged tree actually changes.
|
||||
#
|
||||
# make cannot depend on "the contents of a directory", and sysroot/ is filled
|
||||
# by two different builds (cmd/ from here, ports/ recursively), so the images
|
||||
# below used to depend on the phony sysroot-install -- which meant every image
|
||||
# was rebuilt on every invocation, whether or not a single byte had moved.
|
||||
# That cost 100 seconds of a 108-second no-op build.
|
||||
#
|
||||
# Path, size and mtime rather than content hashes: reading every staged file
|
||||
# would undo the point, and a build system that lies about a file whose mtime
|
||||
# and size both stayed the same has bigger problems. The cmp-then-move keeps
|
||||
# the stamp's mtime unchanged when the summary is unchanged, which is the
|
||||
# whole trick -- writing it unconditionally would just move the problem.
|
||||
$(BUILD)/sysroot.stamp: sysroot-install
|
||||
@mkdir -p $(BUILD)
|
||||
@find $(SYSROOT) -type f -printf '%p %s %T@\n' | sort > $@.new
|
||||
@cmp -s $@.new $@ || mv -f $@.new $@
|
||||
@rm -f $@.new
|
||||
|
||||
$(BUILD)/fs.img: $(MKFS) $(BUILD)/sysroot.stamp
|
||||
@mkdir -p $(BUILD)
|
||||
@echo "Creating Schunix File System Image..."
|
||||
@# A running QEMU holds this file open, and on Windows that makes rm fail
|
||||
@# with "Device or resource busy" -- which stops the build *after* the
|
||||
@# kernel has already relinked, leaving schunix.img stale. The next test run
|
||||
@# then boots the previous kernel and the change appears to have done
|
||||
@# nothing, which has cost several debugging cycles. Say so plainly.
|
||||
@rm -f $@ || { \
|
||||
echo ""; \
|
||||
echo " $@ is in use -- a QEMU is still holding it."; \
|
||||
echo " Close it, or: pwsh -Command \"Get-Process qemu-system-x86_64 |"; \
|
||||
echo " Where-Object { -not \$$_.MainWindowTitle } | Stop-Process -Force\""; \
|
||||
echo ""; \
|
||||
exit 1; \
|
||||
}
|
||||
$(MKFS) $@ --sysroot $(SYSROOT)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# QEMU
|
||||
# -----------------------------------------------------------------------
|
||||
qemu: schunix.img $(BUILD)/schfs.img
|
||||
$(QEMU) -serial mon:stdio $(QEMU_DISPLAY) -cpu qemu64,+rdtscp \
|
||||
-hda schunix.img -hdb $(BUILD)/schfs.img \
|
||||
-m 1G -smp 2 -machine pc-i440fx-9.2
|
||||
|
||||
# The root filesystem. Built from the staged sysroot; the kernel mounts it as
|
||||
# / in forkret().
|
||||
$(BUILD)/schfs.img: $(MKSCHFS) $(BUILD)/sysroot.stamp
|
||||
@mkdir -p $(BUILD)
|
||||
@rm -f $@
|
||||
./$(MKSCHFS) $@ --size 8000 --label schunix --sysroot $(SYSROOT)
|
||||
|
||||
# Boot with a SchFS volume attached as the second disk.
|
||||
#
|
||||
# Needs the ramdisk-root kernel ("make ramroot" first): with RAMROOT the root
|
||||
# filesystem comes out of memory and device 1 is free, which is exactly the
|
||||
# arrangement docs/architecture/schfs.md asks for -- a new-format disk that
|
||||
# fails to mount cannot stop the system booting. Without RAMROOT, device 1 is
|
||||
# the live v1 root and the SchFS probe correctly finds nothing there.
|
||||
#
|
||||
# Once up: cat /proc/schfs
|
||||
qemu-schfs: schunix.img $(BUILD)/schfs.img
|
||||
$(QEMU) -serial mon:stdio $(QEMU_DISPLAY) -cpu qemu64,+rdtscp \
|
||||
-hda schunix.img -hdb $(BUILD)/schfs.img \
|
||||
-m 1G -smp 2 -machine pc-i440fx-9.2
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Misc
|
||||
# -----------------------------------------------------------------------
|
||||
# schunix.img is deliberately NOT here. It is a real file with real
|
||||
# prerequisites; listing it as phony made make skip the timestamp check and
|
||||
# re-run the four dd commands -- including zeroing five megabytes -- on every
|
||||
# single invocation, including ones where nothing had changed.
|
||||
# include/logo.h is generated but committed, exactly as include/font.h is: it
|
||||
# derives from art that changes about never, and making it a build dependency
|
||||
# would put Python on the critical path of every clean build for no benefit.
|
||||
# Run this by hand after changing the art.
|
||||
LOGO_SRC = Assets/Schunix Small Logo.png
|
||||
LOGO_PX = 128
|
||||
|
||||
logo:
|
||||
$(PYTHON) tools/mklogo.py "$(LOGO_SRC)" include/logo.h $(LOGO_PX)
|
||||
|
||||
# The USB vendor/product name database, from linux-usb.org.
|
||||
#
|
||||
# Dual-licensed GPL-2.0-or-later **or** 3-clause BSD; Schunix takes the BSD
|
||||
# option, which is what makes it usable at all. See NOTICE.
|
||||
#
|
||||
# Cached in dist/ and refreshed only when you ask, NOT on every build. That was
|
||||
# the request, and it is the one thing here worth pushing back on: fetching on
|
||||
# every compilation makes an offline build fail, makes the same source produce
|
||||
# different images on different days, and puts a third-party web server on the
|
||||
# critical path of `make`. dist/ exists precisely so a rebuild never needs the
|
||||
# network -- see docs/layout.md. `make usb-ids` is a deliberate act; the file it
|
||||
# writes is committed and dated, so what shipped is always knowable.
|
||||
USB_IDS_URL = http://www.linux-usb.org/usb.ids
|
||||
|
||||
usb-ids:
|
||||
@echo "==> fetching $(USB_IDS_URL)"
|
||||
@curl -sfL --max-time 60 -o dist/usb.ids.new "$(USB_IDS_URL)" || \
|
||||
{ echo " fetch failed; keeping the cached copy"; rm -f dist/usb.ids.new; exit 1; }
|
||||
@# A truncated or error-page download would otherwise silently replace a
|
||||
@# good database with rubbish. Check it looks like what it claims to be.
|
||||
@grep -q "^# Version:" dist/usb.ids.new || \
|
||||
{ echo " that is not a usb.ids file; keeping the cached copy"; \
|
||||
rm -f dist/usb.ids.new; exit 1; }
|
||||
@mv dist/usb.ids.new dist/usb.ids
|
||||
@cp dist/usb.ids $(SYSROOT)/usr/share/usb.ids
|
||||
@grep -m1 "^# Version:" dist/usb.ids
|
||||
|
||||
# Build the kernel's QR encoder on the host and check it.
|
||||
#
|
||||
# A QR symbol with a mistyped table entry still looks like a perfectly plausible
|
||||
# grid of black squares, so the self-checks in qrtest.c cover what can be
|
||||
# verified mechanically and the PNG covers the rest: point a phone at it.
|
||||
QRTEST = $(BUILD)/tools/qrtest$(EXE)
|
||||
|
||||
$(QRTEST): tools/qrtest.c kernel/core/qr.c include/qr.h
|
||||
@mkdir -p $(BUILD)/tools
|
||||
$(HOSTCC) $(HOSTWARN) -I./include -o $@ tools/qrtest.c
|
||||
|
||||
qrtest: $(QRTEST)
|
||||
@mkdir -p $(BUILD)
|
||||
./$(QRTEST) $(BUILD)/qr.png
|
||||
|
||||
# The check that actually decides whether the encoder works: build a symbol and
|
||||
# read it back with a real decoder, plus a module-for-module comparison against
|
||||
# a second encoder. Needs Python packages that are not part of the build, so it
|
||||
# is not wired into `make` -- install them wherever you like and point
|
||||
# PYTHONPATH at it:
|
||||
#
|
||||
# pip install --target build/pylibs opencv-python-headless numpy segno
|
||||
# PYTHONPATH=build/pylibs make qrcheck
|
||||
qrcheck: $(QRTEST)
|
||||
$(PYTHON) tools/qrcheck.py
|
||||
|
||||
.PHONY: all kernel ramroot cmd ports sysroot-install qemu \
|
||||
schfs-test schfs-objtest schfs-image-test schfs-check mkschfs \
|
||||
schfs-fsck crashshot qemu-schfs iso qemu-iso clean distclean \
|
||||
logo qrtest qrcheck crashqr usb-ids
|
||||
.PRECIOUS: $(BUILD)/%.o
|
||||
|
||||
# 'clean' removes generated output only. dist/ (upstream tarballs) survives,
|
||||
# so a rebuild never needs the network.
|
||||
clean:
|
||||
rm -rf $(BUILD) schunix.img schunix.iso kernel/arch/x86_64/vectors.S
|
||||
|
||||
distclean: clean
|
||||
rm -rf dist
|
||||
|
||||
# Header dependencies, from gcc -MD.
|
||||
#
|
||||
# This used to be a set of globs including `$(BUILD)/kernel/**/*.d`. make's
|
||||
# wildcard has no recursive `**`: it behaved as a single `*` and matched
|
||||
# exactly one level, so build/kernel/core/*.d was picked up while
|
||||
# build/kernel/arch/x86_64/*.d and build/kernel/fs/schfs/*.d were not.
|
||||
# Everything under arch/ and schfs/ therefore had NO header dependency
|
||||
# tracking at all -- editing include/schfs.h or include/mmu.h left stale
|
||||
# objects that linked without complaint. find actually recurses.
|
||||
-include $(shell find $(BUILD) -name '*.d' 2>/dev/null)
|
||||
Loading…
Reference in a new issue