7 Commits
Author SHA1 Message Date
subhobhai943andClaude Opus 4.8 0a874341c6 feat(sched): Preemptive Weave scheduling driven by the timer tick
Kernel threads were only ever switched cooperatively (sched_yield, task_exit);
a thread that never yielded held the CPU forever. The Weave scheduler now
preempts from the PIT tick, so CPU-bound threads are time-sliced.

Deferred switch, not mid-handler:
- sched_tick() no longer switches. It charges the running quantum and, when it
  is spent, only raises weave_need_resched. The switch happens at IRQ return in
  the new sched_preempt_on_return(), called from the x86_64 dispatcher after
  the PIC EOI. That keeps the tick path re-entrancy-safe (the EOI is already
  out, so the PIC keeps delivering) and lets the preempted thread unwind
  through the normal iretq on its own kernel stack.

Interrupt-atomic scheduler state:
- The plain weave_lock is not IRQ-safe, so a tick landing mid-mutation would
  deadlock. weave_lock sections now mask interrupts (arch_irq_save/restore,
  added to arch.h for all three arches) so the timer can never fire inside
  them, and weave_rotate holds them masked across the pick and the register
  swap so the switch is one indivisible step.

Preemption-disable count:
- sched_preempt_disable/enable gate the tick and the return-path switch so a
  preemptive switch cannot land while a plain (non-IRQ-safe) spinlock is held.
  printk wraps its critical section with it, closing the most common hazard;
  other locks are a documented follow-up.

Fresh threads opt into interrupts at entry so they are preemptible from birth
without changing the cooperative early-boot self-test, which still runs with
interrupts masked and is unchanged (20 switches, 3/3 workers).

Verified on x86_64 under QEMU: a new boot-time self-test arms preemption and
two threads that NEVER yield are forced to share the CPU -- 20 involuntary
switches, both advance and are woven down to the CPU-bound lanes -- then boot
proceeds cleanly to ring-3 /sbin/init and the shell with zero faults. aarch64
and armv8i still build; preemption is wired into the x86_64 IRQ path only, so
those stay cooperative for now (their switch asm already exists).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_0161Jh7uCEMLYEMNPsjC1LKf
2026-08-25 10:05:29 +00:00
subhobhai943andClaude Opus 5 ecdc5be435 feat(exec): Run userland in ring 3 with an ELF64 loader and per-process page tables
SUB-OS had a syscall table and user GDT descriptors but nothing that could
actually leave ring 0: lazybox and the shell run as kernel code, and
syscall_handler() was never reachable from a user program. This adds the
missing path end to end on x86_64.

Address spaces (arch/x86_64/mm/vmspace.c)
- The boot page tables identity-map the low 4 GB off PML4[0] and leave the
  rest empty, so userland is given its own PML4 slot (512 GiB) rather than
  holes punched in the kernel's 2 MB huge-page map. A process PML4 shares
  entry 0 with the kernel and privately owns entry 1.
- Frames outside the identity map are refused rather than written through an
  unmapped pointer; lifting that needs a recursive map or a higher-half
  direct map, which is noted in the file.

ELF loader (kernel/elf.c)
- Validates ELF64/little-endian/ET_EXEC/machine, bounds-checks every program
  header against both the image and the user address range, and lays PT_LOAD
  segments into an address space. Frames arrive zeroed, so .bss is covered.

Ring transition (arch/x86_64/cpu/usermode.asm)
- user_enter() drops to ring 3 via iretq after scrubbing every GPR, and
  user_return() unwinds back to it when the process exits, so exec looks like
  a call that blocks until the program finishes.

Syscalls
- INT 0x80 installed at DPL 3 with the Linux x86_64 register convention.
  The process gets its own kernel stack for the trap, because TSS.rsp0 must
  not point at the stack user_enter() is standing on.
- Every user pointer is range-checked before use. The process page tables are
  still live during a syscall, so without that check a program could pass a
  kernel address and have write(2) dump kernel text to the console. The new
  /sbin/init asserts both a wild pointer and a kernel pointer are rejected.
- write(2) to stdout/stderr routes through the new console_write(), which
  reuses printk's routing so output reaches serial and the framebuffer
  console rather than only the VGA text buffer.

Faults
- A ring 3 exception now kills that process and reports CR2 instead of
  panicking the machine.

Userland (userland/init/)
- /sbin/init is a real freestanding static executable, linked at the user
  base and embedded in the kernel image by scripts/mkblob.py. It reports its
  pid, verifies .bss was zeroed and .data is writable, checks the two pointer
  rejections, and exits.

aarch64 and armv8i keep building: the loader and exec path are portable, and
those ports get honest vmspace stubs that fail cleanly rather than pretending
to run a program with kernel privileges.

Verified under QEMU: init loads at 0x80004000a0, reports pid 1, .bss zeroed,
.data writable, both bad pointers rejected, and exits 0 back into the shell.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0161Jh7uCEMLYEMNPsjC1LKf
2026-08-25 06:37:20 +00:00
subhobhai943andClaude Opus 5 7dd8f2219a feat(video): Add a framebuffer console so the TTY and nano work after graphics mode
Pressing Esc left the desktop and started the shell, but nothing appeared in the
emulator window -- the session was only visible on the host terminal. Once the
VBE adapter is switched into graphics mode the VGA text buffer at 0xB8000 stops
being scanned out, so every printk after that point reached serial and the
dmesg ring but never the display.

