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
This commit is contained in:
subhobhai943andClaude Opus 5 committed 2026-08-25 01:37:20 +05:30
1 parent 280d7fa2bb
commit ba06264d12
15 files changed
+1711 -153

No files matched your search

+79 -15
View File
@@ -83,30 +83,54 @@ start2:
.mmap_store_done:
; -------------------------------------------------------------
; Step 3: Load Kernel from Disk to 0x10000 (Conventional RAM)
; 12 chunks x 64 sectors = 768 sectors (384 KB)
; Step 3: Load the kernel to 0x100000 (just above 1 MB)
;
; INT 13h can only land data below 1 MB, and staging the whole kernel in
; conventional RAM caps it at roughly 576 KB before the load runs into
; video memory at 0xA0000. So each 32 KB chunk is read into one fixed
; low buffer at 0x10000 and copied straight up to its final address.
;
; The copy needs 32-bit offsets from real mode, which is what unreal mode
; provides: enter protected mode just long enough to load a 4 GB-limit
; descriptor, then drop back. A real-mode segment load afterwards rewrites
; only the base and leaves that limit in the descriptor cache.
; -------------------------------------------------------------
mov si, msg_kernel_load
call print_string_16
mov word [kernel_dap_off], 0x0000
mov word [kernel_dap_seg], 0x1000
mov word [kernel_dap_seg], KERNEL_STAGE_SEG
mov dword [kernel_dap_lba], 16
mov dword [kernel_dap_lba + 4], 0
mov cx, 24 ; 24 chunks * 64 sectors = 1536 sectors (768 KB)
mov dword [kernel_dest], 0x100000
mov cx, KERNEL_CHUNKS
.kernel_chunk_loop:
push cx
; Read one chunk into the staging buffer.
mov ah, 0x42
mov dl, [boot_drive]
mov si, kernel_dap
int 0x13
jc .kernel_disk_error
; Advance LBA by 64 sectors
add dword [kernel_dap_lba], 64
; Advance Segment by 0x800 (64 sectors * 512 bytes = 32768 bytes = 0x800 paragraphs)
add word [kernel_dap_seg], 0x0800
; Relocate it above 1 MB. Unreal mode is re-established every pass, since
; the BIOS disk call is free to reload the segment registers.
call enter_unreal_mode
xor ax, ax
mov ds, ax
mov es, ax
mov esi, KERNEL_STAGE_ADDR
mov edi, [kernel_dest]
mov ecx, (KERNEL_CHUNK_SECTORS * 512) / 4
a32 rep movsd
; Advance the source LBA and the destination address by one chunk.
add dword [kernel_dap_lba], KERNEL_CHUNK_SECTORS
add dword [kernel_dest], KERNEL_CHUNK_SECTORS * 512
pop cx
loop .kernel_chunk_loop
@@ -196,11 +220,7 @@ pm_entry:
mov ss, ax
mov esp, 0x7C00
; Copy kernel from 0x10000 to 0x100000 (1MB mark) - 192 KB (384 sectors)
mov esi, 0x10000
mov edi, 0x100000
mov ecx, (1536 * 512) / 4
rep movsd
; The kernel was relocated to 0x100000 chunk by chunk during the load.
; Enable PAE in CR4
mov eax, cr4
@@ -247,6 +267,50 @@ lm_entry:
; Data Section
; -----------------------------------------------------------------
BITS 16
; Kernel image layout. The image is a 2880-sector floppy and the kernel starts
; at LBA 16, so 44 chunks reads every remaining sector: a 1.375 MB ceiling that
; the loader can grow into without touching this file again.
KERNEL_CHUNK_SECTORS equ 64
KERNEL_CHUNKS equ 44
KERNEL_STAGE_SEG equ 0x1000
KERNEL_STAGE_ADDR equ 0x10000
; -----------------------------------------------------------------------------
; enter_unreal_mode
;
; Leaves the CPU in real mode with DS and ES carrying a 4 GB segment limit, so
; `a32 rep movsd` can reach memory above 1 MB. Clobbers EAX and BX.
; -----------------------------------------------------------------------------
enter_unreal_mode:
cli
push ds
push es
lgdt [gdt_descriptor]
mov eax, cr0
or al, 1
mov cr0, eax
jmp $+2 ; Flush the prefetch queue
mov bx, 0x10 ; Flat 32-bit data descriptor
mov ds, bx
mov es, bx
and al, 0xFE
mov cr0, eax
jmp $+2
; Popping restores the real-mode segment bases; the 4 GB limits loaded
; above stay in the descriptor cache, which is what makes this "unreal".
pop es
pop ds
sti
ret
align 4
kernel_dest: dd 0x100000
align 4
boot_drive: db 0
@@ -261,11 +325,11 @@ align 4
kernel_dap:
db 0x10
db 0
dw 64 ; 64 sectors per chunk (32 KB)
dw KERNEL_CHUNK_SECTORS ; Sectors per chunk (32 KB)
kernel_dap_off:
dw 0x0000
kernel_dap_seg:
dw 0x1000
dw KERNEL_STAGE_SEG
kernel_dap_lba:
dq 16