Files
SUB-OS/Makefile
T
subhobhai943andClaude Opus 4.8 dd4922767a feat(net,gui,sched): async HTTP client + Web browser app, and the scheduler fix that makes background work possible
The visible goal was a Web app: type a URL, see the page, fetched over the
TCP stack SUB-OS built. Getting there surfaced -- and forced fixes to --
two deeper problems.

Async HTTP client (net/http_client.c)
  tcp_connect/recv all block, so fetching from the GUI would freeze the
  compositor. Fetches now run on one long-lived kernel worker thread that
  services a single-slot mailbox; the requester posts a job and polls its
  state. Ownership follows the state, and the two hand-off points (worker
  completion, requester release) run under interrupts-off, so a window can
  close mid-fetch with neither leak nor use-after-free. do_fetch returns
  its terminal state rather than publishing it, closing a window where the
  requester could free the job before finalize read it.

Cooperative scheduling for the idle thread (the core fix)
  The login prompt, the shell and the GUI compositor all run on the pid-0
  idle thread, which the scheduler by design never preempts (sched_tick
  skips pid 0) and never requeues on a yield (weave_on clear). So it could
  neither be preempted to let the worker run, nor cleanly yield -- the
  worker was simply starved, and an early attempt to yield from it froze
  the shell outright. The idle thread now joins the weave rotation once
  hardware interrupts are live, and the network wait loops (tcp, dns, ping,
  the worker's idle) yield through a new net_wait() instead of halting the
  CPU. The compositor spends its frame-budget slack yielding too. Result:
  a background fetch progresses while the desktop holds 54 FPS.

Web app (gui/gui_app_web.c)
  A text browser: URL bar, Go, and a Raw/Reader toggle. Raw shows the
  response verbatim; Reader strips markup (dropping <script>/<style>
  bodies), decodes the common entities, and word-wraps the body. The line
  index is cached against content/view/width so scrolling and idle
  repaints are free. It only ever polls the worker, so it never stalls.

TCP throughput (the limit flagged last time)
  The 32-slot table with a 2 s TIME_WAIT linger capped sustained serving
  at ~16 req/s. The table is now 64 slots, a connection this end closed
  after serving a request lingers 250 ms rather than 2 s, and expired
  TIME_WAIT entries are reaped from the count path so a quiet table still
  drains. netstat/netmon gained served-since-boot and peak-slots counters.

Also: a `webget` CLI (the async client from a shell), listen/accept
introspection already present, and a globe icon for the app.

Verified on x86_64: `webget` fetched an 812-byte page (HTTP 200, 10 ms) on
the worker thread; the GUI Web app rendered the same page in Reader mode
while the desktop held 54 FPS; an 80-request burst through the 64-slot
table returned 80/80 (was 39/40 before) and netstat reported "80 served,
peak 33 of 64 slots" -- a peak that would have overflowed the old table.
Builds clean on x86_64, aarch64 and armv8i.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
2026-08-27 02:21:48 +05:30

542 lines
21 KiB
Makefile

# =============================================================================
# SUB-OS Master Multi-Architecture Linux-Style Makefile
# Supports: x86_64, aarch64, armv8i
# Features: Linux Kconfig TUI (make configure / make menuconfig), dynamic Kbuild
# =============================================================================
-include .config
# Architecture Selection (Default: x86_64)
ifdef CONFIG_ARCH
ARCH ?= $(CONFIG_ARCH)
else
ARCH ?= x86_64
endif
BUILD_DIR = build
BOOT_DIR = boot
# -----------------------------------------------------------------------------
# Toolchain & Architecture Flags
# -----------------------------------------------------------------------------
ifeq ($(ARCH), x86_64)
CROSS_COMPILE ?= x86_64-elf-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
ASM = nasm
CFLAGS = -ffreestanding \
-fno-pie \
-fno-pic \
-mno-red-zone \
-mno-mmx \
-mno-sse \
-mno-sse2 \
-mcmodel=kernel \
-Wall \
-Wextra \
-Wno-unused-function \
-Wno-unused-parameter \
$(if $(CONFIG_OPTIMIZATION),$(CONFIG_OPTIMIZATION),-O2) \
-Iinclude \
-D__x86_64__
ASMFLAGS_ELF = -f elf64
ASMFLAGS_BOOT = -f bin -I$(BOOT_DIR)/
LDFLAGS = -nostdlib -no-pie -T linker.ld
ARCH_C_SRCS = $(shell find arch/x86_64 -name '*.c' 2>/dev/null)
ARCH_ASM_SRCS = $(shell find arch/x86_64 -name '*.asm' 2>/dev/null)
ENTRY_OBJ = $(BUILD_DIR)/arch/x86_64/boot/entry.o
IMAGE = SUB-OS.img
TARGET = $(IMAGE)
QEMU_CMD = qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
-device e1000,netdev=net0 -serial stdio -display none
else ifneq ($(filter $(ARCH), aarch64 arm64),)
CROSS_COMPILE ?= aarch64-linux-gnu-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
AS = $(CROSS_COMPILE)as
CFLAGS = -ffreestanding \
-fno-pie \
-fno-pic \
-Wall \
-Wextra \
-Wno-unused-function \
-Wno-unused-parameter \
-mno-outline-atomics \
$(if $(CONFIG_OPTIMIZATION),$(CONFIG_OPTIMIZATION),-O2) \
-Iinclude \
-D__aarch64__ \
-march=armv8-a
LDFLAGS = -nostdlib -no-pie -T arch/aarch64/linker.ld
ARCH_C_SRCS = $(shell find arch/aarch64 -name '*.c' 2>/dev/null)
ARCH_S_SRCS = $(shell find arch/aarch64 -name '*.S' 2>/dev/null)
ENTRY_OBJ = $(BUILD_DIR)/arch/aarch64/boot/entry.o
TARGET = $(BUILD_DIR)/kernel.elf
QEMU_CMD = qemu-system-aarch64 -M virt -cpu cortex-a57 -m 128M -nographic -kernel $(BUILD_DIR)/kernel.elf
else ifneq ($(filter $(ARCH), armv8i armv81 arm32 arm armv7 armv7-a armv7a aarch32),)
CROSS_COMPILE ?= arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
AS = $(CROSS_COMPILE)as
CFLAGS = -ffreestanding \
-fno-pie \
-fno-pic \
-Wall \
-Wextra \
-Wno-unused-function \
-Wno-unused-parameter \
$(if $(CONFIG_OPTIMIZATION),$(CONFIG_OPTIMIZATION),-O2) \
-Iinclude \
-D__arm__ \
-D__armv8i__ \
-mcpu=cortex-a15 \
-mfpu=neon-vfpv4 \
-mfloat-abi=hard \
-marm
LDFLAGS = -nostdlib -no-pie -T arch/armv8i/linker.ld
ARCH_C_SRCS = $(shell find arch/armv8i -name '*.c' 2>/dev/null)
ARCH_S_SRCS = $(shell find arch/armv8i -name '*.S' 2>/dev/null)
ENTRY_OBJ = $(BUILD_DIR)/arch/armv8i/boot/entry.o
TARGET = $(BUILD_DIR)/kernel.elf
QEMU_CMD = qemu-system-arm -M virt -cpu cortex-a15 -m 128M -nographic -kernel $(BUILD_DIR)/kernel.elf
else
$(error Unsupported Architecture '$(ARCH)'. Supported: x86_64, aarch64, armv8i)
endif
# -----------------------------------------------------------------------------
# Linux-Style Configurable Kernel Subsystems (Kbuild-Style)
# -----------------------------------------------------------------------------
CORE_SRCS = kernel/main.c kernel/task.c kernel/sync.c kernel/timer.c kernel/printk.c \
kernel/panic.c kernel/trace.c kernel/sched.c kernel/cron.c kernel/syscall.c \
kernel/syslog.c kernel/module.c kernel/kobject.c kernel/metrics.c kernel/namespace.c \
kernel/signal.c kernel/workqueue.c kernel/tsc.c kernel/vt_art.c \
kernel/sub/sub_runtime.c kernel/sub/sub_vm.c kernel/nt/ob.c kernel/nt/reg.c kernel/nt/io.c \
kernel/wait.c kernel/futex.c kernel/rcu.c kernel/ktest.c kernel/time/ktime.c \
kernel/elf.c kernel/exec.c \
lib/string.c lib/vsprintf.c lib/bitmap.c lib/rbtree.c lib/kfifo.c lib/hashtable.c lib/font8x8.c \
init/cmdline.c init/service.c init/bootlogo.c init/logo_data.c usr/initramfs.c block/block.c block/elevator.c \
ipc/sem.c ipc/shm.c ipc/shm_posix.c ipc/pipe.c ipc/ipc.c ipc/msg.c \
crypto/sha256.c crypto/md5.c crypto/crc32.c crypto/prng.c sound/beep.c
# If no .config exists, default all core options to y
ifeq ($(wildcard .config),)
CONFIG_MM_PMM = y
CONFIG_MM_SLAB = y
CONFIG_MM_VMA = y
CONFIG_FS_VFS = y
CONFIG_FS_FAT32 = y
CONFIG_FS_EXT2 = y
CONFIG_FS_SYSFS = y
CONFIG_DRV_RAMDISK = y
CONFIG_DRV_AHCI = y
CONFIG_DRV_NVME = y
CONFIG_DRV_ATA = y
CONFIG_DRV_VIRTIO_BLK = y
CONFIG_NET_STACK = y
CONFIG_NET_FILTER = y
CONFIG_NET_HTTPD = y
CONFIG_NET_SSHD = y
CONFIG_DRV_E1000 = y
CONFIG_DRV_RTL8139 = y
CONFIG_DRV_VIRTIO_NET = y
CONFIG_DRV_PTY = y
CONFIG_DRV_VIRTIO_RNG = y
CONFIG_DRV_CANVAS_2D = y
CONFIG_DRV_BOCHS_VBE = y
CONFIG_DRV_SOUND_HDA = y
CONFIG_DRV_SOUND_AC97 = y
CONFIG_DRV_USB_XHCI = y
CONFIG_DRV_HWMON = y
CONFIG_SECURITY_LSM = y
CONFIG_SECURITY_KEYRING = y
CONFIG_BPF_VM = y
CONFIG_IO_URING = y
CONFIG_USERLAND_LAZYBOX = y
endif
CONFIG_SRCS-y := $(CORE_SRCS)
ifeq ($(CONFIG_MM_PMM), y)
CONFIG_SRCS-y += mm/pmm.c mm/kmalloc.c mm/page_cache.c
endif
ifeq ($(CONFIG_MM_SLAB), y)
CONFIG_SRCS-y += mm/slab.c
endif
ifeq ($(CONFIG_MM_VMA), y)
CONFIG_SRCS-y += mm/vma.c
endif
ifeq ($(CONFIG_BPF_VM), y)
CONFIG_SRCS-y += kernel/bpf.c
endif
ifeq ($(CONFIG_IO_URING), y)
CONFIG_SRCS-y += io_uring/io_uring.c
endif
ifeq ($(CONFIG_SECURITY_LSM), y)
CONFIG_SRCS-y += security/lsm.c security/auth.c
endif
ifeq ($(CONFIG_SECURITY_KEYRING), y)
CONFIG_SRCS-y += certs/x509.c
endif
# Filesystems
ifeq ($(CONFIG_FS_VFS), y)
CONFIG_SRCS-y += fs/vfs.c fs/ramfs.c fs/devfs.c fs/procfs.c
endif
ifeq ($(CONFIG_FS_FAT32), y)
CONFIG_SRCS-y += fs/fat32.c
endif
ifeq ($(CONFIG_FS_EXT2), y)
CONFIG_SRCS-y += fs/ext2.c
endif
ifeq ($(CONFIG_FS_SYSFS), y)
CONFIG_SRCS-y += fs/sysfs.c
endif
# Storage Drivers
ifeq ($(CONFIG_DRV_RAMDISK), y)
CONFIG_SRCS-y += drivers/block/ramdisk.c
endif
ifeq ($(CONFIG_DRV_AHCI), y)
CONFIG_SRCS-y += drivers/block/ahci.c
endif
ifeq ($(CONFIG_DRV_NVME), y)
CONFIG_SRCS-y += drivers/block/nvme.c
endif
ifeq ($(CONFIG_DRV_ATA), y)
CONFIG_SRCS-y += drivers/block/ata.c
endif
ifeq ($(CONFIG_DRV_VIRTIO_BLK), y)
CONFIG_SRCS-y += drivers/virtio/virtio_blk.c virt/virtio.c virt/hypervisor.c
endif
# Network Subsystem
ifeq ($(CONFIG_NET_STACK), y)
CONFIG_SRCS-y += net/core/net.c net/core/socket.c net/ipv4/tcp.c net/ipv4/udp.c net/ipv4/dhcp.c net/ipv4/dns.c net/ipv4/dns_cache.c net/http_client.c
endif
ifeq ($(CONFIG_NET_FILTER), y)
CONFIG_SRCS-y += net/filter.c
endif
ifeq ($(CONFIG_NET_HTTPD), y)
CONFIG_SRCS-y += net/httpd.c
endif
ifeq ($(CONFIG_NET_SSHD), y)
CONFIG_SRCS-y += net/ssh.c
endif
ifeq ($(CONFIG_DRV_E1000), y)
CONFIG_SRCS-y += drivers/net/e1000.c drivers/pci/pci.c
endif
ifeq ($(CONFIG_DRV_RTL8139), y)
CONFIG_SRCS-y += drivers/net/rtl8139.c drivers/pci/pci.c
endif
ifeq ($(CONFIG_DRV_VIRTIO_NET), y)
CONFIG_SRCS-y += drivers/virtio/virtio_net.c virt/virtio.c
endif
# Character, Audio, Video Drivers
ifeq ($(CONFIG_DRV_PTY), y)
CONFIG_SRCS-y += drivers/char/pty.c
endif
ifeq ($(CONFIG_DRV_VIRTIO_RNG), y)
CONFIG_SRCS-y += drivers/char/virtio_rng.c
endif
ifeq ($(CONFIG_DRV_USB_XHCI), y)
CONFIG_SRCS-y += drivers/usb/xhci.c drivers/usb/usb.c
endif
ifeq ($(CONFIG_DRV_HWMON), y)
CONFIG_SRCS-y += drivers/hwmon/coretemp.c
endif
ifeq ($(CONFIG_DRV_CANVAS_2D), y)
CONFIG_SRCS-y += drivers/video/canvas.c
endif
ifeq ($(CONFIG_DRV_BOCHS_VBE), y)
CONFIG_SRCS-y += drivers/video/bochs.c drivers/video/fb.c drivers/video/fbcon.c
endif
ifeq ($(CONFIG_DRV_SOUND_HDA), y)
CONFIG_SRCS-y += sound/hda.c sound/sound.c sound/pcm.c sound/tts.c sound/melody.c
endif
ifeq ($(CONFIG_DRV_SOUND_AC97), y)
CONFIG_SRCS-y += sound/ac97.c sound/sound.c sound/pcm.c
endif
# Common Input/Display/Power/GPU/Networking Drivers
CONFIG_SRCS-y += drivers/char/tty.c drivers/char/keyboard.c drivers/char/serial.c drivers/char/vga.c \
drivers/input/mouse.c drivers/power/acpi.c drivers/power/cpufreq.c drivers/rtc/rtc.c drivers/sound/speaker.c \
drivers/net/e1000e.c drivers/virtio/virtio_gpu.c drivers/virtio/virtio_input.c \
drivers/timer/hpet.c drivers/char/lpt.c drivers/char/nvram.c
# Userland & GUI Desktop Subsystem
ifeq ($(CONFIG_USERLAND_LAZYBOX), y)
CONFIG_SRCS-y += userland/lazybox/lazybox.c userland/lazybox/shell.c userland/lazybox/sh.c userland/lazybox/nano.c userland/lazybox/snake.c userland/lazybox/tree.c userland/lazybox/coreutils.c
endif
CONFIG_SRCS-y += gui/gui.c gui/gui_gfx.c gui/gui_cursor.c gui/gui_wm.c gui/gui_desktop.c gui/gui_apps.c \
gui/gui_widgets.c gui/gui_icons.c gui/gui_dialog.c gui/gui_apps_ext.c gui/gui_terminal.c \
gui/gui_app_sorry.c gui/gui_app_netmon.c gui/gui_app_web.c
# Deduplicate
CONFIG_SRCS-y := $(sort $(CONFIG_SRCS-y))
# Rust & C++ Layer Configuration
ifeq ($(ARCH), x86_64)
RUSTC ?= rustc
RUST_SRCS = $(shell find rust/src -name '*.rs' 2>/dev/null)
RUST_LIB = $(BUILD_DIR)/rust/librust_kernel.a
CXX ?= g++
CXXFLAGS = -ffreestanding -fno-pie -fno-pic -mno-red-zone -mno-mmx -mno-sse -mno-sse2 \
-mcmodel=kernel -Wall -Wextra -Wno-unused-function -Wno-unused-parameter \
$(if $(CONFIG_OPTIMIZATION),$(CONFIG_OPTIMIZATION),-O2) -Iinclude -D__x86_64__ \
-std=c++17 -fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-use-cxa-atexit
CPP_SRCS = $(shell find kernel/cpp -name '*.cpp' 2>/dev/null)
CPP_OBJS = $(patsubst %.cpp, $(BUILD_DIR)/%.o, $(CPP_SRCS))
ALL_C_SRCS = $(ARCH_C_SRCS) $(CONFIG_SRCS-y)
C_OBJS = $(patsubst %.c, $(BUILD_DIR)/%.o, $(ALL_C_SRCS))
ASM_OBJS = $(patsubst %.asm, $(BUILD_DIR)/%.o, $(ARCH_ASM_SRCS))
# Userland /sbin/init, linked separately and carried inside the kernel image.
INIT_SRC = userland/init/init.c
INIT_LD = userland/init/user.ld
INIT_OBJ = $(BUILD_DIR)/userland/init/init.o
INIT_ELF = $(BUILD_DIR)/userland/init/init.elf
INIT_BLOB_SRC = $(BUILD_DIR)/usr/init_blob.c
INIT_BLOB_OBJ = $(BUILD_DIR)/usr/init_blob.o
OTHER_OBJS = $(filter-out $(ENTRY_OBJ), $(ASM_OBJS) $(C_OBJS)) $(CPP_OBJS) $(INIT_BLOB_OBJ) $(RUST_LIB)
else
CONFIG_SRCS-y += rust/rust_fallback.c kernel/cpp/cpp_fallback.c
ALL_C_SRCS = $(ARCH_C_SRCS) $(CONFIG_SRCS-y)
C_OBJS = $(patsubst %.c, $(BUILD_DIR)/%.o, $(ALL_C_SRCS))
S_OBJS = $(patsubst %.S, $(BUILD_DIR)/%.o, $(ARCH_S_SRCS))
OTHER_OBJS = $(filter-out $(ENTRY_OBJ), $(S_OBJS) $(C_OBJS))
endif
# Generated dependency files, one per object. The include lives at the very
# end of this file: a .d file's first rule would otherwise become make's
# default goal and turn a bare `make` into a no-op.
DEPS = $(C_OBJS:.o=.d) $(CPP_OBJS:.o=.d) $(S_OBJS:.o=.d)
.DEFAULT_GOAL := all
.PHONY: all clean run run-headless run-fullscreen run-vnc run-server debug info configure menuconfig config tui nconfig defconfig x86_64_defconfig aarch64_defconfig armv8_defconfig armv8i_defconfig armv81_defconfig arm32_defconfig qemu help
all: $(TARGET)
# -----------------------------------------------------------------------------
# Configuration Targets (Linux Kconfig TUI Configurator)
# -----------------------------------------------------------------------------
configure menuconfig config tui nconfig:
@python3 scripts/kconfig/menuconfig.py
defconfig:
@python3 scripts/kconfig/menuconfig.py --defconfig $(ARCH)
x86_64_defconfig:
@python3 scripts/kconfig/menuconfig.py --x86_64
aarch64_defconfig:
@python3 scripts/kconfig/menuconfig.py --aarch64
armv8_defconfig armv8i_defconfig armv81_defconfig arm32_defconfig:
@python3 scripts/kconfig/menuconfig.py --armv8i
# -----------------------------------------------------------------------------
# Compilation Rules
# -----------------------------------------------------------------------------
# Header dependency tracking: without this a header change leaves stale objects
# behind, and a struct layout edit silently corrupts every unrebuilt caller.
DEPFLAGS = -MMD -MP
# C Source Files
$(BUILD_DIR)/%.o: %.c include/config/autoconf.h
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
# C++ Source Files
$(BUILD_DIR)/%.o: %.cpp include/config/autoconf.h
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) $(DEPFLAGS) -c $< -o $@
# x86_64 NASM Assembly Files
$(BUILD_DIR)/%.o: %.asm
@mkdir -p $(dir $@)
$(ASM) $(ASMFLAGS_ELF) $< -o $@
# ARM/AArch64 GNU Assembly Files (.S)
$(BUILD_DIR)/%.o: %.S
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
# Userland Program Compilation
#
# /sbin/init is a separate freestanding executable, not kernel code: it is
# linked at the user base from userland/user.ld and must never see the kernel's
# -mcmodel=kernel, which cannot reach the user half of the address space.
USER_CFLAGS = -ffreestanding -nostdlib -fno-pie -fno-pic -mno-red-zone \
-mcmodel=large -mno-sse -mno-mmx -mno-sse2 \
-Wall -Wextra -Wno-unused-function \
$(if $(CONFIG_OPTIMIZATION),$(CONFIG_OPTIMIZATION),-O2)
$(INIT_OBJ): $(INIT_SRC)
@mkdir -p $(dir $@)
$(CC) $(USER_CFLAGS) -c $< -o $@
$(INIT_ELF): $(INIT_OBJ) $(INIT_LD)
@mkdir -p $(dir $@)
$(LD) -nostdlib -no-pie -T $(INIT_LD) -o $@ $(INIT_OBJ)
@echo "=== [Userland init linked: $@] ==="
$(INIT_BLOB_SRC): $(INIT_ELF) scripts/mkblob.py
@mkdir -p $(dir $@)
@python3 scripts/mkblob.py $(INIT_ELF) $@ subos_init_elf
$(INIT_BLOB_OBJ): $(INIT_BLOB_SRC) include/config/autoconf.h
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# Rust Source Compilation
$(RUST_LIB): rust/src/lib.rs $(RUST_SRCS)
@mkdir -p $(dir $@)
$(RUSTC) --crate-type staticlib -C panic=abort -C relocation-model=static -C opt-level=2 -C no-redzone=y -o $@ rust/src/lib.rs
# Header Generation Fallback
include/config/autoconf.h:
@mkdir -p include/config
@if [ ! -f .config ]; then python3 scripts/kconfig/menuconfig.py --defconfig $(ARCH); fi
# -----------------------------------------------------------------------------
# Link & Image Assembly
# -----------------------------------------------------------------------------
$(BUILD_DIR)/boot/boot.bin: $(BOOT_DIR)/boot.asm | $(BUILD_DIR)/boot
$(ASM) $(ASMFLAGS_BOOT) $< -o $@
$(BUILD_DIR)/boot/stage2.bin: $(BOOT_DIR)/stage2.asm $(BOOT_DIR)/gdt.asm | $(BUILD_DIR)/boot
$(ASM) $(ASMFLAGS_BOOT) $< -o $@
$(BUILD_DIR)/boot:
mkdir -p $(BUILD_DIR)/boot
LIBGCC ?= $(shell $(CC) $(CFLAGS) -print-libgcc-file-name 2>/dev/null)
$(BUILD_DIR)/kernel.elf: $(ENTRY_OBJ) $(OTHER_OBJS)
$(LD) $(LDFLAGS) -o $@ $(ENTRY_OBJ) $(OTHER_OBJS) $(LIBGCC)
@echo "=== [Kernel ELF Linked: $@ (Architecture: $(ARCH))] ==="
$(BUILD_DIR)/kernel.bin: $(BUILD_DIR)/kernel.elf
$(OBJCOPY) -O binary $< $@
$(IMAGE): $(BUILD_DIR)/boot/boot.bin $(BUILD_DIR)/boot/stage2.bin $(BUILD_DIR)/kernel.bin
dd if=/dev/zero of=$@ bs=512 count=2880 status=none
dd if=$(BUILD_DIR)/boot/boot.bin of=$@ bs=512 conv=notrunc status=none
dd if=$(BUILD_DIR)/boot/stage2.bin of=$@ bs=512 seek=1 conv=notrunc status=none
dd if=$(BUILD_DIR)/kernel.bin of=$@ bs=512 seek=16 conv=notrunc status=none
@echo "=== [SUB-OS x86_64 Disk Image Built: $@ (Size: $$(du -h $@ | cut -f1))] ==="
# -----------------------------------------------------------------------------
# Execution & Emulation Targets
# -----------------------------------------------------------------------------
# -vga std gives the Bochs VBE adapter the kernel drives. vgamem_mb is raised
# because a 1280x720 32-bit framebuffer needs more than the 8 MB default.
QEMU_VGA = -device VGA,vgamem_mb=64
# `make run` boots straight into the SUB-OS Graphical Desktop Environment. The
# guest window scales with zoom-to-fit; pass `nogui`/`text` on the kernel
# command line for the emergency TTY. Use `make run-headless` for serial only.
run qemu: $(TARGET)
ifeq ($(ARCH), x86_64)
qemu-system-x86_64 -drive format=raw,file=$(IMAGE) -m 512M \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
-device e1000,netdev=net0 $(QEMU_VGA) -display gtk,zoom-to-fit=on -serial stdio
else ifneq ($(filter $(ARCH), aarch64 arm64),)
qemu-system-aarch64 -M virt -cpu cortex-a57 -m 256M -kernel $(BUILD_DIR)/kernel.elf -device virtio-gpu-pci -serial stdio
else
qemu-system-arm -M virt -cpu cortex-a15 -m 256M -kernel $(BUILD_DIR)/kernel.elf -device virtio-gpu-pci -serial stdio
endif
# Headless: no graphical window, kernel console on the serial line only. Boot
# with `nogui` on the kernel command line to reach the TTY in this mode.
run-headless: $(TARGET)
$(QEMU_CMD)
# Fullscreen without distortion: zoom-to-fit scales the guest while preserving
# its aspect ratio, letterboxing rather than stretching on a wider panel.
run-fullscreen: $(TARGET)
ifeq ($(ARCH), x86_64)
qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
-device e1000,netdev=net0 $(QEMU_VGA) \
-display gtk,zoom-to-fit=on,show-cursor=on -full-screen -serial stdio
else
@echo "run-fullscreen targets x86_64 only"
endif
run-vnc: $(TARGET)
ifeq ($(ARCH), x86_64)
@echo "=== [QEMU VNC Server running at localhost:5900 (VNC Display :0)] ==="
qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
-device e1000,netdev=net0 $(QEMU_VGA) -vnc :0 -serial stdio
else
@echo "=== [QEMU VNC Server running at localhost:5900 (VNC Display :0)] ==="
qemu-system-aarch64 -M virt -cpu cortex-a57 -m 128M -kernel $(BUILD_DIR)/kernel.elf -device virtio-gpu-pci -vnc :0 -serial stdio
endif
run-server: $(IMAGE)
qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
-device e1000,netdev=net0 -serial stdio -display none
debug: $(TARGET)
ifeq ($(ARCH), x86_64)
qemu-system-x86_64 -drive format=raw,file=$(IMAGE) \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80 \
-device e1000,netdev=net0 -serial stdio -s -S
else ifneq ($(filter $(ARCH), aarch64 arm64),)
qemu-system-aarch64 -M virt -cpu cortex-a57 -m 128M -nographic -kernel $(BUILD_DIR)/kernel.elf -s -S
else
qemu-system-arm -M virt -cpu cortex-a15 -m 128M -nographic -kernel $(BUILD_DIR)/kernel.elf -s -S
endif
info:
@echo "Target Architecture: $(ARCH)"
@echo "Cross Compiler: $(CROSS_COMPILE)"
@echo "Compiled Sources: $(words $(ALL_C_SRCS)) C files"
@echo "Target Binary: $(TARGET)"
help:
@echo "SUB-OS Kernel Build System (Linux-Style Kconfig)"
@echo "=================================================="
@echo " make configure - Launch interactive Linux-style TUI Configurator"
@echo " make menuconfig - Alias for make configure"
@echo " make defconfig - Reset to default configuration for active ARCH"
@echo " make x86_64_defconfig - Load x86_64 default configuration"
@echo " make aarch64_defconfig- Load AArch64 default configuration"
@echo " make armv8i_defconfig - Load ARMv8i (32-bit ARM) default configuration"
@echo " make all [ARCH=...] - Compile kernel image for selected architecture"
@echo " make run [ARCH=...] - Boot into the SUB-OS Graphical Desktop in QEMU"
@echo " make run-headless - Boot in QEMU with serial console only (no window)"
@echo " make run-fullscreen - Boot the desktop in a full-screen QEMU window"
@echo " make run-vnc - Boot the desktop and expose it over VNC :0"
@echo " make clean - Remove all compiled objects and disk images"
@echo " make info - Display build configuration and source metrics"
clean:
rm -rf $(BUILD_DIR) $(IMAGE)
-include $(DEPS)