Disabling the DISPI interface does not bring the text console back; QEMU keeps
presenting the last framebuffer surface. So rather than clawing the adapter back
to text mode, the console is now rendered into the framebuffer:

  drivers/video/fbcon.c draws the console with the shared 8x8 font and carries a
  VT100 subset -- SGR colours, cursor addressing, relative moves, erase-display,
  erase-line and save/restore. It presents a fixed 80x25 grid, because that is
  the geometry the userland assumes (nano pads its bars with %-80s, the shell
  wraps at TTY_WIDTH), with the glyph cell scaled to fill the active resolution.

The TTY driver became the console dispatcher: tty_write, tty_clear and
tty_set_cursor route to fbcon while it owns the display. That last one is what
makes nano work -- it positions with tty_set_cursor() rather than escapes, so
its full-screen repaint was landing as an append-only stream.

Also in this change:

  - `video=WIDTHxHEIGHT` on the kernel command line selects the display mode,
    defaulting to 1280x720 so a 16:9 host panel is filled rather than pillarboxed
    when QEMU goes fullscreen. Rejects modes the heap cannot back.
  - `make run-fullscreen`, plus zoom-to-fit on the windowed target, so scaling
    preserves the guest aspect ratio instead of stretching it.
  - QEMU targets raise vgamem_mb, since a 1280x720 32-bit surface exceeds the
    default VGA memory.
  - bochs_vbe_disable()/fb_set_active() let the framebuffer be marked dormant so
    drawing routines no-op after a mode teardown.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01L5yKJT4Wo2cV3rfPrwY5MP
2026-08-25 02:06:55 +05:30
subhobhai943andClaude Opus 5 ba06264d12 feat(boot): Add SUB-OS boot logo and splash, share the console font, and lift the kernel size ceiling
Boot logo (init/bootlogo.c, init/logo_data.c, scripts/mklogo.py):
  The logo appears in two forms because the framebuffer does not exist for the
  first third of boot. An ASCII rendition goes to the text console and serial
  line from the first moments; once the video driver is up at step [5/14] the
  real bitmap takes over as a graphical splash with a progress bar tied to the
  actual boot stages.

  The asset is 4-bit palette indexed, so 160x160 pixels cost 12.5 KB instead of
  100 KB, and carries a transparency mask so the mark composites onto the
  gradient rather than stamping a flat rectangle over it. scripts/mklogo.py
  regenerates init/logo_data.c from the source PNG.

  Boot reaches the desktop in well under a second, so the splash is held for a
  minimum duration and the bar sweeps to 100% during the hold; without that the
  logo would flash past unseen. `splashtime=<ms>` tunes it and `nosplash`,
  `nogui` or `text` skip it.

Bootloader (boot/stage2.asm):
  stage2 staged the whole kernel in conventional RAM before copying it to 1 MB,
  which capped the kernel at roughly 576 KB before the load ran into video
  memory at 0xA0000. The logo pushed it over that line and the kernel stopped
  booting. Each 32 KB chunk now goes into one fixed low buffer and is copied
  straight to its final address, using unreal mode for the 32-bit reach. The
  ceiling is now the image itself, 1.375 MB.

Console font (lib/font8x8.c):
  fb_draw_char was a stub that ignored its character argument and drew a filled
  rectangle, which is why the splash rendered its text as solid blocks. It now
  renders real glyphs, and the table it uses is shared: the framebuffer console,
  the canvas rasterizer and the desktop compositor had three divergent private
  copies, two of which still carried the `// \` line-continuation defect.

printk console sink (kernel/printk.c):
  printk output can be redirected to a callback, so a caller can run a subsystem
  that reports via printk and capture what it printed. Serial and the dmesg ring
  still see everything, which keeps a captured run debuggable.

Shell builtins (userland/lazybox/shell.c):
  The builtin chain is extracted into shell_execute_builtin(), so the kernel TTY
  and the desktop terminal accept the same vocabulary instead of the GUI seeing
  only the applet table. Callers that cannot be suspended pass allow_blocking =
  false to refuse the animating ones.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01L5yKJT4Wo2cV3rfPrwY5MP
2026-08-25 01:37:20 +05:30
subhobhai943 7b646c8cc4 feat(arch): Add armv8i (32-bit ARM/AArch32) architecture & complete TUI configurator
- Support ARMv8i / ARM32 (AArch32) baremetal target with arm-linux-gnueabihf toolchain
- Implement ARMv8i boot entry point, banked exception mode stacks (IRQ, ABT, UND, SVC)
- Implement 8 exception vectors, GICv2 distributor/CPU interface, CP15 Generic Timer (100 Hz), flat Section MMU
- Support PL011 UART for early console & interactive LazyBox TTY input
- Update Makefile with make armv8i_defconfig and automated cross-compilation
- Update scripts/kconfig/menuconfig.py with dedicated armv8i options
2026-08-18 11:13:23 +05:30
subhobhai943 70d5925b16 feat(arch): Add AArch64 / ARMv8i CPU support, cross-compilation & Linux Kconfig TUI
- Implement AArch64 HAL, vector table (16 aligned vectors), GICv2, Generic Timer, Stage-1 MMU (39-bit VA), PL011 UART
- Build unified multi-architecture abstraction layer in include/arch/arch.h
- Create interactive Linux-like Kconfig TUI menuconfig in scripts/kconfig/menuconfig.py
- Support make menuconfig, defconfig, x86_64_defconfig, aarch64_defconfig
- Update master Makefile for cross-compiling x86_64 (x86_64-elf-) and AArch64 (aarch64-linux-gnu-)
- Dynamic architecture awareness across printk, uname, neofetch, and LazyBox
2026-08-18 10:50:58 +05:30
subhobhai943 cd9c786d14 Initial commit: SUB-OS 64-Bit Production Monolithic Operating System with LazyBox userland suite 2026-08-16 21:23:52 +05:30